Skip to content

Commit ce1c43e

Browse files
committed
Fix echo server to use advance-then-check pattern
The session loop checked ec before using the read bytes, silently dropping partial data delivered alongside an error (e.g. EOF). Apply the canonical advance-then-check pattern from the ReadStream contract: always write back the received bytes before inspecting ec.
1 parent d58f3e6 commit ce1c43e

3 files changed

Lines changed: 15 additions & 34 deletions

File tree

doc/modules/ROOT/pages/3.tutorials/3a.echo-server.adoc

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,13 @@ class echo_server : public corosio::tcp_server
6060
{
6161
corosio::io_context& ctx_;
6262
corosio::tcp_socket sock_;
63-
std::string buf_;
63+
char buf_[4096];
6464
6565
public:
6666
explicit worker(corosio::io_context& ctx)
6767
: ctx_(ctx)
6868
, sock_(ctx)
6969
{
70-
buf_.reserve(4096);
7170
}
7271
7372
corosio::tcp_socket& socket() override
@@ -101,22 +100,13 @@ capy::task<> echo_server::worker::do_session()
101100
{
102101
for (;;)
103102
{
104-
buf_.resize(4096);
105-
106-
// Read some data
107103
auto [ec, n] = co_await sock_.read_some(
108-
capy::mutable_buffer(buf_.data(), buf_.size()));
109-
110-
if (ec || n == 0)
111-
break;
112-
113-
buf_.resize(n);
104+
capy::mutable_buffer(buf_, sizeof buf_));
114105
115-
// Echo it back
116106
auto [wec, wn] = co_await corosio::write(
117-
sock_, capy::const_buffer(buf_.data(), buf_.size()));
107+
sock_, capy::const_buffer(buf_, n));
118108
119-
if (wec)
109+
if (wec || ec)
120110
break;
121111
}
122112
@@ -127,7 +117,8 @@ capy::task<> echo_server::worker::do_session()
127117
Notice:
128118

129119
* We reuse the worker's buffer across reads
130-
* `read_some()` returns when _any_ data arrives
120+
* `read_some()` returns when _any_ data arrives — it may deliver bytes alongside an error
121+
* We always write before checking the error (advance-then-check); writing zero bytes is a no-op
131122
* `corosio::write()` writes _all_ data (it's a composed operation)
132123
* When the coroutine ends, the launcher returns the worker to the pool
133124

@@ -218,12 +209,14 @@ For echo servers, we want complete message delivery.
218209

219210
=== Why Not Use Exceptions?
220211

221-
The session loop needs to handle EOF gracefully. Using structured bindings:
212+
The session loop needs to handle EOF gracefully. Using structured bindings
213+
with advance-then-check, we always act on `n` before inspecting `ec`:
222214

223215
[source,cpp]
224216
----
225217
auto [ec, n] = co_await sock.read_some(buf);
226-
if (ec || n == 0)
218+
auto [wec, wn] = co_await corosio::write(sock, const_buffer(buf.data(), n));
219+
if (wec || ec)
227220
break; // Normal termination path
228221
----
229222

example/echo-server/echo_server.cpp

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
#include <cstdlib>
1616
#include <iostream>
17-
#include <string>
1817

1918
namespace corosio = boost::corosio;
2019
namespace capy = boost::capy;
@@ -23,14 +22,13 @@ class echo_worker : public corosio::tcp_server::worker_base
2322
{
2423
corosio::io_context& ctx_;
2524
corosio::tcp_socket sock_;
26-
std::string buf_;
25+
char buf_[4096];
2726

2827
public:
2928
explicit echo_worker(corosio::io_context& ctx)
3029
: ctx_(ctx)
3130
, sock_(ctx)
3231
{
33-
buf_.reserve(4096);
3432
}
3533

3634
corosio::tcp_socket& socket() override
@@ -47,22 +45,13 @@ class echo_worker : public corosio::tcp_server::worker_base
4745
{
4846
for (;;)
4947
{
50-
buf_.resize(4096);
51-
52-
// Read some data
5348
auto [ec, n] = co_await sock_.read_some(
54-
capy::mutable_buffer(buf_.data(), buf_.size()));
55-
56-
if (ec)
57-
break;
58-
59-
buf_.resize(n);
49+
capy::mutable_buffer(buf_, sizeof buf_));
6050

61-
// Echo it back
6251
auto [wec, wn] = co_await capy::write(
63-
sock_, capy::const_buffer(buf_.data(), buf_.size()));
52+
sock_, capy::const_buffer(buf_, n));
6453

65-
if (wec)
54+
if (wec || ec)
6655
break;
6756
}
6857

test/unit/tcp_server.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ class test_worker : public tcp_server::worker_base
4545
char buf[64];
4646
auto [ec, n] = co_await sock->read_some(
4747
capy::mutable_buffer(buf, sizeof(buf)));
48-
if (!ec)
49-
(void)co_await sock->write_some(capy::const_buffer(buf, n));
48+
(void)co_await sock->write_some(capy::const_buffer(buf, n));
5049
sock->close();
5150
}(&sock_));
5251
}

0 commit comments

Comments
 (0)