Skip to content

Commit 6475fa8

Browse files
authored
rpcdaemon: buffered HTTP chunking (#2863)
1 parent 43a6c01 commit 6475fa8

15 files changed

+310
-139
lines changed

silkworm/rpc/core/evm_debug_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct DebugExecutorTest : public test_util::ServiceContextTestBase {
3030
WorkerPool workers{1};
3131
StringWriter writer{4096};
3232
boost::asio::any_io_executor io_executor{ioc_.get_executor()};
33-
json::Stream stream{io_executor, writer};
33+
json::Stream stream{io_executor, writer, /* request_id */ 0};
3434
test::BackEndMock backend;
3535
RemoteChainStorage chain_storage{transaction, ethdb::kv::make_backend_providers(&backend)};
3636
};

silkworm/rpc/http/chunker.hpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2025 The Silkworm Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include <algorithm>
7+
#include <memory>
8+
#include <queue>
9+
10+
namespace silkworm::rpc::http {
11+
12+
inline constexpr int kDefaultMaxChunkSize = 2048;
13+
14+
class Chunker {
15+
public:
16+
Chunker(const Chunker&) = delete;
17+
18+
Chunker() {
19+
current_chunk_.reserve(kDefaultMaxChunkSize);
20+
}
21+
22+
~Chunker() {}
23+
24+
void queue_data(const std::string& new_buffer) {
25+
size_t position = 0;
26+
27+
// creates chunk: even if new:buffer is greater kDefaultMaxChunkSize
28+
while (position < new_buffer.size()) {
29+
size_t available_space = kDefaultMaxChunkSize - current_chunk_.size();
30+
size_t chunk_size = std::min(available_space, new_buffer.size() - position);
31+
32+
current_chunk_.append(new_buffer, position, chunk_size);
33+
position += chunk_size;
34+
35+
// one chunk is completed copy it in complet_chunk
36+
if (current_chunk_.size() == kDefaultMaxChunkSize) {
37+
complete_chunk_.push(current_chunk_);
38+
current_chunk_.clear();
39+
current_chunk_.reserve(kDefaultMaxChunkSize);
40+
}
41+
}
42+
}
43+
44+
std::pair<std::string, bool> get_complete_chunk() {
45+
if (!complete_chunk_.empty()) {
46+
// at least one chunk is availble return it , indicating if first chunk or not
47+
auto ret_first_chunk = !first_chunk_completed_;
48+
first_chunk_completed_ = true;
49+
std::string chunk = complete_chunk_.front();
50+
complete_chunk_.pop();
51+
return std::make_pair(chunk, ret_first_chunk);
52+
}
53+
// queue is empty no chunk are available
54+
return std::make_pair("", false);
55+
}
56+
57+
bool has_chunks() const {
58+
return !complete_chunk_.empty();
59+
}
60+
61+
std::pair<std::string, bool> get_remainder() const {
62+
if (current_chunk_.empty()) {
63+
// no bytes are present on current_chunk so return empty string and indication if first chunk or not
64+
// we are in two possible cases: at least one completed chunk is already produced, or any chunk are produced
65+
return std::make_pair("", !first_chunk_completed_);
66+
}
67+
// returns the chunk
68+
return std::make_pair(current_chunk_, !first_chunk_completed_);
69+
}
70+
71+
private:
72+
std::queue<std::string> complete_chunk_;
73+
std::string current_chunk_;
74+
bool first_chunk_completed_{false};
75+
};
76+
77+
}; // namespace silkworm::rpc::http

0 commit comments

Comments
 (0)