in src/uhttp.c [500:791]
static void on_bytes_received(void* context, const unsigned char* buffer, size_t len)
{
HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
if (http_data != NULL && buffer != NULL && len > 0 && http_data->recv_msg.recv_state != state_error)
{
if (http_data->recv_msg.recv_state == state_parse_complete)
{
// The callback is getting called during a new send.
setup_init_recv_msg(&http_data->recv_msg);
}
if (http_data->recv_msg.recv_state == state_initial || http_data->recv_msg.recv_state == state_open)
{
if (initialize_received_data(http_data) != 0)
{
http_data->recv_msg.recv_state = state_error;
}
else
{
http_data->recv_msg.recv_state = state_process_status_line;
}
}
// Put the data in the buffer
if (BUFFER_append_build(http_data->recv_msg.accrual_buff, buffer, len) != 0)
{
/* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
LogError("Failure appending bytes to buffer.");
http_data->recv_msg.recv_state = state_error;
}
if (http_data->recv_msg.recv_state == state_process_status_line)
{
size_t index = 0;
const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff);
size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
int lineComplete = process_status_code_line(stored_bytes, stored_len, &index, &http_data->recv_msg.status_code);
if (lineComplete == 0 && http_data->recv_msg.status_code > 0)
{
if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0)
{
LogError("Failure appending bytes to buffer.");
http_data->recv_msg.recv_state = state_error;
}
else
{
http_data->recv_msg.recv_state = state_process_headers;
}
}
}
if (http_data->recv_msg.recv_state == state_process_headers)
{
size_t index = 0;
const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff);
size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
int headerComplete = process_header_line(stored_bytes, stored_len, &index, http_data->recv_msg.resp_header, &http_data->recv_msg.total_body_len, &http_data->recv_msg.chunked_reply);
if (headerComplete == 0)
{
if (http_data->recv_msg.total_body_len == 0)
{
if (http_data->recv_msg.chunked_reply)
{
/* Codes_SRS_UHTTP_07_054: [ If the http header does not include a content length then it indicates a chunk response. ] */
http_data->recv_msg.recv_state = state_process_chunked_body;
}
else
{
// Content len is 0 so we are finished with the body
http_data->recv_msg.recv_state = state_send_user_callback;
}
}
else
{
http_data->recv_msg.recv_state = state_process_body;
}
}
if (index > 0)
{
if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0)
{
LogError("Failure appending bytes to buffer.");
http_data->recv_msg.recv_state = state_error;
}
}
}
if (http_data->recv_msg.recv_state == state_process_body)
{
if (http_data->recv_msg.total_body_len != 0)
{
size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
if ((http_data->recv_msg.total_body_len == stored_len) || (http_data->recv_msg.total_body_len == (stored_len - HTTP_END_TOKEN_LEN)))
{
if (http_data->recv_msg.msg_body != NULL)
{
BUFFER_delete(http_data->recv_msg.msg_body);
}
if ((http_data->recv_msg.msg_body = BUFFER_clone(http_data->recv_msg.accrual_buff)) == NULL)
{
LogError("Failure cloning BUFFER.");
http_data->recv_msg.recv_state = state_error;
}
else
{
http_data->recv_msg.recv_state = state_send_user_callback;
}
}
else if (stored_len > http_data->recv_msg.total_body_len)
{
LogError("Failure bytes encountered is greater then body length.");
http_data->recv_msg.recv_state = state_error;
}
}
}
if (http_data->recv_msg.recv_state == state_process_chunked_body)
{
const unsigned char* iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff);
const unsigned char* initial_pos = iterator;
const unsigned char* begin = iterator;
const unsigned char* end = iterator;
size_t accural_len = BUFFER_length(http_data->recv_msg.accrual_buff);
/* Codes_SRS_UHTTP_07_059: [ on_bytes_received shall loop throught the stored data to find the /r/n separator. ] */
while (iterator < (initial_pos + accural_len))
{
if (*iterator == '\r')
{
// Don't need anything
end = iterator;
iterator++;
}
else if (*iterator == '\n')
{
size_t data_length = 0;
/* Codes_SRS_UHTTP_07_055: [ on_bytes_received shall convert the hexs length supplied in the response to the data length of the chunked data. ] */
size_t hex_len = end - begin;
data_length = convert_char_to_hex(begin, hex_len);
if (data_length == 0)
{
if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)
{
http_data->recv_msg.recv_state = state_send_user_callback;
}
else
{
// Need to continue parsing
http_data->recv_msg.recv_state = state_process_headers;
}
break;
}
else if ((data_length + HTTP_CRLF_LEN) < accural_len - (iterator - initial_pos))
{
/* Codes_SRS_UHTTP_07_056: [ After the response chunk is parsed it shall be placed in a BUFFER_HANDLE. ] */
iterator += 1;
if (BUFFER_append_build(http_data->recv_msg.msg_body, iterator, data_length) != 0)
{
/* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the stop processing the request. ] */
LogError("Failure building buffer for chunked data.");
http_data->recv_msg.recv_state = state_error;
}
else
{
/* Codes_SRS_UHTTP_07_060: [ if the data_length specified in the chunk is beyond the amount of data recieved, the parsing shall end and wait for more data. ] */
if (iterator + (data_length + HTTP_CRLF_LEN) > initial_pos + accural_len)
{
LogError("Invalid length specified.");
http_data->recv_msg.recv_state = state_error;
break;
}
else if (iterator + (data_length + HTTP_CRLF_LEN) == initial_pos + accural_len)
{
if (BUFFER_shrink(http_data->recv_msg.accrual_buff, accural_len, false) != 0)
{
LogError("Failure shrinking accural buffer.");
http_data->recv_msg.recv_state = state_error;
}
break;
}
else
{
// Move the iterator beyond the data we read and the /r/n
iterator += (data_length + HTTP_CRLF_LEN);
}
/* Codes_SRS_UHTTP_07_058: [ Once a chunk size value of 0 is encountered on_bytes_received shall call the on_request_callback with the http message ] */
if (*iterator == '0' && (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN))
{
if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)
{
http_data->recv_msg.recv_state = state_send_user_callback;
}
else
{
// Need to continue parsing
http_data->recv_msg.recv_state = state_process_headers;
}
break;
}
else
{
size_t shrink_len = iterator - initial_pos;
if (shrink_len > 0)
{
if (BUFFER_shrink(http_data->recv_msg.accrual_buff, shrink_len, false) != 0)
{
LogError("Failure shrinking accrual buffer.");
http_data->recv_msg.recv_state = state_error;
}
else
{
accural_len = BUFFER_length(http_data->recv_msg.accrual_buff);
initial_pos = iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff);
}
}
}
}
begin = end = iterator;
}
else
{
break;
}
}
else
{
end = iterator;
iterator++;
}
}
}
if (http_data->recv_msg.recv_state == state_send_user_callback || http_data->recv_msg.recv_state == state_error)
{
const unsigned char* reply_data = NULL;
size_t reply_len = 0;
HTTP_CALLBACK_REASON http_reason = HTTP_CALLBACK_REASON_OK;
if (http_data->recv_msg.msg_body != NULL)
{
reply_data = BUFFER_u_char(http_data->recv_msg.msg_body);
reply_len = BUFFER_length(http_data->recv_msg.msg_body);
}
if (http_data->recv_msg.recv_state == state_error)
{
http_reason = HTTP_CALLBACK_REASON_PARSING_ERROR;
}
if (http_data->trace_on)
{
LOG(AZ_LOG_TRACE, LOG_LINE, "\r\nHTTP Status: %d\r\n", http_data->recv_msg.status_code);
// Loop through headers
size_t count;
if (HTTPHeaders_GetHeaderCount(http_data->recv_msg.resp_header, &count) == 0)
{
for (size_t index = 0; index < count; index++)
{
char* header;
if (HTTPHeaders_GetHeader(http_data->recv_msg.resp_header, index, &header) == HTTP_HEADERS_OK)
{
LOG(AZ_LOG_TRACE, LOG_LINE, "%s", header);
free(header);
}
}
}
if (http_data->trace_body && reply_len > 0)
{
LOG(AZ_LOG_TRACE, LOG_LINE, "\r\n%.*s\r\n", (int)reply_len, reply_data);
}
}
http_data->recv_msg.on_request_callback(http_data->recv_msg.user_ctx, http_reason, reply_data, reply_len, http_data->recv_msg.status_code, http_data->recv_msg.resp_header);
http_data->recv_msg.recv_state = state_parse_complete;
}
if (http_data->recv_msg.recv_state == state_parse_complete)
{
HTTPHeaders_Free(http_data->recv_msg.resp_header);
http_data->recv_msg.resp_header = NULL;
BUFFER_delete(http_data->recv_msg.msg_body);
http_data->recv_msg.msg_body = NULL;
BUFFER_delete(http_data->recv_msg.accrual_buff);
http_data->recv_msg.accrual_buff = NULL;
}
}
}