file_body_win32.ipp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. #ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
  10. #define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
  11. #if BOOST_BEAST_USE_WIN32_FILE
  12. #include <boost/beast/core/bind_handler.hpp>
  13. #include <boost/beast/core/type_traits.hpp>
  14. #include <boost/beast/core/detail/clamp.hpp>
  15. #include <boost/beast/http/serializer.hpp>
  16. #include <boost/asio/associated_allocator.hpp>
  17. #include <boost/asio/associated_executor.hpp>
  18. #include <boost/asio/async_result.hpp>
  19. #include <boost/asio/basic_stream_socket.hpp>
  20. #include <boost/asio/executor_work_guard.hpp>
  21. #include <boost/asio/handler_continuation_hook.hpp>
  22. #include <boost/asio/handler_invoke_hook.hpp>
  23. #include <boost/asio/windows/overlapped_ptr.hpp>
  24. #include <boost/make_unique.hpp>
  25. #include <boost/smart_ptr/make_shared_array.hpp>
  26. #include <boost/winapi/basic_types.hpp>
  27. #include <boost/winapi/get_last_error.hpp>
  28. #include <algorithm>
  29. #include <cstring>
  30. namespace boost {
  31. namespace beast {
  32. namespace http {
  33. namespace detail {
  34. template<class, class, bool, class>
  35. class write_some_win32_op;
  36. } // detail
  37. template<>
  38. struct basic_file_body<file_win32>
  39. {
  40. using file_type = file_win32;
  41. class writer;
  42. class reader;
  43. //--------------------------------------------------------------------------
  44. class value_type
  45. {
  46. friend class writer;
  47. friend class reader;
  48. friend struct basic_file_body<file_win32>;
  49. template<class, class, bool, class>
  50. friend class detail::write_some_win32_op;
  51. template<
  52. class Protocol, bool isRequest, class Fields>
  53. friend
  54. std::size_t
  55. write_some(
  56. boost::asio::basic_stream_socket<Protocol>& sock,
  57. serializer<isRequest,
  58. basic_file_body<file_win32>, Fields>& sr,
  59. error_code& ec);
  60. file_win32 file_;
  61. std::uint64_t size_ = 0; // cached file size
  62. std::uint64_t first_; // starting offset of the range
  63. std::uint64_t last_; // ending offset of the range
  64. public:
  65. ~value_type() = default;
  66. value_type() = default;
  67. value_type(value_type&& other) = default;
  68. value_type& operator=(value_type&& other) = default;
  69. bool
  70. is_open() const
  71. {
  72. return file_.is_open();
  73. }
  74. std::uint64_t
  75. size() const
  76. {
  77. return size_;
  78. }
  79. void
  80. close();
  81. void
  82. open(char const* path, file_mode mode, error_code& ec);
  83. void
  84. reset(file_win32&& file, error_code& ec);
  85. };
  86. //--------------------------------------------------------------------------
  87. class writer
  88. {
  89. template<class, class, bool, class>
  90. friend class detail::write_some_win32_op;
  91. template<
  92. class Protocol, bool isRequest, class Fields>
  93. friend
  94. std::size_t
  95. write_some(
  96. boost::asio::basic_stream_socket<Protocol>& sock,
  97. serializer<isRequest,
  98. basic_file_body<file_win32>, Fields>& sr,
  99. error_code& ec);
  100. value_type& body_; // The body we are reading from
  101. std::uint64_t pos_; // The current position in the file
  102. char buf_[4096]; // Small buffer for reading
  103. public:
  104. using const_buffers_type =
  105. boost::asio::const_buffer;
  106. template<bool isRequest, class Fields>
  107. writer(header<isRequest, Fields>&, value_type& b)
  108. : body_(b)
  109. {
  110. }
  111. void
  112. init(error_code&)
  113. {
  114. BOOST_ASSERT(body_.file_.is_open());
  115. pos_ = body_.first_;
  116. }
  117. boost::optional<std::pair<const_buffers_type, bool>>
  118. get(error_code& ec)
  119. {
  120. std::size_t const n = (std::min)(sizeof(buf_),
  121. beast::detail::clamp(body_.last_ - pos_));
  122. if(n == 0)
  123. {
  124. ec.assign(0, ec.category());
  125. return boost::none;
  126. }
  127. auto const nread = body_.file_.read(buf_, n, ec);
  128. if(ec)
  129. return boost::none;
  130. BOOST_ASSERT(nread != 0);
  131. pos_ += nread;
  132. ec.assign(0, ec.category());
  133. return {{
  134. {buf_, nread}, // buffer to return.
  135. pos_ < body_.last_}}; // `true` if there are more buffers.
  136. }
  137. };
  138. //--------------------------------------------------------------------------
  139. class reader
  140. {
  141. value_type& body_;
  142. public:
  143. template<bool isRequest, class Fields>
  144. explicit
  145. reader(header<isRequest, Fields>&, value_type& b)
  146. : body_(b)
  147. {
  148. }
  149. void
  150. init(boost::optional<
  151. std::uint64_t> const& content_length,
  152. error_code& ec)
  153. {
  154. // VFALCO We could reserve space in the file
  155. boost::ignore_unused(content_length);
  156. BOOST_ASSERT(body_.file_.is_open());
  157. ec.assign(0, ec.category());
  158. }
  159. template<class ConstBufferSequence>
  160. std::size_t
  161. put(ConstBufferSequence const& buffers,
  162. error_code& ec)
  163. {
  164. std::size_t nwritten = 0;
  165. for(auto buffer : beast::detail::buffers_range(buffers))
  166. {
  167. nwritten += body_.file_.write(
  168. buffer.data(), buffer.size(), ec);
  169. if(ec)
  170. return nwritten;
  171. }
  172. ec.assign(0, ec.category());
  173. return nwritten;
  174. }
  175. void
  176. finish(error_code& ec)
  177. {
  178. ec.assign(0, ec.category());
  179. }
  180. };
  181. //--------------------------------------------------------------------------
  182. static
  183. std::uint64_t
  184. size(value_type const& body)
  185. {
  186. return body.size();
  187. }
  188. };
  189. //------------------------------------------------------------------------------
  190. inline
  191. void
  192. basic_file_body<file_win32>::
  193. value_type::
  194. close()
  195. {
  196. error_code ignored;
  197. file_.close(ignored);
  198. }
  199. inline
  200. void
  201. basic_file_body<file_win32>::
  202. value_type::
  203. open(char const* path, file_mode mode, error_code& ec)
  204. {
  205. file_.open(path, mode, ec);
  206. if(ec)
  207. return;
  208. size_ = file_.size(ec);
  209. if(ec)
  210. {
  211. close();
  212. return;
  213. }
  214. first_ = 0;
  215. last_ = size_;
  216. }
  217. inline
  218. void
  219. basic_file_body<file_win32>::
  220. value_type::
  221. reset(file_win32&& file, error_code& ec)
  222. {
  223. if(file_.is_open())
  224. {
  225. error_code ignored;
  226. file_.close(ignored);
  227. }
  228. file_ = std::move(file);
  229. if(file_.is_open())
  230. {
  231. size_ = file_.size(ec);
  232. if(ec)
  233. {
  234. close();
  235. return;
  236. }
  237. first_ = 0;
  238. last_ = size_;
  239. }
  240. }
  241. //------------------------------------------------------------------------------
  242. namespace detail {
  243. template<class Unsigned>
  244. inline
  245. boost::winapi::DWORD_
  246. lowPart(Unsigned n)
  247. {
  248. return static_cast<
  249. boost::winapi::DWORD_>(
  250. n & 0xffffffff);
  251. }
  252. template<class Unsigned>
  253. inline
  254. boost::winapi::DWORD_
  255. highPart(Unsigned n, std::true_type)
  256. {
  257. return static_cast<
  258. boost::winapi::DWORD_>(
  259. (n>>32)&0xffffffff);
  260. }
  261. template<class Unsigned>
  262. inline
  263. boost::winapi::DWORD_
  264. highPart(Unsigned, std::false_type)
  265. {
  266. return 0;
  267. }
  268. template<class Unsigned>
  269. inline
  270. boost::winapi::DWORD_
  271. highPart(Unsigned n)
  272. {
  273. return highPart(n, std::integral_constant<
  274. bool, (sizeof(Unsigned)>4)>{});
  275. }
  276. class null_lambda
  277. {
  278. public:
  279. template<class ConstBufferSequence>
  280. void
  281. operator()(error_code&,
  282. ConstBufferSequence const&) const
  283. {
  284. BOOST_ASSERT(false);
  285. }
  286. };
  287. //------------------------------------------------------------------------------
  288. #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
  289. template<
  290. class Protocol, class Handler,
  291. bool isRequest, class Fields>
  292. class write_some_win32_op
  293. {
  294. boost::asio::basic_stream_socket<Protocol>& sock_;
  295. boost::asio::executor_work_guard<decltype(std::declval<
  296. boost::asio::basic_stream_socket<Protocol>&>().get_executor())> wg_;
  297. serializer<isRequest,
  298. basic_file_body<file_win32>, Fields>& sr_;
  299. std::size_t bytes_transferred_ = 0;
  300. Handler h_;
  301. bool header_ = false;
  302. public:
  303. write_some_win32_op(write_some_win32_op&&) = default;
  304. write_some_win32_op(write_some_win32_op const&) = delete;
  305. template<class DeducedHandler>
  306. write_some_win32_op(
  307. DeducedHandler&& h,
  308. boost::asio::basic_stream_socket<Protocol>& s,
  309. serializer<isRequest,
  310. basic_file_body<file_win32>,Fields>& sr)
  311. : sock_(s)
  312. , wg_(sock_.get_executor())
  313. , sr_(sr)
  314. , h_(std::forward<DeducedHandler>(h))
  315. {
  316. }
  317. using allocator_type =
  318. boost::asio::associated_allocator_t<Handler>;
  319. allocator_type
  320. get_allocator() const noexcept
  321. {
  322. return (boost::asio::get_associated_allocator)(h_);
  323. }
  324. using executor_type =
  325. boost::asio::associated_executor_t<Handler, decltype(std::declval<
  326. boost::asio::basic_stream_socket<Protocol>&>().get_executor())>;
  327. executor_type
  328. get_executor() const noexcept
  329. {
  330. return (boost::asio::get_associated_executor)(
  331. h_, sock_.get_executor());
  332. }
  333. void
  334. operator()();
  335. void
  336. operator()(
  337. error_code ec,
  338. std::size_t bytes_transferred = 0);
  339. friend
  340. bool asio_handler_is_continuation(write_some_win32_op* op)
  341. {
  342. using boost::asio::asio_handler_is_continuation;
  343. return asio_handler_is_continuation(
  344. std::addressof(op->h_));
  345. }
  346. template<class Function>
  347. friend
  348. void asio_handler_invoke(Function&& f, write_some_win32_op* op)
  349. {
  350. using boost::asio::asio_handler_invoke;
  351. asio_handler_invoke(f, std::addressof(op->h_));
  352. }
  353. };
  354. template<
  355. class Protocol, class Handler,
  356. bool isRequest, class Fields>
  357. void
  358. write_some_win32_op<
  359. Protocol, Handler, isRequest, Fields>::
  360. operator()()
  361. {
  362. if(! sr_.is_header_done())
  363. {
  364. header_ = true;
  365. sr_.split(true);
  366. return detail::async_write_some_impl(
  367. sock_, sr_, std::move(*this));
  368. }
  369. if(sr_.get().chunked())
  370. {
  371. return detail::async_write_some_impl(
  372. sock_, sr_, std::move(*this));
  373. }
  374. auto& w = sr_.writer_impl();
  375. boost::winapi::DWORD_ const nNumberOfBytesToWrite =
  376. static_cast<boost::winapi::DWORD_>(
  377. (std::min<std::uint64_t>)(
  378. (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
  379. (std::numeric_limits<boost::winapi::DWORD_>::max)()));
  380. boost::asio::windows::overlapped_ptr overlapped{
  381. sock_.get_executor().context(), std::move(*this)};
  382. // Note that we have moved *this, so we cannot access
  383. // the handler since it is now moved-from. We can still
  384. // access simple things like references and built-in types.
  385. auto& ov = *overlapped.get();
  386. ov.Offset = lowPart(w.pos_);
  387. ov.OffsetHigh = highPart(w.pos_);
  388. auto const bSuccess = ::TransmitFile(
  389. sock_.native_handle(),
  390. sr_.get().body().file_.native_handle(),
  391. nNumberOfBytesToWrite,
  392. 0,
  393. overlapped.get(),
  394. nullptr,
  395. 0);
  396. auto const dwError = boost::winapi::GetLastError();
  397. if(! bSuccess && dwError !=
  398. boost::winapi::ERROR_IO_PENDING_)
  399. {
  400. // VFALCO This needs review, is 0 the right number?
  401. // completed immediately (with error?)
  402. overlapped.complete(error_code{static_cast<int>(dwError),
  403. system_category()}, 0);
  404. return;
  405. }
  406. overlapped.release();
  407. }
  408. template<
  409. class Protocol, class Handler,
  410. bool isRequest, class Fields>
  411. void
  412. write_some_win32_op<
  413. Protocol, Handler, isRequest, Fields>::
  414. operator()(
  415. error_code ec, std::size_t bytes_transferred)
  416. {
  417. bytes_transferred_ += bytes_transferred;
  418. if(! ec)
  419. {
  420. if(header_)
  421. {
  422. header_ = false;
  423. return (*this)();
  424. }
  425. auto& w = sr_.writer_impl();
  426. w.pos_ += bytes_transferred;
  427. BOOST_ASSERT(w.pos_ <= w.body_.last_);
  428. if(w.pos_ >= w.body_.last_)
  429. {
  430. sr_.next(ec, null_lambda{});
  431. BOOST_ASSERT(! ec);
  432. BOOST_ASSERT(sr_.is_done());
  433. }
  434. }
  435. h_(ec, bytes_transferred_);
  436. }
  437. #endif
  438. } // detail
  439. //------------------------------------------------------------------------------
  440. template<class Protocol, bool isRequest, class Fields>
  441. std::size_t
  442. write_some(
  443. boost::asio::basic_stream_socket<Protocol>& sock,
  444. serializer<isRequest,
  445. basic_file_body<file_win32>, Fields>& sr,
  446. error_code& ec)
  447. {
  448. if(! sr.is_header_done())
  449. {
  450. sr.split(true);
  451. auto const bytes_transferred =
  452. detail::write_some_impl(sock, sr, ec);
  453. if(ec)
  454. return bytes_transferred;
  455. return bytes_transferred;
  456. }
  457. if(sr.get().chunked())
  458. {
  459. auto const bytes_transferred =
  460. detail::write_some_impl(sock, sr, ec);
  461. if(ec)
  462. return bytes_transferred;
  463. return bytes_transferred;
  464. }
  465. auto& w = sr.writer_impl();
  466. w.body_.file_.seek(w.pos_, ec);
  467. if(ec)
  468. return 0;
  469. boost::winapi::DWORD_ const nNumberOfBytesToWrite =
  470. static_cast<boost::winapi::DWORD_>(
  471. (std::min<std::uint64_t>)(
  472. (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
  473. (std::numeric_limits<boost::winapi::DWORD_>::max)()));
  474. auto const bSuccess = ::TransmitFile(
  475. sock.native_handle(),
  476. w.body_.file_.native_handle(),
  477. nNumberOfBytesToWrite,
  478. 0,
  479. nullptr,
  480. nullptr,
  481. 0);
  482. if(! bSuccess)
  483. {
  484. ec.assign(static_cast<int>(
  485. boost::winapi::GetLastError()),
  486. system_category());
  487. return 0;
  488. }
  489. w.pos_ += nNumberOfBytesToWrite;
  490. BOOST_ASSERT(w.pos_ <= w.body_.last_);
  491. if(w.pos_ < w.body_.last_)
  492. {
  493. ec.assign(0, ec.category());
  494. }
  495. else
  496. {
  497. sr.next(ec, detail::null_lambda{});
  498. BOOST_ASSERT(! ec);
  499. BOOST_ASSERT(sr.is_done());
  500. }
  501. return nNumberOfBytesToWrite;
  502. }
  503. #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
  504. template<
  505. class Protocol,
  506. bool isRequest, class Fields,
  507. class WriteHandler>
  508. BOOST_ASIO_INITFN_RESULT_TYPE(
  509. WriteHandler, void(error_code, std::size_t))
  510. async_write_some(
  511. boost::asio::basic_stream_socket<Protocol>& sock,
  512. serializer<isRequest,
  513. basic_file_body<file_win32>, Fields>& sr,
  514. WriteHandler&& handler)
  515. {
  516. BOOST_BEAST_HANDLER_INIT(
  517. WriteHandler, void(error_code, std::size_t));
  518. detail::write_some_win32_op<
  519. Protocol,
  520. BOOST_ASIO_HANDLER_TYPE(WriteHandler,
  521. void(error_code, std::size_t)),
  522. isRequest, Fields>{
  523. std::move(init.completion_handler), sock, sr}();
  524. return init.result.get();
  525. }
  526. #endif
  527. } // http
  528. } // beast
  529. } // boost
  530. #endif
  531. #endif