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