]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/beast/http/impl/file_body_win32.ipp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / beast / http / impl / file_body_win32.ipp
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