Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
252# Too many small header lines exceeds total headers size allowed253conn = BitcoinHTTPConnection(self.node)254for i in range(headers_above_limit):255conn.add_header(f"header_{i:04}", "foo")256self.send_bad_and_tolerate_disconnect(conn, lambda: conn.get('/x'))258# Compute how much data we can add to a request message body259# to make / break the limit.260base_request_body_size = len('{"jsonrpc": "2.0", "id": "0", "method": "submitblock", "params": [""]}}')261bytes_below_limit = MAX_BODY_SIZE - base_request_body_size262bytes_above_limit = MAX_BODY_SIZE - base_request_body_size + 2264# Large request body size is ok265conn = BitcoinHTTPConnection(self.node)266response4 = conn.post('/', f'{{"jsonrpc": "2.0", "id": "0", "method": "submitblock", "params": ["{"0" * bytes_below_limit}"]}}')267assert_equal(response4.status, http.client.OK)269conn = BitcoinHTTPConnection(self.node)271# Split off the send into a background thread. When the server detects272# the excessive size it will stop reading from the socket, but the client273# will continue trying to write until the backpressure eventually274# drops the TCP window size to 0. While the send operation is blocking until275# it times out, we can still receive the server's response in the foreground.277def send_excessive_body(self, conn):278try:279# Excessive body size is invalid280conn.post_raw('/', f'{{"jsonrpc": "2.0", "id": "0", "method": "submitblock", "params": ["{"0" * bytes_above_limit}"]}}')281# On some platforms (e.g. Windows) the whole request may be282# accepted into the OS send buffer before the server disconnects.283# It's ok to allow that, the server-side behavior is asserted in284# the foreground thread via the 413 response.285self.log.info("Client finished sending request before connection was terminated")286except NETWORK_ERRORS:287self.log.info("Client did not finish sending request before connection was terminated")289send_thread = threading.Thread(target=send_excessive_body, args=(self, conn))290send_thread.start()292response5 = conn.recv_raw().decode()293assert "413 Content too large" in response5295try:296conn.conn.sock.shutdown(socket.SHUT_RDWR)297self.log.info("Send thread force-closed by test framework")298except OSError:299self.log.info("Send thread was already closed by RST from server")300send_thread.join()303def check_pipelining(self, with_invalid_second_request):304"""305Requests are responded to in the order in which they were received306See https://www.rfc-editor.org/rfc/rfc7230#section-6.3.2307"""308self.log.info("Check pipelining" + (" with invalid second request" if with_invalid_second_request else ""))309tip_height = self.node.getblockcount()310conn = BitcoinHTTPConnection(self.node)311conn.set_timeout(5)313# Send two requests in a row.314# The first request will block the second indefinitely315conn.post_raw('/', f'{{"method": "waitforblockheight", "params": [{tip_height + 1}]}}')316if with_invalid_second_request:317conn.post_raw(f'/{"x" * MAX_HEADERS_SIZE * 2}', '{"method": "getblockcount"}')318else:319conn.post_raw('/', '{"method": "getblockcount"}')321# The server should not respond to the second request until the first322# request has been handled. Since the server will not respond at all323# to the first request until we generate a block we expect a socket timeout.324assert_raises(TimeoutError, lambda: conn.recv_raw())326# Use a separate http connection to generate a block327self.generate(self.node, 1, sync_fun=self.no_op)329# Wait for responses to be received330if with_invalid_second_request:331OK = 1332BAD = 1333else:334OK = 2335BAD = 0336res = b""337while True:338res += conn.recv_raw()339if res.count(b"HTTP/1.1 200") == OK and res.count(b"HTTP/1.1 400") == BAD:340break342# waitforblockheight was responded to first, and then getblockcount343# which includes the block added after the request was made344chunks = res.split(b'"result":')345assert chunks[1].startswith(b'{"hash":')346if with_invalid_second_request:347# The response to the in-flight first request is sent before the348# error generated by parsing the second one, even though the second349# request could have been rejected much earlier.350assert res.index(b"HTTP/1.1 200") < res.index(b"HTTP/1.1 400")351else:352assert chunks[2].startswith(bytes(f'{tip_height + 1}', 'utf8'))355def check_chunked_transfer(self):356self.log.info("Check HTTP request encoded with chunked transfer")357conn = BitcoinHTTPConnection(self.node)358headers_chunked = conn.headers.copy()359headers_chunked.update({"Transfer-encoding": "chunked"})360body_chunked = [361b'{"method": "submitblock", "params": ["',362b'0' * 1000000,363b'1' * 1000000,364b'2' * 1000000,365b'3' * 1000000,366b'"]}'367]368conn.conn.request(369method='POST',370url='/',371body=iter(body_chunked),372headers=headers_chunked,373encode_chunked=True)374response1 = conn.recv_raw()375assert b'{"result":"high-hash","error":null}\n' in response1377self.log.info("Check excessive size HTTP request encoded with chunked transfer")378conn = BitcoinHTTPConnection(self.node)379headers_chunked = conn.headers.copy()380headers_chunked.update({"Transfer-encoding": "chunked"})381body_chunked = [382b'{"method": "submitblock", "params": ["',383b'0' * 10000000,384b'1' * 10000000,385b'2' * 10000000,386b'3' * 10000000,387b'"]}'388]390# Split off the send into a background thread. When the server detects391# the excessive size it will stop reading from the socket, but the client392# will continue trying to write until the backpressure eventually393# drops the TCP window size to 0. While the send operation is blocking until394# it times out, we can still receive the server's response in the foreground.396def send_excessive_chunked(self, conn):397try:398conn.conn.request(399method='POST',400url='/',401body=iter(body_chunked),402headers=headers_chunked,403encode_chunked=True)404# On some platforms (e.g. Windows) the whole request may be405# accepted into the OS send buffer before the server disconnects.406# It's ok to allow that, the server-side behavior is asserted in407# the foreground thread via the 413 response.408self.log.info("Client finished sending request before connection was terminated")409except NETWORK_ERRORS:410self.log.info("Client did not finish sending request before connection was terminated")412send_thread = threading.Thread(target=send_excessive_chunked, args=(self, conn))413send_thread.start()415response2 = conn.recv_raw().decode()416assert "413 Content too large" in response2418try:419conn.conn.sock.shutdown(socket.SHUT_RDWR)420self.log.info("Send thread force-closed by test framework")421except OSError:422self.log.info("Send thread was already closed by RST from server")423send_thread.join()426def check_idle_timeout(self):427self.log.info("Check -rpcservertimeout")429# This is the amount of time the server will wait for a client to430# send a complete request. Test it by sending an incomplete but431# so-far otherwise well-formed HTTP request, and never finishing it.432self.restart_node(0, extra_args=[f"-rpcservertimeout={RPCSERVERTIMEOUT}"])434# Copied from http_incomplete_test_() in regress_http.c in libevent.435# A complete request would have an additional "\r\n" at the end.436bad_http_request = "GET /test1 HTTP/1.1\r\nHost: somehost\r\n"437conn = BitcoinHTTPConnection(self.node)438conn.send_raw(bad_http_request.encode("ascii"))440conn.expect_timeout(RPCSERVERTIMEOUT)442# Sanity check -- complete requests don't timeout waiting for completion443good_http_request = "GET /test2 HTTP/1.1\r\nHost: somehost\r\n\r\n"444conn.reset_conn()445conn.send_raw(good_http_request.encode("ascii"))446response = conn.recv_raw()447assert response.startswith(b"HTTP/1.1 404 Not Found")449# Still open450assert not conn.sock_closed()453def check_server_busy_idle_timeout(self):454self.log.info("Check that -rpcservertimeout won't close on a delayed response")456self.restart_node(0, extra_args=[f"-rpcservertimeout={RPCSERVERTIMEOUT}"])458tip_height = self.node.getblockcount()459conn = BitcoinHTTPConnection(self.node)460conn.post_raw('/', f'{{"method": "waitforblockheight", "params": [{tip_height + 1}]}}')462# Wait until after the timeout, then generate a block with a second HTTP connection463time.sleep(RPCSERVERTIMEOUT + 1)464generated_block = self.generate(self.node, 1, sync_fun=self.no_op)[0]466# The first connection gets the response it is patiently waiting for467response1 = conn.recv_raw().decode()468assert generated_block in response1469# The connection is still open470assert not conn.sock_closed()472# Now it will actually close due to idle timeout473conn.expect_timeout(RPCSERVERTIMEOUT)476def check_auth_required(self):477self.log.info("Check that requests without credentials return 401 Unauthorized with WWW-Authenticate")478conn = BitcoinHTTPConnection(self.node)479conn.headers = {}480response = conn.post('/', '{"method": "getbestblockhash"}')481assert_equal(response.status, http.client.UNAUTHORIZED)482assert response.getheader('WWW-Authenticate') is not None485def check_wrong_credentials(self):486self.log.info("Check that incorrect credentials return 401 Unauthorized")487conn = BitcoinHTTPConnection(self.node)488wrong_pair = f"{conn.url.username}:wrong_password"489conn.headers = {"Authorization": f"Basic {str_to_b64str(wrong_pair)}"}490response = conn.post('/', '{"method": "getbestblockhash"}')491assert_equal(response.status, http.client.UNAUTHORIZED)492assert response.getheader('WWW-Authenticate') is not None495def check_malformed_auth_headers(self):496self.log.info("Check that malformed Authorization headers return 401 Unauthorized")497cases = [498"Bearer sometoken123",499'Digest username="user", realm="test"',500"Basic !!!notbase64!!!",
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.