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