mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Bound the request head by the read timeout as a whole
The read timeout applied to each read of the request line and headers, so a client that sent them a byte now and then held a worker for as long as it liked, and enough such clients left no worker for anyone else. Bound the whole head by the read timeout, as the TLS handshake already is, through a new Stream::set_max_timeout() that the socket streams implement with their existing max timeout. A request body is still bounded per read, and a WebSocket holds its worker for its whole life. The README says so, and points to a reverse proxy and the pre-request handler for untrusted clients.
This commit is contained in:
1 parent
ae169c6253
commit
bc837b87fc
4 files changed
+134
-34
No files matched your search
@@ -1997,6 +1997,10 @@ public:
|
||||
(void)usec;
|
||||
}
|
||||
|
||||
// Bounds the reads from now on to `msec` in total, on top of the timeout of
|
||||
// each one. 0 lifts the bound.
|
||||
virtual void set_max_timeout(time_t msec) { (void)msec; }
|
||||
|
||||
// Bytes already pulled off the socket and sitting in this stream's own
|
||||
// buffer. Exposing them lets a line reader scan for a terminator in one
|
||||
// pass instead of asking for a byte at a time. A stream that does no
|
||||
@@ -6739,6 +6743,7 @@ public:
|
||||
socket_t socket() const override;
|
||||
time_t duration() const override;
|
||||
void set_read_timeout(time_t sec, time_t usec = 0) override;
|
||||
void set_max_timeout(time_t msec) override;
|
||||
const char *buffered_data(size_t &size) const override;
|
||||
void consume_buffered(size_t size) override;
|
||||
|
||||
@@ -6759,7 +6764,7 @@ private:
|
||||
time_t write_timeout_sec_;
|
||||
time_t write_timeout_usec_;
|
||||
time_t max_timeout_msec_;
|
||||
const std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
||||
|
||||
std::vector<char> read_buff_;
|
||||
size_t read_buff_off_ = 0;
|
||||
@@ -10631,6 +10636,7 @@ public:
|
||||
socket_t socket() const override;
|
||||
time_t duration() const override;
|
||||
void set_read_timeout(time_t sec, time_t usec = 0) override;
|
||||
void set_max_timeout(time_t msec) override;
|
||||
|
||||
// See SocketStream::set_readable_hint().
|
||||
void set_readable_hint() { readable_hint_ = true; }
|
||||
@@ -10645,7 +10651,7 @@ private:
|
||||
time_t write_timeout_sec_;
|
||||
time_t write_timeout_usec_;
|
||||
time_t max_timeout_msec_;
|
||||
const std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
||||
bool readable_hint_ = false;
|
||||
};
|
||||
|
||||
@@ -12594,6 +12600,11 @@ inline void SocketStream::set_read_timeout(time_t sec, time_t usec) {
|
||||
read_timeout_usec_ = usec;
|
||||
}
|
||||
|
||||
inline void SocketStream::set_max_timeout(time_t msec) {
|
||||
max_timeout_msec_ = msec;
|
||||
start_time_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
// Buffer stream implementation
|
||||
inline bool BufferStream::is_readable() const { return true; }
|
||||
|
||||
@@ -13126,6 +13137,11 @@ inline void SSLSocketStream::set_read_timeout(time_t sec, time_t usec) {
|
||||
read_timeout_usec_ = usec;
|
||||
}
|
||||
|
||||
inline void SSLSocketStream::set_max_timeout(time_t msec) {
|
||||
max_timeout_msec_ = msec;
|
||||
start_time_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
inline WebSocketSSLStream::WebSocketSSLStream(socket_t sock,
|
||||
tls::session_t session,
|
||||
time_t read_timeout_sec,
|
||||
@@ -14777,6 +14793,10 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
||||
|
||||
detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
|
||||
|
||||
// The read timeout bounds the request line and headers as a whole, so a
|
||||
// client sending them a byte at a time cannot hold the worker any longer.
|
||||
strm.set_max_timeout(read_timeout_sec_ * 1000 + read_timeout_usec_ / 1000);
|
||||
|
||||
// Connection has been closed on client
|
||||
if (!line_reader.getline()) { return false; }
|
||||
|
||||
@@ -14825,6 +14845,7 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
||||
output_error_log(Error::InvalidHeaders, &req);
|
||||
return write_response(strm, close_connection, req, res);
|
||||
}
|
||||
strm.set_max_timeout(0);
|
||||
|
||||
// RFC 9112 §6.3: Reject requests whose framing is invalid or ambiguous,
|
||||
// which would otherwise let an intermediary and this parser disagree on
|
||||
|
||||
Reference in new issue
Block a user