[Beast] Slow HTTP connect on Windows. Normal?
Hi. I'm writing a small interaction between: 1) a Boost.Beast-based HTTP server (based on http_server_async.cpp example) 2) a Boost.Beast-based HTTP client (based on http_client_sync.cpp example) When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server): 2020-11-25T09:46:37.473936 Prologue in 0.001s 2020-11-25T09:46:37.482470 Resolved in 0.008s 2020-11-25T09:46:38.506180 Connected in 1.023s 2020-11-25T09:46:38.509607 OK: Authenticated; in 0.003s While the same on Linux (RH7.5) is just over 2ms: 2020-11-25T09:45:45.515926 Prologue in 0.000s 2020-11-25T09:45:45.517083 Resolved in 0.001s 2020-11-25T09:45:45.517550 Connected in 0.000s 2020-11-25T09:45:45.518010 OK: Authenticated; in 0.000s That's a huge difference! Almost 500x... And when I contact the same HTTP server on Windows, but from Chrome this time, it takes about 300ms, and if I hit reload rapidly, the time jumps around to as low as 3ms, and as high as 300ms (the same initial delay), with ~50ms and ~100ms in between. (I also see several different connections being established, for some reason...) I've seen similar differences between Windows and Linux connection times, but with WebSocketPP-based client and server this time, also based on Boost.ASIO. Q1: Am I doing anything wrong? I.e. is this "normal" somehow? Q2: How come Chrome, on Windows too, is 3x faster than ASIO-based clients, with the same Beast-based server? Given the high connect time on Windows, I thought I'd try to keep the connection open on the client, and issue several send-request/read-response pairs, using the Beast-based (sync) HTTP client, but only the first one works correctly, the 2nd errors out with: Error: An established connection was aborted by the software in your host machine I suspect it is a user-error, with the server closing the connection, despite the Keep-Alive HTTP header being present server-side on the request. Q3: Given the Beast HTTP examples I'm based off, would anyone have suggestions on what changes are necessary to allow the client issuing several (non-overlapping) requests to the server, on the same connection? (since connecting is so expensive). Q4: And for better performance, what about overlapping request/response pairs, with HTTP pipelining. How to set that up on the client and server with Beast? Thanks for any help on this. --DD PS: Great examples in Boost.Beast BTW. Thanks Vinnie.
Some replies inline. If you'd prefer to talk directly, I'm in the #beast channel of cpplang Slack: https://cppalliance.org/slack/ On Wed, 25 Nov 2020 at 11:55, Dominique Devienne via Boost-users < boost-users@lists.boost.org> wrote:
Hi. I'm writing a small interaction between: 1) a Boost.Beast-based HTTP server (based on http_server_async.cpp example) 2) a Boost.Beast-based HTTP client (based on http_client_sync.cpp example)
When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server):
2020-11-25T09:46:37.473936 Prologue in 0.001s 2020-11-25T09:46:37.482470 Resolved in 0.008s 2020-11-25T09:46:38.506180 Connected in 1.023s 2020-11-25T09:46:38.509607 OK: Authenticated; in 0.003s
While the same on Linux (RH7.5) is just over 2ms:
2020-11-25T09:45:45.515926 Prologue in 0.000s 2020-11-25T09:45:45.517083 Resolved in 0.001s 2020-11-25T09:45:45.517550 Connected in 0.000s 2020-11-25T09:45:45.518010 OK: Authenticated; in 0.000s
That's a huge difference! Almost 500x...
And when I contact the same HTTP server on Windows, but from Chrome this time, it takes about 300ms, and if I hit reload rapidly, the time jumps around to as low as 3ms, and as high as 300ms (the same initial delay), with ~50ms and ~100ms in between. (I also see several different connections being established, for some reason...)
Chrome eagerly opens connections and keeps them open for as long as possible. It's probably not a good control in this case.
I've seen similar differences between Windows and Linux connection times, but with WebSocketPP-based client and server this time, also based on Boost.ASIO.
Q1: Am I doing anything wrong? I.e. is this "normal" somehow?
Difficult to say without some code so I can try to repeat your results. Are you in a position to post a small project to github that demonstrates the issue?
Q2: How come Chrome, on Windows too, is 3x faster than ASIO-based clients, with the same Beast-based server?
Difficult to say without investigating in-situ.
Given the high connect time on Windows, I thought I'd try to keep the connection open on the client, and issue several send-request/read-response pairs, using the Beast-based (sync) HTTP client, but only the first one works correctly, the 2nd errors out with:
Error: An established connection was aborted by the software in your host machine
I suspect it is a user-error, with the server closing the connection, despite the Keep-Alive HTTP header being present server-side on the request.
Very likely a user error. I'd guess at a data race or logic error on the client, but difficult to be sure at this stage.
Q3: Given the Beast HTTP examples I'm based off, would anyone have suggestions on what changes are necessary to allow the client issuing several (non-overlapping) requests to the server, on the same connection? (since connecting is so expensive).
You'd have to code up a "connection pool" and separate the concept of a "request/response" from a connection. They would be associated for the duration of the request/response only.
Q4: And for better performance, what about overlapping request/response pairs, with HTTP pipelining. How to set that up on the client and server with Beast?
Overlapping should work since even though requests are processed one at a time on the server, the tcp transmit/receive windows will buffer the traffic. Again, this presupposes separating the concerns of connection and request.
Thanks for any help on this. --DD
Happy to provide more help as more information becomes available.
PS: Great examples in Boost.Beast BTW. Thanks Vinnie. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Richard Hodges Staff Engineer C++ Alliance hodges.r@gmail.com office: +442032898513
Some replies inline.
If you'd prefer to talk directly, I'm in the #beast channel of cpplang
Slack: https://cppalliance.org/slack/
On Wed, 25 Nov 2020 at 11:55, Dominique Devienne via Boost-users
Hi. I'm writing a small interaction between: 1) a Boost.Beast-based HTTP server (based on http_server_async.cpp example) 2) a Boost.Beast-based HTTP client (based on http_client_sync.cpp example)
When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server):
2020-11-25T09:46:37.473936 Prologue in 0.001s 2020-11-25T09:46:37.482470 Resolved in 0.008s 2020-11-25T09:46:38.506180 Connected in 1.023s 2020-11-25T09:46:38.509607 OK: Authenticated; in 0.003s
While the same on Linux (RH7.5) is just over 2ms:
2020-11-25T09:45:45.515926 Prologue in 0.000s 2020-11-25T09:45:45.517083 Resolved in 0.001s 2020-11-25T09:45:45.517550 Connected in 0.000s 2020-11-25T09:45:45.518010 OK: Authenticated; in 0.000s
That's a huge difference! Almost 500x...
And when I contact the same HTTP server on Windows, but from Chrome this time, it takes about 300ms, and if I hit reload rapidly, the time jumps around to as low as 3ms, and as high as 300ms (the same initial delay), with ~50ms and ~100ms in between. (I also see several different connections being established, for some reason...)
Chrome eagerly opens connections and keeps them open for as long as possible. It's probably not a good control in this case.
I've seen similar differences between Windows and Linux connection times, but with WebSocketPP-based client and server this time, also based on Boost.ASIO.
Q1: Am I doing anything wrong? I.e. is this "normal" somehow?
Difficult to say without some code so I can try to repeat your results. Are you in a position to post a small project to github that demonstrates the issue?
Q2: How come Chrome, on Windows too, is 3x faster than ASIO-based clients, with the same Beast-based server?
Difficult to say without investigating in-situ.
Given the high connect time on Windows, I thought I'd try to keep the connection open on the client, and issue several send-request/read-response pairs, using the Beast-based (sync) HTTP client, but only the first one works correctly, the 2nd errors out with:
Error: An established connection was aborted by the software in your host machine
I suspect it is a user-error, with the server closing the connection, despite the Keep-Alive HTTP header being present server-side on the request.
Very likely a user error. I'd guess at a data race or logic error on the client, but difficult to be sure at this stage.
Q3: Given the Beast HTTP examples I'm based off, would anyone have suggestions on what changes are necessary to allow the client issuing several (non-overlapping) requests to the server, on the same connection? (since connecting is so expensive).
You'd have to code up a "connection pool" and separate the concept of a "request/response" from a connection. They would be associated for the duration of the request/response only.
Q4: And for better performance, what about overlapping request/response pairs, with HTTP pipelining. How to set that up on the client and server with Beast?
Overlapping should work since even though requests are processed one at a time on the server, the tcp transmit/receive windows will buffer the traffic. Again, this presupposes separating the concerns of connection and request.
Thanks for any help on this. --DD
Happy to provide more help as more information becomes available. -- Richard Hodges Staff Engineer C++ Alliance hodges.r@gmail.com office: +442032898513 On Wed, 25 Nov 2020 at 11:55, Dominique Devienne via Boost-users < boost-users@lists.boost.org> wrote:
Hi. I'm writing a small interaction between: 1) a Boost.Beast-based HTTP server (based on http_server_async.cpp example) 2) a Boost.Beast-based HTTP client (based on http_client_sync.cpp example)
When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server):
2020-11-25T09:46:37.473936 Prologue in 0.001s 2020-11-25T09:46:37.482470 Resolved in 0.008s 2020-11-25T09:46:38.506180 Connected in 1.023s 2020-11-25T09:46:38.509607 OK: Authenticated; in 0.003s
While the same on Linux (RH7.5) is just over 2ms:
2020-11-25T09:45:45.515926 Prologue in 0.000s 2020-11-25T09:45:45.517083 Resolved in 0.001s 2020-11-25T09:45:45.517550 Connected in 0.000s 2020-11-25T09:45:45.518010 OK: Authenticated; in 0.000s
That's a huge difference! Almost 500x...
And when I contact the same HTTP server on Windows, but from Chrome this time, it takes about 300ms, and if I hit reload rapidly, the time jumps around to as low as 3ms, and as high as 300ms (the same initial delay), with ~50ms and ~100ms in between. (I also see several different connections being established, for some reason...)
I've seen similar differences between Windows and Linux connection times, but with WebSocketPP-based client and server this time, also based on Boost.ASIO.
Q1: Am I doing anything wrong? I.e. is this "normal" somehow?
Q2: How come Chrome, on Windows too, is 3x faster than ASIO-based clients, with the same Beast-based server?
Given the high connect time on Windows, I thought I'd try to keep the connection open on the client, and issue several send-request/read-response pairs, using the Beast-based (sync) HTTP client, but only the first one works correctly, the 2nd errors out with:
Error: An established connection was aborted by the software in your host machine
I suspect it is a user-error, with the server closing the connection, despite the Keep-Alive HTTP header being present server-side on the request.
Q3: Given the Beast HTTP examples I'm based off, would anyone have suggestions on what changes are necessary to allow the client issuing several (non-overlapping) requests to the server, on the same connection? (since connecting is so expensive).
Q4: And for better performance, what about overlapping request/response pairs, with HTTP pipelining. How to set that up on the client and server with Beast?
Thanks for any help on this. --DD
PS: Great examples in Boost.Beast BTW. Thanks Vinnie. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212
On 25/11/2020 10:54, Dominique Devienne via Boost-users wrote:
Hi. I'm writing a small interaction between: 1) a Boost.Beast-based HTTP server (based on http_server_async.cpp example) 2) a Boost.Beast-based HTTP client (based on http_client_sync.cpp example)
When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server):
2020-11-25T09:46:37.473936 Prologue in 0.001s 2020-11-25T09:46:37.482470 Resolved in 0.008s 2020-11-25T09:46:38.506180 Connected in 1.023s 2020-11-25T09:46:38.509607 OK: Authenticated; in 0.003s
While the same on Linux (RH7.5) is just over 2ms:
2020-11-25T09:45:45.515926 Prologue in 0.000s 2020-11-25T09:45:45.517083 Resolved in 0.001s 2020-11-25T09:45:45.517550 Connected in 0.000s 2020-11-25T09:45:45.518010 OK: Authenticated; in 0.000s
That's a huge difference! Almost 500x...
I can confirm that from libcurl wrapping ASIO, initiating a new HTTPS connection takes almost the same time whether from Linux or Windows. It's a lot faster than one second as well. Is it possible that Windows is trying IPv6 first, but there is no IPv6 route? Niall
On Wed, Nov 25, 2020 at 6:46 PM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
On 25/11/2020 10:54, Dominique Devienne via Boost-users wrote:
When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server): 2020-11-25T09:46:38.506180 Connected in 1.023s
While the same on Linux (RH7.5) is just over 2ms: 2020-11-25T09:45:45.517550 Connected in 0.000s
That's a huge difference! Almost 500x...
I can confirm that from libcurl wrapping ASIO, initiating a new HTTPS connection takes almost the same time whether from Linux or Windows.
int main(int argc, char* argv[]) { // Check command line arguments. if (argc != 5) { std::cerr << [...] " http-server-async 0.0.0.0 8080 . 1\n"; return EXIT_FAILURE; } auto const address = net::ip::make_address(argv[1]); [...] net::io_context ioc{threads}; // Create and launch a listening port std::make_shared<listener>( ioc, tcp::endpoint{address, port}, doc_root)->run(); [...] return EXIT_SUCCESS; }
Niall _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
(Sorry, incomplete email; CTRL+Return didn't do what I was expecting...)
On Mon, Dec 14, 2020 at 7:10 PM Dominique Devienne
On Wed, Nov 25, 2020 at 6:46 PM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
On 25/11/2020 10:54, Dominique Devienne via Boost-users wrote:
When I log the time it takes to resolve the server address and connect to it, then issue my HTTP request, it takes over 1 second (Win10, VS2019, C++17, Release, localhost for both client and server): 2020-11-25T09:46:38.506180 Connected in 1.023s
While the same on Linux (RH7.5) is just over 2ms: 2020-11-25T09:45:45.517550 Connected in 0.000s
That's a huge difference! Almost 500x...
I can confirm that from libcurl wrapping ASIO, initiating a new HTTPS connection takes almost the same time whether from Linux or Windows.
I finally figured this out, mostly by chance... As the extract below shows, Beast's example uses tcp::endpoint{net::ip::make_address("0.0.0.0"), port} in its sample usage, and I had copy/pasted that in my code (using "0.0.0.0"). This works, in that the server starts fine, and clients work fine too, but leads to those > 1,000ms client connection times on Windows. While using e.g. tcp::endpoint{ tcp::v6(), port } results in 20-30ms connection times on Windows. (2-3ms on Linux). So user error again. But maybe that usage example should use something else? I guess taking an explicit IP is in case the server has multiple network interfaces / IPs? Not sure what could replace it, to save the next poor soul from making the same mistake I did. --DD PS: Niall, I've still seeing 10x between Win10 and RH7, but that's better than 500x, and that's w/o SSL. Even with the server on Win10, and the client on Linux, it's still 3ms. So it's the Win10 client that's slow somehow I guess.
int main(int argc, char* argv[]) { // Check command line arguments. if (argc != 5) { std::cerr << [...] " http-server-async 0.0.0.0 8080 . 1\n"; return EXIT_FAILURE; } auto const address = net::ip::make_address(argv[1]); [...] net::io_context ioc{threads};
// Create and launch a listening port std::make_shared<listener>( ioc, tcp::endpoint{address, port}, doc_root)->run(); [...] return EXIT_SUCCESS; }
On 14/12/2020 18:27, Dominique Devienne via Boost-users wrote:
I finally figured this out, mostly by chance...
As the extract below shows, Beast's example uses tcp::endpoint{net::ip::make_address("0.0.0.0"), port} in its sample usage, and I had copy/pasted that in my code (using "0.0.0.0"). This works, in that the server starts fine, and clients work fine too, but leads to those > 1,000ms client connection times on Windows.
Now you describe this, this is familiar. Windows has always done this. Sorry I didn't think of it sooner.
While using e.g. tcp::endpoint{ tcp::v6(), port } results in 20-30ms connection times on Windows. (2-3ms on Linux).
If you're on loopback and you'd like to close that gap, you need to turn on SIO_LOOPBACK_FASTPATH. I've no idea if ASIO can do that for you, I'm still on a pre-churn ASIO. An alternative is to bind to a real NIC, and not go via loopback. If you're on a real NIC over a real network, I find a 20-30ms connection time abnormal. Windows 10 has a fast TCP stack, compared to previous Windows. It should be single digit milliseconds. Niall
On Mon, Dec 14, 2020 at 7:42 PM Niall Douglas via Boost-users < boost-users@lists.boost.org> wrote:
While using e.g. tcp::endpoint{ tcp::v6(), port } results in 20-30ms connection times on Windows. (2-3ms on Linux).
If you're on loopback and you'd like to close that gap, you need to turn on SIO_LOOPBACK_FASTPATH. I've no idea if ASIO can do that for you, I'm still on a pre-churn ASIO. An alternative is to bind to a real NIC, and not go via loopback.
No sure to follow. Loopback only allows local (same host) connection, no? What do you mean bind to a real NIC? That actual DHCP allocation IP? If you're on a real NIC over a real network, I find a 20-30ms connection
time abnormal. Windows 10 has a fast TCP stack, compared to previous Windows. It should be single digit milliseconds.
Yes to both. Not sure how to diagnose this further though. --DD
If you're on loopback and you'd like to close that gap, you need to turn on SIO_LOOPBACK_FASTPATH. I've no idea if ASIO can do that for you, I'm still on a pre-churn ASIO. An alternative is to bind to a real NIC, and not go via loopback.
You can set any option you want on the underlying socket with socket::native_handle() https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/basic_st... -- Richard Hodges hodges.r@gmail.com office: +442032898513 home: +376841522 mobile: +376380212
On Wed, Nov 25, 2020 at 2:55 AM Dominique Devienne via Boost-users
Given the high connect time on Windows, I thought I'd try to keep the connection open on the client, and issue several send-request/read-response pairs, using the Beast-based (sync) HTTP client, but only the first one works correctly, the 2nd errors out with:
Error: An established connection was aborted by the software in your host machine
This means that the server closed the connection. Are you setting HTTP/1.1 in the request? For HTTP/1.1 the default is to enable keep-alive. But for 1.0 the default is to close the connection. It sounds like your connection is being closed after one request. Beast definitely supports keep-alive, so this is certainly user error. Please inspect the request/response messages taking place and post them on the list unmodified (use a wire sniffer for this if necessary) if you are still having problems. Thanks
participants (4)
-
Dominique Devienne
-
Niall Douglas
-
Richard Hodges
-
Vinnie Falco