Skip to content

Commit cf080ef

Browse files
committed
Fixes #635 - bug in async server's connection read
This changes the example file server to use the read handler for POST/PUT requests. This currently assumes that there's a content-length header (not much error handling is happening here, but it's meant as a proof of concept anyway). Using this code path in an example should be good enough for the moment until we have better tests and a better API for this functionality.
1 parent d23bfb2 commit cf080ef

File tree

2 files changed

+72
-12
lines changed

2 files changed

+72
-12
lines changed

boost/network/protocol/http/server/async_connection.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ struct async_connection
378378
boost::throw_exception(std::system_error(*error_encountered));
379379
if (new_start != read_buffer_.begin()) {
380380
input_range input =
381-
boost::make_iterator_range(new_start, read_buffer_.end());
381+
boost::make_iterator_range(new_start, data_end);
382382
buffer_type::iterator start_tmp = new_start;
383383
new_start = read_buffer_.begin();
384384
auto self = this->shared_from_this();
@@ -392,7 +392,8 @@ struct async_connection
392392
socket().async_read_some(::asio::buffer(read_buffer_),
393393
strand.wrap([this, self, callback](
394394
std::error_code ec, size_t bytes_transferred) {
395-
callback(ec, bytes_transferred);
395+
this->wrap_read_handler(callback, ec,
396+
bytes_transferred);
396397
}));
397398
}
398399

libs/network/example/http/fileserver.cpp

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ struct file_server;
2121
typedef http::server<file_server> server;
2222

2323
struct file_cache {
24-
2524
typedef std::map<std::string, std::pair<void *, std::size_t> > region_map;
2625
typedef std::map<std::string, std::vector<server::response_header> > meta_map;
2726

@@ -66,7 +65,8 @@ struct file_cache {
6665

6766
regions.insert(std::make_pair(real_filename, std::make_pair(region, size)));
6867
static server::response_header common_headers[] = {
69-
{"Connection", "close"}, {"Content-Type", "x-application/octet-stream"},
68+
{"Connection", "close"},
69+
{"Content-Type", "x-application/octet-stream"},
7070
{"Content-Length", "0"}};
7171
std::vector<server::response_header> headers(common_headers,
7272
common_headers + 3);
@@ -91,8 +91,7 @@ struct file_cache {
9191
static std::vector<server::response_header> empty_vector;
9292
auto headers = file_headers.find(doc_root_ + path);
9393
if (headers != file_headers.end()) {
94-
auto begin = headers->second.begin(),
95-
end = headers->second.end();
94+
auto begin = headers->second.begin(), end = headers->second.end();
9695
return boost::make_iterator_range(begin, end);
9796
} else
9897
return boost::make_iterator_range(empty_vector);
@@ -138,22 +137,75 @@ struct connection_handler : std::enable_shared_from_this<connection_handler> {
138137
asio::const_buffers_1(
139138
static_cast<char const *>(mmaped_region.first) + offset,
140139
rightmost_bound - offset),
141-
[=] (std::error_code const &ec) {
140+
[=](std::error_code const &ec) {
142141
self->handle_chunk(mmaped_region, rightmost_bound, connection, ec);
143142
});
144143
}
145144

146145
void handle_chunk(std::pair<void *, std::size_t> mmaped_region, off_t offset,
147146
server::connection_ptr connection,
148147
std::error_code const &ec) {
149-
assert(offset>=0);
148+
assert(offset >= 0);
150149
if (!ec && static_cast<std::size_t>(offset) < mmaped_region.second)
151150
send_file(mmaped_region, offset, connection);
152151
}
153152

154153
file_cache &file_cache_;
155154
};
156155

156+
struct input_consumer : public std::enable_shared_from_this<input_consumer> {
157+
// Maximum size for incoming request bodies.
158+
static constexpr std::size_t MAX_INPUT_BODY_SIZE = 2 << 16;
159+
160+
explicit input_consumer(std::shared_ptr<connection_handler> h,
161+
server::request r)
162+
: request_(std::move(r)), handler_(std::move(h)), content_length_{0} {
163+
for (const auto &header : request_.headers) {
164+
if (boost::iequals(header.name, "content-length")) {
165+
content_length_ = std::stoul(header.value);
166+
std::cerr << "Content length: " << content_length_ << '\n';
167+
break;
168+
}
169+
}
170+
}
171+
172+
void operator()(server::connection::input_range input, std::error_code ec,
173+
std::size_t bytes_transferred,
174+
server::connection_ptr connection) {
175+
std::cerr << "Callback: " << bytes_transferred << "; ec = " << ec << '\n';
176+
if (ec == asio::error::eof) return;
177+
if (!ec) {
178+
if (empty(input))
179+
return (*handler_)(request_.destination, connection, true);
180+
request_.body.insert(request_.body.end(), boost::begin(input),
181+
boost::end(input));
182+
if (request_.body.size() > MAX_INPUT_BODY_SIZE) {
183+
connection->set_status(server::connection::bad_request);
184+
static server::response_header error_headers[] = {
185+
{"Connection", "close"}};
186+
connection->set_headers(
187+
boost::make_iterator_range(error_headers, error_headers + 1));
188+
connection->write("Body too large.");
189+
return;
190+
}
191+
std::cerr << "Body: " << request_.body << '\n';
192+
if (request_.body.size() == content_length_)
193+
return (*handler_)(request_.destination, connection, true);
194+
std::cerr << "Scheduling another read...\n";
195+
auto self = this->shared_from_this();
196+
connection->read([self](server::connection::input_range input,
197+
std::error_code ec, std::size_t bytes_transferred,
198+
server::connection_ptr connection) {
199+
(*self)(input, ec, bytes_transferred, connection);
200+
});
201+
}
202+
}
203+
204+
server::request request_;
205+
std::shared_ptr<connection_handler> handler_;
206+
size_t content_length_;
207+
};
208+
157209
struct file_server {
158210
explicit file_server(file_cache &cache) : cache_(cache) {}
159211

@@ -165,6 +217,14 @@ struct file_server {
165217
} else if (request.method == "GET") {
166218
std::shared_ptr<connection_handler> h(new connection_handler(cache_));
167219
(*h)(request.destination, connection, true);
220+
} else if (request.method == "PUT" || request.method == "POST") {
221+
auto h = std::make_shared<connection_handler>(cache_);
222+
auto c = std::make_shared<input_consumer>(h, request);
223+
connection->read([c](server::connection::input_range input,
224+
std::error_code ec, std::size_t bytes_transferred,
225+
server::connection_ptr connection) {
226+
(*c)(input, ec, bytes_transferred, connection);
227+
});
168228
} else {
169229
static server::response_header error_headers[] = {
170230
{"Connection", "close"}};
@@ -184,11 +244,10 @@ int main(int, char *[]) {
184244
file_server handler(cache);
185245
server::options options(handler);
186246
server instance(options.thread_pool(std::make_shared<utils::thread_pool>(4))
187-
.address("0.0.0.0")
188-
.port("8000"));
247+
.address("0.0.0.0")
248+
.port("8000"));
189249
instance.run();
190-
}
191-
catch (std::exception &e) {
250+
} catch (std::exception &e) {
192251
std::cerr << e.what() << std::endl;
193252
}
194253
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy