]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // |
2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | // Official repository: https://github.com/boostorg/beast | |
8 | // | |
9 | ||
10 | #ifndef BOOST_BEAST_CORE_IMPL_ICY_STREAM_HPP | |
11 | #define BOOST_BEAST_CORE_IMPL_ICY_STREAM_HPP | |
12 | ||
13 | #include <boost/beast/core/async_base.hpp> | |
14 | #include <boost/beast/core/buffer_traits.hpp> | |
15 | #include <boost/beast/core/error.hpp> | |
16 | #include <boost/beast/core/stream_traits.hpp> | |
17 | #include <boost/beast/core/detail/is_invocable.hpp> | |
18 | #include <boost/asio/coroutine.hpp> | |
19 | #include <boost/assert.hpp> | |
20 | #include <boost/throw_exception.hpp> | |
21 | #include <cstring> | |
22 | #include <memory> | |
23 | #include <utility> | |
24 | ||
25 | namespace boost { | |
26 | namespace beast { | |
27 | namespace http { | |
28 | ||
29 | namespace detail { | |
30 | ||
31 | template<class ConstBufferSequence> | |
32 | boost::tribool | |
33 | is_icy(ConstBufferSequence const& buffers) | |
34 | { | |
35 | char buf[3]; | |
36 | auto const n = net::buffer_copy( | |
37 | net::mutable_buffer(buf, 3), | |
38 | buffers); | |
39 | if(n >= 1 && buf[0] != 'I') | |
40 | return false; | |
41 | if(n >= 2 && buf[1] != 'C') | |
42 | return false; | |
43 | if(n >= 3 && buf[2] != 'Y') | |
44 | return false; | |
45 | if(n < 3) | |
46 | return boost::indeterminate; | |
47 | return true; | |
48 | } | |
49 | ||
50 | } // detail | |
51 | ||
52 | template<class NextLayer> | |
53 | struct icy_stream<NextLayer>::ops | |
54 | { | |
55 | ||
56 | template<class Buffers, class Handler> | |
57 | class read_op | |
58 | : public beast::async_base<Handler, | |
59 | beast::executor_type<icy_stream>> | |
60 | , public asio::coroutine | |
61 | { | |
62 | icy_stream& s_; | |
63 | Buffers b_; | |
64 | std::size_t n_ = 0; | |
65 | error_code ec_; | |
66 | bool match_ = false; | |
67 | ||
68 | public: | |
69 | template<class Handler_> | |
70 | read_op( | |
71 | Handler_&& h, | |
72 | icy_stream& s, | |
73 | Buffers const& b) | |
74 | : async_base<Handler, | |
75 | beast::executor_type<icy_stream>>( | |
76 | std::forward<Handler_>(h), s.get_executor()) | |
77 | , s_(s) | |
78 | , b_(b) | |
79 | { | |
80 | (*this)({}, 0, false); | |
81 | } | |
82 | ||
83 | void | |
84 | operator()( | |
85 | error_code ec, | |
86 | std::size_t bytes_transferred, | |
87 | bool cont = true) | |
88 | { | |
89 | BOOST_ASIO_CORO_REENTER(*this) | |
90 | { | |
91 | if(s_.detect_) | |
92 | { | |
93 | BOOST_ASSERT(s_.n_ == 0); | |
94 | for(;;) | |
95 | { | |
96 | // Try to read the first three characters | |
97 | BOOST_ASIO_CORO_YIELD | |
20effc67 TL |
98 | { |
99 | BOOST_ASIO_HANDLER_LOCATION(( | |
100 | __FILE__, __LINE__, | |
101 | "http::icy_stream::async_read_some")); | |
102 | ||
103 | s_.next_layer().async_read_some( | |
104 | net::mutable_buffer( | |
105 | s_.buf_ + s_.n_, 3 - s_.n_), | |
106 | std::move(*this)); | |
107 | } | |
92f5a8d4 TL |
108 | s_.n_ += static_cast<char>(bytes_transferred); |
109 | if(ec) | |
110 | goto upcall; | |
111 | auto result = detail::is_icy( | |
112 | net::const_buffer(s_.buf_, s_.n_)); | |
113 | if(boost::indeterminate(result)) | |
114 | continue; | |
115 | if(result) | |
116 | s_.n_ = static_cast<char>(net::buffer_copy( | |
117 | net::buffer(s_.buf_, sizeof(s_.buf_)), | |
118 | icy_stream::version())); | |
119 | break; | |
120 | } | |
121 | s_.detect_ = false; | |
122 | } | |
123 | if(s_.n_ > 0) | |
124 | { | |
125 | bytes_transferred = net::buffer_copy( | |
126 | b_, net::const_buffer(s_.buf_, s_.n_)); | |
127 | s_.n_ -= static_cast<char>(bytes_transferred); | |
128 | std::memmove( | |
129 | s_.buf_, | |
130 | s_.buf_ + bytes_transferred, | |
131 | sizeof(s_.buf_) - bytes_transferred); | |
132 | } | |
133 | else | |
134 | { | |
135 | BOOST_ASIO_CORO_YIELD | |
20effc67 TL |
136 | { |
137 | BOOST_ASIO_HANDLER_LOCATION(( | |
138 | __FILE__, __LINE__, | |
139 | "http::icy_stream::async_read_some")); | |
140 | ||
141 | s_.next_layer().async_read_some( | |
142 | b_, std::move(*this)); | |
143 | } | |
92f5a8d4 TL |
144 | } |
145 | upcall: | |
146 | if(! cont) | |
147 | { | |
148 | ec_ = ec; | |
149 | n_ = bytes_transferred; | |
150 | BOOST_ASIO_CORO_YIELD | |
20effc67 TL |
151 | { |
152 | BOOST_ASIO_HANDLER_LOCATION(( | |
153 | __FILE__, __LINE__, | |
154 | "http::icy_stream::async_read_some")); | |
155 | ||
156 | s_.next_layer().async_read_some( | |
157 | net::mutable_buffer{}, | |
158 | std::move(*this)); | |
159 | } | |
92f5a8d4 TL |
160 | ec = ec_; |
161 | bytes_transferred = n_; | |
162 | } | |
163 | this->complete_now(ec, bytes_transferred); | |
164 | } | |
165 | } | |
166 | }; | |
167 | ||
168 | struct run_read_op | |
169 | { | |
170 | template<class ReadHandler, class Buffers> | |
171 | void | |
172 | operator()( | |
173 | ReadHandler&& h, | |
174 | icy_stream* s, | |
175 | Buffers const& b) | |
176 | { | |
177 | // If you get an error on the following line it means | |
178 | // that your handler does not meet the documented type | |
179 | // requirements for the handler. | |
180 | ||
181 | static_assert( | |
182 | beast::detail::is_invocable<ReadHandler, | |
183 | void(error_code, std::size_t)>::value, | |
184 | "ReadHandler type requirements not met"); | |
185 | ||
186 | read_op< | |
187 | Buffers, | |
188 | typename std::decay<ReadHandler>::type>( | |
189 | std::forward<ReadHandler>(h), *s, b); | |
190 | } | |
191 | }; | |
192 | ||
193 | }; | |
194 | ||
195 | //------------------------------------------------------------------------------ | |
196 | ||
197 | template<class NextLayer> | |
198 | template<class... Args> | |
199 | icy_stream<NextLayer>:: | |
200 | icy_stream(Args&&... args) | |
201 | : stream_(std::forward<Args>(args)...) | |
202 | { | |
203 | std::memset(buf_, 0, sizeof(buf_)); | |
204 | } | |
205 | ||
206 | template<class NextLayer> | |
207 | template<class MutableBufferSequence> | |
208 | std::size_t | |
209 | icy_stream<NextLayer>:: | |
210 | read_some(MutableBufferSequence const& buffers) | |
211 | { | |
212 | static_assert(is_sync_read_stream<next_layer_type>::value, | |
213 | "SyncReadStream type requirements not met"); | |
214 | static_assert(net::is_mutable_buffer_sequence< | |
215 | MutableBufferSequence>::value, | |
216 | "MutableBufferSequence type requirements not met"); | |
217 | error_code ec; | |
218 | auto n = read_some(buffers, ec); | |
219 | if(ec) | |
220 | BOOST_THROW_EXCEPTION(system_error{ec}); | |
221 | return n; | |
222 | } | |
223 | ||
224 | template<class NextLayer> | |
225 | template<class MutableBufferSequence> | |
226 | std::size_t | |
227 | icy_stream<NextLayer>:: | |
228 | read_some(MutableBufferSequence const& buffers, error_code& ec) | |
229 | { | |
230 | static_assert(is_sync_read_stream<next_layer_type>::value, | |
231 | "SyncReadStream type requirements not met"); | |
232 | static_assert(net::is_mutable_buffer_sequence< | |
233 | MutableBufferSequence>::value, | |
234 | "MutableBufferSequence type requirements not met"); | |
235 | std::size_t bytes_transferred; | |
236 | if(detect_) | |
237 | { | |
238 | BOOST_ASSERT(n_ == 0); | |
239 | for(;;) | |
240 | { | |
241 | // Try to read the first three characters | |
242 | bytes_transferred = next_layer().read_some( | |
243 | net::mutable_buffer(buf_ + n_, 3 - n_), ec); | |
244 | n_ += static_cast<char>(bytes_transferred); | |
245 | if(ec) | |
246 | return 0; | |
247 | auto result = detail::is_icy( | |
248 | net::const_buffer(buf_, n_)); | |
249 | if(boost::indeterminate(result)) | |
250 | continue; | |
251 | if(result) | |
252 | n_ = static_cast<char>(net::buffer_copy( | |
253 | net::buffer(buf_, sizeof(buf_)), | |
254 | icy_stream::version())); | |
255 | break; | |
256 | } | |
257 | detect_ = false; | |
258 | } | |
259 | if(n_ > 0) | |
260 | { | |
261 | bytes_transferred = net::buffer_copy( | |
262 | buffers, net::const_buffer(buf_, n_)); | |
263 | n_ -= static_cast<char>(bytes_transferred); | |
264 | std::memmove( | |
265 | buf_, | |
266 | buf_ + bytes_transferred, | |
267 | sizeof(buf_) - bytes_transferred); | |
268 | } | |
269 | else | |
270 | { | |
271 | bytes_transferred = | |
272 | next_layer().read_some(buffers, ec); | |
273 | } | |
274 | return bytes_transferred; | |
275 | } | |
276 | ||
277 | template<class NextLayer> | |
278 | template< | |
279 | class MutableBufferSequence, | |
20effc67 | 280 | BOOST_BEAST_ASYNC_TPARAM2 ReadHandler> |
92f5a8d4 TL |
281 | BOOST_BEAST_ASYNC_RESULT2(ReadHandler) |
282 | icy_stream<NextLayer>:: | |
283 | async_read_some( | |
284 | MutableBufferSequence const& buffers, | |
285 | ReadHandler&& handler) | |
286 | { | |
287 | static_assert(is_async_read_stream<next_layer_type>::value, | |
288 | "AsyncReadStream type requirements not met"); | |
289 | static_assert(net::is_mutable_buffer_sequence< | |
290 | MutableBufferSequence >::value, | |
291 | "MutableBufferSequence type requirements not met"); | |
292 | return net::async_initiate< | |
293 | ReadHandler, | |
294 | void(error_code, std::size_t)>( | |
295 | typename ops::run_read_op{}, | |
296 | handler, | |
297 | this, | |
298 | buffers); | |
299 | } | |
300 | ||
301 | template<class NextLayer> | |
302 | template<class MutableBufferSequence> | |
303 | std::size_t | |
304 | icy_stream<NextLayer>:: | |
305 | write_some(MutableBufferSequence const& buffers) | |
306 | { | |
307 | static_assert(is_sync_write_stream<next_layer_type>::value, | |
308 | "SyncWriteStream type requirements not met"); | |
309 | static_assert(net::is_const_buffer_sequence< | |
310 | MutableBufferSequence>::value, | |
311 | "MutableBufferSequence type requirements not met"); | |
312 | return stream_.write_some(buffers); | |
313 | } | |
314 | ||
315 | template<class NextLayer> | |
316 | template<class MutableBufferSequence> | |
317 | std::size_t | |
318 | icy_stream<NextLayer>:: | |
319 | write_some(MutableBufferSequence const& buffers, error_code& ec) | |
320 | { | |
321 | static_assert(is_sync_write_stream<next_layer_type>::value, | |
322 | "SyncWriteStream type requirements not met"); | |
323 | static_assert(net::is_const_buffer_sequence< | |
324 | MutableBufferSequence>::value, | |
325 | "MutableBufferSequence type requirements not met"); | |
326 | return stream_.write_some(buffers, ec); | |
327 | } | |
328 | ||
329 | template<class NextLayer> | |
330 | template< | |
331 | class MutableBufferSequence, | |
20effc67 | 332 | BOOST_BEAST_ASYNC_TPARAM2 WriteHandler> |
92f5a8d4 TL |
333 | BOOST_BEAST_ASYNC_RESULT2(WriteHandler) |
334 | icy_stream<NextLayer>:: | |
335 | async_write_some( | |
336 | MutableBufferSequence const& buffers, | |
337 | WriteHandler&& handler) | |
338 | { | |
339 | static_assert(is_async_write_stream<next_layer_type>::value, | |
340 | "AsyncWriteStream type requirements not met"); | |
341 | static_assert(net::is_const_buffer_sequence< | |
342 | MutableBufferSequence>::value, | |
343 | "MutableBufferSequence type requirements not met"); | |
344 | return stream_.async_write_some( | |
345 | buffers, std::forward<WriteHandler>(handler)); | |
346 | } | |
347 | ||
348 | } // http | |
349 | } // beast | |
350 | } // boost | |
351 | ||
352 | #endif |