-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code for communicating with another host via HTTP or HTTPS #823
Conversation
@joka921 GitHub shows 47 changed files for this PR. However, it is really just six files where non-trivial stuff happens, the rest is due to renaming
|
Add a new class HttpClient which can open HTTP and HTTPS connections to a given host, and which can then send GET or POST requests, receive the result synchronously, and write it to a (potentially very large) string. Uses Boost.Beast, just like our HttpServer. Add a combined test for our HttpServer and HttpClient (for a meaningful test one needs a server and a client). So far only tests the HTTP case because our HttpServer does not do HTTPS yet. Used the occasion to rename util/HttpServer to util/http because httpServer was simply a misnomer, which became even more apparent with the addition of HttpClient. This change required many small changes (in includes and in the various CMakeLists.txt).
d6f4e57
to
0a221bf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already a great start.
I have some minor syntactical suggestions and very few questions that are worth discussion.
Currently some CI checks are failing:
- You have to add
OpenSSL-dev
to the Dockerfile(s), this is done in your Service-PR but is also required here. - The failing of GCC12 seems odd to me. It is probably also related to our hacking of boost-asio. Can you try if it goes away if you include
<utility>
in ourbeast.h
? That include has to appear before the include of the firstBoost::Asio
header.
Note to self: rename header |
Note: The seemingly large diff concerning `src/util/http/ContentEncodingHelper.h` stems from the fact that Robin stored that file (as well as several others) in DOS format. I changed it to UNIX format. This only affects the whitespace in the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have two very minor suggestions,
And three not so minor (in relevance, not in size) requests concerning threadsafety.
test/HttpTest.cpp
Outdated
httpServer.exitServerLoopAfterNextRequest(); | ||
HttpClient httpClient; | ||
// Run the server in its own thread. | ||
std::jthread httpServerThread([&]() { httpServer.run(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not neccesary neither correct to put the httpServer.run()
call into a jthread
- Necessary: The server internally creates the threads on which it actually runs (always at least two), it doesn't hijack the calling threads.
- Incorrect: You assume, that the
server.run()
in thejthread
completes before your first client sends a response. That are quite some assumptions on thread scheduling and the network stack that nobody guarantees to you.
TLDR:
A plain httpServer.run()
inside the main thread should suffice and be correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought so, too, initially, and it was the very first thing I did when I wrote this test, but the httpServer.run()
blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, that behavior is even described in detail in the run()
function...
To make it correct, let the Server also have an atomic<bool>
that is called isReady()
or something which is set to true in the listener after the _acceptor.listen()
has succeeded. Your test code can then wait for this bool to become true, and everything is safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answers to Hannah's questions concerning my previous review.
test/HttpTest.cpp
Outdated
httpServer.exitServerLoopAfterNextRequest(); | ||
HttpClient httpClient; | ||
// Run the server in its own thread. | ||
std::jthread httpServerThread([&]() { httpServer.run(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, that behavior is even described in detail in the run()
function...
To make it correct, let the Server also have an atomic<bool>
that is called isReady()
or something which is set to true in the listener after the _acceptor.listen()
has succeeded. Your test code can then wait for this bool to become true, and everything is safe.
Except waiting for the server to be ready in the next. I would prefer to do that together in a 1-1 session, since I am not sure what the best design here is.
Also made the tests safe for the case that the running of the server fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small comments from my own changes.
src/util/http/HttpServer.h
Outdated
net::strand<net::io_context::executor_type> _acceptorStrand = | ||
net::make_strand(_ioContext); | ||
std::atomic<bool> _serverIsReady = false; | ||
std::atomic<bool> _shutDownAfterNextConnectionIsAccepted = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable is now unused.
// next connection right away). | ||
// While the `_acceptor` is open, accept connections and handle each | ||
// connection asynchronously (so that we are ready to accept the next | ||
// connection right away). The `_acceptor` will be closed via the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// connection right away). The `_acceptor` will be closed via the | |
// connection right away). The `_acceptor` can be closed via the |
// While the `_acceptor` is open, accept connections and handle each | ||
// connection asynchronously (so that we are ready to accept the next | ||
// connection right away). The `_acceptor` will be closed via the | ||
// `shutdown` method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// `shutdown` method. | |
// `shutdown` method. This currently only happens in `test/HttpTest.cpp` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much, I think we have converged to some version we both agree with.
Add a new class HttpClient which can open HTTP and HTTPS connections to a given host at a given port, and which can then send GET or POST requests, receive the response synchronously, and write it to a (potentially very large) string. Using Boost.Beast, just like for our HttpServer class.
Add a combined test for our HttpServer and HttpClient (for a meaningful test one needs a server and a client). So far, only tests the HTTP case because our HttpServer does not do HTTPS yet. But that is already much better than before when our HTTP communication is not tested at all.
Use the occasion to rename util/HttpServer (a directory with quite a few files in it) to util/http because httpServer was simply a misnomer. This became very apparent with the addition of the .h and .cpp file for HttpClient. This renaming of the directory required many small changes (in the includes and in the various CMakeLists.txt).
This will be needed in the PR for the support of the SERVICE clause, which comes when we are finished with this one.