static void on_bytes_received()

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;
        }
    }
}