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