2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
11 #define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
13 #if BOOST_BEAST_USE_WIN32_FILE
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/handler_invoke_hook.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/get_last_error.hpp>
38 template<class, class, bool, class>
39 class write_some_win32_op;
43 struct basic_file_body<file_win32>
45 using file_type = file_win32;
50 //--------------------------------------------------------------------------
56 friend struct basic_file_body<file_win32>;
58 template<class, class, bool, class>
59 friend class detail::write_some_win32_op;
61 class Protocol, bool isRequest, class Fields>
65 boost::asio::basic_stream_socket<Protocol>& sock,
67 basic_file_body<file_win32>, Fields>& sr,
71 std::uint64_t size_ = 0; // cached file size
72 std::uint64_t first_; // starting offset of the range
73 std::uint64_t last_; // ending offset of the range
76 ~value_type() = default;
77 value_type() = default;
78 value_type(value_type&& other) = default;
79 value_type& operator=(value_type&& other) = default;
84 return file_.is_open();
97 open(char const* path, file_mode mode, error_code& ec);
100 reset(file_win32&& file, error_code& ec);
103 //--------------------------------------------------------------------------
107 template<class, class, bool, class>
108 friend class detail::write_some_win32_op;
110 class Protocol, bool isRequest, class Fields>
114 boost::asio::basic_stream_socket<Protocol>& sock,
115 serializer<isRequest,
116 basic_file_body<file_win32>, Fields>& sr,
119 value_type& body_; // The body we are reading from
120 std::uint64_t pos_; // The current position in the file
121 char buf_[4096]; // Small buffer for reading
124 using const_buffers_type =
125 boost::asio::const_buffer;
127 template<bool isRequest, class Fields>
128 writer(header<isRequest, Fields>&, value_type& b)
136 BOOST_ASSERT(body_.file_.is_open());
140 boost::optional<std::pair<const_buffers_type, bool>>
143 std::size_t const n = (std::min)(sizeof(buf_),
144 beast::detail::clamp(body_.last_ - pos_));
147 ec.assign(0, ec.category());
150 auto const nread = body_.file_.read(buf_, n, ec);
153 BOOST_ASSERT(nread != 0);
155 ec.assign(0, ec.category());
157 {buf_, nread}, // buffer to return.
158 pos_ < body_.last_}}; // `true` if there are more buffers.
162 //--------------------------------------------------------------------------
169 template<bool isRequest, class Fields>
171 reader(header<isRequest, Fields>&, value_type& b)
177 init(boost::optional<
178 std::uint64_t> const& content_length,
181 // VFALCO We could reserve space in the file
182 boost::ignore_unused(content_length);
183 BOOST_ASSERT(body_.file_.is_open());
184 ec.assign(0, ec.category());
187 template<class ConstBufferSequence>
189 put(ConstBufferSequence const& buffers,
192 std::size_t nwritten = 0;
193 for(auto buffer : beast::detail::buffers_range(buffers))
195 nwritten += body_.file_.write(
196 buffer.data(), buffer.size(), ec);
200 ec.assign(0, ec.category());
205 finish(error_code& ec)
207 ec.assign(0, ec.category());
211 //--------------------------------------------------------------------------
215 size(value_type const& body)
221 //------------------------------------------------------------------------------
225 basic_file_body<file_win32>::
230 file_.close(ignored);
235 basic_file_body<file_win32>::
237 open(char const* path, file_mode mode, error_code& ec)
239 file_.open(path, mode, ec);
242 size_ = file_.size(ec);
254 basic_file_body<file_win32>::
256 reset(file_win32&& file, error_code& ec)
261 file_.close(ignored);
263 file_ = std::move(file);
266 size_ = file_.size(ec);
277 //------------------------------------------------------------------------------
281 template<class Unsigned>
283 boost::winapi::DWORD_
287 boost::winapi::DWORD_>(
291 template<class Unsigned>
293 boost::winapi::DWORD_
294 highPart(Unsigned n, std::true_type)
297 boost::winapi::DWORD_>(
301 template<class Unsigned>
303 boost::winapi::DWORD_
304 highPart(Unsigned, std::false_type)
309 template<class Unsigned>
311 boost::winapi::DWORD_
314 return highPart(n, std::integral_constant<
315 bool, (sizeof(Unsigned)>4)>{});
321 template<class ConstBufferSequence>
323 operator()(error_code&,
324 ConstBufferSequence const&) const
330 //------------------------------------------------------------------------------
332 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
335 class Protocol, class Handler,
336 bool isRequest, class Fields>
337 class write_some_win32_op
339 boost::asio::basic_stream_socket<Protocol>& sock_;
340 serializer<isRequest,
341 basic_file_body<file_win32>, Fields>& sr_;
342 std::size_t bytes_transferred_ = 0;
344 bool header_ = false;
347 write_some_win32_op(write_some_win32_op&&) = default;
348 write_some_win32_op(write_some_win32_op const&) = delete;
350 template<class DeducedHandler>
353 boost::asio::basic_stream_socket<Protocol>& s,
354 serializer<isRequest,
355 basic_file_body<file_win32>,Fields>& sr)
358 , h_(std::forward<DeducedHandler>(h))
362 using allocator_type =
363 boost::asio::associated_allocator_t<Handler>;
366 get_allocator() const noexcept
368 return (boost::asio::get_associated_allocator)(h_);
371 using executor_type =
372 boost::asio::associated_executor_t<Handler, decltype(std::declval<
373 boost::asio::basic_stream_socket<Protocol>&>().get_executor())>;
376 get_executor() const noexcept
378 return (boost::asio::get_associated_executor)(
379 h_, sock_.get_executor());
388 std::size_t bytes_transferred = 0);
391 bool asio_handler_is_continuation(write_some_win32_op* op)
393 using boost::asio::asio_handler_is_continuation;
394 return asio_handler_is_continuation(
395 std::addressof(op->h_));
398 template<class Function>
400 void asio_handler_invoke(Function&& f, write_some_win32_op* op)
402 using boost::asio::asio_handler_invoke;
403 asio_handler_invoke(f, std::addressof(op->h_));
408 class Protocol, class Handler,
409 bool isRequest, class Fields>
412 Protocol, Handler, isRequest, Fields>::
415 if(! sr_.is_header_done())
419 return detail::async_write_some_impl(
420 sock_, sr_, std::move(*this));
422 if(sr_.get().chunked())
424 return detail::async_write_some_impl(
425 sock_, sr_, std::move(*this));
427 auto& w = sr_.writer_impl();
428 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
429 static_cast<boost::winapi::DWORD_>(
430 (std::min<std::uint64_t>)(
431 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
432 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
433 boost::asio::windows::overlapped_ptr overlapped{
434 sock_.get_executor().context(), std::move(*this)};
435 // Note that we have moved *this, so we cannot access
436 // the handler since it is now moved-from. We can still
437 // access simple things like references and built-in types.
438 auto& ov = *overlapped.get();
439 ov.Offset = lowPart(w.pos_);
440 ov.OffsetHigh = highPart(w.pos_);
441 auto const bSuccess = ::TransmitFile(
442 sock_.native_handle(),
443 sr_.get().body().file_.native_handle(),
444 nNumberOfBytesToWrite,
449 auto const dwError = boost::winapi::GetLastError();
450 if(! bSuccess && dwError !=
451 boost::winapi::ERROR_IO_PENDING_)
453 // VFALCO This needs review, is 0 the right number?
454 // completed immediately (with error?)
455 overlapped.complete(error_code{static_cast<int>(dwError),
456 system_category()}, 0);
459 overlapped.release();
463 class Protocol, class Handler,
464 bool isRequest, class Fields>
467 Protocol, Handler, isRequest, Fields>::
469 error_code ec, std::size_t bytes_transferred)
471 bytes_transferred_ += bytes_transferred;
479 auto& w = sr_.writer_impl();
480 w.pos_ += bytes_transferred;
481 BOOST_ASSERT(w.pos_ <= w.body_.last_);
482 if(w.pos_ >= w.body_.last_)
484 sr_.next(ec, null_lambda{});
486 BOOST_ASSERT(sr_.is_done());
489 h_(ec, bytes_transferred_);
496 //------------------------------------------------------------------------------
498 template<class Protocol, bool isRequest, class Fields>
501 boost::asio::basic_stream_socket<Protocol>& sock,
502 serializer<isRequest,
503 basic_file_body<file_win32>, Fields>& sr,
506 if(! sr.is_header_done())
509 auto const bytes_transferred =
510 detail::write_some_impl(sock, sr, ec);
512 return bytes_transferred;
513 return bytes_transferred;
515 if(sr.get().chunked())
517 auto const bytes_transferred =
518 detail::write_some_impl(sock, sr, ec);
520 return bytes_transferred;
521 return bytes_transferred;
523 auto& w = sr.writer_impl();
524 w.body_.file_.seek(w.pos_, ec);
527 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
528 static_cast<boost::winapi::DWORD_>(
529 (std::min<std::uint64_t>)(
530 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
531 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
532 auto const bSuccess = ::TransmitFile(
533 sock.native_handle(),
534 w.body_.file_.native_handle(),
535 nNumberOfBytesToWrite,
542 ec.assign(static_cast<int>(
543 boost::winapi::GetLastError()),
547 w.pos_ += nNumberOfBytesToWrite;
548 BOOST_ASSERT(w.pos_ <= w.body_.last_);
549 if(w.pos_ < w.body_.last_)
551 ec.assign(0, ec.category());
555 sr.next(ec, detail::null_lambda{});
557 BOOST_ASSERT(sr.is_done());
559 return nNumberOfBytesToWrite;
562 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
566 bool isRequest, class Fields,
568 BOOST_ASIO_INITFN_RESULT_TYPE(
569 WriteHandler, void(error_code, std::size_t))
571 boost::asio::basic_stream_socket<Protocol>& sock,
572 serializer<isRequest,
573 basic_file_body<file_win32>, Fields>& sr,
574 WriteHandler&& handler)
576 BOOST_BEAST_HANDLER_INIT(
577 WriteHandler, void(error_code, std::size_t));
578 detail::write_some_win32_op<
580 BOOST_ASIO_HANDLER_TYPE(WriteHandler,
581 void(error_code, std::size_t)),
583 std::move(init.completion_handler), sock, sr}();
584 return init.result.get();