]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // |
2 | // Copyright (c) 2016-2017 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_HTTP_IMPL_FILE_BODY_WIN32_IPP | |
11 | #define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP | |
12 | ||
13 | #if BOOST_BEAST_USE_WIN32_FILE | |
14 | ||
15 | #include <boost/beast/core/bind_handler.hpp> | |
16 | #include <boost/beast/core/type_traits.hpp> | |
17 | #include <boost/beast/core/detail/clamp.hpp> | |
18 | #include <boost/beast/http/serializer.hpp> | |
19 | #include <boost/asio/associated_allocator.hpp> | |
20 | #include <boost/asio/associated_executor.hpp> | |
21 | #include <boost/asio/async_result.hpp> | |
22 | #include <boost/asio/basic_stream_socket.hpp> | |
23 | #include <boost/asio/handler_continuation_hook.hpp> | |
11fdf7f2 | 24 | #include <boost/asio/handler_invoke_hook.hpp> |
b32b8144 FG |
25 | #include <boost/asio/windows/overlapped_ptr.hpp> |
26 | #include <boost/make_unique.hpp> | |
27 | #include <boost/smart_ptr/make_shared_array.hpp> | |
28 | #include <boost/winapi/basic_types.hpp> | |
11fdf7f2 | 29 | #include <boost/winapi/get_last_error.hpp> |
b32b8144 FG |
30 | #include <algorithm> |
31 | #include <cstring> | |
32 | ||
33 | namespace boost { | |
34 | namespace beast { | |
35 | namespace http { | |
36 | ||
37 | namespace detail { | |
38 | template<class, class, bool, class> | |
39 | class write_some_win32_op; | |
40 | } // detail | |
41 | ||
42 | template<> | |
43 | struct basic_file_body<file_win32> | |
44 | { | |
45 | using file_type = file_win32; | |
46 | ||
47 | class writer; | |
48 | class reader; | |
49 | ||
50 | //-------------------------------------------------------------------------- | |
51 | ||
52 | class value_type | |
53 | { | |
54 | friend class writer; | |
55 | friend class reader; | |
56 | friend struct basic_file_body<file_win32>; | |
57 | ||
58 | template<class, class, bool, class> | |
59 | friend class detail::write_some_win32_op; | |
60 | template< | |
61 | class Protocol, bool isRequest, class Fields> | |
62 | friend | |
63 | std::size_t | |
64 | write_some( | |
65 | boost::asio::basic_stream_socket<Protocol>& sock, | |
66 | serializer<isRequest, | |
67 | basic_file_body<file_win32>, Fields>& sr, | |
68 | error_code& ec); | |
69 | ||
70 | file_win32 file_; | |
71 | std::uint64_t size_ = 0; // cached file size | |
72 | std::uint64_t first_; // starting offset of the range | |
73 | std::uint64_t last_; // ending offset of the range | |
74 | ||
75 | public: | |
76 | ~value_type() = default; | |
77 | value_type() = default; | |
78 | value_type(value_type&& other) = default; | |
79 | value_type& operator=(value_type&& other) = default; | |
80 | ||
81 | bool | |
82 | is_open() const | |
83 | { | |
84 | return file_.is_open(); | |
85 | } | |
86 | ||
87 | std::uint64_t | |
88 | size() const | |
89 | { | |
90 | return size_; | |
91 | } | |
92 | ||
93 | void | |
94 | close(); | |
95 | ||
96 | void | |
97 | open(char const* path, file_mode mode, error_code& ec); | |
98 | ||
99 | void | |
100 | reset(file_win32&& file, error_code& ec); | |
101 | }; | |
102 | ||
103 | //-------------------------------------------------------------------------- | |
104 | ||
105 | class writer | |
106 | { | |
107 | template<class, class, bool, class> | |
108 | friend class detail::write_some_win32_op; | |
109 | template< | |
110 | class Protocol, bool isRequest, class Fields> | |
111 | friend | |
112 | std::size_t | |
113 | write_some( | |
114 | boost::asio::basic_stream_socket<Protocol>& sock, | |
115 | serializer<isRequest, | |
116 | basic_file_body<file_win32>, Fields>& sr, | |
117 | error_code& ec); | |
118 | ||
119 | value_type& body_; // The body we are reading from | |
120 | std::uint64_t pos_; // The current position in the file | |
121 | char buf_[4096]; // Small buffer for reading | |
122 | ||
123 | public: | |
124 | using const_buffers_type = | |
125 | boost::asio::const_buffer; | |
126 | ||
127 | template<bool isRequest, class Fields> | |
11fdf7f2 TL |
128 | writer(header<isRequest, Fields>&, value_type& b) |
129 | : body_(b) | |
b32b8144 FG |
130 | { |
131 | } | |
132 | ||
133 | void | |
134 | init(error_code&) | |
135 | { | |
136 | BOOST_ASSERT(body_.file_.is_open()); | |
137 | pos_ = body_.first_; | |
138 | } | |
139 | ||
140 | boost::optional<std::pair<const_buffers_type, bool>> | |
141 | get(error_code& ec) | |
142 | { | |
143 | std::size_t const n = (std::min)(sizeof(buf_), | |
144 | beast::detail::clamp(body_.last_ - pos_)); | |
145 | if(n == 0) | |
146 | { | |
147 | ec.assign(0, ec.category()); | |
148 | return boost::none; | |
149 | } | |
150 | auto const nread = body_.file_.read(buf_, n, ec); | |
151 | if(ec) | |
152 | return boost::none; | |
153 | BOOST_ASSERT(nread != 0); | |
154 | pos_ += nread; | |
155 | ec.assign(0, ec.category()); | |
156 | return {{ | |
157 | {buf_, nread}, // buffer to return. | |
158 | pos_ < body_.last_}}; // `true` if there are more buffers. | |
159 | } | |
160 | }; | |
161 | ||
162 | //-------------------------------------------------------------------------- | |
163 | ||
164 | class reader | |
165 | { | |
166 | value_type& body_; | |
167 | ||
168 | public: | |
169 | template<bool isRequest, class Fields> | |
170 | explicit | |
11fdf7f2 TL |
171 | reader(header<isRequest, Fields>&, value_type& b) |
172 | : body_(b) | |
b32b8144 FG |
173 | { |
174 | } | |
175 | ||
176 | void | |
177 | init(boost::optional< | |
178 | std::uint64_t> const& content_length, | |
179 | error_code& ec) | |
180 | { | |
181 | // VFALCO We could reserve space in the file | |
182 | boost::ignore_unused(content_length); | |
183 | BOOST_ASSERT(body_.file_.is_open()); | |
184 | ec.assign(0, ec.category()); | |
185 | } | |
186 | ||
187 | template<class ConstBufferSequence> | |
188 | std::size_t | |
189 | put(ConstBufferSequence const& buffers, | |
190 | error_code& ec) | |
191 | { | |
192 | std::size_t nwritten = 0; | |
193 | for(auto buffer : beast::detail::buffers_range(buffers)) | |
194 | { | |
195 | nwritten += body_.file_.write( | |
196 | buffer.data(), buffer.size(), ec); | |
197 | if(ec) | |
198 | return nwritten; | |
199 | } | |
200 | ec.assign(0, ec.category()); | |
201 | return nwritten; | |
202 | } | |
203 | ||
204 | void | |
205 | finish(error_code& ec) | |
206 | { | |
207 | ec.assign(0, ec.category()); | |
208 | } | |
209 | }; | |
210 | ||
211 | //-------------------------------------------------------------------------- | |
212 | ||
213 | static | |
214 | std::uint64_t | |
215 | size(value_type const& body) | |
216 | { | |
217 | return body.size(); | |
218 | } | |
219 | }; | |
220 | ||
221 | //------------------------------------------------------------------------------ | |
222 | ||
223 | inline | |
224 | void | |
225 | basic_file_body<file_win32>:: | |
226 | value_type:: | |
227 | close() | |
228 | { | |
229 | error_code ignored; | |
230 | file_.close(ignored); | |
231 | } | |
232 | ||
233 | inline | |
234 | void | |
235 | basic_file_body<file_win32>:: | |
236 | value_type:: | |
237 | open(char const* path, file_mode mode, error_code& ec) | |
238 | { | |
239 | file_.open(path, mode, ec); | |
240 | if(ec) | |
241 | return; | |
242 | size_ = file_.size(ec); | |
243 | if(ec) | |
244 | { | |
245 | close(); | |
246 | return; | |
247 | } | |
248 | first_ = 0; | |
249 | last_ = size_; | |
250 | } | |
251 | ||
252 | inline | |
253 | void | |
254 | basic_file_body<file_win32>:: | |
255 | value_type:: | |
256 | reset(file_win32&& file, error_code& ec) | |
257 | { | |
258 | if(file_.is_open()) | |
259 | { | |
260 | error_code ignored; | |
261 | file_.close(ignored); | |
262 | } | |
263 | file_ = std::move(file); | |
264 | if(file_.is_open()) | |
265 | { | |
266 | size_ = file_.size(ec); | |
267 | if(ec) | |
268 | { | |
269 | close(); | |
270 | return; | |
271 | } | |
272 | first_ = 0; | |
273 | last_ = size_; | |
274 | } | |
275 | } | |
276 | ||
277 | //------------------------------------------------------------------------------ | |
278 | ||
279 | namespace detail { | |
280 | ||
281 | template<class Unsigned> | |
282 | inline | |
283 | boost::winapi::DWORD_ | |
284 | lowPart(Unsigned n) | |
285 | { | |
286 | return static_cast< | |
287 | boost::winapi::DWORD_>( | |
288 | n & 0xffffffff); | |
289 | } | |
290 | ||
291 | template<class Unsigned> | |
292 | inline | |
293 | boost::winapi::DWORD_ | |
294 | highPart(Unsigned n, std::true_type) | |
295 | { | |
296 | return static_cast< | |
297 | boost::winapi::DWORD_>( | |
298 | (n>>32)&0xffffffff); | |
299 | } | |
300 | ||
301 | template<class Unsigned> | |
302 | inline | |
303 | boost::winapi::DWORD_ | |
304 | highPart(Unsigned, std::false_type) | |
305 | { | |
306 | return 0; | |
307 | } | |
308 | ||
309 | template<class Unsigned> | |
310 | inline | |
311 | boost::winapi::DWORD_ | |
312 | highPart(Unsigned n) | |
313 | { | |
314 | return highPart(n, std::integral_constant< | |
315 | bool, (sizeof(Unsigned)>4)>{}); | |
316 | } | |
317 | ||
318 | class null_lambda | |
319 | { | |
320 | public: | |
321 | template<class ConstBufferSequence> | |
322 | void | |
323 | operator()(error_code&, | |
324 | ConstBufferSequence const&) const | |
325 | { | |
326 | BOOST_ASSERT(false); | |
327 | } | |
328 | }; | |
329 | ||
330 | //------------------------------------------------------------------------------ | |
331 | ||
332 | #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR | |
333 | ||
334 | template< | |
335 | class Protocol, class Handler, | |
336 | bool isRequest, class Fields> | |
337 | class write_some_win32_op | |
338 | { | |
339 | boost::asio::basic_stream_socket<Protocol>& sock_; | |
340 | serializer<isRequest, | |
341 | basic_file_body<file_win32>, Fields>& sr_; | |
342 | std::size_t bytes_transferred_ = 0; | |
343 | Handler h_; | |
344 | bool header_ = false; | |
345 | ||
346 | public: | |
347 | write_some_win32_op(write_some_win32_op&&) = default; | |
11fdf7f2 | 348 | write_some_win32_op(write_some_win32_op const&) = delete; |
b32b8144 FG |
349 | |
350 | template<class DeducedHandler> | |
351 | write_some_win32_op( | |
352 | DeducedHandler&& h, | |
353 | boost::asio::basic_stream_socket<Protocol>& s, | |
354 | serializer<isRequest, | |
355 | basic_file_body<file_win32>,Fields>& sr) | |
356 | : sock_(s) | |
357 | , sr_(sr) | |
358 | , h_(std::forward<DeducedHandler>(h)) | |
359 | { | |
360 | } | |
361 | ||
362 | using allocator_type = | |
363 | boost::asio::associated_allocator_t<Handler>; | |
364 | ||
365 | allocator_type | |
366 | get_allocator() const noexcept | |
367 | { | |
11fdf7f2 | 368 | return (boost::asio::get_associated_allocator)(h_); |
b32b8144 FG |
369 | } |
370 | ||
371 | using executor_type = | |
372 | boost::asio::associated_executor_t<Handler, decltype(std::declval< | |
373 | boost::asio::basic_stream_socket<Protocol>&>().get_executor())>; | |
374 | ||
375 | executor_type | |
376 | get_executor() const noexcept | |
377 | { | |
11fdf7f2 | 378 | return (boost::asio::get_associated_executor)( |
b32b8144 FG |
379 | h_, sock_.get_executor()); |
380 | } | |
381 | ||
382 | void | |
383 | operator()(); | |
384 | ||
385 | void | |
386 | operator()( | |
387 | error_code ec, | |
388 | std::size_t bytes_transferred = 0); | |
389 | ||
390 | friend | |
391 | bool asio_handler_is_continuation(write_some_win32_op* op) | |
392 | { | |
393 | using boost::asio::asio_handler_is_continuation; | |
394 | return asio_handler_is_continuation( | |
395 | std::addressof(op->h_)); | |
396 | } | |
11fdf7f2 TL |
397 | |
398 | template<class Function> | |
399 | friend | |
400 | void asio_handler_invoke(Function&& f, write_some_win32_op* op) | |
401 | { | |
402 | using boost::asio::asio_handler_invoke; | |
403 | asio_handler_invoke(f, std::addressof(op->h_)); | |
404 | } | |
b32b8144 FG |
405 | }; |
406 | ||
407 | template< | |
408 | class Protocol, class Handler, | |
409 | bool isRequest, class Fields> | |
410 | void | |
411 | write_some_win32_op< | |
412 | Protocol, Handler, isRequest, Fields>:: | |
413 | operator()() | |
414 | { | |
415 | if(! sr_.is_header_done()) | |
416 | { | |
417 | header_ = true; | |
418 | sr_.split(true); | |
11fdf7f2 | 419 | return detail::async_write_some_impl( |
b32b8144 FG |
420 | sock_, sr_, std::move(*this)); |
421 | } | |
422 | if(sr_.get().chunked()) | |
423 | { | |
11fdf7f2 | 424 | return detail::async_write_some_impl( |
b32b8144 FG |
425 | sock_, sr_, std::move(*this)); |
426 | } | |
11fdf7f2 | 427 | auto& w = sr_.writer_impl(); |
b32b8144 FG |
428 | boost::winapi::DWORD_ const nNumberOfBytesToWrite = |
429 | static_cast<boost::winapi::DWORD_>( | |
430 | (std::min<std::uint64_t>)( | |
11fdf7f2 | 431 | (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()), |
b32b8144 FG |
432 | (std::numeric_limits<boost::winapi::DWORD_>::max)())); |
433 | boost::asio::windows::overlapped_ptr overlapped{ | |
11fdf7f2 TL |
434 | sock_.get_executor().context(), std::move(*this)}; |
435 | // Note that we have moved *this, so we cannot access | |
436 | // the handler since it is now moved-from. We can still | |
437 | // access simple things like references and built-in types. | |
b32b8144 | 438 | auto& ov = *overlapped.get(); |
11fdf7f2 TL |
439 | ov.Offset = lowPart(w.pos_); |
440 | ov.OffsetHigh = highPart(w.pos_); | |
b32b8144 FG |
441 | auto const bSuccess = ::TransmitFile( |
442 | sock_.native_handle(), | |
443 | sr_.get().body().file_.native_handle(), | |
444 | nNumberOfBytesToWrite, | |
445 | 0, | |
446 | overlapped.get(), | |
447 | nullptr, | |
448 | 0); | |
11fdf7f2 | 449 | auto const dwError = boost::winapi::GetLastError(); |
b32b8144 FG |
450 | if(! bSuccess && dwError != |
451 | boost::winapi::ERROR_IO_PENDING_) | |
452 | { | |
453 | // VFALCO This needs review, is 0 the right number? | |
454 | // completed immediately (with error?) | |
11fdf7f2 | 455 | overlapped.complete(error_code{static_cast<int>(dwError), |
b32b8144 FG |
456 | system_category()}, 0); |
457 | return; | |
458 | } | |
459 | overlapped.release(); | |
460 | } | |
461 | ||
462 | template< | |
463 | class Protocol, class Handler, | |
464 | bool isRequest, class Fields> | |
465 | void | |
466 | write_some_win32_op< | |
467 | Protocol, Handler, isRequest, Fields>:: | |
468 | operator()( | |
469 | error_code ec, std::size_t bytes_transferred) | |
470 | { | |
471 | bytes_transferred_ += bytes_transferred; | |
472 | if(! ec) | |
473 | { | |
474 | if(header_) | |
475 | { | |
476 | header_ = false; | |
477 | return (*this)(); | |
478 | } | |
11fdf7f2 TL |
479 | auto& w = sr_.writer_impl(); |
480 | w.pos_ += bytes_transferred; | |
481 | BOOST_ASSERT(w.pos_ <= w.body_.last_); | |
482 | if(w.pos_ >= w.body_.last_) | |
b32b8144 FG |
483 | { |
484 | sr_.next(ec, null_lambda{}); | |
485 | BOOST_ASSERT(! ec); | |
486 | BOOST_ASSERT(sr_.is_done()); | |
487 | } | |
488 | } | |
489 | h_(ec, bytes_transferred_); | |
490 | } | |
491 | ||
492 | #endif | |
493 | ||
494 | } // detail | |
495 | ||
496 | //------------------------------------------------------------------------------ | |
497 | ||
498 | template<class Protocol, bool isRequest, class Fields> | |
499 | std::size_t | |
500 | write_some( | |
501 | boost::asio::basic_stream_socket<Protocol>& sock, | |
502 | serializer<isRequest, | |
503 | basic_file_body<file_win32>, Fields>& sr, | |
504 | error_code& ec) | |
505 | { | |
506 | if(! sr.is_header_done()) | |
507 | { | |
508 | sr.split(true); | |
509 | auto const bytes_transferred = | |
11fdf7f2 | 510 | detail::write_some_impl(sock, sr, ec); |
b32b8144 FG |
511 | if(ec) |
512 | return bytes_transferred; | |
513 | return bytes_transferred; | |
514 | } | |
515 | if(sr.get().chunked()) | |
516 | { | |
517 | auto const bytes_transferred = | |
11fdf7f2 | 518 | detail::write_some_impl(sock, sr, ec); |
b32b8144 FG |
519 | if(ec) |
520 | return bytes_transferred; | |
521 | return bytes_transferred; | |
522 | } | |
11fdf7f2 TL |
523 | auto& w = sr.writer_impl(); |
524 | w.body_.file_.seek(w.pos_, ec); | |
b32b8144 FG |
525 | if(ec) |
526 | return 0; | |
527 | boost::winapi::DWORD_ const nNumberOfBytesToWrite = | |
528 | static_cast<boost::winapi::DWORD_>( | |
529 | (std::min<std::uint64_t>)( | |
11fdf7f2 | 530 | (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()), |
b32b8144 FG |
531 | (std::numeric_limits<boost::winapi::DWORD_>::max)())); |
532 | auto const bSuccess = ::TransmitFile( | |
533 | sock.native_handle(), | |
11fdf7f2 | 534 | w.body_.file_.native_handle(), |
b32b8144 FG |
535 | nNumberOfBytesToWrite, |
536 | 0, | |
537 | nullptr, | |
538 | nullptr, | |
539 | 0); | |
540 | if(! bSuccess) | |
541 | { | |
542 | ec.assign(static_cast<int>( | |
543 | boost::winapi::GetLastError()), | |
544 | system_category()); | |
545 | return 0; | |
546 | } | |
11fdf7f2 TL |
547 | w.pos_ += nNumberOfBytesToWrite; |
548 | BOOST_ASSERT(w.pos_ <= w.body_.last_); | |
549 | if(w.pos_ < w.body_.last_) | |
b32b8144 FG |
550 | { |
551 | ec.assign(0, ec.category()); | |
552 | } | |
553 | else | |
554 | { | |
555 | sr.next(ec, detail::null_lambda{}); | |
556 | BOOST_ASSERT(! ec); | |
557 | BOOST_ASSERT(sr.is_done()); | |
558 | } | |
559 | return nNumberOfBytesToWrite; | |
560 | } | |
561 | ||
562 | #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR | |
563 | ||
564 | template< | |
565 | class Protocol, | |
566 | bool isRequest, class Fields, | |
567 | class WriteHandler> | |
568 | BOOST_ASIO_INITFN_RESULT_TYPE( | |
569 | WriteHandler, void(error_code, std::size_t)) | |
570 | async_write_some( | |
571 | boost::asio::basic_stream_socket<Protocol>& sock, | |
572 | serializer<isRequest, | |
573 | basic_file_body<file_win32>, Fields>& sr, | |
574 | WriteHandler&& handler) | |
575 | { | |
11fdf7f2 TL |
576 | BOOST_BEAST_HANDLER_INIT( |
577 | WriteHandler, void(error_code, std::size_t)); | |
b32b8144 FG |
578 | detail::write_some_win32_op< |
579 | Protocol, | |
580 | BOOST_ASIO_HANDLER_TYPE(WriteHandler, | |
581 | void(error_code, std::size_t)), | |
582 | isRequest, Fields>{ | |
11fdf7f2 | 583 | std::move(init.completion_handler), sock, sr}(); |
b32b8144 FG |
584 | return init.result.get(); |
585 | } | |
586 | ||
587 | #endif | |
588 | ||
589 | } // http | |
590 | } // beast | |
591 | } // boost | |
592 | ||
593 | #endif | |
594 | ||
595 | #endif |