file_body_win32.hpp 17 KB

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