file_body_win32.hpp 14 KB

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