1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2014 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #ifndef ASIO_SERVER_HTTP2_HANDLER_H
26 #define ASIO_SERVER_HTTP2_HANDLER_H
27 
28 #include "nghttp2_config.h"
29 
30 #include <map>
31 #include <functional>
32 #include <string>
33 
34 #include <boost/array.hpp>
35 
36 #include <nghttp2/asio_http2_server.h>
37 
38 namespace nghttp2 {
39 namespace asio_http2 {
40 namespace server {
41 
42 class http2_handler;
43 class stream;
44 class serve_mux;
45 
46 struct callback_guard {
47   callback_guard(http2_handler &h);
48   ~callback_guard();
49   http2_handler &handler;
50 };
51 
52 using connection_write = std::function<void(void)>;
53 
54 class http2_handler : public std::enable_shared_from_this<http2_handler> {
55 public:
56   http2_handler(boost::asio::io_service &io_service,
57                 boost::asio::ip::tcp::endpoint ep, connection_write writefun,
58                 serve_mux &mux);
59 
60   ~http2_handler();
61 
62   int start();
63 
64   stream *create_stream(int32_t stream_id);
65   void close_stream(int32_t stream_id);
66   stream *find_stream(int32_t stream_id);
67 
68   void call_on_request(stream &s);
69 
70   bool should_stop() const;
71 
72   int start_response(stream &s);
73 
74   int submit_trailer(stream &s, header_map h);
75 
76   void stream_error(int32_t stream_id, uint32_t error_code);
77 
78   void initiate_write();
79 
80   void enter_callback();
81   void leave_callback();
82 
83   void resume(stream &s);
84 
85   response *push_promise(boost::system::error_code &ec, stream &s,
86                          std::string method, std::string raw_path_query,
87                          header_map h);
88 
89   void signal_write();
90 
91   boost::asio::io_service &io_service();
92 
93   const boost::asio::ip::tcp::endpoint &remote_endpoint();
94 
95   const std::string &http_date();
96 
97   template <size_t N>
on_read(const boost::array<uint8_t,N> & buffer,std::size_t len)98   int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
99     callback_guard cg(*this);
100 
101     int rv;
102 
103     rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
104 
105     if (rv < 0) {
106       return -1;
107     }
108 
109     return 0;
110   }
111 
112   template <size_t N>
on_write(boost::array<uint8_t,N> & buffer,std::size_t & len)113   int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
114     callback_guard cg(*this);
115 
116     len = 0;
117 
118     if (buf_) {
119       std::copy_n(buf_, buflen_, std::begin(buffer));
120 
121       len += buflen_;
122 
123       buf_ = nullptr;
124       buflen_ = 0;
125     }
126 
127     for (;;) {
128       const uint8_t *data;
129       auto nread = nghttp2_session_mem_send(session_, &data);
130       if (nread < 0) {
131         return -1;
132       }
133 
134       if (nread == 0) {
135         break;
136       }
137 
138       if (len + nread > buffer.size()) {
139         buf_ = data;
140         buflen_ = nread;
141 
142         break;
143       }
144 
145       std::copy_n(data, nread, std::begin(buffer) + len);
146 
147       len += nread;
148     }
149 
150     return 0;
151   }
152 
153 private:
154   std::map<int32_t, std::shared_ptr<stream>> streams_;
155   connection_write writefun_;
156   serve_mux &mux_;
157   boost::asio::io_service &io_service_;
158   boost::asio::ip::tcp::endpoint remote_ep_;
159   nghttp2_session *session_;
160   const uint8_t *buf_;
161   std::size_t buflen_;
162   bool inside_callback_;
163   // true if we have pending on_write call.  This avoids repeated call
164   // of io_service::post.
165   bool write_signaled_;
166   time_t tstamp_cached_;
167   std::string formatted_date_;
168 };
169 
170 } // namespace server
171 } // namespace asio_http2
172 } // namespace nghttp2
173 
174 #endif // ASIO_SERVER_HTTP2_HANDLER_H
175