Line data Source code
1 : #ifndef THINGER_HTTP_SERVER_RESPONSE_SINK_HPP
2 : #define THINGER_HTTP_SERVER_RESPONSE_SINK_HPP
3 :
4 : #include "../common/http_response.hpp"
5 : #include "../../util/types.hpp"
6 : #include "server_connection.hpp"
7 : #include "http_stream.hpp"
8 : #include "response_observer.hpp"
9 : #include <memory>
10 : #include <string>
11 :
12 : namespace thinger::http {
13 :
14 : class request;
15 :
16 : // How the body of a streamed response is delimited (see response::start_stream)
17 : enum class body_framing {
18 : none, // no body (a response to HEAD, 1xx, 204, 304): the head alone
19 : length, // Content-Length, already set in the head
20 : chunked, // chunked transfer coding
21 : close // closing the connection at its end (HTTP/1.0 clients)
22 : };
23 :
24 : // Where a response is written: a connection, or memory for requests dispatched in memory.
25 : // Shared by every copy of a response, which writes it once: whole (send), in chunks
26 : // (begin, append..., finish), streamed with flow control (begin_stream, write_stream...,
27 : // end_stream or abort_stream), or followed by a connection takeover (take_over).
28 : class response_sink {
29 : public:
30 28535 : virtual ~response_sink() = default;
31 :
32 : // Whole response
33 : virtual void send(std::shared_ptr<http_response> response) = 0;
34 :
35 : // Chunked response: the headers, then the data, then the end. They return false if
36 : // the response can no longer be written (e.g. the connection is gone)
37 : virtual bool begin(std::shared_ptr<http_response> headers) = 0;
38 : virtual bool append(const std::string& data) = 0;
39 : virtual bool finish() = 0;
40 :
41 : // Streamed response: the head (its framing headers are set by the sink), then the
42 : // body, each write completing once its data is written (false if it could not be),
43 : // then the end; or an abort, ending the response so the client sees it incomplete.
44 : // By default they are the chunked response above, collected at once.
45 6 : virtual bool begin_stream(std::shared_ptr<http_response> head, body_framing /*framing*/) {
46 6 : return begin(std::move(head));
47 : }
48 6 : virtual awaitable<bool> write_stream(std::string data) {
49 : co_return append(data);
50 12 : }
51 3 : virtual awaitable<bool> end_stream() {
52 : co_return finish();
53 6 : }
54 0 : virtual void abort_stream() {
55 0 : finish();
56 0 : }
57 :
58 : // Whether the connection can be taken over (WebSocket, SSE, response::take_over)
59 12 : virtual bool can_take_over() const { return false; }
60 :
61 : // Send the response, then hand the connection to `handler` (see response::take_over).
62 : // `framing` is close when what the handler writes is the body of the response, ending
63 : // when the connection closes: the response says so
64 0 : virtual void take_over(std::shared_ptr<http_response> /*response*/, takeover_handler /*handler*/,
65 0 : body_framing /*framing*/) {}
66 :
67 : // Connection the response is written to, null if none
68 3 : virtual std::shared_ptr<server_connection> connection() const { return nullptr; }
69 :
70 : // Tell `observer` about the response written (its head, the bytes written and its end),
71 : // for the response hook (see http_application::on_response). Called before the response
72 : // is answered. A sink ignoring it, as by default, has its responses reported as aborted
73 0 : virtual void observe(std::shared_ptr<response_observer> /*observer*/) {}
74 : };
75 :
76 : // Response written to a stream of a server connection
77 : class connection_sink : public response_sink {
78 : public:
79 24 : connection_sink(const std::shared_ptr<server_connection>& connection, const std::shared_ptr<http_stream>& stream)
80 24 : : connection_(connection), stream_(stream) {}
81 :
82 : // For a request read from a connection: the connection and stream it came from (copied as
83 : // the request holds them, without locking them)
84 : explicit connection_sink(const request& req);
85 :
86 : void send(std::shared_ptr<http_response> response) override;
87 : bool begin(std::shared_ptr<http_response> headers) override;
88 : bool append(const std::string& data) override;
89 : bool finish() override;
90 : bool begin_stream(std::shared_ptr<http_response> head, body_framing framing) override;
91 : awaitable<bool> write_stream(std::string data) override;
92 : awaitable<bool> end_stream() override;
93 : void abort_stream() override;
94 4497 : bool can_take_over() const override { return true; }
95 : void take_over(std::shared_ptr<http_response> response, takeover_handler handler,
96 : body_framing framing) override;
97 279 : std::shared_ptr<server_connection> connection() const override { return connection_.lock(); }
98 : void observe(std::shared_ptr<response_observer> observer) override;
99 :
100 : private:
101 : // Queue a frame on the stream; false if the connection is gone
102 : bool write(std::shared_ptr<http_frame> frame);
103 :
104 : // Queue a frame and wait until it is written; false if it could not be
105 : awaitable<bool> write_and_wait(std::shared_ptr<http_frame> frame);
106 :
107 : // Close the connection once this response is written, for `reason` (see
108 : // http_stream::must_close()); before any frame written after this call
109 : void must_close(detail::close_reason reason);
110 :
111 : std::weak_ptr<server_connection> connection_;
112 : std::weak_ptr<http_stream> stream_;
113 :
114 : // Framing of a streamed response
115 : body_framing framing_ = body_framing::none;
116 : };
117 :
118 : } // namespace thinger::http
119 :
120 : #endif // THINGER_HTTP_SERVER_RESPONSE_SINK_HPP
|