Commit Graph

70 Commits

Author SHA1 Message Date
merge-script
4800cb7aea Merge bitcoin/bitcoin#35735: Add state to HTTPRequest
9954aa7728 http: don't parse any new requests from a client if m_req_busy = true (Matthew Zipkin)
c7db3ae1f9 test: cover HTTPRequest state machine (Matthew Zipkin)
90676e24ad Add state to HTTPRequest to avoid duplicate work over I/O cycles (Matthew Zipkin)
507e528e84 http: reuse HTTPHeaders to parse chunked trailer (Matthew Zipkin)
902d8908c9 http: only read one HTTPRequest at a time per client (Matthew Zipkin)

Pull request description:

  This PR reduces the memory consumption of the HTTP Server when reading data from connected clients, and improves performance especially when requests are large (i.e. requiring multiple TCP packets).

  In https://github.com/bitcoin/bitcoin/pull/35182 the server copies as much data as it can from the socket into application memory, and then tries to parse as many complete HTTP requests as possible from that data. If a request is discovered to be incomplete, the in-progress request is abandoned. The server tries again on the next I/O cycle to read the same data from the buffer, duplicating work as many times as it takes before the client finishes sending the request (or times out).

  This PR implements two improvements to this:
  1. Only parse one request at a time from the receive buffer. The server processes requests from each client in series anyway.
  2. Add state to `HTTPRequest` so it can be filled with data from the receive buffer over multiple I/O loop iterations without losing progress.

  If a client sends large or multiple requests, that data will sit in the kernel's socket buffer instead of the application memory. Eventually the socket buffer will fill up and TCP backpressure will kick in, dropping the TCP window to 0 and blocking the client from sending any more.

  A state machine for `HTTPRemoteClient` was [discussed previously](https://github.com/bitcoin/bitcoin/pull/35182#pullrequestreview-4322490068) to control resource consumption. Another nice benefit of this model (for a follow-up PR) will be to insert the RPC authentication check after reading 8kB-limited headers but before the 32MB-limited request body.

ACKs for top commit:
  winterrdog:
    re-ACK 9954aa7728
  janb84:
    re ACK 9954aa7728
  frankomosh:
    ACK 9954aa7728.
  fjahr:
    ACK 9954aa7728

Tree-SHA512: b7c913114283fbf1f360b40f6c65a01390a26731bf3b166f460ec260f9206f25d738b3a06887bfa839911c1c6aaf634448181da47a752a9a881aebd907e44868
2026-08-17 10:19:34 +01:00
Matthew Zipkin
90676e24ad Add state to HTTPRequest to avoid duplicate work over I/O cycles 2026-08-10 10:52:37 -04:00
Matthew Zipkin
507e528e84 http: reuse HTTPHeaders to parse chunked trailer
Chunked transfer trailers are just headers that are included
at the end of the request. We can parse and validate them with code
we already use to read headers. In a future commit we will also
be able to use one MAX_HEADERS_SIZE limit to cover both sections.

Even though we parse and validate, we ignore these data.
2026-08-10 10:14:58 -04:00
Matthew Zipkin
902d8908c9 http: only read one HTTPRequest at a time per client 2026-08-10 10:14:58 -04:00
MarcoFalke
fae759be79 scripted-diff: Use inline constexpr over plain constexpr
Both are identical since C++17 and this refactor shouldn't change any
behavior. The benefits are consistency and to be explicit, to avoid
confusion with the C++11/14 constexpr.

Co-Authored-By: l0rinc <pap.lorinc@gmail.com>

-BEGIN VERIFY SCRIPT-
 sed -i --regexp-extended 's/^constexpr \S+ \w+(\[\])? ?[={]/inline &/' $( \
     git grep -l '^constexpr ' -- \
       '*.h' \
       ':(exclude)src/minisketch' \
 )
-END VERIFY SCRIPT-
2026-08-05 08:35:30 +02:00
MarcoFalke
fad753611b scripted-diff: Use inline constexpr over (static) const
Both are fine and this refactor shouldn't change any behavior.

However, inline constexpr will ensure each symbol has a single address
across all TU, making the release binary smaller.

Note, a follow-up commit will deal with string literals (const char*)
and other static const, which can not be constexpr (e.g. std::vector).

-BEGIN VERIFY SCRIPT-
 # Limit to types that can be constexpr
 type='bool|CAmount|size_t|((signed|unsigned) )?int|u?int[0-9]+_t|std::array|DatabaseFormat|CFeeRate|std::streamsize'
 sed -i --regexp-extended "s/^(static )?const (${type})\>/inline constexpr \2/" $( \
   git grep -l --extended-regexp "^(static )?const " -- \
     '*.h' \
     ':(exclude)src/leveldb' \
     ':(exclude)src/secp256k1' \
 )
-END VERIFY SCRIPT-
2026-08-05 08:34:13 +02:00
merge-script
556988790a Merge bitcoin/bitcoin#35592: http: check rpcallowip immediately after accepting connection
55d3cd51a4 doc: add release note describing change for forbidden clients (Matthew Zipkin)
d1ed2a6e25 http: check rpcallowip immediately after accepting connection (Matthew Zipkin)

Pull request description:

  This is a follow-up to #35182 addressing a review comment from that PR: https://github.com/bitcoin/bitcoin/pull/35182#pullrequestreview-4322490068

  This update to HTTPServer checks the IP subnet allowlist as soon as possible (immediately after receiving a connection from a client) before any data is received. This does not entirely protect the server from the "slow loris" attack or [CWE-400](https://cwe.mitre.org/data/definitions/400.html) but does restrict the attack surface to localhost and clients explicitly allowed by the user.

  If a client is not allowed by the list, we disconnect as soon as possible. This is a behavior change from master branch (and previous release with libevent) where `403 Forbidden` was returned (after a potentially large amount request data was written to memory by the server).

  To facilitate existing unit tests, this commit includes a refactor that moves the subnet allow list and relevant methods into the HTTPServer class instead of static file scope. This is needed because otherwise the allow list would be empty when the unit tests run.

  There is still plenty of refactoring to do in order to modernize `HTTPServer` and de-globalize it, but since this specific issue has a resource allocation guard, I wanted to open it quickly on its own.

ACKs for top commit:
  janb84:
    ACK 55d3cd51a4
  winterrdog:
    ACK 55d3cd51a4
  w0xlt:
    ACK 55d3cd51a4
  fjahr:
    Code review ACK 55d3cd51a4

Tree-SHA512: 545911f2e4d2f97ab8bc854e9e57c39eb896428f8c349d34c8e8025a1f6bfb8cfd436f381e36af8b87592c07df3e16210819f3eef7943e23c6626030e615fdf5
2026-08-01 16:43:57 +01:00
Hodlinator
dff44e4c8f util: LineReader - Drop support for raw std::byte spans
TorControlConnection and HTTPRemoteClient have been updated to use std::string receive buffers which mirrors approach in HTTPClient::ReadResponse().
2026-07-28 13:02:21 +02:00
Matthew Zipkin
d1ed2a6e25 http: check rpcallowip immediately after accepting connection
Instead of sending 403 Forbidden, disconnect as soon as possible.

To facilitate unit testing, this commit includes a refactor
that moves the subnet allow list and relevant methods
into the HTTPServer class instead of file-scope static scope.
2026-07-08 11:29:26 -04:00
Matthew Zipkin
73da2a8a52 http: prevent race condition between worker thread and I/O thread
This prevents a losing race condition that could prevent the server
from reading requests from an HTTP client.

A connected socket can either be written to or read from based on the
result of GenerateWaitSockets(). That method checks the HTTPRemoteClient
flag m_send_ready. If it's `true` the implication is that there is
data in the client's send buffer ready to go. Once that data is sent
and the buffer is empty, MaybeSendBytesFromBuffer() sets it `false` again.

The sad case was when a worker thread calling WriteReply() adds
data to the send buffer, but before it sets m_send_ready to `true`,
the I/O thread sends that data and empties the buffer. With the
buffer unexpectedly empty, WriteReply() sets m_send_ready to `true`.

The effect of this is that the socket will stay in "write" mode
with nothing to write. With nothing to write, MaybeSendBytesFromBuffer()
never sets it back to `false` and the socket is stuck forever.
2026-06-29 10:09:26 -04:00
Hennadii Stepanov
f395acdeee scripted-diff: Rename Sock::{RECV,SEND,ERR}
The `ERR` macro is defined on illumos-based systems in the `regset.h`
header included by the Boost.Test framework, which causes a compilation
error.

-BEGIN VERIFY SCRIPT-

ren1() { sed -i "s/\<$1\>/$2/g" $( git grep -l "$1" ./src/util/sock.* ./src/httpserver.h ) ; }
ren1 RECV RecvEvent
ren1 SEND SendEvent
ren1 ERR  ErrorEvent

ren2() { sed -i "s/\<$1\>/$2/g" $( git grep -l "$1" ./src/ ) ; }
ren2 Sock::RECV Sock::RecvEvent
ren2 Sock::SEND Sock::SendEvent
ren2 Sock::ERR  Sock::ErrorEvent

-END VERIFY SCRIPT-
2026-06-27 11:26:41 +01:00
Matthew Zipkin
8c1eea0777 http: remove libevent usage from this subsystem 2026-06-22 05:47:01 -04:00
Matthew Zipkin
21c7542cf8 http: switch servers from libevent to bitcoin 2026-06-22 05:47:00 -04:00
Matthew Zipkin
cbb8d1fb33 HTTPServer: disconnect after idle timeout (-rpcservertimeout) 2026-06-22 05:47:00 -04:00
Matthew Zipkin
e5f242eef3 HTTPServer: implement control methods to match legacy API 2026-06-22 05:47:00 -04:00
Matthew Zipkin
f946ff5a0b Add helper methods to HTTPRequest to match original API
These methods are called by http_request_cb() and are present in the
original http_libevent::HTTPRequest.
2026-06-22 05:47:00 -04:00
Matthew Zipkin
dd11b5e01b define HTTP request methods at module level outside of class
This is a refactor to prepare for matching the API of HTTPRequest
definitions in both namespaces http_bitcoin and http_libevent. In
particular, to provide a consistent return type for GetRequestMethod()
in both classes.
2026-06-22 05:47:00 -04:00
Matthew Zipkin
7ee7df988e HTTPServer: use a queue to pipeline requests from each connected client
See https://www.rfc-editor.org/rfc/rfc7230#section-6.3.2

> A server MAY process a sequence of pipelined requests in
  parallel if they all have safe methods (Section 4.2.1 of [RFC7231]),
  but it MUST send the corresponding responses in the same order that
  the requests were received.

We choose NOT to process requests in parallel. They are executed in
the order recevied as well as responded to in the order received.
This prevents race conditions where old state may get sent in response
to requests that are very quick to process but were requested later on
in the queue.
2026-06-22 05:47:00 -04:00
Matthew Zipkin
5ef1b80a09 Allow http workers to send data optimistically as an optimization 2026-06-22 05:47:00 -04:00
Matthew Zipkin
a69bb9e1e6 HTTPServer: disconnect clients 2026-06-22 05:47:00 -04:00
Matthew Zipkin
cdf71998e5 HTTPServer: compose and send replies to connected clients
Sockets-touching bits copied and adapted from `CConnman::SocketSendData()`

Testing this requires adding a new feature to the SocketTestingSetup,
returning the DynSock I/O pipes from the mock socket so the received
data can be checked.

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2026-06-22 05:46:59 -04:00
Matthew Zipkin
6734bcdeff HTTPserver: support "chunked" Transfer-Encoding 2026-06-22 05:46:59 -04:00
Matthew Zipkin
80e1cfe5a2 HTTPServer: read requests from connected clients
`SocketHandlerConnected()` adapted from CConnman

Testing this requires adding a new feature to the SocketTestingSetup,
inserting a "request" payload into the mock client that connects
to us.

This commit also moves IOErrorIsPermanent() from sock.cpp to sock.h
so it can be called from the socket handler in httpserver.cpp

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2026-06-22 05:46:51 -04:00
Matthew Zipkin
3c5226ab96 HTTPServer: start an I/O loop in a new thread and accept connections
Socket handling methods are copied from CConnMan:

`CConnman::GenerateWaitSockets()`
`CConnman::SocketHandlerListening()`
`CConnman::ThreadSocketHandler()` and `CConnman::SocketHandler()` are combined into ThreadSocketHandler()`.

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2026-06-22 05:46:38 -04:00
Matthew Zipkin
4ef4ebdc0c http: Introduce HTTPRemoteClient class 2026-06-22 05:46:38 -04:00
Matthew Zipkin
a85286c5c7 HTTPServer: generate sequential Ids for each newly accepted connection
Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2026-06-22 05:46:38 -04:00
Matthew Zipkin
5a3aa1af28 HTTPServer: implement and test AcceptConnection()
AcceptConnection() is mostly copied from CConmann in net.cpp
and then modernized.

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2026-06-22 05:46:37 -04:00
Matthew Zipkin
f5bc018948 http: Introduce HTTPServer class and implement binding to listening socket
Introduce a new low-level socket managing class `HTTPServer`.

BindAndStartListening() was copied from CConnMan's BindListenPort()
in net.cpp and modernized.

Unit-test it with a new class `SocketTestingSetup` which mocks
`CreateSock()` and will enable mock client I/O in future commits.

Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
2026-06-22 05:46:37 -04:00
Matthew Zipkin
9463e98781 http: Implement HTTPRequest class
HTTP Request message:
https://datatracker.ietf.org/doc/html/rfc1945#section-5

Request Line aka Control Line aka first line:
https://datatracker.ietf.org/doc/html/rfc1945#section-5.1

See message_read_status() in libevent http.c for how
`MORE_DATA_EXPECTED` is handled there
2026-06-22 05:46:37 -04:00
Matthew Zipkin
ad50aa4a0f http: Implement HTTPResponse class
HTTP Response message:
https://datatracker.ietf.org/doc/html/rfc1945#section-6

Status line (first line of response):
https://datatracker.ietf.org/doc/html/rfc1945#section-6.1

Status code definitions:
https://datatracker.ietf.org/doc/html/rfc1945#section-9
2026-06-22 05:46:37 -04:00
Matthew Zipkin
68b5d289d1 http: Implement HTTPHeaders class
see:
https://www.rfc-editor.org/rfc/rfc2616#section-4.2
https://www.rfc-editor.org/rfc/rfc7231#section-5
https://www.rfc-editor.org/rfc/rfc7231#section-7
https://httpwg.org/specs/rfc9111.html#header.field.definitions
2026-06-22 05:46:37 -04:00
Matthew Zipkin
89c54ae4cb http: enclose libevent-dependent code in a namespace
This commit is a no-op to isolate HTTP methods and objects that
depend on libevent. Following commits will add replacement objects
and methods in a new namespace for testing and review before
switching over the server.
2026-06-22 05:46:37 -04:00
Ryan Ofsky
df76891a3b refactor: Add ExecuteHTTPRPC function
Add ExecuteHTTPRPC to provide a way to execute an HTTP request without relying
on HTTPRequest and libevent types.

Behavior is not changing in any way, this is just moving code. This commit may
be easiest to review using git's --color-moved option.
2026-02-27 19:26:08 -04:00
furszy
38fd85c676 http: replace WorkQueue and threads handling for ThreadPool
Replace the HTTP server's WorkQueue implementation and single threads
handling code with ThreadPool for processing HTTP requests. The
ThreadPool class encapsulates all this functionality on a reusable
class, properly unit and fuzz tested (the previous code was not
unit nor fuzz tested at all).

This cleanly separates responsibilities:
The HTTP server now focuses solely on receiving and dispatching requests,
while ThreadPool handles concurrency, queuing, and execution.
It simplifies init, shutdown and requests tracking.

This also allows us to experiment with further performance improvements at
the task queuing and execution level, such as a lock-free structure, task
prioritization or any other performance improvement in the future, without
having to deal with HTTP code that lives on a different layer.
2026-01-30 16:17:12 -05:00
MarcoFalke
fa5f297748 scripted-diff: [doc] Unify stale copyright headers
-BEGIN VERIFY SCRIPT-

 sed --in-place --regexp-extended \
   's;( 20[0-2][0-9])(-20[0-2][0-9])? The Bitcoin Core developers;\1-present The Bitcoin Core developers;g' \
   $( git grep -l 'The Bitcoin Core developers' -- ':(exclude)COPYING' ':(exclude)src/ipc/libmultiprocess' ':(exclude)src/minisketch' )

-END VERIFY SCRIPT-
2025-12-16 22:21:15 +01:00
Vasil Dimov
e56fc7ce6a rpc: increase the defaults for -rpcthreads and -rpcworkqueue
`rpcthreads` was introduced with a default of 4 in 2013 in
21eb5adadb

`rpcworkqueue` was introduced with a default of 16 in 2015 in
40b556d374

Resolves: https://github.com/bitcoin/bitcoin/issues/29386
2024-11-04 17:08:21 +01:00
merge-script
01dc38bd01 Merge bitcoin/bitcoin#30406: refactor: modernize-use-equals-default
3333bae9b2 tidy: modernize-use-equals-default (MarcoFalke)

Pull request description:

  Prior to C++20, `modernize-use-equals-default` could have been problematic because it could turn a non-aggregate into an aggregate. The risk would be that aggregate initialization would be enabled where the author did not intend to enable it.

  With C++20, aggregate for those is forbidden either way. (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1008r1.pdf)

  So enabled it for code clarity, consistency, and possibly unlocking compiler optimizations. See https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-equals-default.html

ACKs for top commit:
  stickies-v:
    ACK 3333bae9b2

Tree-SHA512: ab42ff01be7ca7e7d8b4c6a485e68426f59627d83dd827cf292304829562348dc17a52ee009f5f6f3c1c2081d7166ffac4baef23197ebeba8de7767c6ddfe255
2024-07-11 19:08:46 +01:00
MarcoFalke
3333bae9b2 tidy: modernize-use-equals-default 2024-07-08 11:12:01 +02:00
MarcoFalke
fa8f53273c refactor: Remove no longer needed clang-15 workaround for std::span 2024-06-26 18:50:20 +02:00
Roman Zeyde
1556d21599 rest: don't copy data when sending binary response
Also, change `HTTPRequest::WriteReply` to accept `std::span`.
2024-06-26 06:47:30 +03:00
Ryan Ofsky
42e5829d97 refactor: Remove call to ShutdownRequested from HTTPRequest
Pass HTTP server an interrupt object instead of having it depend on shutdown.h
and global shutdown state.

There is no change in behavior in this commit.
2023-12-04 15:39:15 -04:00
Hennadii Stepanov
306ccd4927 scripted-diff: Bump copyright headers
-BEGIN VERIFY SCRIPT-
./contrib/devtools/copyright_header.py update ./
-END VERIFY SCRIPT-

Commits of previous years:
- 2021: f47dda2c58
- 2020: fa0074e2d8
- 2019: aaaaad6ac9
2022-12-24 23:49:50 +00:00
fanquake
d906329c28 Merge bitcoin/bitcoin#24681: build: Bump libevent minimum version up to 2.1.8
e40779a4fe refactor: Remove outdated libevent logging code (Fabian Jahr)
0598f36852 refactor: account for requiring libevent 2.1.8+ (fanquake)
aaf72d62c1 build: Bump libevent minimum version up to 2.1.8 (Hennadii Stepanov)

Pull request description:

  Required to support new functionality in bitcoin/bitcoin#19420.

  `libevent` availability: https://repology.org/project/libevent/versions

ACKs for top commit:
  laanwj:
    Code review ACK e40779a4fe
  fanquake:
    ACK e40779a4fe

Tree-SHA512: ccb14ea2f591484a3df5bc4a19f4f5400ef6b1cfb7dc45dd99f96cb948748215ed3b5debc34869763c91b8c7a26993fdb9b870950c0743c4d01038ab27c5e4e2
2022-04-06 13:19:36 +01:00
Fabian Jahr
e40779a4fe refactor: Remove outdated libevent logging code
The removed code was intended to catch issues with event_enable_debug_logging which was not available prior to libevent 2.1.1. This is not necessary since the minimum libevent version was bumped to 2.1.8.
2022-04-02 19:26:46 +02:00
stickies-v
a09497614e Add GetQueryParameter helper function
Easily get the query parameter from the URI, with optional default value.
2022-03-10 12:01:54 +01:00
MarcoFalke
fa488f131f scripted-diff: Bump copyright headers
-BEGIN VERIFY SCRIPT-
./contrib/devtools/copyright_header.py update ./
-END VERIFY SCRIPT-
2020-04-16 13:33:09 -04:00
practicalswift
cdfb8e7afa tests: Add fuzzing harness for HTTPRequest, libevent's evhttp and related functions 2020-04-06 13:58:51 +00:00
MarcoFalke
aaaaad6ac9 scripted-diff: Bump copyright of files changed in 2019
-BEGIN VERIFY SCRIPT-
./contrib/devtools/copyright_header.py update ./
-END VERIFY SCRIPT-
2019-12-30 10:42:20 +13:00
practicalswift
084e17cebd Remove unused includes 2019-10-15 22:56:43 +00:00
John Newbery
91a25d1e71 [build] Add several util units
Adds the following util units and adds them to libbitcoin_util:

- `util/url.cpp` takes `urlDecode` from `httpserver.cpp`
- `util/error.cpp` takes `TransactionErrorString` from
  `node/transaction.cpp` and `AmountHighWarn` and `AmountErrMsg` from
  `ui_interface.cpp`
- `util/fees.cpp` takes `StringForFeeReason` and `FeeModeFromString` from `policy/fees.cpp`
- `util/rbf.cpp` takes `SignalsOptInRBF` from `policy/rbf.cpp`
- 'util/validation.cpp` takes `FormatStateMessage` and `strMessageMagic` from 'validation.cpp`
2019-04-09 17:53:08 -04:00