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

source code of boost/libs/beast/include/boost/beast/http/impl/file_body_win32.hpp