]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
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
31namespace boost {
32namespace beast {
33namespace http {
34
35namespace detail {
36template<class, class, bool, class>
37class write_some_win32_op;
38} // detail
39
40template<>
41struct 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
222inline
223void
224basic_file_body<file_win32>::
225value_type::
226close()
227{
228 error_code ignored;
229 file_.close(ignored);
230}
231
232inline
233void
234basic_file_body<file_win32>::
235value_type::
236open(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
251inline
252void
253basic_file_body<file_win32>::
254value_type::
255reset(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
278namespace detail {
279
280template<class Unsigned>
281inline
282boost::winapi::DWORD_
283lowPart(Unsigned n)
284{
285 return static_cast<
286 boost::winapi::DWORD_>(
287 n & 0xffffffff);
288}
289
290template<class Unsigned>
291inline
292boost::winapi::DWORD_
293highPart(Unsigned n, std::true_type)
294{
295 return static_cast<
296 boost::winapi::DWORD_>(
297 (n>>32)&0xffffffff);
298}
299
300template<class Unsigned>
301inline
302boost::winapi::DWORD_
303highPart(Unsigned, std::false_type)
304{
305 return 0;
306}
307
308template<class Unsigned>
309inline
310boost::winapi::DWORD_
311highPart(Unsigned n)
312{
313 return highPart(n, std::integral_constant<
314 bool, (sizeof(Unsigned)>4)>{});
315}
316
317class null_lambda
318{
319public:
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
333template<
334 class Protocol, class Handler,
335 bool isRequest, class Fields>
336class 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
345public:
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
398template<
399 class Protocol, class Handler,
400 bool isRequest, class Fields>
401void
402write_some_win32_op<
403 Protocol, Handler, isRequest, Fields>::
404operator()()
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
451template<
452 class Protocol, class Handler,
453 bool isRequest, class Fields>
454void
455write_some_win32_op<
456 Protocol, Handler, isRequest, Fields>::
457operator()(
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
487template<class Protocol, bool isRequest, class Fields>
488std::size_t
489write_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
553template<
554 class Protocol,
555 bool isRequest, class Fields,
556 class WriteHandler>
557BOOST_ASIO_INITFN_RESULT_TYPE(
558 WriteHandler, void(error_code, std::size_t))
559async_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