file_body_win32.hpp 16 KB

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