file_body_win32.hpp 16 KB

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