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