void windows_request_context::transmit_body()

in Release/src/http/listener/http_server_httpsys.cpp [1014:1178]


void windows_request_context::transmit_body()
{
    if (!m_sending_in_chunks && !m_transfer_encoding)
    {
        // We are done sending data.
        std::lock_guard<std::mutex> lock(m_responseCompletedLock);
        m_response_completed.set();
        return;
    }

    msl::safeint3::SafeInt<size_t> safeCount = m_remaining_to_write;
    size_t next_chunk_size = safeCount.Min(CHUNK_SIZE);

    // In both cases here we could perform optimizations to try and use acquire on the streams to avoid an extra copy.
    if (m_sending_in_chunks)
    {
        m_body_data.resize(CHUNK_SIZE);

        streams::rawptr_buffer<unsigned char> buf(&m_body_data[0], next_chunk_size);

        m_response.body().read(buf, next_chunk_size).then([this](pplx::task<size_t> op) {
            size_t bytes_read = 0;

            // If an exception occurs surface the error to user on the server side
            // and cancel the request so the client sees the error.
            try
            {
                bytes_read = op.get();
            }
            catch (...)
            {
                cancel_request(std::current_exception());
                return;
            }
            if (bytes_read == 0)
            {
                cancel_request(std::make_exception_ptr(
                    http_exception(_XPLATSTR("Error unexpectedly encountered the end of the response stream early"))));
                return;
            }

            // Check whether this is the last one to send...
            m_remaining_to_write = m_remaining_to_write - bytes_read;
            m_sending_in_chunks = (m_remaining_to_write > 0);

            send_entity_body(&m_body_data[0], bytes_read);
        });
    }
    else
    {
        // We're transfer-encoding...
        if (m_compressor)
        {
            // ...and compressing.  For simplicity, we allocate a buffer that's "too large to fail" while compressing.
            const size_t body_data_length = 2 * CHUNK_SIZE + http::details::chunked_encoding::additional_encoding_space;
            m_body_data.resize(body_data_length);

            // We'll read into a temporary buffer before compressing
            if (m_compress_buffer.capacity() < next_chunk_size)
            {
                m_compress_buffer.reserve(next_chunk_size);
            }

            streams::rawptr_buffer<unsigned char> buf(m_compress_buffer.data(), next_chunk_size);

            m_response.body().read(buf, next_chunk_size).then([this, body_data_length](pplx::task<size_t> op) {
                size_t bytes_read = 0;

                // If an exception occurs surface the error to user on the server side
                // and cancel the request so the client sees the error.
                try
                {
                    bytes_read = op.get();
                }
                catch (...)
                {
                    cancel_request(std::current_exception());
                    return;
                }
                _ASSERTE(bytes_read >= 0);

                // Compress this chunk; if we read no data, allow the compressor to finalize its stream
                http::compression::operation_hint hint = http::compression::operation_hint::has_more;
                if (!bytes_read)
                {
                    hint = http::compression::operation_hint::is_last;
                }
                m_compressor
                    ->compress(m_compress_buffer.data(),
                               bytes_read,
                               &m_body_data[http::details::chunked_encoding::data_offset],
                               body_data_length,
                               hint)
                    .then([this, bytes_read, body_data_length](pplx::task<http::compression::operation_result> op) {
                        http::compression::operation_result r;

                        try
                        {
                            r = op.get();
                        }
                        catch (...)
                        {
                            cancel_request(std::current_exception());
                            return;
                        }

                        if (r.input_bytes_processed != bytes_read ||
                            r.output_bytes_produced ==
                                body_data_length - http::details::chunked_encoding::additional_encoding_space ||
                            r.done != !bytes_read)
                        {
                            // We chose our parameters so that compression should
                            // never overflow body_data_length; fail if it does
                            cancel_request(std::make_exception_ptr(
                                std::exception("Compressed data exceeds internal buffer size.")));
                            return;
                        }

                        // Check whether this is the last one to send; note that this is a
                        // few lines of near-duplicate code with the non-compression path
                        _ASSERTE(bytes_read <= m_remaining_to_write);
                        m_remaining_to_write -= bytes_read;
                        m_transfer_encoding = (r.output_bytes_produced > 0);
                        size_t offset = http::details::chunked_encoding::add_chunked_delimiters(
                            &m_body_data[0], body_data_length, r.output_bytes_produced);
                        send_entity_body(&m_body_data[offset],
                                         r.output_bytes_produced +
                                             http::details::chunked_encoding::additional_encoding_space - offset);
                    });
            });
        }
        else
        {
            const size_t body_data_length = CHUNK_SIZE + http::details::chunked_encoding::additional_encoding_space;
            m_body_data.resize(body_data_length);

            streams::rawptr_buffer<unsigned char> buf(&m_body_data[http::details::chunked_encoding::data_offset],
                                                      body_data_length);

            m_response.body().read(buf, next_chunk_size).then([this, body_data_length](pplx::task<size_t> op) {
                size_t bytes_read = 0;

                // If an exception occurs surface the error to user on the server side
                // and cancel the request so the client sees the error.
                try
                {
                    bytes_read = op.get();
                }
                catch (...)
                {
                    cancel_request(std::current_exception());
                    return;
                }

                // Check whether this is the last one to send...
                m_transfer_encoding = (bytes_read > 0);
                size_t offset = http::details::chunked_encoding::add_chunked_delimiters(
                    &m_body_data[0], body_data_length, bytes_read);

                auto data_length = bytes_read + (http::details::chunked_encoding::additional_encoding_space - offset);
                send_entity_body(&m_body_data[offset], data_length);
            });
        }
    }
}