Skip to content

Commit

Permalink
cli: handle keep-alive for http proxy properly
Browse files Browse the repository at this point in the history
this fixes disconnect issues with telegram.

TODO we'd better add an idle timeout for keep-alive connection for
security reasons.
  • Loading branch information
Chilledheart committed May 16, 2024
1 parent 7be8216 commit 61c1ba3
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 1 deletion.
21 changes: 20 additions & 1 deletion src/cli/cli_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,8 @@ asio::error_code CliConnection::OnReadHttpRequest(std::shared_ptr<IOBuf> buf) {
<< " http: " << std::string(reinterpret_cast<const char*>(buf->data()), nparsed);
}

http_is_keep_alive_ = false;

if (ok) {
buf->trimStart(nparsed);
buf->retreat(nparsed);
Expand All @@ -722,7 +724,10 @@ asio::error_code CliConnection::OnReadHttpRequest(std::shared_ptr<IOBuf> buf) {
buf->reserve(header.size(), 0);
buf->prepend(header.size());
memcpy(buf->mutable_data(), header.c_str(), header.size());
VLOG(3) << "Connection (client) " << connection_id() << " Host: " << http_host_ << " PORT: " << http_port_;
http_is_keep_alive_ = absl::AsciiStrToLower(parser.connection()) == "keep-alive";
http_keep_alive_remaining_bytes_ += parser.content_length() + header.size() - buf->length();
VLOG(3) << "Connection (client) " << connection_id() << " Host: " << http_host_ << " PORT: " << http_port_
<< " KEEPALIVE: " << std::boolalpha << http_is_keep_alive_;
} else {
VLOG(3) << "Connection (client) " << connection_id() << " CONNECT: " << http_host_ << " PORT: " << http_port_;
}
Expand Down Expand Up @@ -1614,6 +1619,20 @@ std::shared_ptr<IOBuf> CliConnection::GetNextUpstreamBuf(asio::error_code& ec, s
return nullptr;
}

if (http_is_keep_alive_) {
if (http_keep_alive_remaining_bytes_ < (int64_t)read) {
VLOG(1) << "Connection (client) " << connection_id() << " reused for keep-alive connection";
// currently, we assume the host doesn't change
ec = OnReadHttpRequest(buf);
SetState(state_stream);
if (ec) {
return nullptr;
}
} else {
http_keep_alive_remaining_bytes_ -= read;
}
}

#ifdef HAVE_QUICHE
if (adapter_) {
if (!data_frame_) {
Expand Down
4 changes: 4 additions & 0 deletions src/cli/cli_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ class CliConnection : public RefCountedThreadSafe<CliConnection>,
bool http_is_connect_ = false;
/// copy of connect response
static const std::string_view http_connect_reply_;
/// copy of keep alive state
bool http_is_keep_alive_ = false;
/// copy of remaining bytes in keep alive cycle
int64_t http_keep_alive_remaining_bytes_ = 0;

/// copy of upstream request
std::unique_ptr<ss::request> ss_request_;
Expand Down
31 changes: 31 additions & 0 deletions src/net/http_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ static void ReforgeHttpRequestImpl(std::string* header,
if (key == "Proxy-Connection") {
continue;
}
if (key == "Proxy-Authorization") {
continue;
}
ss << key << ": " << value << "\r\n";
}
if (additional_headers) {
Expand Down Expand Up @@ -265,6 +268,12 @@ void HttpRequestParser::ProcessHeaders(const quiche::BalsaHeaders& headers) {
if (key == "Content-Type") {
content_type_ = std::string(value);
}
if (key == "Connection") {
connection_ = std::string(value);
}
if (key == "Proxy-Connection") {
proxy_connection_ = std::string(value);
}
}
}

Expand Down Expand Up @@ -315,6 +324,13 @@ void HttpRequestParser::OnRequestFirstLineInput(std::string_view /*line_input*/,
error_message_ = "HPE_INVALID_VERSION";
return;
}
if (version_input == "HTTP/1.1") {
connection_ = "Keep-Alive";
proxy_connection_ = "Keep-Alive";
} else {
connection_ = "Close";
proxy_connection_ = "Close";
}
}

void HttpRequestParser::OnResponseFirstLineInput(std::string_view /*line_input*/,
Expand Down Expand Up @@ -436,6 +452,15 @@ int HttpRequestParser::Parse(std::shared_ptr<IOBuf> buf, bool* ok) {
size_t nparsed;
nparsed = http_parser_execute(parser_, &settings_connect, reinterpret_cast<const char*>(buf->data()), buf->length());
*ok = HTTP_PARSER_ERRNO(parser_) == HPE_OK;
if (*ok) {
if (parser_->http_major == 1 && parser_->http_minor == 1) {
connection_ = "Keep-Alive";
proxy_connection_ = "Keep-Alive";
} else {
connection_ = "Close";
proxy_connection_ = "Close";
}
}
return nparsed;
}

Expand Down Expand Up @@ -526,6 +551,12 @@ int HttpRequestParser::OnReadHttpRequestHeaderValue(http_parser* parser, const c
if (self->http_field_ == "Content-Type") {
self->content_type_ = std::string(buf, len);
}
if (self->http_field_ == "Connection") {
self->connection_ = std::string(buf, len);
}
if (self->http_field_ == "Proxy-Connection") {
self->proxy_connection_ = std::string(buf, len);
}
return 0;
}

Expand Down
12 changes: 12 additions & 0 deletions src/net/http_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class HttpRequestParser : public quiche::BalsaVisitorInterface {
bool is_connect() const { return http_is_connect_; }
uint64_t content_length() const { return content_length_; }
const std::string& content_type() const { return content_type_; }
const std::string& connection() const { return connection_; }
const std::string& proxy_connection() const { return proxy_connection_; }

void ReforgeHttpRequest(std::string* header,
const absl::flat_hash_map<std::string, std::string>* additional_headers = nullptr);
Expand Down Expand Up @@ -102,6 +104,10 @@ class HttpRequestParser : public quiche::BalsaVisitorInterface {
uint64_t content_length_ = 0;
/// copy of content type
std::string content_type_;
/// copy of connection
std::string connection_;
/// copy of proxy-connection
std::string proxy_connection_;

bool first_byte_processed_ = false;
bool headers_done_ = false;
Expand Down Expand Up @@ -133,6 +139,8 @@ class HttpRequestParser {
bool is_connect() const { return http_is_connect_; }
uint64_t content_length() const { return content_length_; }
const std::string& content_type() const { return content_type_; }
const std::string& connection() const { return connection_; }
const std::string& proxy_connection() const { return proxy_connection_; }

int status_code() const;

Expand Down Expand Up @@ -166,6 +174,10 @@ class HttpRequestParser {
uint64_t content_length_ = 0;
/// copy of content type
std::string content_type_;
/// copy of connection
std::string connection_;
/// copy of proxy-connection
std::string proxy_connection_;
};

class HttpResponseParser : public HttpRequestParser {
Expand Down

0 comments on commit 61c1ba3

Please sign in to comment.