]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
b32b8144 1//
92f5a8d4 2// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
b32b8144
FG
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
92f5a8d4
TL
10#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
11#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
b32b8144
FG
12
13#if BOOST_BEAST_USE_WIN32_FILE
14
92f5a8d4 15#include <boost/beast/core/async_base.hpp>
b32b8144 16#include <boost/beast/core/bind_handler.hpp>
92f5a8d4 17#include <boost/beast/core/buffers_range.hpp>
b32b8144 18#include <boost/beast/core/detail/clamp.hpp>
92f5a8d4 19#include <boost/beast/core/detail/is_invocable.hpp>
f67539c2 20#include <boost/beast/http/error.hpp>
92f5a8d4 21#include <boost/beast/http/write.hpp>
b32b8144 22#include <boost/beast/http/serializer.hpp>
b32b8144
FG
23#include <boost/asio/async_result.hpp>
24#include <boost/asio/basic_stream_socket.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>
f67539c2 29#include <boost/winapi/error_codes.hpp>
11fdf7f2 30#include <boost/winapi/get_last_error.hpp>
b32b8144
FG
31#include <algorithm>
32#include <cstring>
33
34namespace boost {
35namespace beast {
36namespace http {
37
38namespace detail {
92f5a8d4 39template<class, class, bool, class, class>
b32b8144
FG
40class write_some_win32_op;
41} // detail
42
43template<>
44struct 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
92f5a8d4 59 template<class, class, bool, class, class>
b32b8144
FG
60 friend class detail::write_some_win32_op;
61 template<
92f5a8d4
TL
62 class Protocol, class Executor,
63 bool isRequest, class Fields>
b32b8144
FG
64 friend
65 std::size_t
66 write_some(
92f5a8d4 67 net::basic_stream_socket<Protocol, Executor>& sock,
b32b8144
FG
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
f67539c2
TL
83 file_win32& file()
84 {
85 return file_;
86 }
87
b32b8144
FG
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 {
92f5a8d4 114 template<class, class, bool, class, class>
b32b8144
FG
115 friend class detail::write_some_win32_op;
116 template<
92f5a8d4
TL
117 class Protocol, class Executor,
118 bool isRequest, class Fields>
b32b8144
FG
119 friend
120 std::size_t
121 write_some(
92f5a8d4 122 net::basic_stream_socket<Protocol, Executor>& sock,
b32b8144
FG
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 =
92f5a8d4 133 net::const_buffer;
b32b8144
FG
134
135 template<bool isRequest, class Fields>
11fdf7f2
TL
136 writer(header<isRequest, Fields>&, value_type& b)
137 : body_(b)
f67539c2 138 , pos_(body_.first_)
b32b8144 139 {
f67539c2 140 BOOST_ASSERT(body_.file_.is_open());
b32b8144
FG
141 }
142
143 void
f67539c2 144 init(error_code& ec)
b32b8144
FG
145 {
146 BOOST_ASSERT(body_.file_.is_open());
f67539c2 147 ec.clear();
b32b8144
FG
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 {
92f5a8d4 157 ec = {};
b32b8144
FG
158 return boost::none;
159 }
160 auto const nread = body_.file_.read(buf_, n, ec);
161 if(ec)
162 return boost::none;
f67539c2
TL
163 if (nread == 0)
164 {
165 ec = error::short_read;
166 return boost::none;
167 }
b32b8144
FG
168 BOOST_ASSERT(nread != 0);
169 pos_ += nread;
92f5a8d4 170 ec = {};
b32b8144
FG
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
11fdf7f2
TL
186 reader(header<isRequest, Fields>&, value_type& b)
187 : body_(b)
b32b8144
FG
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());
92f5a8d4 199 ec = {};
b32b8144
FG
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;
92f5a8d4 208 for(auto buffer : beast::buffers_range_ref(buffers))
b32b8144
FG
209 {
210 nwritten += body_.file_.write(
211 buffer.data(), buffer.size(), ec);
212 if(ec)
213 return nwritten;
214 }
92f5a8d4 215 ec = {};
b32b8144
FG
216 return nwritten;
217 }
218
219 void
220 finish(error_code& ec)
221 {
92f5a8d4 222 ec = {};
b32b8144
FG
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
238inline
239void
240basic_file_body<file_win32>::
241value_type::
242close()
243{
244 error_code ignored;
245 file_.close(ignored);
246}
247
248inline
249void
250basic_file_body<file_win32>::
251value_type::
252open(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
267inline
268void
269basic_file_body<file_win32>::
270value_type::
271reset(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
294namespace detail {
295
296template<class Unsigned>
b32b8144
FG
297boost::winapi::DWORD_
298lowPart(Unsigned n)
299{
300 return static_cast<
301 boost::winapi::DWORD_>(
302 n & 0xffffffff);
303}
304
305template<class Unsigned>
b32b8144
FG
306boost::winapi::DWORD_
307highPart(Unsigned n, std::true_type)
308{
309 return static_cast<
310 boost::winapi::DWORD_>(
311 (n>>32)&0xffffffff);
312}
313
314template<class Unsigned>
b32b8144
FG
315boost::winapi::DWORD_
316highPart(Unsigned, std::false_type)
317{
318 return 0;
319}
320
321template<class Unsigned>
b32b8144
FG
322boost::winapi::DWORD_
323highPart(Unsigned n)
324{
325 return highPart(n, std::integral_constant<
326 bool, (sizeof(Unsigned)>4)>{});
327}
328
329class null_lambda
330{
331public:
332 template<class ConstBufferSequence>
333 void
334 operator()(error_code&,
335 ConstBufferSequence const&) const
336 {
337 BOOST_ASSERT(false);
338 }
339};
340
f67539c2
TL
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?
347inline
348error_code
349make_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
369inline
370error_code
371make_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
b32b8144
FG
382//------------------------------------------------------------------------------
383
384#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
385
386template<
92f5a8d4
TL
387 class Protocol, class Executor,
388 bool isRequest, class Fields,
389 class Handler>
b32b8144 390class write_some_win32_op
92f5a8d4 391 : public beast::async_base<Handler, Executor>
b32b8144 392{
92f5a8d4
TL
393 net::basic_stream_socket<
394 Protocol, Executor>& sock_;
b32b8144
FG
395 serializer<isRequest,
396 basic_file_body<file_win32>, Fields>& sr_;
b32b8144
FG
397 bool header_ = false;
398
399public:
92f5a8d4 400 template<class Handler_>
b32b8144 401 write_some_win32_op(
92f5a8d4
TL
402 Handler_&& h,
403 net::basic_stream_socket<
404 Protocol, Executor>& s,
b32b8144
FG
405 serializer<isRequest,
406 basic_file_body<file_win32>,Fields>& sr)
92f5a8d4
TL
407 : async_base<
408 Handler, Executor>(
409 std::forward<Handler_>(h),
410 s.get_executor())
411 , sock_(s)
b32b8144 412 , sr_(sr)
b32b8144 413 {
92f5a8d4 414 (*this)();
b32b8144
FG
415 }
416
92f5a8d4
TL
417 void
418 operator()()
b32b8144 419 {
92f5a8d4
TL
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?)
f67539c2
TL
460 overlapped.complete(
461 make_win32_error(dwError), 0);
92f5a8d4
TL
462 return;
463 }
464 overlapped.release();
b32b8144
FG
465 }
466
b32b8144
FG
467 void
468 operator()(
469 error_code ec,
92f5a8d4 470 std::size_t bytes_transferred = 0)
11fdf7f2 471 {
f67539c2
TL
472 if(ec)
473 {
474 ec = make_win32_error(ec);
475 }
476 else if(! ec && ! header_)
92f5a8d4 477 {
92f5a8d4
TL
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 }
f67539c2 488 this->complete_now(ec, bytes_transferred);
11fdf7f2 489 }
b32b8144
FG
490};
491
92f5a8d4 492struct run_write_some_win32_op
b32b8144 493{
92f5a8d4
TL
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)
b32b8144 505 {
92f5a8d4
TL
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);
b32b8144 520 }
92f5a8d4 521};
b32b8144
FG
522
523#endif
524
525} // detail
526
527//------------------------------------------------------------------------------
528
92f5a8d4
TL
529template<
530 class Protocol, class Executor,
531 bool isRequest, class Fields>
b32b8144
FG
532std::size_t
533write_some(
92f5a8d4
TL
534 net::basic_stream_socket<
535 Protocol, Executor>& sock,
b32b8144
FG
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 =
11fdf7f2 544 detail::write_some_impl(sock, sr, ec);
b32b8144
FG
545 if(ec)
546 return bytes_transferred;
547 return bytes_transferred;
548 }
549 if(sr.get().chunked())
550 {
551 auto const bytes_transferred =
11fdf7f2 552 detail::write_some_impl(sock, sr, ec);
b32b8144
FG
553 if(ec)
554 return bytes_transferred;
555 return bytes_transferred;
556 }
11fdf7f2
TL
557 auto& w = sr.writer_impl();
558 w.body_.file_.seek(w.pos_, ec);
b32b8144
FG
559 if(ec)
560 return 0;
561 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
562 static_cast<boost::winapi::DWORD_>(
563 (std::min<std::uint64_t>)(
11fdf7f2 564 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
b32b8144
FG
565 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
566 auto const bSuccess = ::TransmitFile(
567 sock.native_handle(),
11fdf7f2 568 w.body_.file_.native_handle(),
b32b8144
FG
569 nNumberOfBytesToWrite,
570 0,
571 nullptr,
572 nullptr,
573 0);
574 if(! bSuccess)
575 {
f67539c2
TL
576 ec = detail::make_win32_error(
577 boost::winapi::GetLastError());
b32b8144
FG
578 return 0;
579 }
11fdf7f2
TL
580 w.pos_ += nNumberOfBytesToWrite;
581 BOOST_ASSERT(w.pos_ <= w.body_.last_);
582 if(w.pos_ < w.body_.last_)
b32b8144 583 {
92f5a8d4 584 ec = {};
b32b8144
FG
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
597template<
92f5a8d4 598 class Protocol, class Executor,
b32b8144 599 bool isRequest, class Fields,
20effc67 600 BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
92f5a8d4 601BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
b32b8144 602async_write_some(
92f5a8d4
TL
603 net::basic_stream_socket<
604 Protocol, Executor>& sock,
b32b8144
FG
605 serializer<isRequest,
606 basic_file_body<file_win32>, Fields>& sr,
607 WriteHandler&& handler)
92f5a8d4
TL
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);
b32b8144
FG
616}
617
618#endif
619
620} // http
621} // beast
622} // boost
623
624#endif
625
626#endif