Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions proxy/http/HttpTransact.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6757,11 +6757,10 @@ HttpTransact::handle_response_keep_alive_headers(State *s, HTTPVersion ver, HTTP
// to the client to keep the connection alive.
// Insert a Transfer-Encoding header in the response if necessary.

// check that the client is HTTP 1.1 and the conf allows chunking or the client
// protocol unchunks before returning to the user agent (i.e. is http/2)
if (s->client_info.http_version == HTTPVersion(1, 1) &&
(s->txn_conf->chunking_enabled == 1 ||
(s->state_machine->plugin_tag && (!strncmp(s->state_machine->plugin_tag, "http/2", 6)))) &&
// check that the client protocol is HTTP/1.1 and the conf allows chunking or
// the client protocol doesn't support chunked transfer coding (i.e. HTTP/1.0, HTTP/2, and HTTP/3)
if (s->state_machine->ua_txn && s->state_machine->ua_txn->is_chunked_encoding_supported() &&
s->client_info.http_version == HTTPVersion(1, 1) && s->txn_conf->chunking_enabled == 1 &&
// if we're not sending a body, don't set a chunked header regardless of server response
!is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) &&
// we do not need chunked encoding for internal error messages
Expand Down Expand Up @@ -6794,10 +6793,12 @@ HttpTransact::handle_response_keep_alive_headers(State *s, HTTPVersion ver, HTTP

// Close the connection if client_info is not keep-alive.
// Otherwise, if we cannot trust the content length, we will close the connection
// unless we are going to use chunked encoding or the client issued
// a PUSH request
// unless we are going to use chunked encoding on HTTP/1.1 or the client issued a PUSH request
if (s->client_info.keep_alive != HTTP_KEEPALIVE) {
ka_action = KA_DISABLED;
} else if (s->state_machine->client_protocol && (IP_PROTO_TAG_HTTP_3.compare(s->state_machine->client_protocol) == 0 ||
strncmp(s->state_machine->client_protocol, "http/2", 6) == 0)) {
ka_action = KA_CONNECTION;
} else if (s->hdr_info.trust_response_cl == false &&
!(s->client_info.receive_chunked_response == true ||
(s->method == HTTP_WKSIDX_PUSH && s->client_info.keep_alive == HTTP_KEEPALIVE))) {
Expand Down
93 changes: 5 additions & 88 deletions proxy/http2/Http2Stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ void
Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len, bool call_update)
{
if (!this->is_client_state_writeable() || closed || _proxy_ssn == nullptr || write_vio.mutex == nullptr ||
(buf_reader == nullptr && write_len == 0)) {
(buf_reader == nullptr && write_len == 0) || this->response_reader == nullptr) {
return;
}

Expand All @@ -589,22 +589,7 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,

SCOPED_MUTEX_LOCK(lock, write_vio.mutex, this_ethread());

// if response is chunked, limit the dechunked_buffer size.
bool is_done = false;
if (this->chunked) {
if (chunked_handler.dechunked_buffer && chunked_handler.dechunked_buffer->max_read_avail() > HTTP2_MAX_BUFFER_USAGE) {
if (buffer_full_write_event == nullptr) {
buffer_full_write_event = _thread->schedule_imm(this, VC_EVENT_WRITE_READY);
}
} else {
this->response_process_data(is_done);
}
}

if (this->response_get_data_reader() == nullptr) {
return;
}
int64_t bytes_avail = this->response_get_data_reader()->read_avail();
int64_t bytes_avail = this->response_reader->read_avail();
if (write_vio.nbytes > 0 && write_vio.ntodo() > 0) {
int64_t num_to_write = write_vio.ntodo();
if (num_to_write > write_len) {
Expand All @@ -619,7 +604,7 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,
", reader.read_avail=%" PRId64,
write_vio.nbytes, write_vio.ndone, write_vio.get_writer()->write_avail(), bytes_avail);

if (bytes_avail <= 0 && !is_done) {
if (bytes_avail <= 0) {
return;
}

Expand Down Expand Up @@ -654,17 +639,7 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,
h2_proxy_ssn->connection_state.send_headers_frame(this);
}

// See if the response is chunked. Set up the dechunking logic if it is
// Make sure to check if the chunk is complete and signal appropriately
this->response_initialize_data_handling(is_done);

// If there is additional data, send it along in a data frame. Or if this was header only
// make sure to send the end of stream
is_done |= (write_vio.ntodo() + this->response_header.length_get()) == bytes_avail;
if (this->response_is_data_available() || is_done) {
if (is_done) {
this->mark_body_done();
}
if (this->response_reader->is_read_avail_more_than(0)) {
this->_milestones.mark(Http2StreamMilestone::START_TX_DATA_FRAMES);
this->send_response_body(call_update);
}
Expand All @@ -677,9 +652,6 @@ Http2Stream::update_write_request(IOBufferReader *buf_reader, int64_t write_len,
break;
}
} else {
if (write_vio.ntodo() == bytes_avail || is_done) {
this->mark_body_done();
}
this->_milestones.mark(Http2StreamMilestone::START_TX_DATA_FRAMES);
this->send_response_body(call_update);
}
Expand Down Expand Up @@ -820,7 +792,6 @@ Http2Stream::destroy()
if (header_blocks) {
ats_free(header_blocks);
}
chunked_handler.clear();
clear_timers();
clear_io_events();
http_parser_clear(&http_parser);
Expand All @@ -829,53 +800,10 @@ Http2Stream::destroy()
THREAD_FREE(this, http2StreamAllocator, this_ethread());
}

void
Http2Stream::response_initialize_data_handling(bool &is_done)
{
is_done = false;
int chunked_index = response_header.value_get_index(TS_MIME_FIELD_TRANSFER_ENCODING, TS_MIME_LEN_TRANSFER_ENCODING,
TS_HTTP_VALUE_CHUNKED, TS_HTTP_LEN_CHUNKED);
// -1 means this value was not found for this field
if (chunked_index >= 0) {
Http2StreamDebug("Response is chunked");
chunked = true;
this->chunked_handler.init_by_action(this->response_reader, ChunkedHandler::ACTION_DECHUNK);
this->chunked_handler.state = ChunkedHandler::CHUNK_READ_SIZE;
this->chunked_handler.dechunked_reader = this->chunked_handler.dechunked_buffer->alloc_reader();
this->response_reader->dealloc();
this->response_reader = nullptr;
// Get things going if there is already data waiting
if (this->chunked_handler.chunked_reader->is_read_avail_more_than(0)) {
response_process_data(is_done);
}
}
}

void
Http2Stream::response_process_data(bool &done)
{
done = false;
if (chunked) {
do {
if (chunked_handler.state == ChunkedHandler::CHUNK_FLOW_CONTROL) {
chunked_handler.state = ChunkedHandler::CHUNK_READ_SIZE_START;
}
done = this->chunked_handler.process_chunked_content();
} while (chunked_handler.state == ChunkedHandler::CHUNK_FLOW_CONTROL);
}
}

bool
Http2Stream::response_is_data_available() const
{
IOBufferReader *reader = this->response_get_data_reader();
return reader ? reader->is_read_avail_more_than(0) : false;
}

IOBufferReader *
Http2Stream::response_get_data_reader() const
{
return (chunked) ? chunked_handler.dechunked_reader : response_reader;
return this->response_reader;
}

void
Expand Down Expand Up @@ -1053,17 +981,6 @@ Http2Stream::_switch_thread_if_not_on_right_thread(int event, void *edata)
return true;
}

void
Http2Stream::mark_body_done()
{
body_done = true;
if (response_is_chunked()) {
ink_assert(chunked_handler.state == ChunkedHandler::CHUNK_READ_DONE ||
chunked_handler.state == ChunkedHandler::CHUNK_READ_ERROR);
this->write_vio.nbytes = response_header.length_get() + chunked_handler.dechunked_size;
}
}

int
Http2Stream::get_transaction_priority_weight() const
{
Expand Down
18 changes: 2 additions & 16 deletions proxy/http2/Http2Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
#pragma once

#include "HTTP2.h"
#include "../ProxyTransaction.h"
#include "ProxyTransaction.h"
#include "Http2DebugNames.h"
#include "../http/HttpTunnel.h" // To get ChunkedHandler
#include "Http2DependencyTree.h"
#include "tscore/History.h"
#include "Milestones.h"
Expand Down Expand Up @@ -107,15 +106,13 @@ class Http2Stream : public ProxyTransaction

bool is_client_state_writeable() const;
bool is_closed() const;
bool response_is_chunked() const;
IOBufferReader *response_get_data_reader() const;

void mark_milestone(Http2StreamMilestone type);

void increment_data_length(uint64_t length);
bool payload_length_is_valid() const;
bool is_body_done() const;
void mark_body_done();
void update_sent_count(unsigned num_bytes);
Http2StreamId get_id() const;
Http2StreamState get_state() const;
Expand Down Expand Up @@ -146,8 +143,6 @@ class Http2Stream : public ProxyTransaction
Http2DependencyTree::Node *priority_node = nullptr;

private:
void response_initialize_data_handling(bool &is_done);
void response_process_data(bool &is_done);
bool response_is_data_available() const;
Event *send_tracked_event(Event *event, int send_event, VIO *vio);
void send_response_body(bool call_update);
Expand All @@ -173,8 +168,6 @@ class Http2Stream : public ProxyTransaction
Milestones<Http2StreamMilestone, static_cast<size_t>(Http2StreamMilestone::LAST_ENTRY)> _milestones;

bool trailing_header = false;
bool body_done = false;
bool chunked = false;

// A brief discussion of similar flags and state variables: _state, closed, terminate_stream
//
Expand Down Expand Up @@ -208,7 +201,6 @@ class Http2Stream : public ProxyTransaction
std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
int _recent_rwnd_increment_index = 0;

ChunkedHandler chunked_handler;
Event *cross_thread_event = nullptr;
Event *buffer_full_write_event = nullptr;

Expand Down Expand Up @@ -240,7 +232,7 @@ Http2Stream::mark_milestone(Http2StreamMilestone type)
inline bool
Http2Stream::is_body_done() const
{
return body_done;
return this->write_vio.ntodo() == 0;
}

inline void
Expand Down Expand Up @@ -300,12 +292,6 @@ Http2Stream::payload_length_is_valid() const
return content_length == 0 || content_length == data_length;
}

inline bool
Http2Stream::response_is_chunked() const
{
return chunked;
}

inline bool
Http2Stream::allow_half_open() const
{
Expand Down