Squashed 'src/beast/' changes from 6d5547a..3bcd986

3bcd986 Set version to 79
4f42f8c Remove spurious fallthrough guidance
fa1ac16 Set version to 78
0cb9b63 Fix warning in root ca declaration
f78c73a Tidy up file_posix unused variable
72ce9ef Tidy up dstream for existing Boost versions
efe8e58 Add Boost.Locale utf8 benchmark comparison
f7c745e Remove string_view_body
7a79efa Tidy up FieldsReader doc
e51aefd Header file tidying
69898f4 Fix warning in zlib
53723c0 Add message keep_alive, chunked, content_length members
d7af73b Fix spurious uninitialized warning
ca42cc0 Tidy up invalid characters in test vector
48d3e60 Use make_unique_noinit
a1ff804 span, string, vector bodies are public
feab6a0 Documentation work
116c0b0 Add span_body
7fb901d Tidy up includes and javadocs
f0f58be Add span
7a8982b Add vector_body
524f73a Tidy up basic_string_body
a8ad67b Set version to 77
d555859 file_posix works without large file support
1bc30cb Set version to 76
9a1e7a8 Disable SSE4.2 optimizations
09af312 Fix parse illegal characters in obs-fold
7dd684c Add file_body_win32:
1bbc71c serializing file_body is not const
9a4b55e BodyReader, BodyWriter use two-phase init (API Change):
dc400ce Serializer members are not const
1a33c37 Rename to serializer::keep_alive (API Change):
55935c5 Add serializer::chunked
63ace37 Add serializer::get
2c4047b BodyReader may construct from a non-const message
0a0a225 Use Boost.Config
6f83d07 Always go through write_some:
0e23066 Set version to 75
28f3ece Doc tidying
3495331 Using SSE4.2 intrinsics in basic_parser if available
bc1f0ac file_body tests
4e03d7e Add serializer::limit
85e3ee8 Shrink serializer buffers using buffers_ref
78bcdb1 Tidy up BEAST_NO_BIG_VARIANTS
3ea6cf2 Construct buffer_prefix_view in-place
69f9f7a Use file_body for valid requests, string_body otherwise.
6f88f01 Set version to 74
93fed8e remove redundant flush() from example:
e0f56da Fix Beast include directories for cmake targets
5ff9e0a Add file_posix
1bb5705 Remove common/file_body.hpp
5c89d87 Add file_body
67a55c8 Add file_win32
647d3b0 Add file_stdio and File concept
89c416c Set version to 73
0efc32f Fixes for gcc-4.8
c8910ab Initialize local variable in basic_parser
8a28193 Adjust benchmarks
81e51d8 Verify certificates in SSL clients
a43f6d4 Jamroot tweak
8c85ee8 Put more... links on overload reference pages
ff1104e Documentation tidy
826ff0e serializer::next replaces serializer::get (API Change):
8d67775 Refactor header and message constructors:
6c79f19 Add basic_parser tests
25127d9 basic_parser optimizations:
9d082fd Set version to 72
c88e2b9 Various improvements to http_server_fast.cpp:
20b0fdb Documentation tidying
afd1fa7 Add websocket-server-async example
954b597 Add http-server-threaded example
df8f253 Refactor file_body for best practices
11c1037 Newly constructed responses have a 200 OK result
a648817 Refine Body::size specification
40aad37 Tidy up set payload in http-server-fast
52cefbc Set version to 71
8c51c77 Tidy up Jamroot /permissive-
5efecea Update README.md
5a47acd Tidy up http_sync_port error check
a2af2b5 Concept check in basic_dynamic_body
8b80a6f Fix buffer overflow handling for string_body and mutable_body
ec3b4fd Return std::size_t from Body::writer::put (API Change)
effbb37 Check trailers in test
f5368cf Call prepare_payload in HTTP example
a3e5e01 Fix spurious on_chunk invocation
96d94eb Add options for building examples and tests. Move zlib test sources to test/zlib
e0efdc7 Allow close, ping, and write to happen concurrently
9c1a419 Refactor websocket composed ops
d5659a4 Fine tune websocket asserts
b8e8943 std::pair "last" -> "first" in http_message.qbk
c691bf4 Fix can/cannot thinko in FAQ.
6dd006b Documentation revision
6d2e315 Fix extra ; warning
78a065b Set version to 70
00c7e9d Fix HEAD response in file_service
67d70d2 Fix BEAST_FALLTHROUGH in config
4f33655 Add parser::on_header to set a callback
9c16b21 Add basic_parser header and body limits:
b64d1f7 Rename to message::base (API Change):
436c66a Serialize in one step when possible
3e1061b Set version to 69
f709273 Add /permissive- to msvc toolchain
0dae464 Use BEAST_FALLTHROUGH to silence warnings
a70d386 basic_parser optimizations
4269f35 Set version to 68
544327f Link statically on cmake MSVC
e213ffe Add const_body, mutable_body to examples
0568763 Optimize field lookups
8fc3001 Use string_ref in older Boost versions
8af77da bad_target replaces bad_path (API Change):
325dd62 Adjust buffer size in fast server
be59785 Doc erratum
d9b44f3 Small speed up in fields comparisons
3e6ce38 Use Boost master on Appveyor
09f3d64 Split common tests to a new project
adfd22a Remove BodyReader::is_deferred (API Change):
582d28d Change BodyReader, BodyWriter requirements (API Change):
8982e14 Set version to 67
daa58a2 Group common example headers
afd8f1a Rename to http-server-fast
07cb9f7 control_callback replaces ping_callback (API Change):
91e83ed Use boost::string_view
067db87 Merge stream_base to stream and tidy
d61241a Add http-server-small example
eb08e92 Fix doc example link
7fb75d0 Set version to 66
df86723 Respect debug flag for marked output
c08565a Squelch spurious warning on gcc
188ef7c Documentation work
1c62d3a Add http-server example
3f54582 basic_fields optimizations
a8b05b8 Add header aliases
b94eac3 Tidy up message piecewise ctors
9c48b52 Handle bad_alloc in parser
1b57c54 Fix costly potential value-init in parser
1edc41e Make consuming_buffers smaller
72ac918 Add serializer request/response aliases
18f7606 string_param optimizations
c675252 Set version to 65
c398cdd Enable msvc warnings in Jamfile
380cceb Fix unused variable warnings
4172e7e Enable unused variable warning on msvc cmake
f04d227 Fix integer warnings
ca975b3 Fix narrowing in deflate_stream
2fab796 Fix narrowing in inflate_stream
cff87f6 Fix narrowing in ostream
1956886 Fix narrowing in static_ostream
69cdc4b Fix integer types in deflate_stream::bi_reverse
3e3dd21 Enable narrowing warning on msvc cmake
e11a294 Set version to 64
a00e070 Remove make_serializer (API Change):
8449697 Add link_directories to cmake
7b2b066 Doc tidying
158d3e8 async_write requires a non-const message:
d13328b Better User-Agent in examples
ebcb2c0 Exemplars are compiled code
b9054d3 Simplify websocket write_op
c2571fe Simplify ssl teardown composed op
d8ad3d1 Simplify buffered_read_stream composed op
f68dc34 Set version to 63
a99f7ef Control running with valgrind explicitly
4eb7af4 Tidy up Jamfiles
49b42a5 Tidy up CMakeLists.txt
dadb54f Only run the tests under ubasan
d1c7696 Move benchmarks to a separate project
a4aada8 Only build and run tests in variant=coverage
f835b9a Don't use cached Boost
a0edd82 Put num_jobs back up on Travis
a8d5823 Use std::to_string instead of lexical_cast
45d8b81 Set version to 62
09e07ce Put slow tests back for coverage builds
295b1d7 Doc tidy
f58425c Squelch harmless not_connected errors
9b537f7 Add http::is_fields trait
d43701b message::prepare_payload replaces message::prepare (API Change):
42ba289 Refine FieldsReader concept (API Change)
bde90a1 Narrow the use of Fields parameters:
5f47526 parser requires basic_fields (API Change):
60f58e4 Avoid explicit operator bool for error
352f8de Clear the error faster
34befd8 Tidy up namespaces in examples
9e0b4b5 Doc fixes and tidy
c003a2a Tidy up test build scripts and projects
b929130 Add server-framework tests
03d4301 Increase detail::static_ostream coverage
80d7cbc Remove libssl-dev from a Travis matrix item
4c15db4 Set version to 61
1dfbd0b Don't run slow tests on certain targets
6bb1109 Use one job less on CI
6f58342 Tidy up resolver calls
530b044 Add multi_port to server-framework
bfef5d1 Tidy up http-crawl example
e2f2f33 Reorganize SSL examples
adc301b Fix shadowing warnings
c60185e Add server-framework SSL HTTP and WebSocket ports
7912fb8 Refactor WebSocket, HTTP examples:
cd4b9e0 Flush the output stream in the example
d046b20 Tidy up names in error categories
9d4a422 status-codes is unsigned (API Change)
e3599b0 header::version is unsigned (API Change)
a26b043 Add message::header_part()
fc8d2e9 Tidy up some integer conversion warnings
c91732e Reorganize SSL examples
9907b31 Documentation work
4b2e78e Use generic_cateogry for errno
38c46cd Remove Spirit dependency
c111d6f Set version to 60
d78dc12 Documentation work
141a524 New server-framework, full featured server example:
3f7ffd9 Fix response message type in async websocket accept
13f3750 String comparisons are public interfaces
4e4bcf8 Set version to 59
5015cdb Remove obsolete doc/README.md
71c3f0a Fix base64 alphabet
aa2b843 Change Body::size signature (API Change):
80a599a Documentation work
9c19449 Integrated Beast interface.
3f8097b Set version to 58
4581777 Better message formal parameter names
5879cd8 Fix parsing chunk size with leading zeroes
56bd228 Remove redundant code
534ca63 Use static string in basic_fields::reader
a7b3810 basic_parser::put doc
1e4413f basic_fields::set optimization:
9b244c1 Fix basic_fields insert ordering
4f854d0 Avoid std::string in websocket
dc8f146 Renamed to basic_fields::set (API Change):
660c465 Specification for http read
981285b Documentation tidy
983d676 Reorganize examples:
a2a5c57 Qualify size_t in message template
d86769c Fix unaligned reads in utf8-checker
8ba182c Set version to 57
42e2791 Update doc/ for docca
1ee0afd Merge commit '101d7dbfb9725674cb9ce5a4196f19aa1d4bb801' as 'doc/docca'
101d7db Squashed 'doc/docca/' content from commit c50b3ba5
900c04e Documentation work
8eee932 Fix warning in basic_parser.cpp
437a616 Fix message.hpp javadocs
18c68ce Set version to 56
b058e90 Convert buffer in range loops
cbc9212 Add Beast INTERFACE CMake target
2914b59 More basic_parser tests
ed5c317 Reset error codes
ba14251 Test error code handling
e45e50b Tidy up README.md build instructions
16efb9b Try harder to find Boost (cmake)
e281d91 HTTP/1 is the default version
916fe4a Call on_chunk when the extension is empty
9855598 Add string_view_body
19d4520 Tidy up
6e59f9b Add provisional IANA header field names
84722f2 Revert "Add a Beast CMake interface target:"
fde6929 Set version to 55
01f6cc4 Documentation work
a7a388c read_size replaces read_size_helper:
ed8f0bb Tidy up type_traits
c2f6268 Avoid a parser allocation using non-flat buffer:
906db45 Add a Beast CMake interface target:
47f2541 Don't allocate memory to handle obs-fold
6a8912a Set version to 54
296ef3b Documentation work
e3e9b61 Fix incorrect use of [[fallthrough]]
3c44398 Retain ownership when reading using a message
a71bb2b basic_fields refactor (API Change):
d8d3562 Add string_param
83b2558 basic_fields members and coverage
c4f5fa5 consuming_buffers members and coverage
e10507c multi_buffer members and coverage
0e6bd3f flat_buffer coverage
7351d6e static_buffer coverage
18a8ef5 Set version to 53
452df59 Remove extraneous doc file
3ef0359 Fix read_size_helper usage:
b64e6d3 Fix basic_parser::maybe_flatten (#462)
76402f7 Set version to 52
b0ceb2a Add drain_buffer class
4c6735a flat_buffer is an AllocatorAwareContainer
9d5d4d5 Documentation work
d4ec693 finish(error_code&) is a BodyReader requirement (API Change)
7b24cad opcode is private (API Change):
068c2ac Documentation work
a1ff89b Disable std::future snippet for libstdc++ bug
b5ef664 read_frame returns `bool` fin (API Change):
7911847 Remove opcode from read, async_read (API Change):
c72d70f ping_callback is a member of stream (API Change):
720a309 write_buffer_size is a member of stream (API Change):
7ff0178 read_message_max is a member of stream (API Change):
cd40964 read_buffer_size is a member of stream (API Change):
a58e5e1 binary, text are members of stream (API Change):
ad35846 auto_fragment is a member of stream (API Change):
ccee139 Documentation work
13c64e3 Set version to 51
cafc8e2 Fix infinite loop in basic_parser
dc4b69d Add construct, destroy to handler_alloc
58c2739 multi_buffer implementation change (API Change):
dd7f5c0 DynamicBuffer benchmarks
1c4811b Use BOOST_STRINGIZE
31051ac Use BOOST_FALLTHROUGH
8f2430b Documentation work
eb35b92 Fix file_body::get() not setting the more flag correctly
566244a Tidy up file_body
53cbeea Tune up static_buffer (API Change):
20c59b7 Fix operator<< for header
a2c1117 Set version to 50
6045b74 http read_some, async_read_some don't return bytes (API Change):
4df6885 Fix chunk header parsing
36bf32b Fix test::pipe read_size
bf69ce1 Fix chunk delimiter parsing
0c6b6b1 Add missing handler_alloc nested types
a06b8f9 Tidy up message javadoc
3bd8260 Remove obsolete serializer allocator (API Change)
001c979 Remove message free functions (API Change)
745876b Remove message connection settings (API Change)
bcf2c33 Body documentation work
1e303b0 Fields concept work
9d0464a Tidy up basic_fields, header, and concepts
3ba81b5 Use field in basic_parser
b5f6cc1 Use field in basic_fields and call sites
cfd6d14 Documentation reference tidy
2adc80a Protect basic_fields special members (API Change)
d55b079 Fix basic_fields allocator awareness
d8febda Documentation work
485a6e5 Refactor prepare (API Change)
9fd3071 Derive header from Fields (API Change)
8ad26b8 Use allocator more in basic_fields
0071039 Add verb to on_request for parsers (API Change)
74f6cbb Add field enumeration
be0d74f Documentation fixes
054ac40 Remove header_parser (API Change)
a007eba parser is constructible from other body types
d89809f Documentation work (buffer_body)
ac5bc4f Set version to 49
af47128 Documentation work
a1848f0 Add HEAD request example
ddfbfbf Use <iosfwd> instead of <ostream>
e67c0ab Refactor header status, reason, and target (API Change):
60f044a Tidy up empty_body writer error
7d267f4 Canonicalize string_view parameter types
ac175cb Refactor method and verb (API Change):
e18efed Documentation work
d77e423 Set version to 48
d3a5a05 Documentation work
acf18fb Tidy up traits
6cb188e Remove detail::sync_ostream
d6092bc Documentation work
4707b21 Rename to parser (API Change):
3cb385d Consolidate parsers to parser.hpp
290bdf1 Documentation work
7cb442c Make buffer_prefix_view public
ef0b121 Rename to buffer_cat_view (API Change)
b9df187 Tidy up chunk decorator (API Change):
458fa7e Set version to 47
fc83a03 Documentation work
1ee3013 Fix leak in basic_flat_buffer
55fbf76 Fix undefined behavior in pausation
fe75a7c Refactor HTTP serialization and parsing (API Change):
50cba32 buffer_size overload for basic_multi_buffer::const_buffers_type
d977bf2 Disable operator<< for buffer_body
5db707a Refactor treatment of status code and obsolete reason (API Change):
9a585a8 Refactor treatment of request-method (API Change):
3ae76d0 Set version to 46
6004712 Documentation work
34ea0b3 Refactor serialization algorithms:
407b046 Rename to make_serializer
c29451a Refactor type_traits (API Change):
8578419 Refactor HTTP serialization (API Change):
f8612aa Remove HTTP header aliases (API Change):
b0054e3 Add test::pipe
dfba72b Set version to 45
6ba3697 Disable reverse_iterator buffer_view test
266ebac buffer_view skips empty buffer sequences
96b9892 Documentation work
c23f1e2 Fix header::reason
9796106 Better test::enable_yield_to
9a8bcb7 Fix message doc image
7a5e87e Workaround for boost::asio::basic_streambuf type check
663c275 Set version to 44
f205976 Make buffers_adapter meet requirements
8e39c60 Tidy up is_dynamic_buffer traits test
0088f7c Add buffers_adapter regression test
8a23de1 Fix README websocket example
949504a Fix async return values in docs
cd9f41b Use BOOST_STATIC_ASSERT
1b616fa Tidy up and make get_lowest_layer public
612e616 Require Boost 1.58 or later
1b1daa7 Tidy up read_size_helper and dynamic buffers
bf0145d Use BOOST_THROW_EXCEPTION
e762818 Add GitHub issue template
dab679c Set version to 43
386b817 Reformat README.md QR code
50e5123 Additional constructors for consuming_buffers
f7289b9 Add write limit to test::string_ostream
3aa87e0 Tidy up buffer_prefix overloads and test
bee583c Fix strict aliasing warnings in buffers_view
6b54d3a Require Boost 1.64.0
76f1084 Set version to 42
0bdb148 Make buffers_view a public interface
338fc81 Add formal review notes
784f965 Fix javadoc typo
823aee2 Set version to v41
88adbdd Remove handler helpers, tidy up hook invocations (API Change)
4974af2 Rename prepare_buffer(s) to buffer_prefix (API Change)
ebd459b Tidy up websocket::close_code enum and constructors
c3fd6f9 Tidy up formal parameter names
210cd70 Remove coveralls integration
d811962 Concept revision and documentation (API Change):
bdae92a Replace asynchronous helper macros with template aliases (API Change)
df95a09 Move prepare_buffers to prepare_buffer.hpp (API Change)
787de21 Remove placeholders (API Change)
c59b544 Trim Appveyor matrix rows
b7184f3 Return http::error::end_of_stream on HTTP read eof (API Change)
f2d8255 Set version to 40
40b9194 Tidy up .travis.yml:
9b240c7 Fix basic_streambuf movable trait
76a2617 Consolidate get_lowest_layer in type_traits.hpp
6d00321 Add to_static_string:
f888136 Set version to 39
47c82b5 Better travis deps
4ed7865 Squelch openssl spurious leak and memory errors
b6bc26f Fixed braced-init error with older gcc
59b2f8f ostream workaround for gcc 4.8.4
8363d86 Increase ostream test coverage
5631936 Tidy up HTTP reason_string (API Change):
2bf5150 Harmonize concepts and identifiers with net-ts (API Change):
728e9d8 Tidy up basic_parser javadocs
1c9067b Use beast::string_view alias
771c5ca Doc fixes and tidying
e2b5c31 Rename to buffered_read_stream (API Change):
a753f1c Rename to static_buffer, static_buffer_n (API Change):
24b6686 Rename to flat_buffer, basic_flat_buffer (API Change):
69259ef Rename to multi_buffer, basic_multi_buffer (API Change):
bef9ae1 New buffers() replaces to_string() (API Change):
a7ef4f5 New ostream() returns dynamic buffer output stream (API Change):
87fd60c Fix eof error on ssl::stream shutdown
606fc9d Add websocket async echo ssl server test:
ff5e659 Refactor http::header contents (API Change):
dd02097 Set version to 1.0.0-b38
5596e97 Prevent basic_fields operator[] assignment
c2b32dc Remove websocket::keep_alive option (API Change):
32dbfb2 Refactor WebSocket error codes (API Change):
dd6b500 WebSocket doc work
0b4d87c More flat_streambuf tests
aacefb4 Add test_allocator to extras/test
931a5fb Simplify get_lowest_layer test
ba4228a Use static_string for WebSocket handshakes:
6df3ff3 Refactor base64:
19b124d Refactor static_string:
30e8d16 Set version to 1.0.0-b37
b141020 Fix narrowing warning in table constants
d554b81 Add -funsigned-char to asan build target
bcc6ad8 Add ub sanitizer blacklist
e1f08e9 Fix flat_streambuf:
7d08f59 Fix typo in documentation example
21ef97d Rename to http::dynamic_body, consolidate header:
45a2d73 Rename project to http-bench
c86fee9 Move everything in basic_fields.hpp to fields.hpp (API Change)
a14a5d6 Rename to detail::is_invocable
540d037 Rename to websocket::detail::pausation
84e1739 Document websocket::stream thread safety
dc274af Add is_upgrade() free function:
2c17d04 Refactor websocket decorators (API Change):
235fe68 Provide websocket::stream accept() overloads (API Change):
a715825 CMake hide command lines in .vcxproj Output windows
32024d8 Set version to 1.0.0-b36
f48b95f Update README.md
d8db5f1 Set version to 1.0.0-b35
dd2a514 Tidy up doc declarations
2c50aba Fix README.md CMake instructions
4ffdce2 Update .gitignore for VS2017
403011f Remove http::empty_body (API Change)
f47b661 New HTTP interfaces (API Change):
f6835b8 Rename to BEAST_DOXYGEN
7e37723 Add flat_streambuf:
5b68faa Doc XSL support for list and table markdown
3de46de Make websocket::close_code a proper enum:
0128743 Tidy up MSVC CMake configuration
ccd188e Add appveyor build script

git-subtree-dir: src/beast
git-subtree-split: 3bcd9865f80f12ba5faad35c564918f85b02e271
This commit is contained in:
Miguel Portilla
2017-07-11 12:17:02 -04:00
parent d8dea963fa
commit f0f96bd1da
487 changed files with 60952 additions and 23793 deletions

19
include/beast.hpp Normal file
View File

@@ -0,0 +1,19 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HPP
#define BEAST_HPP
#include <beast/config.hpp>
#include <beast/core.hpp>
#include <beast/http.hpp>
#include <beast/version.hpp>
#include <beast/websocket.hpp>
#include <beast/zlib.hpp>
#endif

View File

@@ -8,17 +8,23 @@
#ifndef BEAST_CONFIG_HPP
#define BEAST_CONFIG_HPP
#include <boost/config.hpp>
// Available to every header
#include <boost/core/ignore_unused.hpp>
#include <boost/static_assert.hpp>
/*
_MSC_VER and _MSC_FULL_VER by version:
14.0 (2015) 1900 190023026
14.0 (2015 Update 1) 1900 190023506
14.0 (2015 Update 2) 1900 190023918
14.0 (2015 Update 3) 1900 190024210
14.0 (2015) 1900 190023026
14.0 (2015 Update 1) 1900 190023506
14.0 (2015 Update 2) 1900 190023918
14.0 (2015 Update 3) 1900 190024210
*/
#if defined(_MSC_FULL_VER)
#if _MSC_FULL_VER < 190024210
#ifdef BOOST_MSVC
#if BOOST_MSVC_FULL_VER < 190024210
static_assert(false,
"This library requires Visual Studio 2015 Update 3 or later");
#endif

View File

@@ -10,25 +10,31 @@
#include <beast/config.hpp>
#include <beast/core/async_completion.hpp>
#include <beast/core/async_result.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/buffer_prefix.hpp>
#include <beast/core/buffered_read_stream.hpp>
#include <beast/core/buffers_adapter.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/drain_buffer.hpp>
#include <beast/core/error.hpp>
#include <beast/core/file.hpp>
#include <beast/core/file_base.hpp>
#include <beast/core/file_posix.hpp>
#include <beast/core/file_stdio.hpp>
#include <beast/core/file_win32.hpp>
#include <beast/core/flat_buffer.hpp>
#include <beast/core/handler_alloc.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
#include <beast/core/placeholders.hpp>
#include <beast/core/prepare_buffers.hpp>
#include <beast/core/static_streambuf.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/core/ostream.hpp>
#include <beast/core/read_size.hpp>
#include <beast/core/span.hpp>
#include <beast/core/static_buffer.hpp>
#include <beast/core/static_string.hpp>
#include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/dynabuf_readstream.hpp>
#include <beast/core/to_string.hpp>
#include <beast/core/write_dynabuf.hpp>
#include <beast/core/string.hpp>
#include <beast/core/string_param.hpp>
#include <beast/core/type_traits.hpp>
#endif

View File

@@ -1,88 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_ASYNC_COMPLETION_HPP
#define BEAST_ASYNC_COMPLETION_HPP
#include <beast/config.hpp>
#include <beast/core/handler_concepts.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/handler_type.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** Helper for customizing the return type of asynchronous initiation functions.
This class template is used to transform caller-provided completion
handlers in calls to asynchronous initiation functions. The transformation
allows customization of the return type of the initiating function, and the
function signature of the final handler.
@tparam CompletionHandler A completion handler, or a user defined type
with specializations for customizing the return type (for example,
`boost::asio::use_future` or `boost::asio::yield_context`).
@tparam Signature The callable signature of the final completion handler.
Example:
@code
...
template<class CompletionHandler>
typename async_completion<CompletionHandler,
void(error_code)>::result_type
async_initfn(..., CompletionHandler&& handler)
{
async_completion<CompletionHandler,
void(error_code)> completion{handler};
...
return completion.result.get();
}
@endcode
@note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
Library Foundations For Asynchronous Operations</a>
*/
template<class CompletionHandler, class Signature>
struct async_completion
{
/** The type of the final handler called by the asynchronous initiation function.
Objects of this type will be callable with the specified signature.
*/
using handler_type =
typename boost::asio::handler_type<
CompletionHandler, Signature>::type;
/// The type of the value returned by the asynchronous initiation function.
using result_type = typename
boost::asio::async_result<handler_type>::type;
/** Construct the helper.
@param token The completion handler. Copies will be made as
required. If `CompletionHandler` is movable, it may also be moved.
*/
async_completion(typename std::remove_reference<CompletionHandler>::type& token)
: handler(std::forward<CompletionHandler>(token))
, result(handler)
{
static_assert(is_CompletionHandler<handler_type, Signature>::value,
"Handler requirements not met");
}
/// The final completion handler, callable with the specified signature.
handler_type handler;
/// The return value of the asynchronous initiation function.
boost::asio::async_result<handler_type> result;
};
} // beast
#endif

View File

@@ -0,0 +1,205 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_ASYNC_COMPLETION_HPP
#define BEAST_ASYNC_COMPLETION_HPP
#include <beast/config.hpp>
#include <beast/core/type_traits.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/handler_type.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** An interface for customising the behaviour of an asynchronous initiation function.
This class is used for determining:
@li The concrete completion handler type to be called at the end of the
asynchronous operation;
@li the initiating function return type; and
@li how the return value of the initiating function is obtained.
The trait allows the handler and return types to be determined at the point
where the specific completion handler signature is known.
This template takes advantage of specializations of both
`boost::asio::async_result` and `boost::asio::handler_type` for user-defined
completion token types. The primary template assumes that the
@b CompletionToken is the completion handler.
@par Example
The example shows how to define an asynchronous initiation function
whose completion handler receives an error code:
@code
template<
class AsyncStream, // A stream supporting asynchronous read and write
class Handler // The handler to call with signature void(error_code)
>
async_return_type< // This provides the return type customization
Handler, void(error_code)>
do_async(
AsyncStream& stream, // The stream to work on
Handler&& handler) // Could be an rvalue or const reference
{
// Make sure we have an async stream
static_assert(is_async_stream<AsyncWriteStream>::value,
"AsyncStream requirements not met");
// This helper converts the handler into the real handler type
async_completion<WriteHandler, void(error_code)> init{handler};
... // Create and invoke the composed operation
// This provides the return value and executor customization
return init.result.get();
}
@endcode
@see @ref async_completion, @ref async_return_type, @ref handler_type
*/
template<class CompletionToken, class Signature>
class async_result
{
BOOST_STATIC_ASSERT(
! std::is_reference<CompletionToken>::value);
boost::asio::async_result<typename
boost::asio::handler_type<CompletionToken,
Signature>::type> impl_;
async_result(async_result const&) = delete;
async_result& operator=(async_result const&) = delete;
public:
/// The concrete completion handler type for the specific signature.
using completion_handler_type =
typename boost::asio::handler_type<
CompletionToken, Signature>::type;
/// The return type of the initiating function.
using return_type =
typename boost::asio::async_result<
completion_handler_type>::type;
/** Construct an async result from a given handler.
When using a specalised async_result, the constructor has
an opportunity to initialise some state associated with the
completion handler, which is then returned from the initiating
function.
*/
explicit
async_result(completion_handler_type& h)
: impl_(h)
{
}
/// Obtain the value to be returned from the initiating function.
return_type
get()
{
return impl_.get();
}
};
/** Helper for customizing the return type of asynchronous initiation functions.
This class template is used to transform caller-provided completion
handlers in calls to asynchronous initiation functions. The transformation
allows customization of the return type of the initiating function, and the
function signature of the final handler.
Example:
@code
...
template<class CompletionToken>
typename async_completion<CompletionToken, void(error_code)>::result_type
async_initfn(..., CompletionToken&& handler)
{
async_completion<CompletionToken, void(error_code)> completion{handler};
...
return completion.result.get();
}
@endcode
@tparam CompletionToken Specifies the model used to obtain the result of
the asynchronous operation.
@tparam Signature The call signature for the completion handler type invoked
on completion of the asynchronous operation.
@note See <a href="http://cplusplus.github.io/networking-ts/draft.pdf">
Working Draft, C++ Extensions for Networking</a>
@see @ref async_return_type, @ref handler_type
*/
template<class CompletionToken, class Signature>
struct async_completion
{
/** The type of the final handler called by the asynchronous initiation function.
Objects of this type will be callable with the specified signature.
*/
using completion_handler_type = typename async_result<
typename std::decay<CompletionToken>::type,
Signature>::completion_handler_type;
/** Constructor
The constructor creates the concrete completion handler and
makes the link between the handler and the asynchronous
result.
@param token The completion token. If this is a regular completion
handler, copies may be made as needed. If the handler is movable,
it may also be moved.
*/
explicit
async_completion(CompletionToken& token)
: completion_handler(static_cast<typename std::conditional<
std::is_same<CompletionToken, completion_handler_type>::value,
completion_handler_type&, CompletionToken&&>::type>(token))
, result(completion_handler)
{
// CompletionToken is not invokable with the given signature
static_assert(is_completion_handler<
completion_handler_type, Signature>::value,
"CompletionToken requirements not met: signature mismatch");
}
/// The final completion handler, callable with the specified signature.
typename std::conditional<std::is_same<
CompletionToken, completion_handler_type>::value,
completion_handler_type&,
completion_handler_type
>::type completion_handler;
/// The return value of the asynchronous initiation function.
async_result<typename std::decay<
CompletionToken>::type, Signature> result;
};
template<class CompletionToken, typename Signature>
using handler_type = typename beast::async_result<
typename std::decay<CompletionToken>::type,
Signature>::completion_handler_type;
template<class CompletionToken, typename Signature>
using async_return_type = typename beast::async_result<
typename std::decay<CompletionToken>::type,
Signature>::return_type;
} // beast
#endif

View File

@@ -9,7 +9,7 @@
#define BEAST_BIND_HANDLER_HPP
#include <beast/config.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/core/detail/bind_handler.hpp>
#include <type_traits>
#include <utility>
@@ -24,24 +24,23 @@ namespace beast {
the returned handler, which provides the same `io_service`
execution guarantees as the original handler.
Unlike `io_service::wrap`, the returned handler can be used in
a subsequent call to `io_service::post` instead of
`io_service::dispatch`, to ensure that the handler will not be
invoked immediately by the calling function.
Unlike `boost::asio::io_service::wrap`, the returned handler can
be used in a subsequent call to `boost::asio::io_service::post`
instead of `boost::asio::io_service::dispatch`, to ensure that
the handler will not be invoked immediately by the calling
function.
Example:
@code
template<class AsyncReadStream, class ReadHandler>
void
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
signal_aborted(AsyncReadStream& stream, ReadHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::forward<ReadHandler>(handler),
boost::asio::error::operation_aborted, 0));
}
@endcode
@param handler The handler to wrap.
@@ -50,7 +49,7 @@ namespace beast {
arguments are forwarded into the returned object.
*/
template<class Handler, class... Args>
#if GENERATING_DOCS
#if BEAST_DOXYGEN
implementation_defined
#else
detail::bound_handler<
@@ -58,7 +57,7 @@ detail::bound_handler<
#endif
bind_handler(Handler&& handler, Args&&... args)
{
static_assert(is_CompletionHandler<
static_assert(is_completion_handler<
Handler, void(Args...)>::value,
"Handler requirements not met");
return detail::bound_handler<typename std::decay<

View File

@@ -9,17 +9,73 @@
#define BEAST_BUFFER_CAT_HPP
#include <beast/config.hpp>
#include <beast/core/detail/buffer_cat.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <new>
#include <stdexcept>
#include <beast/core/detail/type_traits.hpp>
#include <tuple>
#include <utility>
namespace beast {
/** A buffer sequence representing a concatenation of buffer sequences.
@see @ref buffer_cat
*/
template<class... Buffers>
class buffer_cat_view
{
#if 0
static_assert(
detail::is_all_const_buffer_sequence<Buffers...>::value,
"BufferSequence requirements not met");
#endif
std::tuple<Buffers...> bn_;
public:
/** The type of buffer returned when dereferencing an iterator.
If every buffer sequence in the view is a @b MutableBufferSequence,
then `value_type` will be `boost::asio::mutable_buffer`.
Otherwise, `value_type` will be `boost::asio::const_buffer`.
*/
using value_type =
#if BEAST_DOXYGEN
implementation_defined;
#else
typename detail::common_buffers_type<Buffers...>::type;
#endif
/// The type of iterator used by the concatenated sequence
class const_iterator;
/// Move constructor
buffer_cat_view(buffer_cat_view&&) = default;
/// Copy constructor
buffer_cat_view(buffer_cat_view const&) = default;
/// Move assignment
buffer_cat_view& operator=(buffer_cat_view&&) = default;
// Copy assignment
buffer_cat_view& operator=(buffer_cat_view const&) = default;
/** Constructor
@param buffers The list of buffer sequences to concatenate.
Copies of the arguments will be made; however, the ownership
of memory is not transferred.
*/
explicit
buffer_cat_view(Buffers const&... buffers);
/// Return an iterator to the beginning of the concatenated sequence.
const_iterator
begin() const;
/// Return an iterator to the end of the concatenated sequence.
const_iterator
end() const;
};
/** Concatenate 2 or more buffer sequences.
This function returns a constant or mutable buffer sequence which,
@@ -34,26 +90,30 @@ namespace beast {
@return A new buffer sequence that represents the concatenation of
the input buffer sequences. This buffer sequence will be a
@b MutableBufferSequence if each of the passed buffer sequences is
also a @b MutableBufferSequence, else the returned buffer sequence
will be a @b ConstBufferSequence.
also a @b MutableBufferSequence; otherwise the returned buffer
sequence will be a @b ConstBufferSequence.
@see @ref buffer_cat_view
*/
#if GENERATING_DOCS
#if BEAST_DOXYGEN
template<class... BufferSequence>
implementation_defined
buffer_cat_view<BufferSequence...>
buffer_cat(BufferSequence const&... buffers)
#else
template<class B1, class B2, class... Bn>
detail::buffer_cat_helper<B1, B2, Bn...>
inline
buffer_cat_view<B1, B2, Bn...>
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
#endif
{
static_assert(
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
detail::is_all_const_buffer_sequence<B1, B2, Bn...>::value,
"BufferSequence requirements not met");
return detail::buffer_cat_helper<
B1, B2, Bn...>{b1, b2, bn...};
return buffer_cat_view<B1, B2, Bn...>{b1, b2, bn...};
}
} // beast
#include <beast/core/impl/buffer_cat.ipp>
#endif

View File

@@ -1,62 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_BUFFER_CONCEPTS_HPP
#define BEAST_BUFFER_CONCEPTS_HPP
#include <beast/config.hpp>
#include <beast/core/detail/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` meets the requirements of @b `BufferSequence`.
template<class T, class BufferType>
#if GENERATING_DOCS
struct is_BufferSequence : std::integral_constant<bool, ...>
#else
struct is_BufferSequence : detail::is_BufferSequence<T, BufferType>::type
#endif
{
};
/// Determine if `T` meets the requirements of @b `ConstBufferSequence`.
template<class T>
#if GENERATING_DOCS
struct is_ConstBufferSequence : std::integral_constant<bool, ...>
#else
struct is_ConstBufferSequence :
is_BufferSequence<T, boost::asio::const_buffer>
#endif
{
};
/// Determine if `T` meets the requirements of @b `DynamicBuffer`.
template<class T>
#if GENERATING_DOCS
struct is_DynamicBuffer : std::integral_constant<bool, ...>
#else
struct is_DynamicBuffer : detail::is_DynamicBuffer<T>::type
#endif
{
};
/// Determine if `T` meets the requirements of @b `MutableBufferSequence`.
template<class T>
#if GENERATING_DOCS
struct is_MutableBufferSequence : std::integral_constant<bool, ...>
#else
struct is_MutableBufferSequence :
is_BufferSequence<T, boost::asio::mutable_buffer>
#endif
{
};
} // beast
#endif

View File

@@ -0,0 +1,208 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_BUFFER_PREFIX_HPP
#define BEAST_BUFFER_PREFIX_HPP
#include <beast/config.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/core/detail/in_place_init.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <type_traits>
namespace beast {
/** A buffer sequence adapter that shortens the sequence size.
The class adapts a buffer sequence to efficiently represent
a shorter subset of the original list of buffers starting
with the first byte of the original sequence.
@tparam BufferSequence The buffer sequence to adapt.
*/
template<class BufferSequence>
class buffer_prefix_view
{
using buffers_type = typename
std::decay<BufferSequence>::type;
using iter_type = typename buffers_type::const_iterator;
BufferSequence bs_;
iter_type back_;
iter_type end_;
std::size_t size_;
template<class Deduced>
buffer_prefix_view(Deduced&& other,
std::size_t nback, std::size_t nend)
: bs_(std::forward<Deduced>(other).bs_)
, back_(std::next(bs_.begin(), nback))
, end_(std::next(bs_.begin(), nend))
, size_(other.size_)
{
}
void
setup(std::size_t n);
public:
/// The type for each element in the list of buffers.
using value_type = typename std::conditional<
std::is_convertible<typename
std::iterator_traits<iter_type>::value_type,
boost::asio::mutable_buffer>::value,
boost::asio::mutable_buffer,
boost::asio::const_buffer>::type;
#if BEAST_DOXYGEN
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/// Move constructor.
buffer_prefix_view(buffer_prefix_view&&);
/// Copy constructor.
buffer_prefix_view(buffer_prefix_view const&);
/// Move assignment.
buffer_prefix_view& operator=(buffer_prefix_view&&);
/// Copy assignment.
buffer_prefix_view& operator=(buffer_prefix_view const&);
/** Construct a buffer sequence prefix.
@param n The maximum number of bytes in the prefix.
If this is larger than the size of passed, buffers,
the resulting sequence will represent the entire
input sequence.
@param buffers The buffer sequence to adapt. A copy of
the sequence will be made, but ownership of the underlying
memory is not transferred.
*/
buffer_prefix_view(std::size_t n, BufferSequence const& buffers);
/** Construct a buffer sequence prefix in-place.
@param n The maximum number of bytes in the prefix.
If this is larger than the size of passed, buffers,
the resulting sequence will represent the entire
input sequence.
@param args Arguments forwarded to the contained buffers constructor.
*/
template<class... Args>
buffer_prefix_view(std::size_t n,
boost::in_place_init_t, Args&&... args);
/// Get a bidirectional iterator to the first element.
const_iterator
begin() const;
/// Get a bidirectional iterator to one past the last element.
const_iterator
end() const;
};
/** Returns a prefix of a constant buffer.
The returned buffer points to the same memory as the
passed buffer, but with a size that is equal to or less
than the size of the original buffer.
@param n The size of the returned buffer.
@param buffer The buffer to shorten. The underlying
memory is not modified.
@return A new buffer that points to the first `n` bytes
of the original buffer.
*/
inline
boost::asio::const_buffer
buffer_prefix(std::size_t n,
boost::asio::const_buffer buffer)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return { buffer_cast<void const*>(buffer),
(std::min)(n, buffer_size(buffer)) };
}
/** Returns a prefix of a mutable buffer.
The returned buffer points to the same memory as the
passed buffer, but with a size that is equal to or less
than the size of the original buffer.
@param n The size of the returned buffer.
@param buffer The buffer to shorten. The underlying
memory is not modified.
@return A new buffer that points to the first `n` bytes
of the original buffer.
*/
inline
boost::asio::mutable_buffer
buffer_prefix(std::size_t n,
boost::asio::mutable_buffer buffer)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return { buffer_cast<void*>(buffer),
(std::min)(n, buffer_size(buffer)) };
}
/** Returns a prefix of a buffer sequence.
This function returns a new buffer sequence which when iterated,
presents a shorter subset of the original list of buffers starting
with the first byte of the original sequence.
@param n The maximum number of bytes in the wrapped
sequence. If this is larger than the size of passed,
buffers, the resulting sequence will represent the
entire input sequence.
@param buffers An instance of @b ConstBufferSequence or
@b MutableBufferSequence to adapt. A copy of the sequence
will be made, but ownership of the underlying memory is
not transferred.
*/
template<class BufferSequence>
#if BEAST_DOXYGEN
buffer_prefix_view<BufferSequence>
#else
inline
typename std::enable_if<
! std::is_same<BufferSequence, boost::asio::const_buffer>::value &&
! std::is_same<BufferSequence, boost::asio::mutable_buffer>::value,
buffer_prefix_view<BufferSequence>>::type
#endif
buffer_prefix(std::size_t n, BufferSequence const& buffers)
{
static_assert(
is_const_buffer_sequence<BufferSequence>::value ||
is_mutable_buffer_sequence<BufferSequence>::value,
"BufferSequence requirements not met");
return buffer_prefix_view<BufferSequence>(n, buffers);
}
} // beast
#include <beast/core/impl/buffer_prefix.ipp>
#endif

View File

@@ -5,16 +5,14 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DYNABUF_READSTREAM_HPP
#define BEAST_DYNABUF_READSTREAM_HPP
#ifndef BEAST_BUFFERED_READ_STREAM_HPP
#define BEAST_BUFFERED_READ_STREAM_HPP
#include <beast/config.hpp>
#include <beast/core/async_completion.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/async_result.hpp>
#include <beast/core/error.hpp>
#include <beast/core/stream_concepts.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/detail/get_lowest_layer.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/core/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <cstdint>
@@ -22,11 +20,11 @@
namespace beast {
/** A @b `Stream` with attached @b `DynamicBuffer` to buffer reads.
/** A @b Stream with attached @b DynamicBuffer to buffer reads.
This wraps a @b `Stream` implementation so that calls to write are
This wraps a @b Stream implementation so that calls to write are
passed through to the underlying stream, while calls to read will
first consume the input sequence stored in a @b `DynamicBuffer` which
first consume the input sequence stored in a @b DynamicBuffer which
is part of the object.
The use-case for this class is different than that of the
@@ -52,7 +50,7 @@ namespace beast {
//
template<class DynamicBuffer>
void process_http_message(
dynabuf_readstream<DynamicBuffer>& stream)
buffered_read_stream<DynamicBuffer>& stream)
{
// Read up to and including the end of the HTTP
// header, leaving the sequence in the stream's
@@ -64,11 +62,11 @@ namespace beast {
boost::asio::read_until(
stream.next_layer(), stream.buffer(), "\r\n\r\n");
// Use prepare_buffers() to limit the input
// Use buffer_prefix() to limit the input
// sequence to only the data up to and including
// the trailing "\r\n\r\n".
//
auto header_buffers = prepare_buffers(
auto header_buffers = buffer_prefix(
bytes_transferred, stream.buffer().data());
...
@@ -88,9 +86,9 @@ namespace beast {
@tparam DynamicBuffer The type of stream buffer to use.
*/
template<class Stream, class DynamicBuffer>
class dynabuf_readstream
class buffered_read_stream
{
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
template<class Buffers, class Handler>
@@ -102,7 +100,7 @@ class dynabuf_readstream
public:
/// The type of the internal buffer
using dynabuf_type = DynamicBuffer;
using buffer_type = DynamicBuffer;
/// The type of the next layer.
using next_layer_type =
@@ -110,26 +108,21 @@ public:
/// The type of the lowest layer.
using lowest_layer_type =
#if GENERATING_DOCS
implementation_defined;
#else
typename detail::get_lowest_layer<
next_layer_type>::type;
#endif
typename get_lowest_layer<next_layer_type>::type;
/** Move constructor.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
dynabuf_readstream(dynabuf_readstream&&) = default;
buffered_read_stream(buffered_read_stream&&) = default;
/** Move assignment.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
dynabuf_readstream& operator=(dynabuf_readstream&&) = default;
buffered_read_stream& operator=(buffered_read_stream&&) = default;
/** Construct the wrapping stream.
@@ -137,7 +130,7 @@ public:
*/
template<class... Args>
explicit
dynabuf_readstream(Args&&... args);
buffered_read_stream(Args&&... args);
/// Get a reference to the next layer.
next_layer_type&
@@ -272,10 +265,10 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class MutableBufferSequence, class ReadHandler>
#if GENERATING_DOCS
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<ReadHandler, void(error_code)>::result_type
async_return_type<ReadHandler, void(error_code)>
#endif
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler);
@@ -296,7 +289,7 @@ public:
std::size_t
write_some(ConstBufferSequence const& buffers)
{
static_assert(is_SyncWriteStream<next_layer_type>::value,
static_assert(is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers);
}
@@ -318,7 +311,7 @@ public:
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_SyncWriteStream<next_layer_type>::value,
static_assert(is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers, ec);
}
@@ -347,10 +340,10 @@ public:
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class ConstBufferSequence, class WriteHandler>
#if GENERATING_DOCS
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<WriteHandler, void(error_code)>::result_type
async_return_type<WriteHandler, void(error_code)>
#endif
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler);
@@ -358,6 +351,6 @@ public:
} // beast
#include <beast/core/impl/dynabuf_readstream.ipp>
#include <beast/core/impl/buffered_read_stream.ipp>
#endif

View File

@@ -9,16 +9,16 @@
#define BEAST_BUFFERS_ADAPTER_HPP
#include <beast/config.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
namespace beast {
/** Adapts a @b `MutableBufferSequence` into a @b `DynamicBuffer`.
/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer.
This class wraps a @b `MutableBufferSequence` to meet the requirements
of @b `DynamicBuffer`. Upon construction the input and output sequences are
This class wraps a @b MutableBufferSequence to meet the requirements
of @b DynamicBuffer. Upon construction the input and output sequences are
empty. A copy of the mutable buffer sequence object is stored; however,
ownership of the underlying memory is not transferred. The caller is
responsible for making sure that referenced memory remains valid
@@ -32,7 +32,7 @@ namespace beast {
template<class MutableBufferSequence>
class buffers_adapter
{
static_assert(is_MutableBufferSequence<MutableBufferSequence>::value,
static_assert(is_mutable_buffer_sequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using iter_type = typename MutableBufferSequence::const_iterator;
@@ -64,7 +64,7 @@ class buffers_adapter
}
public:
#if GENERATING_DOCS
#if BEAST_DOXYGEN
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
@@ -112,6 +112,13 @@ public:
{
return in_size_;
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const
{
return max_size_;
}
/** Get a list of buffers that represents the output sequence, with the given size.

View File

@@ -9,8 +9,9 @@
#define BEAST_CONSUMING_BUFFERS_HPP
#include <beast/config.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/in_place_init.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <cstdint>
#include <iterator>
#include <type_traits>
@@ -40,7 +41,6 @@ class consuming_buffers
BufferSequence bs_;
iter_type begin_;
iter_type end_;
std::size_t skip_ = 0;
template<class Deduced>
@@ -59,7 +59,7 @@ public:
`boost::asio::mutable_buffer`, else this type will be
`boost::asio::const_buffer`.
*/
#if GENERATING_DOCS
#if BEAST_DOXYGEN
using value_type = ...;
#else
using value_type = typename std::conditional<
@@ -70,7 +70,7 @@ public:
boost::asio::const_buffer>::type;
#endif
#if GENERATING_DOCS
#if BEAST_DOXYGEN
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
@@ -79,18 +79,15 @@ public:
#endif
/// Move constructor.
/// Constructor
consuming_buffers();
/// Move constructor
consuming_buffers(consuming_buffers&&);
/// Copy constructor.
/// Copy constructor
consuming_buffers(consuming_buffers const&);
/// Move assignment.
consuming_buffers& operator=(consuming_buffers&&);
/// Copy assignment.
consuming_buffers& operator=(consuming_buffers const&);
/** Construct to represent a buffer sequence.
A copy of the buffer sequence is made. Ownership of the
@@ -99,6 +96,19 @@ public:
explicit
consuming_buffers(BufferSequence const& buffers);
/** Construct a buffer sequence in-place.
@param args Arguments forwarded to the contained buffers constructor.
*/
template<class... Args>
consuming_buffers(boost::in_place_init_t, Args&&... args);
/// Move assignment
consuming_buffers& operator=(consuming_buffers&&);
/// Copy assignmen
consuming_buffers& operator=(consuming_buffers const&);
/// Get a bidirectional iterator to the first element.
const_iterator
begin() const;

View File

@@ -5,15 +5,6 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BASE64_HPP
#define BEAST_DETAIL_BASE64_HPP
#include <cctype>
#include <string>
namespace beast {
namespace detail {
/*
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
Copyright notice:
@@ -44,77 +35,195 @@ namespace detail {
*/
template<class = void>
std::string const&
base64_alphabet()
#ifndef BEAST_DETAIL_BASE64_HPP
#define BEAST_DETAIL_BASE64_HPP
#include <cctype>
#include <string>
#include <utility>
namespace beast {
namespace detail {
namespace base64 {
inline
char const*
get_alphabet()
{
static std::string const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
return alphabet;
static char constexpr tab[] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
};
return &tab[0];
}
inline
bool
is_base64(unsigned char c)
signed char const*
get_inverse()
{
return (std::isalnum(c) || (c == '+') || (c == '/'));
static signed char constexpr tab[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
};
return &tab[0];
}
template<class = void>
std::string
base64_encode (std::uint8_t const* data,
std::size_t in_len)
/// Returns max chars needed to encode a base64 string
inline
std::size_t constexpr
encoded_size(std::size_t n)
{
return 4 * ((n + 2) / 3);
}
/// Returns max bytes needed to decode a base64 string
inline
std::size_t constexpr
decoded_size(std::size_t n)
{
return n / 4 * 3; // requires n&3==0, smaller
//return 3 * n / 4;
}
/** Encode a series of octets as a padded, base64 string.
The resulting string will not be null terminated.
@par Requires
The memory pointed to by `out` points to valid memory
of at least `encoded_size(len)` bytes.
@return The number of characters written to `out`. This
will exclude any null termination.
*/
template<class = void>
std::size_t
encode(void* dest, void const* src, std::size_t len)
{
char* out = static_cast<char*>(dest);
char const* in = static_cast<char const*>(src);
auto const tab = base64::get_alphabet();
for(auto n = len / 3; n--;)
{
*out++ = tab[ (in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
*out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
*out++ = tab[ in[2] & 0x3f];
in += 3;
}
switch(len % 3)
{
case 2:
*out++ = tab[ (in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
*out++ = tab[ (in[1] & 0x0f) << 2];
*out++ = '=';
break;
case 1:
*out++ = tab[ (in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4)];
*out++ = '=';
*out++ = '=';
break;
case 0:
break;
}
return out - static_cast<char*>(dest);
}
/** Decode a padded base64 string into a series of octets.
@par Requires
The memory pointed to by `out` points to valid memory
of at least `decoded_size(len)` bytes.
@return The number of octets written to `out`, and
the number of characters read from the input string,
expressed as a pair.
*/
template<class = void>
std::pair<std::size_t, std::size_t>
decode(void* dest, char const* src, std::size_t len)
{
char* out = static_cast<char*>(dest);
auto in = reinterpret_cast<unsigned char const*>(src);
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
std::string ret;
ret.reserve (3 + in_len * 8 / 6);
auto const inverse = base64::get_inverse();
char const* alphabet (base64_alphabet().data());
while(in_len--)
while(len-- && *in != '=')
{
c3[i++] = *(data++);
if(i == 3)
auto const v = inverse[*in];
if(v == -1)
break;
++in;
c4[i] = v;
if(++i == 4)
{
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(i = 0; (i < 4); i++)
ret += alphabet[c4[i]];
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(i = 0; i < 3; i++)
*out++ = c3[i];
i = 0;
}
}
if(i)
{
for(j = i; j < 3; j++)
c3[j] = '\0';
c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(j = 0; (j < i + 1); j++)
ret += alphabet[c4[j]];
while((i++ < 3))
ret += '=';
for(j = 0; j < i - 1; j++)
*out++ = c3[j];
}
return ret;
return {out - static_cast<char*>(dest),
in - reinterpret_cast<unsigned char const*>(src)};
}
} // base64
template<class = void>
std::string
base64_encode (std::string const& s)
base64_encode (std::uint8_t const* data,
std::size_t len)
{
std::string dest;
dest.resize(base64::encoded_size(len));
dest.resize(base64::encode(&dest[0], data, len));
return dest;
}
inline
std::string
base64_encode(std::string const& s)
{
return base64_encode (reinterpret_cast <
std::uint8_t const*> (s.data()), s.size());
@@ -124,52 +233,12 @@ template<class = void>
std::string
base64_decode(std::string const& data)
{
auto in_len = data.size();
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
int in_ = 0;
std::string ret;
ret.reserve (in_len * 6 / 8); // ???
while(in_len-- && (data[in_] != '=') &&
is_base64(data[in_]))
{
c4[i++] = data[in_]; in_++;
if(i == 4) {
for(i = 0; i < 4; i++)
c4[i] = static_cast<unsigned char>(
base64_alphabet().find(c4[i]));
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(i = 0; (i < 3); i++)
ret += c3[i];
i = 0;
}
}
if(i)
{
for(j = i; j < 4; j++)
c4[j] = 0;
for(j = 0; j < 4; j++)
c4[j] = static_cast<unsigned char>(
base64_alphabet().find(c4[j]));
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(j = 0; (j < i - 1); j++)
ret += c3[j];
}
return ret;
std::string dest;
dest.resize(base64::decoded_size(data.size()));
auto const result = base64::decode(
&dest[0], data.data(), data.size());
dest.resize(result.first);
return dest;
}
} // detail

View File

@@ -8,8 +8,10 @@
#ifndef BEAST_BIND_DETAIL_HANDLER_HPP
#define BEAST_BIND_DETAIL_HANDLER_HPP
#include <beast/core/handler_helpers.hpp>
#include <beast/core/detail/integer_sequence.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <utility>
namespace beast {
@@ -68,8 +70,9 @@ public:
asio_handler_allocate(
std::size_t size, bound_handler* h)
{
return beast_asio_helpers::
allocate(size, h->h_);
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(h->h_));
}
friend
@@ -77,16 +80,17 @@ public:
asio_handler_deallocate(
void* p, std::size_t size, bound_handler* h)
{
beast_asio_helpers::
deallocate(p, size, h->h_);
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(h->h_));
}
friend
bool
asio_handler_is_continuation(bound_handler* h)
{
return beast_asio_helpers::
is_continuation (h->h_);
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(std::addressof(h->h_));
}
template<class F>
@@ -94,8 +98,9 @@ public:
void
asio_handler_invoke(F&& f, bound_handler* h)
{
beast_asio_helpers::
invoke(f, h->h_);
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(h->h_));
}
};

View File

@@ -1,180 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP
#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP
#include <boost/asio/buffer.hpp>
#include <iterator>
#include <type_traits>
namespace beast {
namespace detail {
// Types that meet the requirements,
// for use with std::declval only.
template<class BufferType>
struct BufferSequence
{
using value_type = BufferType;
using const_iterator = BufferType const*;
~BufferSequence();
BufferSequence(BufferSequence const&) = default;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
using ConstBufferSequence =
BufferSequence<boost::asio::const_buffer>;
using MutableBufferSequence =
BufferSequence<boost::asio::mutable_buffer>;
template<class T, class BufferType>
class is_BufferSequence
{
template<class U, class R = std::is_convertible<
typename U::value_type, BufferType> >
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_base_of<
#if 0
std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::forward_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#endif
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = typename
std::is_convertible<decltype(
std::declval<U>().begin()),
typename U::const_iterator>::type>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = typename std::is_convertible<decltype(
std::declval<U>().end()),
typename U::const_iterator>::type>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
public:
using type = std::integral_constant<bool,
std::is_copy_constructible<T>::value &&
std::is_destructible<T>::value &&
type1::value && type2::value &&
type3::value && type4::value>;
};
template<class B1, class... Bn>
struct is_all_ConstBufferSequence
: std::integral_constant<bool,
is_BufferSequence<B1, boost::asio::const_buffer>::type::value &&
is_all_ConstBufferSequence<Bn...>::value>
{
};
template<class B1>
struct is_all_ConstBufferSequence<B1>
: is_BufferSequence<B1, boost::asio::const_buffer>::type
{
};
template<class T>
class is_DynamicBuffer
{
// size()
template<class U, class R = std::is_convertible<decltype(
std::declval<U const>().size()), std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
// max_size()
template<class U, class R = std::is_convertible<decltype(
std::declval<U const>().max_size()), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
// capacity()
template<class U, class R = std::is_convertible<decltype(
std::declval<U const>().capacity()), std::size_t>>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
// data()
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U const>().data()),
boost::asio::const_buffer>::type::value>>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
// prepare()
template<class U, class R = std::integral_constant<
bool, is_BufferSequence<decltype(
std::declval<U>().prepare(1)),
boost::asio::mutable_buffer>::type::value>>
static R check5(int);
template<class>
static std::false_type check5(...);
using type5 = decltype(check5<T>(0));
// commit()
template<class U, class R = decltype(
std::declval<U>().commit(1), std::true_type{})>
static R check6(int);
template<class>
static std::false_type check6(...);
using type6 = decltype(check6<T>(0));
// consume
template<class U, class R = decltype(
std::declval<U>().consume(1), std::true_type{})>
static R check7(int);
template<class>
static std::false_type check7(...);
using type7 = decltype(check7<T>(0));
public:
using type = std::integral_constant<bool,
type1::value
&& type2::value
//&& type3::value // Networking TS
&& type4::value
&& type5::value
&& type6::value
&& type7::value
>;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,61 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFERS_REF_HPP
#define BEAST_DETAIL_BUFFERS_REF_HPP
#include <beast/core/type_traits.hpp>
namespace beast {
namespace detail {
// A very lightweight reference to a buffer sequence
template<class BufferSequence>
class buffers_ref
{
BufferSequence const& buffers_;
public:
using value_type =
typename BufferSequence::value_type;
using const_iterator =
typename BufferSequence::const_iterator;
buffers_ref(buffers_ref const&) = default;
explicit
buffers_ref(BufferSequence const& buffers)
: buffers_(buffers)
{
}
const_iterator
begin() const
{
return buffers_.begin();
}
const_iterator
end() const
{
return buffers_.end();
}
};
// Return a reference to a buffer sequence
template<class BufferSequence>
buffers_ref<BufferSequence>
make_buffers_ref(BufferSequence const& buffers)
{
return buffers_ref<BufferSequence>(buffers);
}
} // detail
} // beast
#endif

View File

@@ -1,106 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
#include <boost/range/algorithm/equal.hpp>
#include <boost/utility/string_ref.hpp>
#include <array>
#include <cstdint>
namespace beast {
namespace detail {
inline
char
tolower(char c)
{
static std::array<std::uint8_t, 256> constexpr tab = {{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
}
template<std::size_t N>
inline
boost::string_ref
string_helper(const char (&s)[N])
{
return boost::string_ref{s, N-1};
}
template<class T>
inline
T const&
string_helper(T const& t)
{
return t;
}
// Case-insensitive less
struct ci_less
{
static bool const is_transparent = true;
template<class S1, class S2>
bool
operator()(S1 const& lhs, S2 const& rhs) const noexcept
{
using std::begin;
using std::end;
auto const s1 = string_helper(lhs);
auto const s2 = string_helper(rhs);
return std::lexicographical_compare(
begin(s1), end(s1), begin(s2), end(s2),
[](char lhs, char rhs)
{
return tolower(lhs) < tolower(rhs);
}
);
}
};
// Case-insensitive equal
struct ci_equal_pred
{
bool
operator()(char c1, char c2) const noexcept
{
return tolower(c1) == tolower(c2);
}
};
// Case-insensitive equal
template<class S1, class S2>
bool
ci_equal(S1 const& lhs, S2 const& rhs)
{
return boost::range::equal(
string_helper(lhs), string_helper(rhs),
ci_equal_pred{});
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,20 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_DETAIL_CONFIG_HPP
#define BEAST_CORE_DETAIL_CONFIG_HPP
#include <boost/config.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION >= 106500 || ! defined(BOOST_GCC) || BOOST_GCC < 70000
# define BEAST_FALLTHROUGH BOOST_FALLTHROUGH
#else
# define BEAST_FALLTHROUGH __attribute__((fallthrough))
#endif
#endif

View File

@@ -0,0 +1,95 @@
//
// Copyright (c) 2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_CPU_INFO_HPP
#define BEAST_DETAIL_CPU_INFO_HPP
#include <boost/config.hpp>
#ifndef BEAST_NO_INTRINSICS
# if defined(BOOST_MSVC) || ((defined(BOOST_GCC) || defined(BOOST_CLANG)) && defined(__SSE4_2__))
# define BEAST_NO_INTRINSICS 0
# else
# define BEAST_NO_INTRINSICS 1
# endif
#endif
#if ! BEAST_NO_INTRINSICS
#ifdef BOOST_MSVC
#include <intrin.h> // __cpuid
#else
#include <cpuid.h> // __get_cpuid
#endif
namespace beast {
namespace detail {
/* Portions from Boost,
Copyright Andrey Semashev 2007 - 2015.
*/
template<class = void>
void
cpuid(
std::uint32_t id,
std::uint32_t& eax,
std::uint32_t& ebx,
std::uint32_t& ecx,
std::uint32_t& edx)
{
#ifdef BOOST_MSVC
int regs[4];
__cpuid(regs, id);
eax = regs[0];
ebx = regs[1];
ecx = regs[2];
edx = regs[3];
#else
__get_cpuid(id, &eax, &ebx, &ecx, &edx);
#endif
}
struct cpu_info
{
bool sse42 = false;
cpu_info();
};
inline
cpu_info::
cpu_info()
{
constexpr std::uint32_t SSE42 = 1 << 20;
std::uint32_t eax = 0;
std::uint32_t ebx = 0;
std::uint32_t ecx = 0;
std::uint32_t edx = 0;
cpuid(0, eax, ebx, ecx, edx);
if(eax >= 1)
{
cpuid(1, eax, ebx, ecx, edx);
sse42 = (ecx & SSE42) != 0;
}
}
template<class = void>
cpu_info const&
get_cpu_info()
{
static cpu_info const ci;
return ci;
}
} // detail
} // beast
#endif
#endif

View File

@@ -1,53 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#include <type_traits>
namespace beast {
namespace detail {
template<class T>
class has_lowest_layer
{
template<class U, class R =
typename U::lowest_layer_type>
static std::true_type check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
template<class T, bool B>
struct maybe_get_lowest_layer
{
using type = T;
};
template<class T>
struct maybe_get_lowest_layer<T, true>
{
using type = typename T::lowest_layer_type;
};
// If T has a nested type lowest_layer_type,
// returns that, else returns T.
template<class T>
struct get_lowest_layer
{
using type = typename maybe_get_lowest_layer<T,
has_lowest_layer<T>::value>::type;
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,41 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_IN_PLACE_INIT_HPP
#define BEAST_DETAIL_IN_PLACE_INIT_HPP
#include <boost/version.hpp>
#include <boost/optional/optional.hpp>
// Provide boost::in_place_init_t and boost::in_place_init
// for Boost versions earlier than 1.63.0.
#if BOOST_VERSION < 106300
namespace boost {
namespace optional_ns {
// a tag for in-place initialization of contained value
struct in_place_init_t
{
struct init_tag{};
explicit in_place_init_t(init_tag){}
};
const in_place_init_t in_place_init ((in_place_init_t::init_tag()));
} // namespace optional_ns
using optional_ns::in_place_init_t;
using optional_ns::in_place_init;
}
#endif
#endif

View File

@@ -5,9 +5,10 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
#define BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_HPP
#define BEAST_DETAIL_INTEGER_SEQUENCE_HPP
#include <boost/config.hpp>
#include <cstddef>
#include <type_traits>
#include <utility>
@@ -19,8 +20,7 @@ template<class T, T... Ints>
struct integer_sequence
{
using value_type = T;
static_assert (std::is_integral<T>::value,
"std::integer_sequence can only be instantiated with an integral type" );
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
static std::size_t constexpr static_size = sizeof...(Ints);
@@ -40,9 +40,9 @@ struct sizeof_workaround
static std::size_t constexpr size = sizeof... (Args);
};
#ifdef _MSC_VER
#ifdef BOOST_MSVC
// This implementation compiles on MSVC and clang but not gcc
// This implementation compiles on real MSVC and clang but not gcc
template<class T, unsigned long long N, class Seq>
struct make_integer_sequence_unchecked;
@@ -65,11 +65,8 @@ struct make_integer_sequence_unchecked<
template<class T, T N>
struct make_integer_sequence_checked
{
static_assert (std::is_integral<T>::value,
"T must be an integral type");
static_assert (N >= 0,
"N must be non-negative");
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
BOOST_STATIC_ASSERT(N >= 0);
using type = typename make_integer_sequence_unchecked<
T, N, integer_sequence<T>>::type;
@@ -117,11 +114,8 @@ struct integer_sequence_helper;
template<class T, T N, std::size_t... Ints>
struct integer_sequence_helper<T, N, index_tuple<Ints...>>
{
static_assert (std::is_integral<T>::value,
"T must be an integral type");
static_assert (N >= 0,
"N must be non-negative");
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
BOOST_STATIC_ASSERT(N >= 0);
using type = integer_sequence<T, static_cast<T> (Ints)...>;
};

View File

@@ -1,54 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
#define BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
#include <type_traits>
namespace beast {
namespace detail {
template<class R, class C, class ...A>
auto
is_call_possible_test(C&& c, int, A&& ...a)
-> decltype(std::is_convertible<
decltype(c(a...)), R>::value ||
std::is_same<R, void>::value,
std::true_type());
template<class R, class C, class ...A>
std::false_type
is_call_possible_test(C&& c, long, A&& ...a);
/** Metafunction returns `true` if F callable as R(A...)
Example:
@code
is_call_possible<T, void(std::string)>
@endcode
*/
/** @{ */
template<class C, class F>
struct is_call_possible
: std::false_type
{
};
template<class C, class R, class ...A>
struct is_call_possible<C, R(A...)>
: decltype(is_call_possible_test<R>(
std::declval<C>(), 1, std::declval<A>()...))
{
};
/** @} */
} // detail
} // beast
#endif

View File

@@ -0,0 +1,318 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_OSTREAM_HPP
#define BEAST_DETAIL_OSTREAM_HPP
#include <boost/asio/buffer.hpp>
#include <beast/core/read_size.hpp>
#include <memory>
#include <iosfwd>
#include <streambuf>
#include <type_traits>
#include <utility>
namespace beast {
namespace detail {
template<class Buffers>
class buffers_helper
{
Buffers b_;
public:
explicit
buffers_helper(Buffers const& b)
: b_(b)
{
}
template<class B>
friend
std::ostream&
operator<<(std::ostream& os,
buffers_helper<B> const& v);
};
template<class Buffers>
std::ostream&
operator<<(std::ostream& os,
buffers_helper<Buffers> const& v)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
for(boost::asio::const_buffer b : v.b_)
os.write(buffer_cast<char const*>(b),
buffer_size(b));
return os;
}
//------------------------------------------------------------------------------
struct basic_streambuf_movable_helper :
std::basic_streambuf<char, std::char_traits<char>>
{
basic_streambuf_movable_helper(
basic_streambuf_movable_helper&&) = default;
};
using basic_streambuf_movable =
std::is_move_constructible<basic_streambuf_movable_helper>;
//------------------------------------------------------------------------------
template<class DynamicBuffer,
class CharT, class Traits, bool isMovable>
class ostream_buffer;
template<class DynamicBuffer, class CharT, class Traits>
class ostream_buffer
<DynamicBuffer, CharT, Traits, true>
: public std::basic_streambuf<CharT, Traits>
{
using int_type = typename
std::basic_streambuf<CharT, Traits>::int_type;
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
static std::size_t constexpr max_size = 512;
DynamicBuffer& buf_;
public:
ostream_buffer(ostream_buffer&&) = default;
ostream_buffer(ostream_buffer const&) = delete;
~ostream_buffer() noexcept
{
sync();
}
explicit
ostream_buffer(DynamicBuffer& buf)
: buf_(buf)
{
prepare();
}
int_type
overflow(int_type ch) override
{
if(! Traits::eq_int_type(ch, Traits::eof()))
{
Traits::assign(*this->pptr(),
static_cast<CharT>(ch));
flush(1);
prepare();
return ch;
}
flush();
return traits_type::eof();
}
int
sync() override
{
flush();
prepare();
return 0;
}
private:
void
prepare()
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
auto mbs = buf_.prepare(
read_size_or_throw(buf_, max_size));
auto const mb = *mbs.begin();
auto const p = buffer_cast<CharT*>(mb);
this->setp(p,
p + buffer_size(mb) / sizeof(CharT) - 1);
}
void
flush(int extra = 0)
{
buf_.commit(
(this->pptr() - this->pbase() + extra) *
sizeof(CharT));
}
};
// This nonsense is all to work around a glitch in libstdc++
// where std::basic_streambuf copy constructor is private:
// https://github.com/gcc-mirror/gcc/blob/gcc-4_8-branch/libstdc%2B%2B-v3/include/std/streambuf#L799
template<class DynamicBuffer, class CharT, class Traits>
class ostream_buffer
<DynamicBuffer, CharT, Traits, false>
: public std::basic_streambuf<CharT, Traits>
{
using int_type = typename
std::basic_streambuf<CharT, Traits>::int_type;
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
static std::size_t constexpr max_size = 512;
DynamicBuffer& buf_;
public:
ostream_buffer(ostream_buffer&&) = delete;
ostream_buffer(ostream_buffer const&) = delete;
~ostream_buffer() noexcept
{
sync();
}
explicit
ostream_buffer(DynamicBuffer& buf)
: buf_(buf)
{
prepare();
}
int_type
overflow(int_type ch) override
{
if(! Traits::eq_int_type(ch, Traits::eof()))
{
Traits::assign(*this->pptr(),
static_cast<CharT>(ch));
flush(1);
prepare();
return ch;
}
flush();
return traits_type::eof();
}
int
sync() override
{
flush();
prepare();
return 0;
}
private:
void
prepare()
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
auto mbs = buf_.prepare(
read_size_or_throw(buf_, max_size));
auto const mb = *mbs.begin();
auto const p = buffer_cast<CharT*>(mb);
this->setp(p,
p + buffer_size(mb) / sizeof(CharT) - 1);
}
void
flush(int extra = 0)
{
buf_.commit(
(this->pptr() - this->pbase() + extra) *
sizeof(CharT));
}
};
//------------------------------------------------------------------------------
template<class DynamicBuffer,
class CharT, class Traits, bool isMovable>
class ostream_helper;
template<class DynamicBuffer, class CharT, class Traits>
class ostream_helper<
DynamicBuffer, CharT, Traits, true>
: public std::basic_ostream<CharT, Traits>
{
ostream_buffer<
DynamicBuffer, CharT, Traits, true> osb_;
public:
explicit
ostream_helper(DynamicBuffer& buf);
ostream_helper(ostream_helper&& other);
};
template<class DynamicBuffer, class CharT, class Traits>
ostream_helper<DynamicBuffer, CharT, Traits, true>::
ostream_helper(DynamicBuffer& buf)
: std::basic_ostream<CharT, Traits>(
&this->osb_)
, osb_(buf)
{
}
template<class DynamicBuffer, class CharT, class Traits>
ostream_helper<DynamicBuffer, CharT, Traits, true>::
ostream_helper(
ostream_helper&& other)
: std::basic_ostream<CharT, Traits>(&osb_)
, osb_(std::move(other.osb_))
{
}
// This work-around is for libstdc++ versions that
// don't have a movable std::basic_streambuf
template<class T>
class ostream_helper_base
{
protected:
std::unique_ptr<T> member;
ostream_helper_base(
ostream_helper_base&&) = default;
explicit
ostream_helper_base(T* t)
: member(t)
{
}
};
template<class DynamicBuffer, class CharT, class Traits>
class ostream_helper<
DynamicBuffer, CharT, Traits, false>
: private ostream_helper_base<ostream_buffer<
DynamicBuffer, CharT, Traits, false>>
, public std::basic_ostream<CharT, Traits>
{
public:
explicit
ostream_helper(DynamicBuffer& buf)
: ostream_helper_base<ostream_buffer<
DynamicBuffer, CharT, Traits, false>>(
new ostream_buffer<DynamicBuffer,
CharT, Traits, false>(buf))
, std::basic_ostream<CharT, Traits>(this->member.get())
{
}
ostream_helper(ostream_helper&& other)
: ostream_helper_base<ostream_buffer<
DynamicBuffer, CharT, Traits, false>>(
std::move(other))
, std::basic_ostream<CharT, Traits>(this->member.get())
{
}
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,138 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_STATIC_OSTREAM_HPP
#define BEAST_DETAIL_STATIC_OSTREAM_HPP
#include <locale>
#include <ostream>
#include <streambuf>
namespace beast {
namespace detail {
// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
class static_ostream_buffer
: public std::basic_streambuf<char>
{
using CharT = char;
using Traits = std::char_traits<CharT>;
using int_type = typename
std::basic_streambuf<CharT, Traits>::int_type;
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
char* data_;
std::size_t size_;
std::size_t len_ = 0;
std::string s_;
public:
static_ostream_buffer(static_ostream_buffer&&) = delete;
static_ostream_buffer(static_ostream_buffer const&) = delete;
static_ostream_buffer(char* data, std::size_t size)
: data_(data)
, size_(size)
{
this->setp(data_, data_ + size - 1);
}
~static_ostream_buffer() noexcept
{
}
string_view
str() const
{
if(! s_.empty())
return {s_.data(), len_};
return {data_, len_};
}
int_type
overflow(int_type ch) override
{
if(! Traits::eq_int_type(ch, Traits::eof()))
{
Traits::assign(*this->pptr(),
static_cast<CharT>(ch));
flush(1);
prepare();
return ch;
}
flush();
return traits_type::eof();
}
int
sync() override
{
flush();
prepare();
return 0;
}
private:
void
prepare()
{
static auto const growth_factor = 1.5;
if(len_ < size_ - 1)
{
this->setp(
data_ + len_, data_ + size_ - 2);
return;
}
if(s_.empty())
{
s_.resize(static_cast<std::size_t>(
growth_factor * len_));
Traits::copy(&s_[0], data_, len_);
}
else
{
s_.resize(static_cast<std::size_t>(
growth_factor * len_));
}
this->setp(&s_[len_], &s_[len_] +
s_.size() - len_ - 1);
}
void
flush(int extra = 0)
{
len_ += static_cast<std::size_t>(
this->pptr() - this->pbase() + extra);
}
};
class static_ostream : public std::basic_ostream<char>
{
static_ostream_buffer osb_;
public:
static_ostream(char* data, std::size_t size)
: std::basic_ostream<char>(&this->osb_)
, osb_(data, size)
{
imbue(std::locale::classic());
}
string_view
str() const
{
return osb_.str();
}
};
} // detail
} // beast
#endif

View File

@@ -0,0 +1,131 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_STATIC_STRING_HPP
#define BEAST_DETAIL_STATIC_STRING_HPP
#include <beast/core/string.hpp>
#include <boost/assert.hpp>
#include <iterator>
#include <type_traits>
namespace beast {
namespace detail {
// Because k-ballo said so
template<class T>
using is_input_iterator =
std::integral_constant<bool,
! std::is_integral<T>::value>;
template<class CharT, class Traits>
int
lexicographical_compare(
CharT const* s1, std::size_t n1,
CharT const* s2, std::size_t n2)
{
if(n1 < n2)
return Traits::compare(
s1, s2, n1) <= 0 ? -1 : 1;
if(n1 > n2)
return Traits::compare(
s1, s2, n2) >= 0 ? 1 : -1;
return Traits::compare(s1, s2, n1);
}
template<class CharT, class Traits>
inline
int
lexicographical_compare(
basic_string_view<CharT, Traits> s1,
CharT const* s2, std::size_t n2)
{
return lexicographical_compare<CharT, Traits>(
s1.data(), s1.size(), s2, n2);
}
template<class CharT, class Traits>
inline
int
lexicographical_compare(
basic_string_view<CharT, Traits> s1,
basic_string_view<CharT, Traits> s2)
{
return lexicographical_compare<CharT, Traits>(
s1.data(), s1.size(), s2.data(), s2.size());
}
// Maximum number of characters in the decimal
// representation of a binary number. This includes
// the potential minus sign.
//
inline
std::size_t constexpr
max_digits(std::size_t bytes)
{
return static_cast<std::size_t>(
bytes * 2.41) + 1 + 1;
}
template<class CharT, class Integer, class Traits>
CharT*
raw_to_string(
CharT* buf, Integer x, std::true_type)
{
if(x == 0)
{
Traits::assign(*--buf, '0');
return buf;
}
if(x < 0)
{
x = -x;
for(;x > 0; x /= 10)
Traits::assign(*--buf ,
"0123456789"[x % 10]);
Traits::assign(*--buf, '-');
return buf;
}
for(;x > 0; x /= 10)
Traits::assign(*--buf ,
"0123456789"[x % 10]);
return buf;
}
template<class CharT, class Integer, class Traits>
CharT*
raw_to_string(
CharT* buf, Integer x, std::false_type)
{
if(x == 0)
{
*--buf = '0';
return buf;
}
for(;x > 0; x /= 10)
Traits::assign(*--buf ,
"0123456789"[x % 10]);
return buf;
}
template<
class CharT,
class Integer,
class Traits = std::char_traits<CharT>>
CharT*
raw_to_string(CharT* last, std::size_t size, Integer i)
{
boost::ignore_unused(size);
BOOST_ASSERT(size >= max_digits(sizeof(Integer)));
return raw_to_string<CharT, Integer, Traits>(
last, i, std::is_signed<Integer>{});
}
} // detail
} // beast
#endif

View File

@@ -1,134 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/error.hpp>
#include <boost/asio/io_service.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace detail {
// Types that meet the requirements,
// for use with std::declval only.
struct StreamHandler
{
StreamHandler(StreamHandler const&) = default;
void operator()(error_code ec, std::size_t);
};
using ReadHandler = StreamHandler;
using WriteHandler = StreamHandler;
template<class T>
class has_get_io_service
{
template<class U, class R = typename std::is_same<
decltype(std::declval<U>().get_io_service()),
boost::asio::io_service&>>
static R check(int);
template<class>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
template<class T>
class is_AsyncReadStream
{
template<class U, class R = decltype(
std::declval<U>().async_read_some(
std::declval<MutableBufferSequence>(),
std::declval<ReadHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type1 = decltype(check<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
has_get_io_service<T>::type::value>;
};
template<class T>
class is_AsyncWriteStream
{
template<class U, class R = decltype(
std::declval<U>().async_write_some(
std::declval<ConstBufferSequence>(),
std::declval<WriteHandler>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type1 = decltype(check<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
has_get_io_service<T>::type::value>;
};
template<class T>
class is_SyncReadStream
{
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().read_some(
std::declval<MutableBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value>;
};
template<class T>
class is_SyncWriteStream
{
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>())),
std::size_t>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_same<decltype(
std::declval<U>().write_some(
std::declval<ConstBufferSequence>(),
std::declval<error_code&>())), std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
using type = std::integral_constant<bool,
type1::value && type2::value>;
};
} // detail
} // beast
#endif

View File

@@ -1,93 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_SYNC_OSTREAM_HPP
#define BEAST_DETAIL_SYNC_OSTREAM_HPP
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <ostream>
namespace beast {
namespace detail {
/** A SyncWriteStream which outputs to a `std::ostream`
Objects of this type meet the requirements of @b SyncWriteStream.
*/
class sync_ostream
{
std::ostream& os_;
public:
/** Construct the stream.
@param os The associated `std::ostream`. All buffers
written will be passed to the associated output stream.
*/
sync_ostream(std::ostream& os)
: os_(os)
{
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers);
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code& ec);
};
template<class ConstBufferSequence>
std::size_t
sync_ostream::
write_some(ConstBufferSequence const& buffers)
{
static_assert(
is_ConstBufferSequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class ConstBufferSequence>
std::size_t
sync_ostream::
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
static_assert(
is_ConstBufferSequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
std::size_t n = 0;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
for(auto const& buffer : buffers)
{
os_.write(buffer_cast<char const*>(buffer),
buffer_size(buffer));
if(os_.fail())
{
ec = errc::make_error_code(
errc::no_stream_resources);
break;
}
n += buffer_size(buffer);
}
return n;
}
} // detail
} // beast
#endif

View File

@@ -8,14 +8,46 @@
#ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
#define BEAST_DETAIL_TYPE_TRAITS_HPP
#include <beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <stdexcept>
#include <string>
// A few workarounds to keep things working
namespace boost {
namespace asio {
// for has_get_io_service
class io_service;
// for is_dynamic_buffer
template<class Allocator>
class basic_streambuf;
namespace detail {
// for is_buffer_sequence
template<class Buffer, class Buffers>
class consuming_buffers;
} // detail
} // asio
} // boost
//------------------------------------------------------------------------------
namespace beast {
namespace detail {
//
// utilities
//
template<class... Ts>
struct make_void
{
@@ -25,18 +57,10 @@ struct make_void
template<class... Ts>
using void_t = typename make_void<Ts...>::type;
template<class... Ts>
template<class T>
inline
void
ignore_unused(Ts const& ...)
{
}
template<class... Ts>
inline
void
ignore_unused()
{}
accept_rv(T){}
template<class U>
std::size_t constexpr
@@ -80,17 +104,159 @@ struct repeat_tuple<0, T>
using type = std::tuple<>;
};
template<class Exception>
Exception
make_exception(char const* reason, char const* file, int line)
template<class R, class C, class ...A>
auto
is_invocable_test(C&& c, int, A&& ...a)
-> decltype(std::is_convertible<
decltype(c(a...)), R>::value ||
std::is_same<R, void>::value,
std::true_type());
template<class R, class C, class ...A>
std::false_type
is_invocable_test(C&& c, long, A&& ...a);
/** Metafunction returns `true` if F callable as R(A...)
Example:
@code
is_invocable<T, void(std::string)>
@endcode
*/
/** @{ */
template<class C, class F>
struct is_invocable : std::false_type
{
char const* n = file;
for(auto p = file; *p; ++p)
if(*p == '\\' || *p == '/')
n = p + 1;
return Exception{std::string(reason) + " (" +
n + ":" + std::to_string(line) + ")"};
}
};
template<class C, class R, class ...A>
struct is_invocable<C, R(A...)>
: decltype(is_invocable_test<R>(
std::declval<C>(), 1, std::declval<A>()...))
{
};
/** @} */
// for span
template<class T, class E, class = void>
struct is_contiguous_container: std::false_type {};
template<class T, class E>
struct is_contiguous_container<T, E, void_t<
decltype(
std::declval<std::size_t&>() = std::declval<T const&>().size(),
std::declval<E*&>() = std::declval<T&>().data(),
(void)0),
typename std::enable_if<
std::is_same<
typename std::remove_cv<E>::type,
typename std::remove_cv<
typename std::remove_pointer<
decltype(std::declval<T&>().data())
>::type
>::type
>::value
>::type>>: std::true_type
{};
//------------------------------------------------------------------------------
//
// buffer concepts
//
// Types that meet the requirements,
// for use with std::declval only.
template<class BufferType>
struct BufferSequence
{
using value_type = BufferType;
using const_iterator = BufferType const*;
~BufferSequence();
BufferSequence(BufferSequence const&) = default;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
using ConstBufferSequence =
BufferSequence<boost::asio::const_buffer>;
using MutableBufferSequence =
BufferSequence<boost::asio::mutable_buffer>;
template<class T, class B, class = void>
struct is_buffer_sequence : std::false_type {};
template<class T, class B>
struct is_buffer_sequence<T, B, void_t<decltype(
std::declval<typename T::value_type>(),
std::declval<typename T::const_iterator&>() =
std::declval<T const&>().begin(),
std::declval<typename T::const_iterator&>() =
std::declval<T const&>().end(),
(void)0)>> : std::integral_constant<bool,
std::is_convertible<typename T::value_type, B>::value &&
#if 0
std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename T::const_iterator>::iterator_category>::value
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::is_base_of<std::forward_iterator_tag,
typename std::iterator_traits<
typename T::const_iterator>::iterator_category>::value
#endif
>
{
};
#if 0
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
template<class Buffer, class Buffers, class B>
struct is_buffer_sequence<
boost::asio::detail::consuming_buffers<Buffer, Buffers>>
: std::true_type
{
};
#endif
template<class B1, class... Bn>
struct is_all_const_buffer_sequence
: std::integral_constant<bool,
is_buffer_sequence<B1, boost::asio::const_buffer>::value &&
is_all_const_buffer_sequence<Bn...>::value>
{
};
template<class B1>
struct is_all_const_buffer_sequence<B1>
: is_buffer_sequence<B1, boost::asio::const_buffer>
{
};
template<class... Bn>
struct common_buffers_type
{
using type = typename std::conditional<
std::is_convertible<std::tuple<Bn...>,
typename repeat_tuple<sizeof...(Bn),
boost::asio::mutable_buffer>::type>::value,
boost::asio::mutable_buffer,
boost::asio::const_buffer>::type;
};
// Types that meet the requirements,
// for use with std::declval only.
struct StreamHandler
{
StreamHandler(StreamHandler const&) = default;
void operator()(error_code ec, std::size_t);
};
using ReadHandler = StreamHandler;
using WriteHandler = StreamHandler;
} // detail
} // beast

View File

@@ -1,140 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_WRITE_DYNABUF_HPP
#define BEAST_DETAIL_WRITE_DYNABUF_HPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <utility>
namespace beast {
namespace detail {
// detects string literals.
template<class T>
struct is_string_literal : std::integral_constant<bool,
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
std::is_same<char, typename std::remove_extent<T>::type>::value>
{
};
// `true` if a call to boost::asio::buffer(T const&) is possible
// note: we exclude string literals because boost::asio::buffer()
// will include the null terminator, which we don't want.
template<class T>
class is_BufferConvertible
{
template<class U, class R = decltype(
boost::asio::buffer(std::declval<U const&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool const value = type::value &&
! is_string_literal<T>::value;
};
template<class DynamicBuffer>
void
write_dynabuf(DynamicBuffer& dynabuf,
boost::asio::const_buffer const& buffer)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffer)),
buffer));
}
template<class DynamicBuffer>
void
write_dynabuf(DynamicBuffer& dynabuf,
boost::asio::mutable_buffer const& buffer)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffer)),
buffer));
}
template<class DynamicBuffer, class T>
typename std::enable_if<
is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
auto const buffers = boost::asio::buffer(t);
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffers)),
buffers));
}
template<class DynamicBuffer, class Buffers>
typename std::enable_if<
is_ConstBufferSequence<Buffers>::value &&
! is_BufferConvertible<Buffers>::value &&
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
>::type
write_dynabuf(DynamicBuffer& dynabuf, Buffers const& buffers)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
dynabuf.commit(buffer_copy(
dynabuf.prepare(buffer_size(buffers)),
buffers));
}
template<class DynamicBuffer, std::size_t N>
void
write_dynabuf(DynamicBuffer& dynabuf, const char (&s)[N])
{
using boost::asio::buffer_copy;
dynabuf.commit(buffer_copy(
dynabuf.prepare(N - 1),
boost::asio::buffer(s, N - 1)));
}
template<class DynamicBuffer, class T>
typename std::enable_if<
! is_string_literal<T>::value &&
! is_ConstBufferSequence<T>::value &&
! is_BufferConvertible<T>::value &&
! std::is_convertible<T, boost::asio::const_buffer>::value &&
! std::is_convertible<T, boost::asio::mutable_buffer>::value
>::type
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto const s = boost::lexical_cast<std::string>(t);
dynabuf.commit(buffer_copy(
dynabuf.prepare(s.size()), buffer(s)));
}
template<class DynamicBuffer, class T0, class T1, class... TN>
void
write_dynabuf(DynamicBuffer& dynabuf,
T0 const& t0, T1 const& t1, TN const&... tn)
{
write_dynabuf(dynabuf, t0);
write_dynabuf(dynabuf, t1, tn...);
}
} // detail
} // beast
#endif

View File

@@ -0,0 +1,122 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DRAIN_BUFFER_HPP
#define BEAST_DRAIN_BUFFER_HPP
#include <beast/config.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/throw_exception.hpp>
namespace beast {
/** A @b DynamicBuffer which does not retain its input sequence.
This object implements a dynamic buffer with a fixed size
output area, not dynamically allocated, and whose input
sequence is always length zero. Bytes committed from the
output area to the input area are always discarded. This
is useful for calling interfaces that require a dynamic
buffer for storage, but where the caller does not want
to retain the data.
*/
class drain_buffer
{
char buf_[512];
std::size_t n_ = 0;
public:
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = boost::asio::null_buffers;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = boost::asio::mutable_buffers_1;
/// Constructor
drain_buffer() = default;
/// Copy constructor
drain_buffer(drain_buffer const&)
{
// Previously returned ranges are invalidated
}
/// Copy assignment
drain_buffer&
operator=(drain_buffer const&)
{
n_ = 0;
return *this;
}
/// Return the size of the input sequence.
std::size_t
size() const
{
return 0;
}
/// Return the maximum sum of the input and output sequence sizes.
std::size_t
max_size() const
{
return sizeof(buf_);
}
/// Return the maximum sum of input and output sizes that can be held without an allocation.
std::size_t
capacity() const
{
return max_size();
}
/** Get a list of buffers that represent the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const
{
return {};
}
/** Get a list of buffers that represent the output sequence, with the given size.
@throws std::length_error if the size would exceed the buffer limit
*/
mutable_buffers_type
prepare(std::size_t n)
{
if(n > sizeof(buf_))
BOOST_THROW_EXCEPTION(std::length_error{
"buffer overflow"});
n_ = n;
return {buf_, n_};
}
/** Move bytes from the output sequence to the input sequence.
This call always discards the output sequence.
The size of the input sequence will remain at zero.
*/
void
commit(std::size_t)
{
}
/** Remove bytes from the input sequence.
This call has no effect.
*/
void
consume(std::size_t) const
{
}
};
} // beast
#endif

View File

@@ -23,8 +23,16 @@ using system_error = boost::system::system_error;
/// The type of error category used by the library
using error_category = boost::system::error_category;
/// A function to return the generic error category used by the library
#if BEAST_DOXYGEN
error_category const&
generic_category();
#else
using boost::system::generic_category;
#endif
/// A function to return the system error category used by the library
#if GENERATING_DOCS
#if BEAST_DOXYGEN
error_category const&
system_category();
#else
@@ -35,7 +43,7 @@ using boost::system::system_category;
using error_condition = boost::system::error_condition;
/// The set of constants used for cross-platform error codes
#if GENERATING_DOCS
#if BEAST_DOXYGEN
enum errc{};
#else
namespace errc = boost::system::errc;

View File

@@ -0,0 +1,41 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_FILE_HPP
#define BEAST_CORE_FILE_HPP
#include <beast/config.hpp>
#include <beast/core/file_base.hpp>
#include <beast/core/file_posix.hpp>
#include <beast/core/file_stdio.hpp>
#include <beast/core/file_win32.hpp>
#include <boost/config.hpp>
namespace beast {
/** An implementation of File.
This alias is set to the best available implementation
of @b File given the platform and build settings.
*/
#if BEAST_DOXYGEN
struct file : file_stdio
{
};
#else
#if BEAST_USE_WIN32_FILE
using file = file_win32;
#elif BEAST_USE_POSIX_FILE
using file = file_posix;
#else
using file = file_stdio;
#endif
#endif
} // beast
#endif

View File

@@ -0,0 +1,88 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_FILE_BASE_HPP
#define BEAST_CORE_FILE_BASE_HPP
#include <beast/config.hpp>
#include <beast/core/string.hpp>
namespace beast {
/// The type of file path used by the library
using file_path = string_view;
/** File open modes
These modes are used when opening files using
instances of the @b File concept.
@see file_stdio
*/
enum class file_mode
{
/// Random reading
read,
/// Sequential reading
scan,
/** Random writing to a new or truncated file
@li If the file does not exist, it is created.
@li If the file exists, it is truncated to
zero size upon opening.
*/
write,
/** Random writing to new file only
If the file exists, an error is generated.
*/
write_new,
/** Random writing to existing file
If the file does not exist, an error is generated.
*/
write_existing,
/** Appending to a new or truncated file
The current file position shall be set to the end of
the file prior to each write.
@li If the file does not exist, it is created.
@li If the file exists, it is truncated to
zero size upon opening.
*/
append,
/** Appending to a new file only
The current file position shall be set to the end of
the file prior to each write.
If the file exists, an error is generated.
*/
append_new,
/** Appending to an existing file
The current file position shall be set to the end of
the file prior to each write.
If the file does not exist, an error is generated.
*/
append_existing
};
} // beast
#endif

View File

@@ -0,0 +1,171 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_FILE_POSIX_HPP
#define BEAST_CORE_FILE_POSIX_HPP
#include <boost/config.hpp>
#if ! defined(BEAST_NO_POSIX_FILE)
# if ! defined(__APPLE__) && ! defined(__linux__)
# define BEAST_NO_POSIX_FILE
# endif
#endif
#if ! defined(BEAST_USE_POSIX_FILE)
# if ! defined(BEAST_NO_POSIX_FILE)
# define BEAST_USE_POSIX_FILE 1
# else
# define BEAST_USE_POSIX_FILE 0
# endif
#endif
#if BEAST_USE_POSIX_FILE
#include <beast/core/error.hpp>
#include <beast/core/file_base.hpp>
#include <cstdint>
namespace beast {
/** An implementation of File for POSIX systems.
This class implements a @b File using POSIX interfaces.
*/
class file_posix
{
int fd_ = -1;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
using native_handle_type = int;
/** Destructor
If the file is open it is first closed.
*/
~file_posix();
/** Constructor
There is no open file initially.
*/
file_posix() = default;
/** Constructor
The moved-from object behaves as if default constructed.
*/
file_posix(file_posix&& other);
/** Assignment
The moved-from object behaves as if default constructed.
*/
file_posix& operator=(file_posix&& other);
/// Returns the native handle associated with the file.
native_handle_type
native_handle() const
{
return fd_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param fd The native file handle to assign.
*/
void
native_handle(native_handle_type fd);
/// Returns `true` if the file is open
bool
is_open() const
{
return fd_ != -1;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
std::uint64_t
pos(error_code& ec) const;
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@param ec Set to the error, if any occurred
*/
std::size_t
read(void* buffer, std::size_t n, error_code& ec) const;
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@param ec Set to the error, if any occurred
*/
std::size_t
write(void const* buffer, std::size_t n, error_code& ec);
};
} // beast
#include <beast/core/impl/file_posix.ipp>
#endif
#endif

View File

@@ -0,0 +1,154 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_FILE_STDIO_HPP
#define BEAST_CORE_FILE_STDIO_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/file_base.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
/** An implementation of File which uses cstdio.
This class implements a file using the interfaces present
in the C++ Standard Library, in `<stdio>`.
*/
class file_stdio
{
FILE* f_ = nullptr;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
using native_handle_type = FILE*;
/** Destructor
If the file is open it is first closed.
*/
~file_stdio();
/** Constructor
There is no open file initially.
*/
file_stdio() = default;
/** Constructor
The moved-from object behaves as if default constructed.
*/
file_stdio(file_stdio&& other);
/** Assignment
The moved-from object behaves as if default constructed.
*/
file_stdio& operator=(file_stdio&& other);
/// Returns the native handle associated with the file.
FILE*
native_handle() const
{
return f_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param f The native file handle to assign.
*/
void
native_handle(FILE* f);
/// Returns `true` if the file is open
bool
is_open() const
{
return f_ != nullptr;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
std::uint64_t
pos(error_code& ec) const;
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@param ec Set to the error, if any occurred
*/
std::size_t
read(void* buffer, std::size_t n, error_code& ec) const;
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@param ec Set to the error, if any occurred
*/
std::size_t
write(void const* buffer, std::size_t n, error_code& ec);
};
} // beast
#include <beast/core/impl/file_stdio.ipp>
#endif

View File

@@ -0,0 +1,173 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_FILE_WIN32_HPP
#define BEAST_CORE_FILE_WIN32_HPP
#include <boost/config.hpp>
#if ! defined(BEAST_USE_WIN32_FILE)
# ifdef BOOST_MSVC
# define BEAST_USE_WIN32_FILE 1
# else
# define BEAST_USE_WIN32_FILE 0
# endif
#endif
#if BEAST_USE_WIN32_FILE
#include <beast/core/error.hpp>
#include <beast/core/file_base.hpp>
#include <boost/detail/winapi/basic_types.hpp>
#include <boost/detail/winapi/handles.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
/** An implementation of File for Win32.
This class implements a @b File using Win32 native interfaces.
*/
class file_win32
{
boost::detail::winapi::HANDLE_ h_ =
boost::detail::winapi::INVALID_HANDLE_VALUE_;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
#if BEAST_DOXYGEN
using native_handle_type = HANDLE;
#else
using native_handle_type = boost::detail::winapi::HANDLE_;
#endif
/** Destructor
If the file is open it is first closed.
*/
~file_win32();
/** Constructor
There is no open file initially.
*/
file_win32() = default;
/** Constructor
The moved-from object behaves as if default constructed.
*/
file_win32(file_win32&& other);
/** Assignment
The moved-from object behaves as if default constructed.
*/
file_win32& operator=(file_win32&& other);
/// Returns the native handle associated with the file.
native_handle_type
native_handle()
{
return h_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param h The native file handle to assign.
*/
void
native_handle(native_handle_type h);
/// Returns `true` if the file is open
bool
is_open() const
{
return h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
std::uint64_t
pos(error_code& ec);
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@param ec Set to the error, if any occurred
*/
std::size_t
read(void* buffer, std::size_t n, error_code& ec);
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@param ec Set to the error, if any occurred
*/
std::size_t
write(void const* buffer, std::size_t n, error_code& ec);
};
} // beast
#include <beast/core/impl/file_win32.ipp>
#endif
#endif

View File

@@ -0,0 +1,341 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_FLAT_BUFFER_HPP
#define BEAST_FLAT_BUFFER_HPP
#include <beast/config.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/asio/buffer.hpp>
#include <limits>
#include <memory>
namespace beast {
/** A linear dynamic buffer.
Objects of this type meet the requirements of @b DynamicBuffer
and offer additional invariants:
@li Buffer sequences returned by @ref data and @ref prepare
will always be of length one.
@li A configurable maximum buffer size may be set upon
construction. Attempts to exceed the buffer size will throw
`std::length_error`.
Upon construction, a maximum size for the buffer may be
specified. If this limit is exceeded, the `std::length_error`
exception will be thrown.
@note This class is designed for use with algorithms that
take dynamic buffers as parameters, and are optimized
for the case where the input sequence or output sequence
is stored in a single contiguous buffer.
*/
template<class Allocator>
class basic_flat_buffer
#if ! BEAST_DOXYGEN
: private detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif
{
public:
#if BEAST_DOXYGEN
/// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<char>;
#endif
private:
enum
{
min_size = 512
};
template<class OtherAlloc>
friend class basic_flat_buffer;
using alloc_traits =
std::allocator_traits<allocator_type>;
static
inline
std::size_t
dist(char const* first, char const* last)
{
return static_cast<std::size_t>(last - first);
}
char* begin_;
char* in_;
char* out_;
char* last_;
char* end_;
std::size_t max_;
public:
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = boost::asio::const_buffers_1;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = boost::asio::mutable_buffers_1;
/// Destructor
~basic_flat_buffer();
/** Constructor
Upon construction, capacity will be zero.
*/
basic_flat_buffer();
/** Constructor
Upon construction, capacity will be zero.
@param limit The setting for @ref max_size.
*/
explicit
basic_flat_buffer(std::size_t limit);
/** Constructor
Upon construction, capacity will be zero.
@param alloc The allocator to construct with.
*/
explicit
basic_flat_buffer(Allocator const& alloc);
/** Constructor
Upon construction, capacity will be zero.
@param limit The setting for @ref max_size.
@param alloc The allocator to use.
*/
basic_flat_buffer(
std::size_t limit, Allocator const& alloc);
/** Move constructor
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
*/
basic_flat_buffer(basic_flat_buffer&& other);
/** Move constructor
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
@param alloc The allocator to use.
*/
basic_flat_buffer(
basic_flat_buffer&& other, Allocator const& alloc);
/** Copy constructor
@param other The object to copy from.
*/
basic_flat_buffer(basic_flat_buffer const& other);
/** Copy constructor
@param other The object to copy from.
@param alloc The allocator to use.
*/
basic_flat_buffer(basic_flat_buffer const& other,
Allocator const& alloc);
/** Copy constructor
@param other The object to copy from.
*/
template<class OtherAlloc>
basic_flat_buffer(
basic_flat_buffer<OtherAlloc> const& other);
/** Copy constructor
@param other The object to copy from.
@param alloc The allocator to use.
*/
template<class OtherAlloc>
basic_flat_buffer(
basic_flat_buffer<OtherAlloc> const& other,
Allocator const& alloc);
/** Move assignment
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
*/
basic_flat_buffer&
operator=(basic_flat_buffer&& other);
/** Copy assignment
After the copy, `*this` will have an empty output sequence.
@param other The object to copy from.
*/
basic_flat_buffer&
operator=(basic_flat_buffer const& other);
/** Copy assignment
After the copy, `*this` will have an empty output sequence.
@param other The object to copy from.
*/
template<class OtherAlloc>
basic_flat_buffer&
operator=(basic_flat_buffer<OtherAlloc> const& other);
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
return this->member();
}
/// Returns the size of the input sequence.
std::size_t
size() const
{
return dist(in_, out_);
}
/// Return the maximum sum of the input and output sequence sizes.
std::size_t
max_size() const
{
return max_;
}
/// Return the maximum sum of input and output sizes that can be held without an allocation.
std::size_t
capacity() const
{
return dist(begin_, end_);
}
/// Get a list of buffers that represent the input sequence.
const_buffers_type
data() const
{
return {in_, dist(in_, out_)};
}
/** Get a list of buffers that represent the output sequence, with the given size.
@throws std::length_error if `size() + n` exceeds `max_size()`.
@note All previous buffers sequences obtained from
calls to @ref data or @ref prepare are invalidated.
*/
mutable_buffers_type
prepare(std::size_t n);
/** Move bytes from the output sequence to the input sequence.
@param n The number of bytes to move. If this is larger than
the number of bytes in the output sequences, then the entire
output sequences is moved.
@note All previous buffers sequences obtained from
calls to @ref data or @ref prepare are invalidated.
*/
void
commit(std::size_t n)
{
out_ += (std::min)(n, dist(out_, last_));
}
/** Remove bytes from the input sequence.
If `n` is greater than the number of bytes in the input
sequence, all bytes in the input sequence are removed.
@note All previous buffers sequences obtained from
calls to @ref data or @ref prepare are invalidated.
*/
void
consume(std::size_t n);
/** Reallocate the buffer to fit the input sequence.
@note All previous buffers sequences obtained from
calls to @ref data or @ref prepare are invalidated.
*/
void
shrink_to_fit();
/// Exchange two flat buffers
template<class Alloc>
friend
void
swap(
basic_flat_buffer<Alloc>& lhs,
basic_flat_buffer<Alloc>& rhs);
private:
void
reset();
template<class DynamicBuffer>
void
copy_from(DynamicBuffer const& other);
void
move_assign(basic_flat_buffer&, std::true_type);
void
move_assign(basic_flat_buffer&, std::false_type);
void
copy_assign(basic_flat_buffer const&, std::true_type);
void
copy_assign(basic_flat_buffer const&, std::false_type);
void
swap(basic_flat_buffer&);
void
swap(basic_flat_buffer&, std::true_type);
void
swap(basic_flat_buffer&, std::false_type);
};
using flat_buffer =
basic_flat_buffer<std::allocator<char>>;
} // beast
#include <beast/core/impl/flat_buffer.ipp>
#endif

View File

@@ -9,7 +9,9 @@
#define BEAST_HANDLER_ALLOC_HPP
#include <beast/config.hpp>
#include <beast/core/handler_helpers.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/config.hpp>
#include <cstddef>
#include <cstdlib>
#include <memory>
#include <type_traits>
@@ -35,7 +37,7 @@ namespace beast {
the handler is invoked or undefined behavior results. This behavior
is described as the "deallocate before invocation" Asio guarantee.
*/
#if GENERATING_DOCS
#if BEAST_DOXYGEN
template<class T, class Handler>
class handler_alloc;
#else
@@ -56,6 +58,12 @@ private:
public:
using value_type = T;
using is_always_equal = std::true_type;
using pointer = T*;
using reference = T&;
using const_pointer = T const*;
using const_reference = T const&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
template<class U>
struct rebind
@@ -89,38 +97,46 @@ public:
}
value_type*
allocate(std::ptrdiff_t n)
allocate(size_type n)
{
auto const size = n * sizeof(T);
using boost::asio::asio_handler_allocate;
return static_cast<value_type*>(
beast_asio_helpers::allocate(
size, h_));
asio_handler_allocate(size, std::addressof(h_)));
}
void
deallocate(value_type* p, std::ptrdiff_t n)
deallocate(value_type* p, size_type n)
{
auto const size = n * sizeof(T);
beast_asio_helpers::deallocate(
p, size, h_);
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(p, size, std::addressof(h_));
}
#ifdef _MSC_VER
// Work-around for MSVC not using allocator_traits
// in the implementation of shared_ptr
//
//#if BOOST_WORKAROUND(BOOST_GCC, < 60000) // Works, but too coarse
#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
template<class U, class... Args>
void
destroy(T* t)
construct(U* ptr, Args&&... args)
{
t->~T();
::new((void*)ptr) U(std::forward<Args>(args)...);
}
template<class U>
void
destroy(U* ptr)
{
ptr->~U();
}
#endif
template<class U>
friend
bool
operator==(handler_alloc const& lhs,
handler_alloc<U, Handler> const& rhs)
operator==(
handler_alloc const&,
handler_alloc<U, Handler> const&)
{
return true;
}
@@ -128,7 +144,8 @@ public:
template<class U>
friend
bool
operator!=(handler_alloc const& lhs,
operator!=(
handler_alloc const& lhs,
handler_alloc<U, Handler> const& rhs)
{
return ! (lhs == rhs);

View File

@@ -1,28 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HANDLER_CONCEPTS_HPP
#define BEAST_HANDLER_CONCEPTS_HPP
#include <beast/config.hpp>
#include <beast/core/detail/is_call_possible.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` meets the requirements of @b `CompletionHandler`.
template<class T, class Signature>
#if GENERATING_DOCS
using is_CompletionHandler = std::integral_constant<bool, ...>;
#else
using is_CompletionHandler = std::integral_constant<bool,
std::is_copy_constructible<typename std::decay<T>::type>::value &&
detail::is_call_possible<T, Signature>::value>;
#endif
} // beast
#endif

View File

@@ -1,105 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HANDLER_HELPERS_HPP
#define BEAST_HANDLER_HELPERS_HPP
#include <beast/config.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <memory>
/* Calls to:
* asio_handler_allocate
* asio_handler_deallocate
* asio_handler_invoke
* asio_handler_is_continuation
must be made from a namespace that does not
contain overloads of this function. The beast_asio_helpers
namespace is defined here for that purpose.
*/
namespace beast_asio_helpers {
/// Allocation function for handlers.
template <class Handler>
inline
void*
allocate(std::size_t s, Handler& handler)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
return ::operator new(s);
#else
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(s, std::addressof(handler));
#endif
}
/// Deallocation function for handlers.
template<class Handler>
inline
void
deallocate(void* p, std::size_t s, Handler& handler)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
::operator delete(p);
#else
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(p, s, std::addressof(handler));
#endif
}
/// Invoke function for handlers.
template<class Function, class Handler>
inline
void
invoke(Function& function, Handler& handler)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
Function tmp(function);
tmp();
#else
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, std::addressof(handler));
#endif
}
/// Invoke function for handlers.
template<class Function, class Handler>
inline
void
invoke(Function const& function, Handler& handler)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
Function tmp(function);
tmp();
#else
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, std::addressof(handler));
#endif
}
/// Returns true if handler represents a continuation of the asynchronous operation
template<class Handler>
inline
bool
is_continuation(Handler& handler)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
return false;
#else
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(std::addressof(handler));
#endif
}
} // beast_asio_helpers
#endif

View File

@@ -191,6 +191,11 @@ public:
deallocation-before-invocation Asio guarantee. All
instances of @ref handler_ptr which refer to the
same owned object will be reset, including this instance.
@note Care must be taken when the arguments are themselves
stored in the owned object. Such arguments must first be
moved to the stack or elsewhere, and then passed, or else
undefined behavior will result.
*/
template<class... Args>
void

View File

@@ -5,12 +5,13 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
#define BEAST_DETAIL_BUFFER_CAT_HPP
#ifndef BEAST_IMPL_BUFFER_CAT_IPP
#define BEAST_IMPL_BUFFER_CAT_IPP
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/throw_exception.hpp>
#include <array>
#include <cstdint>
#include <iterator>
#include <new>
@@ -19,57 +20,22 @@
#include <utility>
namespace beast {
namespace detail {
template<class... Bn>
struct common_buffers_type
class buffer_cat_view<Bn...>::const_iterator
{
using type = typename std::conditional<
std::is_convertible<std::tuple<Bn...>,
typename repeat_tuple<sizeof...(Bn),
boost::asio::mutable_buffer>::type>::value,
boost::asio::mutable_buffer,
boost::asio::const_buffer>::type;
};
#if 0
static_assert(
detail::is_all_const_buffer_sequence<Bn...>::value,
"BufferSequence requirements not met");
#endif
template<class... Bn>
class buffer_cat_helper
{
std::tuple<Bn...> bn_;
public:
using value_type = typename
common_buffers_type<Bn...>::type;
class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
explicit
buffer_cat_helper(Bn const&... bn)
: bn_(bn...)
{
}
const_iterator
begin() const;
const_iterator
end() const;
};
template<class... Bn>
class buffer_cat_helper<Bn...>::const_iterator
{
std::size_t n_;
std::tuple<Bn...> const* bn_;
std::array<std::uint8_t,
max_sizeof<typename Bn::const_iterator...>()> buf_;
std::array<char, detail::max_sizeof<
typename Bn::const_iterator...>()> buf_;
friend class buffer_cat_helper<Bn...>;
friend class buffer_cat_view<Bn...>;
template<std::size_t I>
using C = std::integral_constant<std::size_t, I>;
@@ -84,8 +50,7 @@ class buffer_cat_helper<Bn...>::const_iterator
{
// type-pun
return *reinterpret_cast<
iter_t<I>*>(static_cast<void*>(
buf_.data()));
iter_t<I>*>(static_cast<void*>(buf_.data()));
}
template<std::size_t I>
@@ -100,7 +65,7 @@ class buffer_cat_helper<Bn...>::const_iterator
public:
using value_type = typename
common_buffers_type<Bn...>::type;
detail::common_buffers_type<Bn...>::type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -120,7 +85,7 @@ public:
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
return ! (*this == other);
}
reference
@@ -133,23 +98,13 @@ public:
operator++();
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
operator++(int);
const_iterator&
operator--();
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
operator--(int);
private:
const_iterator(
@@ -166,17 +121,48 @@ private:
void
construct(C<I> const&)
{
if(std::get<I>(*bn_).begin() !=
std::get<I>(*bn_).end())
if(boost::asio::buffer_size(
std::get<I>(*bn_)) != 0)
{
n_ = I;
new(buf_.data()) iter_t<I>{
new(&buf_[0]) iter_t<I>{
std::get<I>(*bn_).begin()};
return;
}
construct(C<I+1>{});
}
void
rconstruct(C<0> const&)
{
auto constexpr I = 0;
if(boost::asio::buffer_size(
std::get<I>(*bn_)) != 0)
{
n_ = I;
new(&buf_[0]) iter_t<I>{
std::get<I>(*bn_).end()};
return;
}
BOOST_THROW_EXCEPTION(std::logic_error{
"invalid iterator"});
}
template<std::size_t I>
void
rconstruct(C<I> const&)
{
if(boost::asio::buffer_size(
std::get<I>(*bn_)) != 0)
{
n_ = I;
new(&buf_[0]) iter_t<I>{
std::get<I>(*bn_).end()};
return;
}
rconstruct(C<I-1>{});
}
void
destroy(C<sizeof...(Bn)> const&)
{
@@ -209,7 +195,7 @@ private:
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
new(&buf_[0]) iter_t<I>{
std::move(other.iter<I>())};
return;
}
@@ -229,7 +215,7 @@ private:
{
if(n_ == I)
{
new(buf_.data()) iter_t<I>{
new(&buf_[0]) iter_t<I>{
other.iter<I>()};
return;
}
@@ -257,8 +243,8 @@ private:
reference
dereference(C<sizeof...(Bn)> const&) const
{
throw detail::make_exception<std::logic_error>(
"invalid iterator", __FILE__, __LINE__);
BOOST_THROW_EXCEPTION(std::logic_error{
"invalid iterator"});
}
template<std::size_t I>
@@ -274,8 +260,8 @@ private:
void
increment(C<sizeof...(Bn)> const&)
{
throw detail::make_exception<std::logic_error>(
"invalid iterator", __FILE__, __LINE__);
BOOST_THROW_EXCEPTION(std::logic_error{
"invalid iterator"});
}
template<std::size_t I>
@@ -299,27 +285,10 @@ private:
{
auto constexpr I = sizeof...(Bn);
if(n_ == I)
{
--n_;
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bn_).end()};
}
rconstruct(C<I-1>{});
decrement(C<I-1>{});
}
void
decrement(C<0> const&)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bn_).begin())
{
--iter<I>();
return;
}
throw detail::make_exception<std::logic_error>(
"invalid iterator", __FILE__, __LINE__);
}
template<std::size_t I>
void
decrement(C<I> const&)
@@ -334,24 +303,36 @@ private:
--n_;
using Iter = iter_t<I>;
iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bn_).end()};
rconstruct(C<I-1>{});
}
decrement(C<I-1>{});
}
void
decrement(C<0> const&)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bn_).begin())
{
--iter<I>();
return;
}
BOOST_THROW_EXCEPTION(std::logic_error{
"invalid iterator"});
}
};
//------------------------------------------------------------------------------
template<class... Bn>
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::~const_iterator()
{
destroy(C<0>{});
}
template<class... Bn>
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::const_iterator()
: n_(sizeof...(Bn))
, bn_(nullptr)
@@ -359,7 +340,7 @@ const_iterator::const_iterator()
}
template<class... Bn>
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::const_iterator(
std::tuple<Bn...> const& bn, bool at_end)
: bn_(&bn)
@@ -371,7 +352,7 @@ const_iterator::const_iterator(
}
template<class... Bn>
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::const_iterator(const_iterator&& other)
: n_(other.n_)
, bn_(other.bn_)
@@ -380,7 +361,7 @@ const_iterator::const_iterator(const_iterator&& other)
}
template<class... Bn>
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::const_iterator(const_iterator const& other)
: n_(other.n_)
, bn_(other.bn_)
@@ -390,7 +371,7 @@ const_iterator::const_iterator(const_iterator const& other)
template<class... Bn>
auto
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::operator=(const_iterator&& other) ->
const_iterator&
{
@@ -399,13 +380,14 @@ const_iterator::operator=(const_iterator&& other) ->
destroy(C<0>{});
n_ = other.n_;
bn_ = other.bn_;
// VFALCO What about exceptions?
move(std::move(other), C<0>{});
return *this;
}
template<class... Bn>
auto
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::operator=(const_iterator const& other) ->
const_iterator&
{
@@ -414,13 +396,14 @@ const_iterator&
destroy(C<0>{});
n_ = other.n_;
bn_ = other.bn_;
// VFALCO What about exceptions?
copy(other, C<0>{});
return *this;
}
template<class... Bn>
bool
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::operator==(const_iterator const& other) const
{
if(bn_ != other.bn_)
@@ -432,7 +415,7 @@ const_iterator::operator==(const_iterator const& other) const
template<class... Bn>
auto
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::operator*() const ->
reference
{
@@ -441,7 +424,7 @@ const_iterator::operator*() const ->
template<class... Bn>
auto
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::operator++() ->
const_iterator&
{
@@ -451,7 +434,18 @@ const_iterator::operator++() ->
template<class... Bn>
auto
buffer_cat_helper<Bn...>::
buffer_cat_view<Bn...>::
const_iterator::operator++(int) ->
const_iterator
{
auto temp = *this;
++(*this);
return temp;
}
template<class... Bn>
auto
buffer_cat_view<Bn...>::
const_iterator::operator--() ->
const_iterator&
{
@@ -459,10 +453,31 @@ const_iterator::operator--() ->
return *this;
}
template<class... Bn>
auto
buffer_cat_view<Bn...>::
const_iterator::operator--(int) ->
const_iterator
{
auto temp = *this;
--(*this);
return temp;
}
//------------------------------------------------------------------------------
template<class... Bn>
buffer_cat_view<Bn...>::
buffer_cat_view(Bn const&... bn)
: bn_(bn...)
{
}
template<class... Bn>
inline
auto
buffer_cat_helper<Bn...>::begin() const ->
buffer_cat_view<Bn...>::begin() const ->
const_iterator
{
return const_iterator{bn_, false};
@@ -471,13 +486,12 @@ buffer_cat_helper<Bn...>::begin() const ->
template<class... Bn>
inline
auto
buffer_cat_helper<Bn...>::end() const ->
buffer_cat_view<Bn...>::end() const ->
const_iterator
{
return const_iterator{bn_, true};
}
} // detail
} // beast
#endif

View File

@@ -5,11 +5,9 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_PREPARED_BUFFERS_HPP
#define BEAST_DETAIL_PREPARED_BUFFERS_HPP
#ifndef BEAST_IMPL_BUFFER_PREFIX_IPP
#define BEAST_IMPL_BUFFER_PREFIX_IPP
#include <beast/core/prepare_buffer.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
@@ -18,102 +16,40 @@
#include <utility>
namespace beast {
namespace detail {
/** A buffer sequence adapter that shortens the sequence size.
The class adapts a buffer sequence to efficiently represent
a shorter subset of the original list of buffers starting
with the first byte of the original sequence.
@tparam BufferSequence The buffer sequence to adapt.
*/
template<class BufferSequence>
class prepared_buffers
inline
boost::asio::const_buffer
buffer_prefix(std::size_t n,
boost::asio::const_buffer buffer)
{
using iter_type =
typename BufferSequence::const_iterator;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return { buffer_cast<void const*>(buffer),
(std::min)(n, buffer_size(buffer)) };
}
BufferSequence bs_;
iter_type back_;
iter_type end_;
std::size_t size_;
inline
boost::asio::mutable_buffer
buffer_prefix(std::size_t n,
boost::asio::mutable_buffer buffer)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return { buffer_cast<void*>(buffer),
(std::min)(n, buffer_size(buffer)) };
}
template<class Deduced>
prepared_buffers(Deduced&& other,
std::size_t nback, std::size_t nend)
: bs_(std::forward<Deduced>(other).bs_)
, back_(std::next(bs_.begin(), nback))
, end_(std::next(bs_.begin(), nend))
, size_(other.size_)
{
}
void
setup(std::size_t n);
public:
/// The type for each element in the list of buffers.
using value_type = typename std::conditional<
std::is_convertible<typename
std::iterator_traits<iter_type>::value_type,
boost::asio::mutable_buffer>::value,
boost::asio::mutable_buffer,
boost::asio::const_buffer>::type;
#if GENERATING_DOCS
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
/// Move constructor.
prepared_buffers(prepared_buffers&&);
/// Copy constructor.
prepared_buffers(prepared_buffers const&);
/// Move assignment.
prepared_buffers& operator=(prepared_buffers&&);
/// Copy assignment.
prepared_buffers& operator=(prepared_buffers const&);
/** Construct a shortened buffer sequence.
@param n The maximum number of bytes in the wrapped
sequence. If this is larger than the size of passed,
buffers, the resulting sequence will represent the
entire input sequence.
@param buffers The buffer sequence to adapt. A copy of
the sequence will be made, but ownership of the underlying
memory is not transferred.
*/
prepared_buffers(std::size_t n, BufferSequence const& buffers);
/// Get a bidirectional iterator to the first element.
const_iterator
begin() const;
/// Get a bidirectional iterator to one past the last element.
const_iterator
end() const;
};
} // detail
template<class BufferSequence>
class prepared_buffers<BufferSequence>::const_iterator
class buffer_prefix_view<BufferSequence>::const_iterator
{
friend class prepared_buffers<BufferSequence>;
friend class buffer_prefix_view<BufferSequence>;
using iter_type =
typename BufferSequence::const_iterator;
prepared_buffers const* b_ = nullptr;
typename BufferSequence::const_iterator it_;
buffer_prefix_view const* b_ = nullptr;
iter_type it_;
public:
using value_type = typename std::conditional<
@@ -150,7 +86,7 @@ public:
operator*() const
{
if(it_ == b_->back_)
return prepare_buffer(b_->size_, *it_);
return detail::buffer_prefix(b_->size_, *it_);
return *it_;
}
@@ -188,7 +124,7 @@ public:
}
private:
const_iterator(prepared_buffers const& b,
const_iterator(buffer_prefix_view const& b,
bool at_end)
: b_(&b)
, it_(at_end ? b.end_ : b.bs_.begin())
@@ -198,7 +134,7 @@ private:
template<class BufferSequence>
void
prepared_buffers<BufferSequence>::
buffer_prefix_view<BufferSequence>::
setup(std::size_t n)
{
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
@@ -218,7 +154,8 @@ setup(std::size_t n)
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::const_iterator::
buffer_prefix_view<BufferSequence>::
const_iterator::
const_iterator(const_iterator&& other)
: b_(other.b_)
, it_(std::move(other.it_))
@@ -226,7 +163,8 @@ const_iterator(const_iterator&& other)
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::const_iterator::
buffer_prefix_view<BufferSequence>::
const_iterator::
const_iterator(const_iterator const& other)
: b_(other.b_)
, it_(other.it_)
@@ -235,7 +173,8 @@ const_iterator(const_iterator const& other)
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::const_iterator::
buffer_prefix_view<BufferSequence>::
const_iterator::
operator=(const_iterator&& other) ->
const_iterator&
{
@@ -246,7 +185,8 @@ operator=(const_iterator&& other) ->
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::const_iterator::
buffer_prefix_view<BufferSequence>::
const_iterator::
operator=(const_iterator const& other) ->
const_iterator&
{
@@ -258,18 +198,18 @@ operator=(const_iterator const& other) ->
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers&& other)
: prepared_buffers(std::move(other),
buffer_prefix_view<BufferSequence>::
buffer_prefix_view(buffer_prefix_view&& other)
: buffer_prefix_view(std::move(other),
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(prepared_buffers const& other)
: prepared_buffers(other,
buffer_prefix_view<BufferSequence>::
buffer_prefix_view(buffer_prefix_view const& other)
: buffer_prefix_view(other,
std::distance<iter_type>(other.bs_.begin(), other.back_),
std::distance<iter_type>(other.bs_.begin(), other.end_))
{
@@ -277,9 +217,9 @@ prepared_buffers(prepared_buffers const& other)
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers&& other) ->
prepared_buffers&
buffer_prefix_view<BufferSequence>::
operator=(buffer_prefix_view&& other) ->
buffer_prefix_view&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
@@ -294,9 +234,9 @@ operator=(prepared_buffers&& other) ->
template<class BufferSequence>
auto
prepared_buffers<BufferSequence>::
operator=(prepared_buffers const& other) ->
prepared_buffers&
buffer_prefix_view<BufferSequence>::
operator=(buffer_prefix_view const& other) ->
buffer_prefix_view&
{
auto const nback = std::distance<iter_type>(
other.bs_.begin(), other.back_);
@@ -310,17 +250,27 @@ operator=(prepared_buffers const& other) ->
}
template<class BufferSequence>
prepared_buffers<BufferSequence>::
prepared_buffers(std::size_t n, BufferSequence const& bs)
buffer_prefix_view<BufferSequence>::
buffer_prefix_view(std::size_t n, BufferSequence const& bs)
: bs_(bs)
{
setup(n);
}
template<class BufferSequence>
template<class... Args>
buffer_prefix_view<BufferSequence>::
buffer_prefix_view(std::size_t n,
boost::in_place_init_t, Args&&... args)
: bs_(std::forward<Args>(args)...)
{
setup(n);
}
template<class BufferSequence>
inline
auto
prepared_buffers<BufferSequence>::begin() const ->
buffer_prefix_view<BufferSequence>::begin() const ->
const_iterator
{
return const_iterator{*this, false};
@@ -329,13 +279,12 @@ prepared_buffers<BufferSequence>::begin() const ->
template<class BufferSequence>
inline
auto
prepared_buffers<BufferSequence>::end() const ->
buffer_prefix_view<BufferSequence>::end() const ->
const_iterator
{
return const_iterator{*this, true};
}
} // detail
} // beast
#endif

View File

@@ -5,38 +5,30 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_DYNABUF_READSTREAM_HPP
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
#ifndef BEAST_IMPL_BUFFERED_READ_STREAM_IPP
#define BEAST_IMPL_BUFFERED_READ_STREAM_IPP
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/core/detail/config.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/throw_exception.hpp>
namespace beast {
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler>
class dynabuf_readstream<
class buffered_read_stream<
Stream, DynamicBuffer>::read_some_op
{
// VFALCO What about bool cont for is_continuation?
struct data
{
dynabuf_readstream& srs;
MutableBufferSequence bs;
int state = 0;
data(Handler&, dynabuf_readstream& srs_,
MutableBufferSequence const& bs_)
: srs(srs_)
, bs(bs_)
{
}
};
handler_ptr<data, Handler> d_;
int step_ = 0;
buffered_read_stream& s_;
MutableBufferSequence b_;
Handler h_;
public:
read_some_op(read_some_op&&) = default;
@@ -44,11 +36,12 @@ public:
template<class DeducedHandler, class... Args>
read_some_op(DeducedHandler&& h,
dynabuf_readstream& srs, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
srs, std::forward<Args>(args)...)
buffered_read_stream& s,
MutableBufferSequence const& b)
: s_(s)
, b_(b)
, h_(std::forward<DeducedHandler>(h))
{
(*this)(error_code{}, 0);
}
void
@@ -59,99 +52,92 @@ public:
void* asio_handler_allocate(
std::size_t size, read_some_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_some_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(read_some_op* op)
{
return beast_asio_helpers::
is_continuation(op->d_.handler());
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_some_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
using boost::asio::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
};
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class Handler>
void
dynabuf_readstream<Stream, DynamicBuffer>::
buffered_read_stream<Stream, DynamicBuffer>::
read_some_op<MutableBufferSequence, Handler>::operator()(
error_code const& ec, std::size_t bytes_transferred)
{
auto& d = *d_;
while(! ec && d.state != 99)
switch(step_)
{
switch(d.state)
case 0:
if(s_.sb_.size() == 0)
{
case 0:
if(d.srs.sb_.size() == 0)
if(s_.capacity_ == 0)
{
d.state =
d.srs.capacity_ > 0 ? 2 : 1;
break;
// read (unbuffered)
step_ = 1;
return s_.next_layer_.async_read_some(
b_, std::move(*this));
}
d.state = 4;
d.srs.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
case 1:
// read (unbuffered)
d.state = 99;
d.srs.next_layer_.async_read_some(
d.bs, std::move(*this));
return;
case 2:
// read
d.state = 3;
d.srs.next_layer_.async_read_some(
d.srs.sb_.prepare(d.srs.capacity_),
step_ = 2;
return s_.next_layer_.async_read_some(
s_.sb_.prepare(s_.capacity_),
std::move(*this));
return;
// got data
case 3:
d.state = 4;
d.srs.sb_.commit(bytes_transferred);
break;
// copy
case 4:
bytes_transferred =
boost::asio::buffer_copy(
d.bs, d.srs.sb_.data());
d.srs.sb_.consume(bytes_transferred);
// call handler
d.state = 99;
break;
}
step_ = 3;
s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
case 1:
// upcall
break;
case 2:
s_.sb_.commit(bytes_transferred);
BEAST_FALLTHROUGH;
case 3:
bytes_transferred =
boost::asio::buffer_copy(b_, s_.sb_.data());
s_.sb_.consume(bytes_transferred);
break;
}
d_.invoke(ec, bytes_transferred);
h_(ec, bytes_transferred);
}
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer>
template<class... Args>
dynabuf_readstream<Stream, DynamicBuffer>::
dynabuf_readstream(Args&&... args)
buffered_read_stream<Stream, DynamicBuffer>::
buffered_read_stream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
}
@@ -159,18 +145,17 @@ dynabuf_readstream(Args&&... args)
template<class Stream, class DynamicBuffer>
template<class ConstBufferSequence, class WriteHandler>
auto
dynabuf_readstream<Stream, DynamicBuffer>::
buffered_read_stream<Stream, DynamicBuffer>::
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler) ->
typename async_completion<
WriteHandler, void(error_code)>::result_type
WriteHandler&& handler) ->
async_return_type<WriteHandler, void(error_code)>
{
static_assert(is_AsyncWriteStream<next_layer_type>::value,
static_assert(is_async_write_stream<next_layer_type>::value,
"AsyncWriteStream requirements not met");
static_assert(is_ConstBufferSequence<
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
static_assert(is_CompletionHandler<WriteHandler,
static_assert(is_completion_handler<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler requirements not met");
return next_layer_.async_write_some(buffers,
@@ -180,32 +165,32 @@ async_write_some(ConstBufferSequence const& buffers,
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence>
std::size_t
dynabuf_readstream<Stream, DynamicBuffer>::
buffered_read_stream<Stream, DynamicBuffer>::
read_some(
MutableBufferSequence const& buffers)
{
static_assert(is_SyncReadStream<next_layer_type>::value,
static_assert(is_sync_read_stream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence<
static_assert(is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
error_code ec;
auto n = read_some(buffers, ec);
if(ec)
throw system_error{ec};
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence>
std::size_t
dynabuf_readstream<Stream, DynamicBuffer>::
buffered_read_stream<Stream, DynamicBuffer>::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_SyncReadStream<next_layer_type>::value,
static_assert(is_sync_read_stream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence<
static_assert(is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_size;
@@ -219,6 +204,10 @@ read_some(MutableBufferSequence const& buffers,
if(ec)
return 0;
}
else
{
ec.assign(0, ec.category());
}
auto bytes_transferred =
buffer_copy(buffers, sb_.data());
sb_.consume(bytes_transferred);
@@ -228,25 +217,23 @@ read_some(MutableBufferSequence const& buffers,
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class ReadHandler>
auto
dynabuf_readstream<Stream, DynamicBuffer>::
async_read_some(
MutableBufferSequence const& buffers,
buffered_read_stream<Stream, DynamicBuffer>::
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler) ->
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_return_type<ReadHandler, void(error_code)>
{
static_assert(is_AsyncReadStream<next_layer_type>::value,
static_assert(is_async_read_stream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_MutableBufferSequence<
static_assert(is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
beast::async_completion<
ReadHandler, void(error_code, std::size_t)
> completion{handler};
read_some_op<MutableBufferSequence,
decltype(completion.handler)>{
completion.handler, *this, buffers};
return completion.result.get();
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
read_some_op<MutableBufferSequence, handler_type<
ReadHandler, void(error_code, std::size_t)>>{
init.completion_handler, *this, buffers}(
error_code{}, 0);
return init.result.get();
}
} // beast

View File

@@ -10,6 +10,7 @@
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
@@ -414,8 +415,8 @@ buffers_adapter<MutableBufferSequence>::prepare(std::size_t n) ->
}
}
if(n > 0)
throw detail::make_exception<std::length_error>(
"no space", __FILE__, __LINE__);
BOOST_THROW_EXCEPTION(std::length_error{
"buffer overflow"});
return mutable_buffers_type{*this};
}

View File

@@ -8,8 +8,7 @@
#ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <beast/core/type_traits.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
@@ -18,13 +17,13 @@
namespace beast {
template<class BufferSequence>
class consuming_buffers<BufferSequence>::const_iterator
template<class Buffers>
class consuming_buffers<Buffers>::const_iterator
{
friend class consuming_buffers<BufferSequence>;
friend class consuming_buffers<Buffers>;
using iter_type =
typename BufferSequence::const_iterator;
typename Buffers::const_iterator;
iter_type it_;
consuming_buffers const* b_ = nullptr;
@@ -110,8 +109,17 @@ private:
}
};
template<class BufferSequence>
consuming_buffers<BufferSequence>::
//------------------------------------------------------------------------------
template<class Buffers>
consuming_buffers<Buffers>::
consuming_buffers()
: begin_(bs_.begin())
{
}
template<class Buffers>
consuming_buffers<Buffers>::
consuming_buffers(consuming_buffers&& other)
: consuming_buffers(std::move(other),
std::distance<iter_type>(
@@ -119,8 +127,8 @@ consuming_buffers(consuming_buffers&& other)
{
}
template<class BufferSequence>
consuming_buffers<BufferSequence>::
template<class Buffers>
consuming_buffers<Buffers>::
consuming_buffers(consuming_buffers const& other)
: consuming_buffers(other,
std::distance<iter_type>(
@@ -128,9 +136,35 @@ consuming_buffers(consuming_buffers const& other)
{
}
template<class BufferSequence>
template<class Buffers>
consuming_buffers<Buffers>::
consuming_buffers(Buffers const& bs)
: bs_(bs)
, begin_(bs_.begin())
{
static_assert(
is_const_buffer_sequence<Buffers>::value||
is_mutable_buffer_sequence<Buffers>::value,
"BufferSequence requirements not met");
}
template<class Buffers>
template<class... Args>
consuming_buffers<Buffers>::
consuming_buffers(boost::in_place_init_t, Args&&... args)
: bs_(std::forward<Args>(args)...)
, begin_(bs_.begin())
{
static_assert(sizeof...(Args) > 0,
"Missing constructor arguments");
static_assert(
std::is_constructible<Buffers, Args...>::value,
"Buffers not constructible from arguments");
}
template<class Buffers>
auto
consuming_buffers<BufferSequence>::
consuming_buffers<Buffers>::
operator=(consuming_buffers&& other) ->
consuming_buffers&
{
@@ -142,9 +176,9 @@ operator=(consuming_buffers&& other) ->
return *this;
}
template<class BufferSequence>
template<class Buffers>
auto
consuming_buffers<BufferSequence>::
consuming_buffers<Buffers>::
operator=(consuming_buffers const& other) ->
consuming_buffers&
{
@@ -156,40 +190,29 @@ operator=(consuming_buffers const& other) ->
return *this;
}
template<class BufferSequence>
consuming_buffers<BufferSequence>::
consuming_buffers(BufferSequence const& bs)
: bs_(bs)
, begin_(bs_.begin())
{
static_assert(
is_BufferSequence<BufferSequence, value_type>::value,
"BufferSequence requirements not met");
}
template<class BufferSequence>
template<class Buffers>
inline
auto
consuming_buffers<BufferSequence>::
consuming_buffers<Buffers>::
begin() const ->
const_iterator
{
return const_iterator{*this, begin_};
}
template<class BufferSequence>
template<class Buffers>
inline
auto
consuming_buffers<BufferSequence>::
consuming_buffers<Buffers>::
end() const ->
const_iterator
{
return const_iterator{*this, bs_.end()};
}
template<class BufferSequence>
template<class Buffers>
void
consuming_buffers<BufferSequence>::
consuming_buffers<Buffers>::
consume(std::size_t n)
{
using boost::asio::buffer_size;

View File

@@ -0,0 +1,331 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_IMPL_FILE_POSIX_IPP
#define BEAST_CORE_IMPL_FILE_POSIX_IPP
#include <limits>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
namespace beast {
namespace detail {
inline
int
file_posix_close(int fd)
{
for(;;)
{
if(! ::close(fd))
break;
int const ev = errno;
if(errno != EINTR)
return ev;
}
return 0;
}
} // detail
inline
file_posix::
~file_posix()
{
if(fd_ != -1)
detail::file_posix_close(fd_);
}
inline
file_posix::
file_posix(file_posix&& other)
: fd_(other.fd_)
{
other.fd_ = -1;
}
inline
file_posix&
file_posix::
operator=(file_posix&& other)
{
if(&other == this)
return *this;
if(fd_ != -1)
detail::file_posix_close(fd_);
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
inline
void
file_posix::
native_handle(native_handle_type fd)
{
if(fd_ != -1)
detail::file_posix_close(fd_);
fd_ = fd;
}
inline
void
file_posix::
close(error_code& ec)
{
if(fd_ != -1)
{
auto const ev =
detail::file_posix_close(fd_);
if(ev)
ec.assign(ev, generic_category());
else
ec.assign(0, ec.category());
fd_ = -1;
}
else
{
ec.assign(0, ec.category());
}
}
inline
void
file_posix::
open(char const* path, file_mode mode, error_code& ec)
{
if(fd_ != -1)
{
auto const ev =
detail::file_posix_close(fd_);
if(ev)
ec.assign(ev, generic_category());
else
ec.assign(0, ec.category());
fd_ = -1;
}
int f = 0;
#ifndef __APPLE__
int advise = 0;
#endif
switch(mode)
{
default:
case file_mode::read:
f = O_RDONLY;
#ifndef __APPLE__
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::scan:
f = O_RDONLY;
#ifndef __APPLE__
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
case file_mode::write:
f = O_RDWR | O_CREAT | O_TRUNC;
#ifndef __APPLE__
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::write_new:
f = O_RDWR | O_CREAT | O_EXCL;
#ifndef __APPLE__
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::write_existing:
f = O_RDWR | O_EXCL;
#ifndef __APPLE__
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::append:
f = O_RDWR | O_CREAT | O_TRUNC;
#ifndef __APPLE__
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
case file_mode::append_new:
f = O_RDWR | O_CREAT | O_EXCL;
#ifndef __APPLE__
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
case file_mode::append_existing:
f = O_RDWR | O_EXCL;
#ifndef __APPLE__
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
}
for(;;)
{
fd_ = ::open(path, f, 0644);
if(fd_ != -1)
break;
auto const ev = errno;
if(ev != EINTR)
{
ec.assign(ev, generic_category());
return;
}
}
#ifndef __APPLE__
if(::posix_fadvise(fd_, 0, 0, advise))
{
auto const ev = errno;
detail::file_posix_close(fd_);
fd_ = -1;
ec.assign(ev, generic_category());
return;
}
#endif
ec.assign(0, ec.category());
}
inline
std::uint64_t
file_posix::
size(error_code& ec) const
{
if(fd_ == -1)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
struct stat st;
if(::fstat(fd_, &st) != 0)
{
ec.assign(errno, generic_category());
return 0;
}
ec.assign(0, ec.category());
return st.st_size;
}
inline
std::uint64_t
file_posix::
pos(error_code& ec) const
{
if(fd_ == -1)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
auto const result = ::lseek(fd_, 0, SEEK_CUR);
if(result == (off_t)-1)
{
ec.assign(errno, generic_category());
return 0;
}
ec.assign(0, ec.category());
return result;
}
inline
void
file_posix::
seek(std::uint64_t offset, error_code& ec)
{
if(fd_ == -1)
{
ec.assign(errc::invalid_argument, generic_category());
return;
}
auto const result = ::lseek(fd_, offset, SEEK_SET);
if(result == static_cast<off_t>(-1))
{
ec.assign(errno, generic_category());
return;
}
ec.assign(0, ec.category());
}
inline
std::size_t
file_posix::
read(void* buffer, std::size_t n, error_code& ec) const
{
if(fd_ == -1)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
std::size_t nread = 0;
while(n > 0)
{
auto const amount = static_cast<ssize_t>((std::min)(
n, static_cast<std::size_t>(SSIZE_MAX)));
auto const result = ::read(fd_, buffer, amount);
if(result == -1)
{
auto const ev = errno;
if(ev == EINTR)
continue;
ec.assign(ev, generic_category());
return nread;
}
if(result == 0)
{
// short read
return nread;
}
n -= result;
nread += result;
buffer = reinterpret_cast<char*>(buffer) + result;
}
return nread;
}
inline
std::size_t
file_posix::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(fd_ == -1)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
std::size_t nwritten = 0;
while(n > 0)
{
auto const amount = static_cast<ssize_t>((std::min)(
n, static_cast<std::size_t>(SSIZE_MAX)));
auto const result = ::write(fd_, buffer, amount);
if(result == -1)
{
auto const ev = errno;
if(ev == EINTR)
continue;
ec.assign(ev, generic_category());
return nwritten;
}
n -= result;
nwritten += result;
buffer = reinterpret_cast<char const*>(buffer) + result;
}
return nwritten;
}
} // beast
#endif

View File

@@ -0,0 +1,225 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_IMPL_FILE_STDIO_IPP
#define BEAST_CORE_IMPL_FILE_STDIO_IPP
#include <limits>
namespace beast {
inline
file_stdio::
~file_stdio()
{
if(f_)
fclose(f_);
}
inline
file_stdio::
file_stdio(file_stdio&& other)
: f_(other.f_)
{
other.f_ = nullptr;
}
inline
file_stdio&
file_stdio::
operator=(file_stdio&& other)
{
if(&other == this)
return *this;
if(f_)
fclose(f_);
f_ = other.f_;
other.f_ = nullptr;
return *this;
}
inline
void
file_stdio::
native_handle(FILE* f)
{
if(f_)
fclose(f_);
f_ = f;
}
inline
void
file_stdio::
close(error_code& ec)
{
if(f_)
{
int failed = fclose(f_);
f_ = nullptr;
if(failed)
{
ec.assign(errno, generic_category());
return;
}
}
ec.assign(0, ec.category());
}
inline
void
file_stdio::
open(char const* path, file_mode mode, error_code& ec)
{
if(f_)
{
fclose(f_);
f_ = nullptr;
}
char const* s;
switch(mode)
{
default:
case file_mode::read: s = "rb"; break;
case file_mode::scan: s = "rb"; break;
case file_mode::write: s = "wb"; break;
case file_mode::write_new: s = "wbx"; break;
case file_mode::write_existing: s = "wb"; break;
case file_mode::append: s = "ab"; break;
case file_mode::append_new: s = "abx"; break;
case file_mode::append_existing: s = "ab"; break;
}
f_ = std::fopen(path, s);
if(! f_)
{
ec.assign(errno, generic_category());
return;
}
ec.assign(0, ec.category());
}
inline
std::uint64_t
file_stdio::
size(error_code& ec) const
{
if(! f_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
long pos = std::ftell(f_);
if(pos == -1L)
{
ec.assign(errno, generic_category());
return 0;
}
int result = std::fseek(f_, 0, SEEK_END);
if(result != 0)
{
ec.assign(errno, generic_category());
return 0;
}
long size = std::ftell(f_);
if(size == -1L)
{
ec.assign(errno, generic_category());
std::fseek(f_, pos, SEEK_SET);
return 0;
}
result = std::fseek(f_, pos, SEEK_SET);
if(result != 0)
ec.assign(errno, generic_category());
else
ec.assign(0, ec.category());
return size;
}
inline
std::uint64_t
file_stdio::
pos(error_code& ec) const
{
if(! f_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
long pos = std::ftell(f_);
if(pos == -1L)
{
ec.assign(errno, generic_category());
return 0;
}
ec.assign(0, ec.category());
return pos;
}
inline
void
file_stdio::
seek(std::uint64_t offset, error_code& ec)
{
if(! f_)
{
ec.assign(errc::invalid_argument, generic_category());
return;
}
if(offset > (std::numeric_limits<long>::max)())
{
ec = make_error_code(errc::invalid_seek);
return;
}
int result = std::fseek(f_,
static_cast<long>(offset), SEEK_SET);
if(result != 0)
ec.assign(errno, generic_category());
else
ec.assign(0, ec.category());
}
inline
std::size_t
file_stdio::
read(void* buffer, std::size_t n, error_code& ec) const
{
if(! f_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
auto nread = std::fread(buffer, 1, n, f_);
if(std::ferror(f_))
{
ec.assign(errno, generic_category());
return 0;
}
return nread;
}
inline
std::size_t
file_stdio::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(! f_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
auto nwritten = std::fwrite(buffer, 1, n, f_);
if(std::ferror(f_))
{
ec.assign(errno, generic_category());
return 0;
}
return nwritten;
}
} // beast
#endif

View File

@@ -0,0 +1,356 @@
//
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_IMPL_FILE_WIN32_IPP
#define BEAST_CORE_IMPL_FILE_WIN32_IPP
#include <boost/detail/winapi/access_rights.hpp>
#include <boost/detail/winapi/error_codes.hpp>
#include <boost/detail/winapi/file_management.hpp>
#include <boost/detail/winapi/get_last_error.hpp>
#include <limits>
#include <utility>
namespace beast {
namespace detail {
// VFALCO Can't seem to get boost/detail/winapi to work with
// this so use the non-Ex version for now.
inline
boost::detail::winapi::BOOL_
set_file_pointer_ex(
boost::detail::winapi::HANDLE_ hFile,
boost::detail::winapi::LARGE_INTEGER_ lpDistanceToMove,
boost::detail::winapi::PLARGE_INTEGER_ lpNewFilePointer,
boost::detail::winapi::DWORD_ dwMoveMethod)
{
auto dwHighPart = lpDistanceToMove.u.HighPart;
auto dwLowPart = boost::detail::winapi::SetFilePointer(
hFile,
lpDistanceToMove.u.LowPart,
&dwHighPart,
dwMoveMethod);
if(dwLowPart == boost::detail::winapi::INVALID_SET_FILE_POINTER_)
return 0;
if(lpNewFilePointer)
{
lpNewFilePointer->u.LowPart = dwLowPart;
lpNewFilePointer->u.HighPart = dwHighPart;
}
return 1;
}
} // detail
inline
file_win32::
~file_win32()
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
boost::detail::winapi::CloseHandle(h_);
}
inline
file_win32::
file_win32(file_win32&& other)
: h_(other.h_)
{
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
inline
file_win32&
file_win32::
operator=(file_win32&& other)
{
if(&other == this)
return *this;
if(h_)
boost::detail::winapi::CloseHandle(h_);
h_ = other.h_;
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
return *this;
}
inline
void
file_win32::
native_handle(native_handle_type h)
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
boost::detail::winapi::CloseHandle(h_);
h_ = h;
}
inline
void
file_win32::
close(error_code& ec)
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
if(! boost::detail::winapi::CloseHandle(h_))
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
else
ec.assign(0, ec.category());
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
else
{
ec.assign(0, ec.category());
}
}
inline
void
file_win32::
open(char const* path, file_mode mode, error_code& ec)
{
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
boost::detail::winapi::CloseHandle(h_);
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
}
boost::detail::winapi::DWORD_ dw1 = 0;
boost::detail::winapi::DWORD_ dw2 = 0;
boost::detail::winapi::DWORD_ dw3 = 0;
/*
| When the file...
This argument: | Exists Does not exist
-------------------------+------------------------------------------------------
CREATE_ALWAYS | Truncates Creates
CREATE_NEW +-----------+ Fails Creates
OPEN_ALWAYS ===| does this |===> Opens Creates
OPEN_EXISTING +-----------+ Opens Fails
TRUNCATE_EXISTING | Truncates Fails
*/
switch(mode)
{
default:
case file_mode::read:
dw1 = boost::detail::winapi::GENERIC_READ_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::scan:
dw1 = boost::detail::winapi::GENERIC_READ_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::write:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::write_new:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_NEW_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::write_existing:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::append:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::append_new:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::CREATE_NEW_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::append_existing:
dw1 = boost::detail::winapi::GENERIC_READ_ |
boost::detail::winapi::GENERIC_WRITE_;
dw2 = boost::detail::winapi::OPEN_EXISTING_;
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
}
h_ = ::CreateFileA(
path,
dw1,
0,
NULL,
dw2,
dw3,
NULL);
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
else
ec.assign(0, ec.category());
}
inline
std::uint64_t
file_win32::
size(error_code& ec) const
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
boost::detail::winapi::LARGE_INTEGER_ fileSize;
if(! boost::detail::winapi::GetFileSizeEx(h_, &fileSize))
{
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
return 0;
}
ec.assign(0, ec.category());
return fileSize.QuadPart;
}
inline
std::uint64_t
file_win32::
pos(error_code& ec)
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
boost::detail::winapi::LARGE_INTEGER_ in;
boost::detail::winapi::LARGE_INTEGER_ out;
in.QuadPart = 0;
if(! detail::set_file_pointer_ex(h_, in, &out,
boost::detail::winapi::FILE_CURRENT_))
{
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
return 0;
}
ec.assign(0, ec.category());
return out.QuadPart;
}
inline
void
file_win32::
seek(std::uint64_t offset, error_code& ec)
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return;
}
boost::detail::winapi::LARGE_INTEGER_ in;
in.QuadPart = offset;
if(! detail::set_file_pointer_ex(h_, in, 0,
boost::detail::winapi::FILE_BEGIN_))
{
ec.assign(boost::detail::winapi::GetLastError(),
system_category());
return;
}
ec.assign(0, ec.category());
}
inline
std::size_t
file_win32::
read(void* buffer, std::size_t n, error_code& ec)
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
std::size_t nread = 0;
while(n > 0)
{
boost::detail::winapi::DWORD_ amount;
if(n > (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)())
amount = (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)();
else
amount = static_cast<
boost::detail::winapi::DWORD_>(n);
boost::detail::winapi::DWORD_ bytesRead;
if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
{
auto const dwError = ::GetLastError();
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
ec.assign(::GetLastError(), system_category());
else
ec.assign(0, ec.category());
return nread;
}
if(bytesRead == 0)
return nread;
n -= bytesRead;
nread += bytesRead;
buffer = reinterpret_cast<char*>(buffer) + bytesRead;
}
ec.assign(0, ec.category());
return nread;
}
inline
std::size_t
file_win32::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(errc::invalid_argument, generic_category());
return 0;
}
std::size_t nwritten = 0;
while(n > 0)
{
boost::detail::winapi::DWORD_ amount;
if(n > (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)())
amount = (std::numeric_limits<
boost::detail::winapi::DWORD_>::max)();
else
amount = static_cast<
boost::detail::winapi::DWORD_>(n);
boost::detail::winapi::DWORD_ bytesWritten;
if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
{
auto const dwError = ::GetLastError();
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
ec.assign(::GetLastError(), system_category());
else
ec.assign(0, ec.category());
return nwritten;
}
if(bytesWritten == 0)
return nwritten;
n -= bytesWritten;
nwritten += bytesWritten;
buffer = reinterpret_cast<char const*>(buffer) + bytesWritten;
}
ec.assign(0, ec.category());
return nwritten;
}
} // beast
#endif

View File

@@ -0,0 +1,471 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_FLAT_BUFFER_HPP
#define BEAST_IMPL_FLAT_BUFFER_HPP
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
namespace beast {
/* Memory is laid out thusly:
begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
*/
template<class Allocator>
basic_flat_buffer<Allocator>::
~basic_flat_buffer()
{
if(begin_)
alloc_traits::deallocate(
this->member(), begin_, dist(begin_, end_));
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer()
: begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_((std::numeric_limits<std::size_t>::max)())
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(std::size_t limit)
: begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(limit)
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_((std::numeric_limits<std::size_t>::max)())
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(std::size_t limit, Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(limit)
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer&& other)
: detail::empty_base_optimization<allocator_type>(
std::move(other.member()))
, begin_(other.begin_)
, in_(other.in_)
, out_(other.out_)
, last_(out_)
, end_(other.end_)
, max_(other.max_)
{
other.begin_ = nullptr;
other.in_ = nullptr;
other.out_ = nullptr;
other.last_ = nullptr;
other.end_ = nullptr;
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer&& other,
Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
{
if(this->member() != other.member())
{
begin_ = nullptr;
in_ = nullptr;
out_ = nullptr;
last_ = nullptr;
end_ = nullptr;
max_ = other.max_;
copy_from(other);
other.reset();
}
else
{
begin_ = other.begin_;
in_ = other.in_;
out_ = other.out_;
last_ = out_;
end_ = other.end_;
max_ = other.max_;
other.begin_ = nullptr;
other.in_ = nullptr;
other.out_ = nullptr;
other.last_ = nullptr;
other.end_ = nullptr;
}
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer const& other)
: detail::empty_base_optimization<allocator_type>(
alloc_traits::select_on_container_copy_construction(
other.member()))
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer const& other,
Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
basic_flat_buffer<OtherAlloc> const& other)
: begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer<OtherAlloc> const& other,
Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
auto
basic_flat_buffer<Allocator>::
operator=(basic_flat_buffer&& other) ->
basic_flat_buffer&
{
if(this != &other)
move_assign(other,
typename alloc_traits::propagate_on_container_move_assignment{});
return *this;
}
template<class Allocator>
auto
basic_flat_buffer<Allocator>::
operator=(basic_flat_buffer const& other) ->
basic_flat_buffer&
{
if(this != &other)
copy_assign(other,
typename alloc_traits::propagate_on_container_copy_assignment{});
return *this;
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_flat_buffer<Allocator>::
operator=(basic_flat_buffer<OtherAlloc> const& other) ->
basic_flat_buffer&
{
reset();
max_ = other.max_;
copy_from(other);
return *this;
}
//------------------------------------------------------------------------------
template<class Allocator>
auto
basic_flat_buffer<Allocator>::
prepare(std::size_t n) ->
mutable_buffers_type
{
if(n <= dist(out_, end_))
{
// existing capacity is sufficient
last_ = out_ + n;
return{out_, n};
}
auto const len = size();
if(n <= capacity() - len)
{
// after a memmove,
// existing capacity is sufficient
if(len > 0)
std::memmove(begin_, in_, len);
in_ = begin_;
out_ = in_ + len;
last_ = out_ + n;
return {out_, n};
}
// enforce maximum capacity
if(n > max_ - len)
BOOST_THROW_EXCEPTION(std::length_error{
"basic_flat_buffer overflow"});
// allocate a new buffer
auto const new_size = std::min<std::size_t>(
max_,
std::max<std::size_t>(2 * len, len + n));
auto const p = alloc_traits::allocate(
this->member(), new_size);
if(begin_)
{
BOOST_ASSERT(p);
BOOST_ASSERT(in_);
std::memcpy(p, in_, len);
alloc_traits::deallocate(
this->member(), begin_, capacity());
}
begin_ = p;
in_ = begin_;
out_ = in_ + len;
last_ = out_ + n;
end_ = begin_ + new_size;
return {out_, n};
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
consume(std::size_t n)
{
if(n >= dist(in_, out_))
{
in_ = begin_;
out_ = begin_;
return;
}
in_ += n;
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
shrink_to_fit()
{
auto const len = size();
if(len == capacity())
return;
char* p;
if(len > 0)
{
BOOST_ASSERT(begin_);
BOOST_ASSERT(in_);
p = alloc_traits::allocate(
this->member(), len);
std::memcpy(p, in_, len);
}
else
{
p = nullptr;
}
alloc_traits::deallocate(
this->member(), begin_, dist(begin_, end_));
begin_ = p;
in_ = begin_;
out_ = begin_ + len;
last_ = out_;
end_ = out_;
}
//------------------------------------------------------------------------------
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
reset()
{
consume(size());
shrink_to_fit();
}
template<class Allocator>
template<class DynamicBuffer>
inline
void
basic_flat_buffer<Allocator>::
copy_from(DynamicBuffer const& buffer)
{
if(buffer.size() == 0)
return;
using boost::asio::buffer_copy;
commit(buffer_copy(
prepare(buffer.size()), buffer.data()));
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
move_assign(basic_flat_buffer& other, std::true_type)
{
reset();
this->member() = std::move(other.member());
begin_ = other.begin_;
in_ = other.in_;
out_ = other.out_;
last_ = out_;
end_ = other.end_;
max_ = other.max_;
other.begin_ = nullptr;
other.in_ = nullptr;
other.out_ = nullptr;
other.last_ = nullptr;
other.end_ = nullptr;
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
move_assign(basic_flat_buffer& other, std::false_type)
{
reset();
if(this->member() != other.member())
{
copy_from(other);
other.reset();
}
else
{
move_assign(other, std::true_type{});
}
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
copy_assign(basic_flat_buffer const& other, std::true_type)
{
reset();
max_ = other.max_;
this->member() = other.member();
copy_from(other);
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
copy_assign(basic_flat_buffer const& other, std::false_type)
{
reset();
max_ = other.max_;
copy_from(other);
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
swap(basic_flat_buffer& other)
{
swap(other, typename
alloc_traits::propagate_on_container_swap{});
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
swap(basic_flat_buffer& other, std::true_type)
{
using std::swap;
swap(this->member(), other.member());
swap(max_, other.max_);
swap(begin_, other.begin_);
swap(in_, other.in_);
swap(out_, other.out_);
last_ = this->out_;
other.last_ = other.out_;
swap(end_, other.end_);
}
template<class Allocator>
inline
void
basic_flat_buffer<Allocator>::
swap(basic_flat_buffer& other, std::false_type)
{
BOOST_ASSERT(this->member() == other.member());
using std::swap;
swap(max_, other.max_);
swap(begin_, other.begin_);
swap(in_, other.in_);
swap(out_, other.out_);
last_ = this->out_;
other.last_ = other.out_;
swap(end_, other.end_);
}
template<class Allocator>
void
swap(
basic_flat_buffer<Allocator>& lhs,
basic_flat_buffer<Allocator>& rhs)
{
lhs.swap(rhs);
}
} // beast
#endif

View File

@@ -8,8 +8,9 @@
#ifndef BEAST_IMPL_HANDLER_PTR_HPP
#define BEAST_IMPL_HANDLER_PTR_HPP
#include <beast/core/handler_helpers.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/assert.hpp>
#include <memory>
@@ -23,9 +24,10 @@ P(DeducedHandler&& h, Args&&... args)
: n(1)
, handler(std::forward<DeducedHandler>(h))
{
using boost::asio::asio_handler_allocate;
t = reinterpret_cast<T*>(
beast_asio_helpers::
allocate(sizeof(T), handler));
asio_handler_allocate(
sizeof(T), std::addressof(handler)));
try
{
t = new(t) T{handler,
@@ -33,8 +35,9 @@ P(DeducedHandler&& h, Args&&... args)
}
catch(...)
{
beast_asio_helpers::
deallocate(t, sizeof(T), handler);
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
t, sizeof(T), std::addressof(handler));
throw;
}
}
@@ -50,8 +53,9 @@ handler_ptr<T, Handler>::
if(p_->t)
{
p_->t->~T();
beast_asio_helpers::
deallocate(p_->t, sizeof(T), p_->handler);
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p_->t, sizeof(T), std::addressof(p_->handler));
}
delete p_;
}
@@ -80,8 +84,7 @@ handler_ptr(Handler&& handler, Args&&... args)
: p_(new P{std::move(handler),
std::forward<Args>(args)...})
{
static_assert(! std::is_array<T>::value,
"T must not be an array type");
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
}
template<class T, class Handler>
@@ -90,8 +93,7 @@ handler_ptr<T, Handler>::
handler_ptr(Handler const& handler, Args&&... args)
: p_(new P{handler, std::forward<Args>(args)...})
{
static_assert(! std::is_array<T>::value,
"T must not be an array type");
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
}
template<class T, class Handler>
@@ -103,8 +105,9 @@ release_handler() ->
BOOST_ASSERT(p_);
BOOST_ASSERT(p_->t);
p_->t->~T();
beast_asio_helpers::
deallocate(p_->t, sizeof(T), p_->handler);
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p_->t, sizeof(T), std::addressof(p_->handler));
p_->t = nullptr;
return std::move(p_->handler);
}
@@ -118,8 +121,9 @@ invoke(Args&&... args)
BOOST_ASSERT(p_);
BOOST_ASSERT(p_->t);
p_->t->~T();
beast_asio_helpers::
deallocate(p_->t, sizeof(T), p_->handler);
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p_->t, sizeof(T), std::addressof(p_->handler));
p_->t = nullptr;
p_->handler(std::forward<Args>(args)...);
}

View File

@@ -5,12 +5,12 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_STREAMBUF_IPP
#define BEAST_IMPL_STREAMBUF_IPP
#ifndef BEAST_IMPL_MULTI_BUFFER_IPP
#define BEAST_IMPL_MULTI_BUFFER_IPP
#include <beast/core/detail/type_traits.hpp>
#include <beast/core/detail/write_dynabuf.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <exception>
#include <sstream>
@@ -84,7 +84,7 @@ namespace beast {
*/
template<class Allocator>
class basic_streambuf<Allocator>::element
class basic_multi_buffer<Allocator>::element
: public boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
@@ -118,14 +118,14 @@ public:
};
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type
class basic_multi_buffer<Allocator>::const_buffers_type
{
basic_streambuf const* sb_;
basic_multi_buffer const* b_;
friend class basic_streambuf;
friend class basic_multi_buffer;
explicit
const_buffers_type(basic_streambuf const& sb);
const_buffers_type(basic_multi_buffer const& b);
public:
// Why?
@@ -142,17 +142,24 @@ public:
const_iterator
end() const;
friend
std::size_t
buffer_size(const_buffers_type const& buffers)
{
return buffers.b_->size();
}
};
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type
class basic_multi_buffer<Allocator>::mutable_buffers_type
{
basic_streambuf const* sb_;
basic_multi_buffer const* b_;
friend class basic_streambuf;
friend class basic_multi_buffer;
explicit
mutable_buffers_type(basic_streambuf const& sb);
mutable_buffers_type(basic_multi_buffer const& b);
public:
using value_type = mutable_buffer;
@@ -173,9 +180,9 @@ public:
//------------------------------------------------------------------------------
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type::const_iterator
class basic_multi_buffer<Allocator>::const_buffers_type::const_iterator
{
basic_streambuf const* sb_ = nullptr;
basic_multi_buffer const* b_ = nullptr;
typename list_type::const_iterator it_;
public:
@@ -193,9 +200,9 @@ public:
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(basic_streambuf const& sb,
const_iterator(basic_multi_buffer const& b,
typename list_type::const_iterator const& it)
: sb_(&sb)
: b_(&b)
, it_(it)
{
}
@@ -203,7 +210,7 @@ public:
bool
operator==(const_iterator const& other) const
{
return sb_ == other.sb_ && it_ == other.it_;
return b_ == other.b_ && it_ == other.it_;
}
bool
@@ -217,9 +224,9 @@ public:
{
auto const& e = *it_;
return value_type{e.data(),
(sb_->out_ == sb_->list_.end() ||
&e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
(&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0);
(b_->out_ == b_->list_.end() ||
&e != &*b_->out_) ? e.size() : b_->out_pos_} +
(&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
}
pointer
@@ -257,36 +264,42 @@ public:
};
template<class Allocator>
basic_streambuf<Allocator>::const_buffers_type::const_buffers_type(
basic_streambuf const& sb)
: sb_(&sb)
basic_multi_buffer<Allocator>::
const_buffers_type::
const_buffers_type(
basic_multi_buffer const& b)
: b_(&b)
{
}
template<class Allocator>
auto
basic_streambuf<Allocator>::const_buffers_type::begin() const ->
basic_multi_buffer<Allocator>::
const_buffers_type::
begin() const ->
const_iterator
{
return const_iterator{*sb_, sb_->list_.begin()};
return const_iterator{*b_, b_->list_.begin()};
}
template<class Allocator>
auto
basic_streambuf<Allocator>::const_buffers_type::end() const ->
basic_multi_buffer<Allocator>::
const_buffers_type::
end() const ->
const_iterator
{
return const_iterator{*sb_, sb_->out_ ==
sb_->list_.end() ? sb_->list_.end() :
std::next(sb_->out_)};
return const_iterator{*b_, b_->out_ ==
b_->list_.end() ? b_->list_.end() :
std::next(b_->out_)};
}
//------------------------------------------------------------------------------
template<class Allocator>
class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
{
basic_streambuf const* sb_ = nullptr;
basic_multi_buffer const* b_ = nullptr;
typename list_type::const_iterator it_;
public:
@@ -304,9 +317,9 @@ public:
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(basic_streambuf const& sb,
const_iterator(basic_multi_buffer const& b,
typename list_type::const_iterator const& it)
: sb_(&sb)
: b_(&b)
, it_(it)
{
}
@@ -314,7 +327,7 @@ public:
bool
operator==(const_iterator const& other) const
{
return sb_ == other.sb_ && it_ == other.it_;
return b_ == other.b_ && it_ == other.it_;
}
bool
@@ -328,9 +341,9 @@ public:
{
auto const& e = *it_;
return value_type{e.data(),
&e == &*std::prev(sb_->list_.end()) ?
sb_->out_end_ : e.size()} +
(&e == &*sb_->out_ ? sb_->out_pos_ : 0);
&e == &*std::prev(b_->list_.end()) ?
b_->out_end_ : e.size()} +
(&e == &*b_->out_ ? b_->out_pos_ : 0);
}
pointer
@@ -368,42 +381,84 @@ public:
};
template<class Allocator>
basic_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
basic_streambuf const& sb)
: sb_(&sb)
basic_multi_buffer<Allocator>::
mutable_buffers_type::
mutable_buffers_type(
basic_multi_buffer const& b)
: b_(&b)
{
}
template<class Allocator>
auto
basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
basic_multi_buffer<Allocator>::
mutable_buffers_type::
begin() const ->
const_iterator
{
return const_iterator{*sb_, sb_->out_};
return const_iterator{*b_, b_->out_};
}
template<class Allocator>
auto
basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
basic_multi_buffer<Allocator>::
mutable_buffers_type::
end() const ->
const_iterator
{
return const_iterator{*sb_, sb_->list_.end()};
return const_iterator{*b_, b_->list_.end()};
}
//------------------------------------------------------------------------------
template<class Allocator>
basic_streambuf<Allocator>::~basic_streambuf()
basic_multi_buffer<Allocator>::
~basic_multi_buffer()
{
delete_list();
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf&& other)
basic_multi_buffer<Allocator>::
basic_multi_buffer()
: out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit)
: max_(limit)
, out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(Allocator const& alloc)
: detail::empty_base_optimization<
allocator_type>(alloc)
, out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(std::size_t limit,
Allocator const& alloc)
: detail::empty_base_optimization<
allocator_type>(alloc)
, max_(limit)
, out_(list_.end())
{
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer&& other)
: detail::empty_base_optimization<allocator_type>(
std::move(other.member()))
, alloc_size_(other.alloc_size_)
, max_(other.max_)
, in_size_(other.in_size_)
, in_pos_(other.in_pos_)
, out_pos_(other.out_pos_)
@@ -421,116 +476,127 @@ basic_streambuf(basic_streambuf&& other)
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf&& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
using boost::asio::buffer_copy;
if(this->member() != other.member())
commit(buffer_copy(prepare(other.size()), other.data()));
else
move_assign(other, std::true_type{});
}
template<class Allocator>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf&& other) -> basic_streambuf&
{
if(this == &other)
return *this;
// VFALCO If any memory allocated we could use it first?
clear();
alloc_size_ = other.alloc_size_;
move_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_move_assignment::value>{});
return *this;
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf const& other)
: basic_streambuf(other.alloc_size_,
alloc_traits::select_on_container_copy_construction(other.member()))
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
basic_streambuf<Allocator>::
basic_streambuf(basic_streambuf const& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf const& other) ->
basic_streambuf&
{
if(this == &other)
return *this;
using boost::asio::buffer_copy;
clear();
copy_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_copy_assignment::value>{});
commit(buffer_copy(prepare(other.size()), other.data()));
return *this;
}
template<class Allocator>
template<class OtherAlloc>
basic_streambuf<Allocator>::basic_streambuf(
basic_streambuf<OtherAlloc> const& other)
: basic_streambuf(other.alloc_size_)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
basic_streambuf<Allocator>::basic_streambuf(
basic_streambuf<OtherAlloc> const& other,
allocator_type const& alloc)
: basic_streambuf(other.alloc_size_, alloc)
{
using boost::asio::buffer_copy;
commit(buffer_copy(prepare(other.size()), other.data()));
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_streambuf<Allocator>::operator=(
basic_streambuf<OtherAlloc> const& other) ->
basic_streambuf&
{
using boost::asio::buffer_copy;
clear();
commit(buffer_copy(prepare(other.size()), other.data()));
return *this;
}
template<class Allocator>
basic_streambuf<Allocator>::basic_streambuf(
std::size_t alloc_size, Allocator const& alloc)
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer&& other,
Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, out_(list_.end())
, alloc_size_(alloc_size)
, max_(other.max_)
{
if(alloc_size <= 0)
throw detail::make_exception<std::invalid_argument>(
"invalid alloc_size", __FILE__, __LINE__);
if(this->member() != other.member())
{
out_ = list_.end();
copy_from(other);
other.reset();
}
else
{
auto const at_end =
other.out_ == other.list_.end();
list_ = std::move(other.list_);
out_ = at_end ? list_.end() : other.out_;
in_size_ = other.in_size_;
in_pos_ = other.in_pos_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
other.in_size_ = 0;
other.out_ = other.list_.end();
other.in_pos_ = 0;
other.out_pos_ = 0;
other.out_end_ = 0;
}
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const& other)
: detail::empty_base_optimization<allocator_type>(
alloc_traits::select_on_container_copy_construction(other.member()))
, max_(other.max_)
, out_(list_.end())
{
copy_from(other);
}
template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(basic_multi_buffer const& other,
Allocator const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, max_(other.max_)
, out_(list_.end())
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other)
: out_(list_.end())
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc)
: detail::empty_base_optimization<allocator_type>(alloc)
, max_(other.max_)
, out_(list_.end())
{
copy_from(other);
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::
operator=(basic_multi_buffer&& other) ->
basic_multi_buffer&
{
if(this == &other)
return *this;
reset();
max_ = other.max_;
move_assign(other, typename
alloc_traits::propagate_on_container_move_assignment{});
return *this;
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::
operator=(basic_multi_buffer const& other) ->
basic_multi_buffer&
{
if(this == &other)
return *this;
copy_assign(other, typename
alloc_traits::propagate_on_container_copy_assignment{});
return *this;
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_multi_buffer<Allocator>::
operator=(
basic_multi_buffer<OtherAlloc> const& other) ->
basic_multi_buffer&
{
reset();
max_ = other.max_;
copy_from(other);
return *this;
}
template<class Allocator>
std::size_t
basic_streambuf<Allocator>::capacity() const
basic_multi_buffer<Allocator>::
capacity() const
{
auto pos = out_;
if(pos == list_.end())
@@ -543,7 +609,7 @@ basic_streambuf<Allocator>::capacity() const
template<class Allocator>
auto
basic_streambuf<Allocator>::
basic_multi_buffer<Allocator>::
data() const ->
const_buffers_type
{
@@ -552,18 +618,27 @@ data() const ->
template<class Allocator>
auto
basic_streambuf<Allocator>::prepare(size_type n) ->
basic_multi_buffer<Allocator>::
prepare(size_type n) ->
mutable_buffers_type
{
if(in_size_ + n > max_)
BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"});
list_type reuse;
std::size_t total = in_size_;
// put all empty buffers on reuse list
if(out_ != list_.end())
{
total += out_->size() - out_pos_;
if(out_ != list_.iterator_to(list_.back()))
{
out_end_ = out_->size();
reuse.splice(reuse.end(), list_,
std::next(out_), list_.end());
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
auto const avail = out_->size() - out_pos_;
if(n > avail)
@@ -576,13 +651,17 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
out_end_ = out_pos_ + n;
n = 0;
}
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
// get space from reuse buffers
while(n > 0 && ! reuse.empty())
{
auto& e = reuse.front();
reuse.erase(reuse.iterator_to(e));
list_.push_back(e);
total += e.size();
if(n > e.size())
{
out_end_ = e.size();
@@ -593,11 +672,28 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
out_end_ = n;
n = 0;
}
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
while(n > 0)
BOOST_ASSERT(total <= max_);
for(auto it = reuse.begin(); it != reuse.end();)
{
auto const size = std::max(alloc_size_, n);
auto& e = *it++;
reuse.erase(list_.iterator_to(e));
delete_element(e);
}
if(n > 0)
{
static auto const growth_factor = 2.0f;
auto const size =
std::min<std::size_t>(
max_ - total,
std::max<std::size_t>({
static_cast<std::size_t>(
in_size_ * growth_factor - in_size_),
512,
n}));
auto& e = *reinterpret_cast<element*>(static_cast<
void*>(alloc_traits::allocate(this->member(),
sizeof(element) + size)));
@@ -605,33 +701,18 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
list_.push_back(e);
if(out_ == list_.end())
out_ = list_.iterator_to(e);
if(n >= e.size())
{
out_end_ = e.size();
n -= e.size();
}
else
{
out_end_ = n;
n = 0;
}
out_end_ = n;
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
}
for(auto it = reuse.begin(); it != reuse.end();)
{
auto& e = *it++;
reuse.erase(list_.iterator_to(e));
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
#endif
}
return mutable_buffers_type(*this);
}
template<class Allocator>
void
basic_streambuf<Allocator>::commit(size_type n)
basic_multi_buffer<Allocator>::
commit(size_type n)
{
if(list_.empty())
return;
@@ -647,14 +728,18 @@ basic_streambuf<Allocator>::commit(size_type n)
{
out_pos_ += n;
in_size_ += n;
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
n = (std::min)(n, out_end_ - out_pos_);
@@ -666,26 +751,31 @@ basic_streambuf<Allocator>::commit(size_type n)
out_pos_ = 0;
out_end_ = 0;
}
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
template<class Allocator>
void
basic_streambuf<Allocator>::consume(size_type n)
basic_multi_buffer<Allocator>::
consume(size_type n)
{
if(list_.empty())
return;
for(;;)
{
if(list_.begin() != out_)
{
auto const avail = list_.front().size() - in_pos_;
auto const avail =
list_.front().size() - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
break;
}
n -= avail;
@@ -693,11 +783,10 @@ basic_streambuf<Allocator>::consume(size_type n)
in_pos_ = 0;
auto& e = list_.front();
list_.erase(list_.iterator_to(e));
auto const len = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
delete_element(e);
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
}
else
{
@@ -724,20 +813,45 @@ basic_streambuf<Allocator>::consume(size_type n)
out_end_ = 0;
}
}
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
break;
}
}
}
template<class Allocator>
inline
void
basic_streambuf<Allocator>::
clear()
basic_multi_buffer<Allocator>::
delete_element(element& e)
{
auto const len = sizeof(e) + e.size();
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), len);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
delete_list()
{
for(auto iter = list_.begin(); iter != list_.end();)
delete_element(*iter++);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
reset()
{
delete_list();
list_.clear();
out_ = list_.begin();
out_ = list_.end();
in_size_ = 0;
in_pos_ = 0;
out_pos_ = 0;
@@ -745,24 +859,41 @@ clear()
}
template<class Allocator>
template<class DynamicBuffer>
inline
void
basic_streambuf<Allocator>::
move_assign(basic_streambuf& other, std::false_type)
basic_multi_buffer<Allocator>::
copy_from(DynamicBuffer const& buffer)
{
if(buffer.size() == 0)
return;
using boost::asio::buffer_copy;
if(this->member() != other.member())
{
commit(buffer_copy(prepare(other.size()), other.data()));
other.clear();
}
else
move_assign(other, std::true_type{});
commit(buffer_copy(
prepare(buffer.size()), buffer.data()));
}
template<class Allocator>
inline
void
basic_streambuf<Allocator>::
move_assign(basic_streambuf& other, std::true_type)
basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer& other, std::false_type)
{
if(this->member() != other.member())
{
copy_from(other);
other.reset();
}
else
{
move_assign(other, std::true_type{});
}
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
move_assign(basic_multi_buffer& other, std::true_type)
{
this->member() = std::move(other.member());
auto const at_end =
@@ -783,38 +914,101 @@ move_assign(basic_streambuf& other, std::true_type)
}
template<class Allocator>
inline
void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::false_type)
basic_multi_buffer<Allocator>::
copy_assign(
basic_multi_buffer const& other, std::false_type)
{
beast::detail::ignore_unused(other);
reset();
max_ = other.max_;
copy_from(other);
}
template<class Allocator>
inline
void
basic_streambuf<Allocator>::
copy_assign(basic_streambuf const& other, std::true_type)
basic_multi_buffer<Allocator>::
copy_assign(
basic_multi_buffer const& other, std::true_type)
{
reset();
max_ = other.max_;
this->member() = other.member();
copy_from(other);
}
template<class Allocator>
inline
void
basic_streambuf<Allocator>::delete_list()
basic_multi_buffer<Allocator>::
swap(basic_multi_buffer& other)
{
for(auto iter = list_.begin(); iter != list_.end();)
{
auto& e = *iter++;
auto const n = e.size() + sizeof(e);
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(),
reinterpret_cast<char*>(&e), n);
}
swap(other, typename
alloc_traits::propagate_on_container_swap{});
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
swap(basic_multi_buffer& other, std::true_type)
{
using std::swap;
auto const at_end0 =
out_ == list_.end();
auto const at_end1 =
other.out_ == other.list_.end();
swap(this->member(), other.member());
swap(list_, other.list_);
swap(out_, other.out_);
if(at_end1)
out_ = list_.end();
if(at_end0)
other.out_ = other.list_.end();
swap(in_size_, other.in_size_);
swap(in_pos_, other.in_pos_);
swap(out_pos_, other.out_pos_);
swap(out_end_, other.out_end_);
}
template<class Allocator>
inline
void
basic_multi_buffer<Allocator>::
swap(basic_multi_buffer& other, std::false_type)
{
BOOST_ASSERT(this->member() == other.member());
using std::swap;
auto const at_end0 =
out_ == list_.end();
auto const at_end1 =
other.out_ == other.list_.end();
swap(list_, other.list_);
swap(out_, other.out_);
if(at_end1)
out_ = list_.end();
if(at_end0)
other.out_ = other.list_.end();
swap(in_size_, other.in_size_);
swap(in_pos_, other.in_pos_);
swap(out_pos_, other.out_pos_);
swap(out_end_, other.out_end_);
}
template<class Allocator>
void
basic_streambuf<Allocator>::debug_check() const
swap(
basic_multi_buffer<Allocator>& lhs,
basic_multi_buffer<Allocator>& rhs)
{
lhs.swap(rhs);
}
template<class Allocator>
void
basic_multi_buffer<Allocator>::
debug_check() const
{
#ifndef NDEBUG
using boost::asio::buffer_size;
@@ -852,33 +1046,6 @@ basic_streambuf<Allocator>::debug_check() const
#endif
}
template<class Allocator>
std::size_t
read_size_helper(basic_streambuf<
Allocator> const& streambuf, std::size_t max_size)
{
BOOST_ASSERT(max_size >= 1);
// If we already have an allocated
// buffer, try to fill that up first
auto const avail = streambuf.capacity() - streambuf.size();
if (avail > 0)
return (std::min)(avail, max_size);
// Try to have just one new block allocated
constexpr std::size_t low = 512;
if (streambuf.alloc_size_ > low)
return (std::min)(max_size, streambuf.alloc_size_);
// ...but enforce a 512 byte minimum.
return (std::min)(max_size, low);
}
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
{
detail::write_dynabuf(streambuf, t);
return streambuf;
}
} // beast
#endif

View File

@@ -0,0 +1,75 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_READ_SIZE_IPP
#define BEAST_IMPL_READ_SIZE_IPP
namespace beast {
namespace detail {
template<class T, class = void>
struct has_read_size_helper : std::false_type {};
template<class T>
struct has_read_size_helper<T, decltype(
read_size_helper(std::declval<T&>(), 512),
(void)0)> : std::true_type
{
};
template<class DynamicBuffer>
std::size_t
read_size(DynamicBuffer& buffer,
std::size_t max_size, std::true_type)
{
return read_size_helper(buffer, max_size);
}
template<class DynamicBuffer>
std::size_t
read_size(DynamicBuffer& buffer,
std::size_t max_size, std::false_type)
{
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(max_size >= 1);
auto const size = buffer.size();
auto const limit = buffer.max_size() - size;
BOOST_ASSERT(size <= buffer.max_size());
return std::min<std::size_t>(
std::max<std::size_t>(512, buffer.capacity() - size),
std::min<std::size_t>(max_size, limit));
}
} // detail
template<class DynamicBuffer>
inline
std::size_t
read_size(
DynamicBuffer& buffer, std::size_t max_size)
{
return detail::read_size(buffer, max_size,
detail::has_read_size_helper<DynamicBuffer>{});
}
template<class DynamicBuffer>
std::size_t
read_size_or_throw(
DynamicBuffer& buffer, std::size_t max_size)
{
auto const n = read_size(buffer, max_size);
if(n == 0)
BOOST_THROW_EXCEPTION(std::length_error{
"buffer overflow"});
return n;
}
} // beast
#endif

View File

@@ -0,0 +1,129 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_STATIC_BUFFER_IPP
#define BEAST_IMPL_STATIC_BUFFER_IPP
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace beast {
/* Memory is laid out thusly:
begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
*/
inline
auto
static_buffer::
data() const ->
const_buffers_type
{
return {in_, dist(in_, out_)};
}
inline
auto
static_buffer::
prepare(std::size_t n) ->
mutable_buffers_type
{
return prepare_impl(n);
}
inline
void
static_buffer::
reset(void* p, std::size_t n)
{
reset_impl(p, n);
}
template<class>
void
static_buffer::
reset_impl(void* p, std::size_t n)
{
begin_ =
reinterpret_cast<char*>(p);
in_ = begin_;
out_ = begin_;
last_ = begin_;
end_ = begin_ + n;
}
template<class>
auto
static_buffer::
prepare_impl(std::size_t n) ->
mutable_buffers_type
{
if(n <= dist(out_, end_))
{
last_ = out_ + n;
return {out_, n};
}
auto const len = size();
if(n > capacity() - len)
BOOST_THROW_EXCEPTION(std::length_error{
"buffer overflow"});
if(len > 0)
std::memmove(begin_, in_, len);
in_ = begin_;
out_ = in_ + len;
last_ = out_ + n;
return {out_, n};
}
template<class>
void
static_buffer::
consume_impl(std::size_t n)
{
if(n >= size())
{
in_ = begin_;
out_ = in_;
return;
}
in_ += n;
}
//------------------------------------------------------------------------------
template<std::size_t N>
static_buffer_n<N>::
static_buffer_n(static_buffer_n const& other)
: static_buffer(buf_, N)
{
using boost::asio::buffer_copy;
this->commit(buffer_copy(
this->prepare(other.size()), other.data()));
}
template<std::size_t N>
auto
static_buffer_n<N>::
operator=(static_buffer_n const& other) ->
static_buffer_n<N>&
{
using boost::asio::buffer_copy;
this->consume(this->size());
this->commit(buffer_copy(
this->prepare(other.size()), other.data()));
return *this;
}
} // beast
#endif

View File

@@ -1,307 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_STATIC_STREAMBUF_IPP
#define BEAST_IMPL_STATIC_STREAMBUF_IPP
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace beast {
class static_streambuf::const_buffers_type
{
std::size_t n_;
std::uint8_t const* p_;
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = delete;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
const_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
const_buffers_type(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::const_buffers_type::const_iterator
{
std::size_t n_ = 0;
std::uint8_t const* p_ = nullptr;
public:
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class const_buffers_type;
const_iterator(
std::uint8_t const* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::const_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::const_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
class static_streambuf::mutable_buffers_type
{
std::size_t n_;
std::uint8_t* p_;
public:
using value_type = boost::asio::mutable_buffer;
class const_iterator;
mutable_buffers_type() = delete;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class static_streambuf;
mutable_buffers_type(
std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
class static_streambuf::mutable_buffers_type::const_iterator
{
std::size_t n_ = 0;
std::uint8_t* p_ = nullptr;
public:
using value_type = boost::asio::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return p_ == other.p_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return value_type{p_, n_};
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
p_ += n_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
p_ -= n_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(std::uint8_t* p, std::size_t n)
: n_(n)
, p_(p)
{
}
};
inline
auto
static_streambuf::mutable_buffers_type::begin() const ->
const_iterator
{
return const_iterator{p_, n_};
}
inline
auto
static_streambuf::mutable_buffers_type::end() const ->
const_iterator
{
return const_iterator{p_ + n_, n_};
}
//------------------------------------------------------------------------------
inline
auto
static_streambuf::data() const ->
const_buffers_type
{
return const_buffers_type{in_,
static_cast<std::size_t>(out_ - in_)};
}
inline
auto
static_streambuf::prepare(std::size_t n) ->
mutable_buffers_type
{
if(n > static_cast<std::size_t>(end_ - out_))
throw detail::make_exception<std::length_error>(
"no space in streambuf", __FILE__, __LINE__);
last_ = out_ + n;
return mutable_buffers_type{out_, n};
}
} // beast
#endif

View File

@@ -0,0 +1,614 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_STATIC_STRING_IPP
#define BEAST_IMPL_STATIC_STRING_IPP
#include <beast/core/detail/static_string.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/throw_exception.hpp>
namespace beast {
//
// (constructor)
//
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string()
{
n_ = 0;
term();
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(size_type count, CharT ch)
{
assign(count, ch);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& other,
size_type pos)
{
assign(other, pos);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& other,
size_type pos, size_type count)
{
assign(other, pos, count);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(CharT const* s, size_type count)
{
assign(s, count);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(CharT const* s)
{
assign(s);
}
template<std::size_t N, class CharT, class Traits>
template<class InputIt>
static_string<N, CharT, Traits>::
static_string(InputIt first, InputIt last)
{
assign(first, last);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(static_string const& s)
{
assign(s);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& s)
{
assign(s);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(std::initializer_list<CharT> init)
{
assign(init.begin(), init.end());
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(string_view_type sv)
{
assign(sv);
}
template<std::size_t N, class CharT, class Traits>
template<class T, class>
static_string<N, CharT, Traits>::
static_string(T const& t, size_type pos, size_type n)
{
assign(t, pos, n);
}
//
// (assignment)
//
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign(size_type count, CharT ch) ->
static_string&
{
if(count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"count > max_size()"});
n_ = count;
Traits::assign(&s_[0], n_, ch);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign(static_string const& str) ->
static_string&
{
n_ = str.n_;
Traits::copy(&s_[0], &str.s_[0], n_ + 1);
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
assign(static_string<M, CharT, Traits> const& str,
size_type pos, size_type count) ->
static_string&
{
auto const ss = str.substr(pos, count);
return assign(ss.data(), ss.size());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign(CharT const* s, size_type count) ->
static_string&
{
if(count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"count > max_size()"});
n_ = count;
Traits::copy(&s_[0], s, n_);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<class InputIt>
auto
static_string<N, CharT, Traits>::
assign(InputIt first, InputIt last) ->
static_string&
{
std::size_t const n = std::distance(first, last);
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
n_ = n;
for(auto it = &s_[0]; first != last; ++it, ++first)
Traits::assign(*it, *first);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<class T>
auto
static_string<N, CharT, Traits>::
assign(T const& t, size_type pos, size_type count) ->
typename std::enable_if<std::is_convertible<T,
string_view_type>::value, static_string&>::type
{
auto const sv = string_view_type(t).substr(pos, count);
if(sv.size() > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"sv.size() > max_size()"});
n_ = sv.size();
Traits::copy(&s_[0], &sv[0], n_);
term();
return *this;
}
//
// Element access
//
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) ->
reference
{
if(pos >= size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos >= size()"});
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) const ->
const_reference
{
if(pos >= size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos >= size()"});
return s_[pos];
}
//
// Capacity
//
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
reserve(std::size_t n)
{
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
}
//
// Operations
//
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
clear()
{
n_ = 0;
term();
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
insert(size_type index, size_type count, CharT ch) ->
static_string&
{
if(index > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"index > size()"});
insert(begin() + index, count, ch);
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
insert(size_type index, CharT const* s, size_type count) ->
static_string&
{
if(index > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"index > size()"});
if(size() + count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() + count > max_size()"});
Traits::move(
&s_[index + count], &s_[index], size() - index);
n_ += count;
Traits::copy(&s_[index], s, count);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
insert(size_type index,
static_string<M, CharT, Traits> const& str,
size_type index_str, size_type count) ->
static_string&
{
auto const ss = str.substr(index_str, count);
return insert(index, ss.data(), ss.size());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
insert(const_iterator pos, size_type count, CharT ch) ->
iterator
{
if(size() + count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() + count() > max_size()"});
auto const index = pos - &s_[0];
Traits::move(
&s_[index + count], &s_[index], size() - index);
n_ += count;
Traits::assign(&s_[index], count, ch);
term();
return &s_[index];
}
template<std::size_t N, class CharT, class Traits>
template<class InputIt>
auto
static_string<N, CharT, Traits>::
insert(const_iterator pos, InputIt first, InputIt last) ->
typename std::enable_if<
detail::is_input_iterator<InputIt>::value,
iterator>::type
{
std::size_t const count = std::distance(first, last);
if(size() + count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() + count > max_size()"});
std::size_t const index = pos - begin();
Traits::move(
&s_[index + count], &s_[index], size() - index);
n_ += count;
for(auto it = begin() + index;
first != last; ++it, ++first)
Traits::assign(*it, *first);
term();
return begin() + index;
}
template<std::size_t N, class CharT, class Traits>
template<class T>
auto
static_string<N, CharT, Traits>::
insert(size_type index, const T& t,
size_type index_str, size_type count) ->
typename std::enable_if<std::is_convertible<
T const&, string_view_type>::value &&
! std::is_convertible<T const&, CharT const*>::value,
static_string&>::type
{
auto const str =
string_view_type(t).substr(index_str, count);
return insert(index, str.data(), str.size());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
erase(size_type index, size_type count) ->
static_string&
{
if(index > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"index > size()"});
auto const n = (std::min)(count, size() - index);
Traits::move(
&s_[index], &s_[index + n], size() - (index + n) + 1);
n_ -= n;
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
erase(const_iterator pos) ->
iterator
{
erase(pos - begin(), 1);
return begin() + (pos - begin());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
erase(const_iterator first, const_iterator last) ->
iterator
{
erase(first - begin(),
std::distance(first, last));
return begin() + (first - begin());
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
push_back(CharT ch)
{
if(size() >= max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() >= max_size()"});
Traits::assign(s_[n_++], ch);
term();
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
append(static_string<M, CharT, Traits> const& str,
size_type pos, size_type count) ->
static_string&
{
// Valid range is [0, size)
if(pos >= str.size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos > str.size()"});
string_view_type const ss{&str.s_[pos],
(std::min)(count, str.size() - pos)};
insert(size(), ss.data(), ss.size());
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
substr(size_type pos, size_type count) const ->
string_view_type
{
if(pos > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos > size()"});
return{&s_[pos], (std::min)(count, size() - pos)};
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
copy(CharT* dest, size_type count, size_type pos) const ->
size_type
{
auto const str = substr(pos, count);
Traits::copy(dest, str.data(), str.size());
return str.size();
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n)
{
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
n_ = n;
term();
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n, CharT c)
{
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
if(n > n_)
Traits::assign(&s_[n_], n - n_, c);
n_ = n;
term();
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
swap(static_string& str)
{
static_string tmp(str);
str.n_ = n_;
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
n_ = tmp.n_;
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
void
static_string<N, CharT, Traits>::
swap(static_string<M, CharT, Traits>& str)
{
if(size() > str.max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() > str.max_size()"});
if(str.size() > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"str.size() > max_size()"});
static_string tmp(str);
str.n_ = n_;
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
n_ = tmp.n_;
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign_char(CharT ch, std::true_type) ->
static_string&
{
n_ = 1;
Traits::assign(s_[0], ch);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign_char(CharT, std::false_type) ->
static_string&
{
BOOST_THROW_EXCEPTION(std::length_error{
"max_size() == 0"});
}
namespace detail {
template<class Integer>
static_string<max_digits(sizeof(Integer))>
to_static_string(Integer x, std::true_type)
{
if(x == 0)
return {'0'};
static_string<detail::max_digits(
sizeof(Integer))> s;
if(x < 0)
{
x = -x;
char buf[max_digits(sizeof(x))];
char* p = buf;
for(;x > 0; x /= 10)
*p++ = "0123456789"[x % 10];
s.resize(1 + p - buf);
s[0] = '-';
auto d = &s[1];
while(p > buf)
*d++ = *--p;
}
else
{
char buf[max_digits(sizeof(x))];
char* p = buf;
for(;x > 0; x /= 10)
*p++ = "0123456789"[x % 10];
s.resize(p - buf);
auto d = &s[0];
while(p > buf)
*d++ = *--p;
}
return s;
}
template<class Integer>
static_string<max_digits(sizeof(Integer))>
to_static_string(Integer x, std::false_type)
{
if(x == 0)
return {'0'};
char buf[max_digits(sizeof(x))];
char* p = buf;
for(;x > 0; x /= 10)
*p++ = "0123456789"[x % 10];
static_string<detail::max_digits(
sizeof(Integer))> s;
s.resize(p - buf);
auto d = &s[0];
while(p > buf)
*d++ = *--p;
return s;
}
} // detail
template<class Integer>
static_string<detail::max_digits(sizeof(Integer))>
to_static_string(Integer x)
{
using CharT = char;
using Traits = std::char_traits<CharT>;
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
char buf[detail::max_digits(sizeof(Integer))];
auto last = buf + sizeof(buf);
auto it = detail::raw_to_string<
CharT, Integer, Traits>(last, sizeof(buf), x);
static_string<detail::max_digits(sizeof(Integer))> s;
s.resize(static_cast<std::size_t>(last - it));
auto p = s.data();
while(it < last)
Traits::assign(*p++, *it++);
return s;
}
} // beast
#endif

View File

@@ -0,0 +1,104 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_IMPL_STRING_PARAM_IPP
#define BEAST_IMPL_STRING_PARAM_IPP
namespace beast {
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
string_param::
print(T const& t)
{
auto const last = buf_ + sizeof(buf_);
auto const it = detail::raw_to_string<
char, T, std::char_traits<char>>(
last, sizeof(buf_), t);
sv_ = {it, static_cast<std::size_t>(
last - it)};
}
template<class T>
typename std::enable_if<
! std::is_integral<T>::value &&
! std::is_convertible<T, string_view>::value
>::type
string_param::
print(T const& t)
{
os_.emplace(buf_, sizeof(buf_));
*os_ << t;
os_->flush();
sv_ = os_->str();
}
inline
void
string_param::
print(string_view const& sv)
{
sv_ = sv;
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
string_param::
print_1(T const& t)
{
char buf[detail::max_digits(sizeof(T))];
auto const last = buf + sizeof(buf);
auto const it = detail::raw_to_string<
char, T, std::char_traits<char>>(
last, sizeof(buf), t);
*os_ << string_view{it,
static_cast<std::size_t>(last - it)};
}
template<class T>
typename std::enable_if<
! std::is_integral<T>::value>::type
string_param::
print_1(T const& t)
{
*os_ << t;
}
template<class T0, class... TN>
void
string_param::
print_n(T0 const& t0, TN const&... tn)
{
print_1(t0);
print_n(tn...);
}
template<class T0, class T1, class... TN>
void
string_param::
print(T0 const& t0, T1 const& t1, TN const&... tn)
{
os_.emplace(buf_, sizeof(buf_));
print_1(t0);
print_1(t1);
print_n(tn...);
os_->flush();
sv_ = os_->str();
}
template<class... Args>
string_param::
string_param(Args const&... args)
{
print(args...);
}
} // beast
#endif

View File

@@ -0,0 +1,323 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_MULTI_BUFFER_HPP
#define BEAST_MULTI_BUFFER_HPP
#include <beast/config.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
namespace beast {
/** A @b DynamicBuffer that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b DynamicBuffer.
@tparam Allocator The allocator to use for managing memory.
*/
template<class Allocator>
class basic_multi_buffer
#if ! BEAST_DOXYGEN
: private detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif
{
public:
#if BEAST_DOXYGEN
/// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<char>;
#endif
private:
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes.
class element;
using alloc_traits = std::allocator_traits<allocator_type>;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
using size_type = typename std::allocator_traits<Allocator>::size_type;
using const_buffer = boost::asio::const_buffer;
using mutable_buffer = boost::asio::mutable_buffer;
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
std::size_t max_ =
(std::numeric_limits<std::size_t>::max)();
list_type list_; // list of allocated buffers
iterator out_; // element that contains out_pos_
size_type in_size_ = 0; // size of the input sequence
size_type in_pos_ = 0; // input offset in list_.front()
size_type out_pos_ = 0; // output offset in *out_
size_type out_end_ = 0; // output end offset in list_.back()
public:
#if BEAST_DOXYGEN
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Destructor
~basic_multi_buffer();
/** Constructor
Upon construction, capacity will be zero.
*/
basic_multi_buffer();
/** Constructor.
@param limit The setting for @ref max_size.
*/
explicit
basic_multi_buffer(std::size_t limit);
/** Constructor.
@param alloc The allocator to use.
*/
basic_multi_buffer(Allocator const& alloc);
/** Constructor.
@param limit The setting for @ref max_size.
@param alloc The allocator to use.
*/
basic_multi_buffer(
std::size_t limit, Allocator const& alloc);
/** Move constructor
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
*/
basic_multi_buffer(basic_multi_buffer&& other);
/** Move constructor
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
@param alloc The allocator to use.
*/
basic_multi_buffer(basic_multi_buffer&& other,
Allocator const& alloc);
/** Copy constructor.
@param other The object to copy from.
*/
basic_multi_buffer(basic_multi_buffer const& other);
/** Copy constructor
@param other The object to copy from.
@param alloc The allocator to use.
*/
basic_multi_buffer(basic_multi_buffer const& other,
Allocator const& alloc);
/** Copy constructor.
@param other The object to copy from.
*/
template<class OtherAlloc>
basic_multi_buffer(basic_multi_buffer<
OtherAlloc> const& other);
/** Copy constructor.
@param other The object to copy from.
@param alloc The allocator to use.
*/
template<class OtherAlloc>
basic_multi_buffer(basic_multi_buffer<
OtherAlloc> const& other, allocator_type const& alloc);
/** Move assignment
After the move, `*this` will have an empty output sequence.
@param other The object to move from. After the move,
The object's state will be as if constructed using
its current allocator and limit.
*/
basic_multi_buffer&
operator=(basic_multi_buffer&& other);
/** Copy assignment
After the copy, `*this` will have an empty output sequence.
@param other The object to copy from.
*/
basic_multi_buffer& operator=(basic_multi_buffer const& other);
/** Copy assignment
After the copy, `*this` will have an empty output sequence.
@param other The object to copy from.
*/
template<class OtherAlloc>
basic_multi_buffer& operator=(
basic_multi_buffer<OtherAlloc> const& other);
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
return this->member();
}
/// Returns the size of the input sequence.
size_type
size() const
{
return in_size_;
}
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
size_type
max_size() const
{
return max_;
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const;
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represents the output sequence, with the given size.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(size_type n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(size_type n);
/// Remove bytes from the input sequence.
void
consume(size_type n);
template<class Alloc>
friend
void
swap(
basic_multi_buffer<Alloc>& lhs,
basic_multi_buffer<Alloc>& rhs);
private:
template<class OtherAlloc>
friend class basic_multi_buffer;
void
delete_element(element& e);
void
delete_list();
void
reset();
template<class DynamicBuffer>
void
copy_from(DynamicBuffer const& other);
void
move_assign(basic_multi_buffer& other, std::false_type);
void
move_assign(basic_multi_buffer& other, std::true_type);
void
copy_assign(basic_multi_buffer const& other, std::false_type);
void
copy_assign(basic_multi_buffer const& other, std::true_type);
void
swap(basic_multi_buffer&);
void
swap(basic_multi_buffer&, std::true_type);
void
swap(basic_multi_buffer&, std::false_type);
void
debug_check() const;
};
/// A typical multi buffer
using multi_buffer = basic_multi_buffer<std::allocator<char>>;
} // beast
#include <beast/core/impl/multi_buffer.ipp>
#endif

View File

@@ -0,0 +1,99 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_WRITE_OSTREAM_HPP
#define BEAST_WRITE_OSTREAM_HPP
#include <beast/config.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/core/detail/ostream.hpp>
#include <type_traits>
#include <streambuf>
#include <utility>
namespace beast {
/** Return an object representing a @b ConstBufferSequence.
This function wraps a reference to a buffer sequence and permits
the following operation:
@li `operator<<` to `std::ostream`. No character translation is
performed; unprintable and null characters will be transferred
as-is to the output stream.
@par Example
@code
multi_buffer b;
...
std::cout << buffers(b.data()) << std::endl;
@endcode
@param b An object meeting the requirements of @b ConstBufferSequence
to be streamed. The implementation will make a copy of this object.
Ownership of the underlying memory is not transferred, the application
is still responsible for managing its lifetime.
*/
template<class ConstBufferSequence>
#if BEAST_DOXYGEN
implementation_defined
#else
detail::buffers_helper<ConstBufferSequence>
#endif
buffers(ConstBufferSequence const& b)
{
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
return detail::buffers_helper<
ConstBufferSequence>{b};
}
/** Return an output stream that formats values into a @b DynamicBuffer.
This function wraps the caller provided @b DynamicBuffer into
a `std::ostream` derived class, to allow `operator<<` stream style
formatting operations.
@par Example
@code
ostream(buffer) << "Hello, world!" << std::endl;
@endcode
@note Calling members of the underlying buffer before the output
stream is destroyed results in undefined behavior.
@param buffer An object meeting the requirements of @b DynamicBuffer
into which the formatted output will be placed.
@return An object derived from `std::ostream` which redirects output
The wrapped dynamic buffer is not modified, a copy is made instead.
Ownership of the underlying memory is not transferred, the application
is still responsible for managing its lifetime. The caller is
responsible for ensuring the dynamic buffer is not destroyed for the
lifetime of the output stream.
*/
template<class DynamicBuffer>
#if BEAST_DOXYGEN
implementation_defined
#else
detail::ostream_helper<
DynamicBuffer, char, std::char_traits<char>,
detail::basic_streambuf_movable::value>
#endif
ostream(DynamicBuffer& buffer)
{
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
return detail::ostream_helper<
DynamicBuffer, char, std::char_traits<char>,
detail::basic_streambuf_movable::value>{buffer};
}
} // beast
#endif

View File

@@ -1,30 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_PLACEHOLDERS_HPP
#define BEAST_PLACEHOLDERS_HPP
#include <beast/config.hpp>
#include <functional>
namespace beast {
namespace asio {
namespace placeholders {
// asio placeholders that work with std::bind
namespace {
static auto const error (std::placeholders::_1);
static auto const bytes_transferred (std::placeholders::_2);
static auto const iterator (std::placeholders::_2);
static auto const signal_number (std::placeholders::_2);
}
} // placeholders
} // asio
} // beast
#endif

View File

@@ -1,69 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_PREPARE_BUFFER_HPP
#define BEAST_PREPARE_BUFFER_HPP
#include <beast/config.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
namespace beast {
/** Return a shortened buffer.
The returned buffer points to the same memory as the
passed buffer, but with a size that is equal to or less
than the size of the original buffer.
@param n The size of the returned buffer.
@param buffer The buffer to shorten. Ownership of the
underlying memory is not transferred.
@return A new buffer that points to the first `n` bytes
of the original buffer.
*/
inline
boost::asio::const_buffer
prepare_buffer(std::size_t n,
boost::asio::const_buffer buffer)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return { buffer_cast<void const*>(buffer),
(std::min)(n, buffer_size(buffer)) };
}
/** Return a shortened buffer.
The returned buffer points to the same memory as the
passed buffer, but with a size that is equal to or less
than the size of the original buffer.
@param n The size of the returned buffer.
@param buffer The buffer to shorten. Ownership of the
underlying memory is not transferred.
@return A new buffer that points to the first `n` bytes
of the original buffer.
*/
inline
boost::asio::mutable_buffer
prepare_buffer(std::size_t n,
boost::asio::mutable_buffer buffer)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
return { buffer_cast<void*>(buffer),
(std::min)(n, buffer_size(buffer)) };
}
} // beast
#endif

View File

@@ -1,53 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_PREPARE_BUFFERS_HPP
#define BEAST_PREPARE_BUFFERS_HPP
#include <beast/config.hpp>
#include <beast/core/detail/prepare_buffers.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace beast {
/** Return a shortened buffer sequence.
This function returns a new buffer sequence which adapts the
passed buffer sequence and efficiently presents a shorter subset
of the original list of buffers starting with the first byte of
the original sequence.
@param n The maximum number of bytes in the wrapped
sequence. If this is larger than the size of passed,
buffers, the resulting sequence will represent the
entire input sequence.
@param buffers The buffer sequence to adapt. A copy of
the sequence will be made, but ownership of the underlying
memory is not transferred.
*/
template<class BufferSequence>
#if GENERATING_DOCS
implementation_defined
#else
inline
detail::prepared_buffers<BufferSequence>
#endif
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return detail::prepared_buffers<BufferSequence>(n, buffers);
}
} // beast
#endif

View File

@@ -0,0 +1,60 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_READ_SIZE_HELPER_HPP
#define BEAST_READ_SIZE_HELPER_HPP
#include <beast/config.hpp>
#include <beast/core/type_traits.hpp>
#include <boost/throw_exception.hpp>
namespace beast {
/** Returns a natural read size.
This function inspects the capacity, size, and maximum
size of the dynamic buffer. Then it computes a natural
read size given the passed-in upper limit. It favors
a read size that does not require a reallocation, subject
to a reasonable minimum to avoid tiny reads.
@param buffer The dynamic buffer to inspect.
@param max_size An upper limit on the returned value.
@note If the buffer is already at its maximum size, zero
is returned.
*/
template<class DynamicBuffer>
std::size_t
read_size(DynamicBuffer& buffer, std::size_t max_size);
/** Returns a natural read size or throw if the buffer is full.
This function inspects the capacity, size, and maximum
size of the dynamic buffer. Then it computes a natural
read size given the passed-in upper limit. It favors
a read size that does not require a reallocation, subject
to a reasonable minimum to avoid tiny reads.
@param buffer The dynamic buffer to inspect.
@param max_size An upper limit on the returned value.
@throws std::length_error if `max_size > 0` and the buffer
is full.
*/
template<class DynamicBuffer>
std::size_t
read_size_or_throw(DynamicBuffer& buffer,
std::size_t max_size);
} // beast
#include <beast/core/impl/read_size.ipp>
#endif

211
include/beast/core/span.hpp Normal file
View File

@@ -0,0 +1,211 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_CORE_SPAN_HPP
#define BEAST_CORE_SPAN_HPP
#include <beast/config.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <algorithm>
#include <iterator>
#include <string>
#include <type_traits>
namespace beast {
/** A range of bytes expressed as a ContiguousContainer
This class implements a non-owning reference to a storage
area of a certain size and having an underlying integral
type with size of 1.
@tparam T The type pointed to by span iterators
*/
template<class T>
class span
{
T* data_ = nullptr;
std::size_t size_ = 0;
public:
/// The type of value, including cv qualifiers
using element_type = T;
/// The type of value of each span element
using value_type = typename std::remove_const<T>::type;
/// The type of integer used to index the span
using index_type = std::ptrdiff_t;
/// A pointer to a span element
using pointer = T*;
/// A reference to a span element
using reference = T&;
/// The iterator used by the container
using iterator = pointer;
/// The const pointer used by the container
using const_pointer = T const*;
/// The const reference used by the container
using const_reference = T const&;
/// The const iterator used by the container
using const_iterator = const_pointer;
/// Constructor
span() = default;
/// Constructor
span(span const&) = default;
/// Assignment
span& operator=(span const&) = default;
/** Constructor
@param data A pointer to the beginning of the range of elements
@param size The number of elements pointed to by `data`
*/
span(T* data, std::size_t size)
: data_(data), size_(size)
{
}
/** Constructor
@param container The container to construct from
*/
template<class ContiguousContainer
#if ! BEAST_DOXYGEN
, class = typename std::enable_if<
detail::is_contiguous_container<
ContiguousContainer, T>::value>::type
#endif
>
explicit
span(ContiguousContainer&& container)
: data_(container.data())
, size_(container.size())
{
}
#if ! BEAST_DOXYGEN
template<class CharT, class Traits, class Allocator>
explicit
span(std::basic_string<CharT, Traits, Allocator>& s)
: data_(&s[0])
, size_(s.size())
{
}
template<class CharT, class Traits, class Allocator>
explicit
span(std::basic_string<CharT, Traits, Allocator> const& s)
: data_(s.data())
, size_(s.size())
{
}
#endif
/** Assignment
@param container The container to assign from
*/
template<class ContiguousContainer>
#if BEAST_DOXYGEN
span&
#else
typename std::enable_if<detail::is_contiguous_container<
ContiguousContainer, T>::value,
span&>::type
#endif
operator=(ContiguousContainer&& container)
{
data_ = container.data();
size_ = container.size();
return *this;
}
#if ! BEAST_DOXYGEN
template<class CharT, class Traits, class Allocator>
span&
operator=(std::basic_string<
CharT, Traits, Allocator>& s)
{
data_ = &s[0];
size_ = s.size();
return *this;
}
template<class CharT, class Traits, class Allocator>
span&
operator=(std::basic_string<
CharT, Traits, Allocator> const& s)
{
data_ = s.data();
size_ = s.size();
return *this;
}
#endif
/// Returns `true` if the span is empty
bool
empty() const
{
return size_ == 0;
}
/// Returns a pointer to the beginning of the span
T*
data() const
{
return data_;
}
/// Returns the number of elements in the span
std::size_t
size() const
{
return size_;
}
/// Returns an iterator to the beginning of the span
const_iterator
begin() const
{
return data_;
}
/// Returns an iterator to the beginning of the span
const_iterator
cbegin() const
{
return data_;
}
/// Returns an iterator to one past the end of the span
const_iterator
end() const
{
return data_ + size_;
}
/// Returns an iterator to one past the end of the span
const_iterator
cend() const
{
return data_ + size_;
}
};
} // beast
#endif

View File

@@ -0,0 +1,218 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STATIC_BUFFER_HPP
#define BEAST_STATIC_BUFFER_HPP
#include <beast/config.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstddef>
#include <cstring>
namespace beast {
/** A @b DynamicBuffer with a fixed size internal buffer.
Ownership of the underlying storage belongs to the derived class.
@note Variables are usually declared using the template class
@ref static_buffer_n; however, to reduce the number of instantiations
of template functions receiving static stream buffer arguments in a
deduced context, the signature of the receiving function should use
@ref static_buffer.
When used with @ref static_buffer_n this implements a dynamic
buffer using no memory allocations.
@see @ref static_buffer_n
*/
class static_buffer
{
char* begin_;
char* in_;
char* out_;
char* last_;
char* end_;
static_buffer(static_buffer const& other) = delete;
static_buffer& operator=(static_buffer const&) = delete;
public:
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = boost::asio::const_buffers_1;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = boost::asio::mutable_buffers_1;
/** Constructor.
This creates a dynamic buffer using the provided storage area.
@param p A pointer to valid storage of at least `n` bytes.
@param n The number of valid bytes pointed to by `p`.
*/
static_buffer(void* p, std::size_t n)
{
reset_impl(p, n);
}
/// Return the size of the input sequence.
std::size_t
size() const
{
return out_ - in_;
}
/// Return the maximum sum of the input and output sequence sizes.
std::size_t
max_size() const
{
return dist(begin_, end_);
}
/// Return the maximum sum of input and output sizes that can be held without an allocation.
std::size_t
capacity() const
{
return max_size();
}
/** Get a list of buffers that represent the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represent the output sequence, with the given size.
@throws std::length_error if the size would exceed the limit
imposed by the underlying mutable buffer sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(std::size_t n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(std::size_t n)
{
out_ += std::min<std::size_t>(n, last_ - out_);
}
/// Remove bytes from the input sequence.
void
consume(std::size_t n)
{
consume_impl(n);
}
protected:
/** Default constructor.
The buffer will be in an undefined state. It is necessary
for the derived class to call @ref reset in order to
initialize the object.
*/
static_buffer();
/** Reset the pointed-to buffer.
This function resets the internal state to the buffer provided.
All input and output sequences are invalidated. This function
allows the derived class to construct its members before
initializing the static buffer.
@param p A pointer to valid storage of at least `n` bytes.
@param n The number of valid bytes pointed to by `p`.
*/
void
reset(void* p, std::size_t n);
private:
static
inline
std::size_t
dist(char const* first, char const* last)
{
return static_cast<std::size_t>(last - first);
}
template<class = void>
void
reset_impl(void* p, std::size_t n);
template<class = void>
mutable_buffers_type
prepare_impl(std::size_t n);
template<class = void>
void
consume_impl(std::size_t n);
};
//------------------------------------------------------------------------------
/** A @b DynamicBuffer with a fixed size internal buffer.
This implements a dynamic buffer using no memory allocations.
@tparam N The number of bytes in the internal buffer.
@note To reduce the number of template instantiations when passing
objects of this type in a deduced context, the signature of the
receiving function should use @ref static_buffer instead.
@see @ref static_buffer
*/
template<std::size_t N>
class static_buffer_n : public static_buffer
{
char buf_[N];
public:
/// Copy constructor
static_buffer_n(static_buffer_n const&);
/// Copy assignment
static_buffer_n& operator=(static_buffer_n const&);
/// Construct a static buffer.
static_buffer_n()
: static_buffer(buf_, N)
{
}
/// Returns the @ref static_buffer portion of this object
static_buffer&
base()
{
return *this;
}
/// Returns the @ref static_buffer portion of this object
static_buffer const&
base() const
{
return *this;
}
};
} // beast
#include <beast/core/impl/static_buffer.ipp>
#endif

View File

@@ -1,199 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STATIC_STREAMBUF_HPP
#define BEAST_STATIC_STREAMBUF_HPP
#include <beast/config.hpp>
#include <boost/utility/base_from_member.hpp>
#include <algorithm>
#include <array>
#include <cstring>
namespace beast {
/** A @b `DynamicBuffer` with a fixed size internal buffer.
Ownership of the underlying storage belongs to the derived class.
@note Variables are usually declared using the template class
@ref static_streambuf_n; however, to reduce the number of instantiations
of template functions receiving static stream buffer arguments in a
deduced context, the signature of the receiving function should use
@ref static_streambuf.
*/
class static_streambuf
{
#if GENERATING_DOCS
private:
#else
protected:
#endif
std::uint8_t* begin_;
std::uint8_t* in_;
std::uint8_t* out_;
std::uint8_t* last_;
std::uint8_t* end_;
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
static_streambuf(
static_streambuf const& other) noexcept = delete;
static_streambuf& operator=(
static_streambuf const&) noexcept = delete;
#endif
/// Return the size of the input sequence.
std::size_t
size() const
{
return out_ - in_;
}
/// Return the maximum sum of the input and output sequence sizes.
std::size_t
max_size() const
{
return end_ - begin_;
}
/// Return the maximum sum of input and output sizes that can be held without an allocation.
std::size_t
capacity() const
{
return end_ - in_;
}
/** Get a list of buffers that represent the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represent the output sequence, with the given size.
@throws std::length_error if the size would exceed the limit
imposed by the underlying mutable buffer sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(std::size_t n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(std::size_t n)
{
out_ += std::min<std::size_t>(n, last_ - out_);
}
/// Remove bytes from the input sequence.
void
consume(std::size_t n)
{
in_ += std::min<std::size_t>(n, out_ - in_);
}
#if GENERATING_DOCS
private:
#else
protected:
#endif
static_streambuf(std::uint8_t* p, std::size_t n)
{
reset(p, n);
}
void
reset(std::uint8_t* p, std::size_t n)
{
begin_ = p;
in_ = p;
out_ = p;
last_ = p;
end_ = p + n;
}
};
//------------------------------------------------------------------------------
/** A `DynamicBuffer` with a fixed size internal buffer.
@tparam N The number of bytes in the internal buffer.
@note To reduce the number of template instantiations when passing
objects of this type in a deduced context, the signature of the
receiving function should use `static_streambuf` instead.
*/
template<std::size_t N>
class static_streambuf_n
: public static_streambuf
#if ! GENERATING_DOCS
, private boost::base_from_member<
std::array<std::uint8_t, N>>
#endif
{
using member_type = boost::base_from_member<
std::array<std::uint8_t, N>>;
public:
#if GENERATING_DOCS
private:
#endif
static_streambuf_n(
static_streambuf_n const&) = delete;
static_streambuf_n& operator=(
static_streambuf_n const&) = delete;
#if GENERATING_DOCS
public:
#endif
/// Construct a static stream buffer.
static_streambuf_n()
: static_streambuf(
member_type::member.data(),
member_type::member.size())
{
}
/** Reset the stream buffer.
Postconditions:
The input sequence and output sequence are empty,
`max_size()` returns `N`.
*/
void
reset()
{
static_streambuf::reset(
member_type::member.data(),
member_type::member.size());
}
};
} // beast
#include <beast/core/impl/static_streambuf.ipp>
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,77 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STREAM_CONCEPTS_HPP
#define BEAST_STREAM_CONCEPTS_HPP
#include <beast/config.hpp>
#include <beast/core/detail/stream_concepts.hpp>
#include <type_traits>
namespace beast {
/// Determine if `T` has the `get_io_service` member.
template<class T>
#if GENERATING_DOCS
struct has_get_io_service : std::integral_constant<bool, ...>{};
#else
using has_get_io_service = typename detail::has_get_io_service<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `AsyncReadStream`.
template<class T>
#if GENERATING_DOCS
struct is_AsyncReadStream : std::integral_constant<bool, ...>{};
#else
using is_AsyncReadStream = typename detail::is_AsyncReadStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `AsyncWriteStream`.
template<class T>
#if GENERATING_DOCS
struct is_AsyncWriteStream : std::integral_constant<bool, ...>{};
#else
using is_AsyncWriteStream = typename detail::is_AsyncWriteStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `SyncReadStream`.
template<class T>
#if GENERATING_DOCS
struct is_SyncReadStream : std::integral_constant<bool, ...>{};
#else
using is_SyncReadStream = typename detail::is_SyncReadStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `SyncWriterStream`.
template<class T>
#if GENERATING_DOCS
struct is_SyncWriteStream : std::integral_constant<bool, ...>{};
#else
using is_SyncWriteStream = typename detail::is_SyncWriteStream<T>::type;
#endif
/// Determine if `T` meets the requirements of @b `AsyncStream`.
template<class T>
#if GENERATING_DOCS
struct is_AsyncStream : std::integral_constant<bool, ...>{};
#else
using is_AsyncStream = std::integral_constant<bool,
is_AsyncReadStream<T>::value && is_AsyncWriteStream<T>::value>;
#endif
/// Determine if `T` meets the requirements of @b `SyncStream`.
template<class T>
#if GENERATING_DOCS
struct is_SyncStream : std::integral_constant<bool, ...>{};
#else
using is_SyncStream = std::integral_constant<bool,
is_SyncReadStream<T>::value && is_SyncWriteStream<T>::value>;
#endif
} // beast
#endif

View File

@@ -1,345 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STREAMBUF_HPP
#define BEAST_STREAMBUF_HPP
#include <beast/config.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
namespace beast {
/** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b DynamicBuffer.
@tparam Allocator The allocator to use for managing memory.
*/
template<class Allocator>
class basic_streambuf
#if ! GENERATING_DOCS
: private detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif
{
public:
#if GENERATING_DOCS
/// The type of allocator used.
using allocator_type = Allocator;
#else
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<char>;
#endif
private:
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes.
class element;
using alloc_traits = std::allocator_traits<allocator_type>;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
using size_type = typename std::allocator_traits<Allocator>::size_type;
using const_buffer = boost::asio::const_buffer;
using mutable_buffer = boost::asio::mutable_buffer;
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"BidirectionalIterator requirements not met");
list_type list_; // list of allocated buffers
iterator out_; // element that contains out_pos_
size_type alloc_size_; // min amount to allocate
size_type in_size_ = 0; // size of the input sequence
size_type in_pos_ = 0; // input offset in list_.front()
size_type out_pos_ = 0; // output offset in *out_
size_type out_end_ = 0; // output end offset in list_.back()
public:
#if GENERATING_DOCS
/// The type used to represent the input sequence as a list of buffers.
using const_buffers_type = implementation_defined;
/// The type used to represent the output sequence as a list of buffers.
using mutable_buffers_type = implementation_defined;
#else
class const_buffers_type;
class mutable_buffers_type;
#endif
/// Destructor.
~basic_streambuf();
/** Move constructor.
The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
*/
basic_streambuf(basic_streambuf&&);
/** Move constructor.
The new object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
@param alloc The allocator to associate with the
stream buffer.
*/
basic_streambuf(basic_streambuf&&,
allocator_type const& alloc);
/** Move assignment.
This object will have the input sequence of
the other stream buffer, and an empty output sequence.
@note After the move, the moved-from object will have
an empty input and output sequence, with no internal
buffers allocated.
*/
basic_streambuf&
operator=(basic_streambuf&&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf(basic_streambuf const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the
stream buffer.
*/
basic_streambuf(basic_streambuf const&,
allocator_type const& alloc);
/** Copy assignment.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
basic_streambuf& operator=(basic_streambuf const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
template<class OtherAlloc>
basic_streambuf(basic_streambuf<OtherAlloc> const&);
/** Copy constructor.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
@param alloc The allocator to associate with the
stream buffer.
*/
template<class OtherAlloc>
basic_streambuf(basic_streambuf<OtherAlloc> const&,
allocator_type const& alloc);
/** Copy assignment.
This object will have a copy of the other stream
buffer's input sequence, and an empty output sequence.
*/
template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const&);
/** Construct a stream buffer.
@param alloc_size The size of buffer to allocate. This is a
soft limit, calls to prepare for buffers exceeding this size
will allocate the larger size. The default allocation size
is 1KB (1024 bytes).
@param alloc The allocator to use. If this parameter is
unspecified, a default constructed allocator will be used.
*/
explicit
basic_streambuf(std::size_t alloc_size = 1024,
Allocator const& alloc = allocator_type{});
/// Returns a copy of the associated allocator.
allocator_type
get_allocator() const
{
return this->member();
}
/** Returns the default allocation size.
This is the smallest size that the stream buffer will allocate.
The size of the allocation can influence capacity, which will
affect algorithms that use capacity to efficiently read from
streams.
*/
std::size_t
alloc_size() const
{
return alloc_size_;
}
/** Set the default allocation size.
This is the smallest size that the stream buffer will allocate.
The size of the allocation can influence capacity, which will
affect algorithms that use capacity to efficiently read from
streams.
@note This will not affect any already-existing allocations.
@param n The number of bytes.
*/
void
alloc_size(std::size_t n)
{
alloc_size_ = n;
}
/// Returns the size of the input sequence.
size_type
size() const
{
return in_size_;
}
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
size_type
max_size() const
{
return (std::numeric_limits<std::size_t>::max)();
}
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
std::size_t
capacity() const;
/** Get a list of buffers that represents the input sequence.
@note These buffers remain valid across subsequent calls to `prepare`.
*/
const_buffers_type
data() const;
/** Get a list of buffers that represents the output sequence, with the given size.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
mutable_buffers_type
prepare(size_type n);
/** Move bytes from the output sequence to the input sequence.
@note Buffers representing the input sequence acquired prior to
this call remain valid.
*/
void
commit(size_type n);
/// Remove bytes from the input sequence.
void
consume(size_type n);
// Helper for boost::asio::read_until
template<class OtherAllocator>
friend
std::size_t
read_size_helper(basic_streambuf<
OtherAllocator> const& streambuf, std::size_t max_size);
private:
void
clear();
void
move_assign(basic_streambuf& other, std::false_type);
void
move_assign(basic_streambuf& other, std::true_type);
void
copy_assign(basic_streambuf const& other, std::false_type);
void
copy_assign(basic_streambuf const& other, std::true_type);
void
delete_list();
void
debug_check() const;
};
/** A @b `DynamicBuffer` that uses multiple buffers internally.
The implementation uses a sequence of one or more character arrays
of varying sizes. Additional character array objects are appended to
the sequence to accommodate changes in the size of the character
sequence.
@note Meets the requirements of @b `DynamicBuffer`.
*/
using streambuf = basic_streambuf<std::allocator<char>>;
/** Format output to a @ref basic_streambuf.
@param streambuf The @ref basic_streambuf to write to.
@param t The object to write.
@return A reference to the @ref basic_streambuf.
*/
template<class Allocator, class T>
basic_streambuf<Allocator>&
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
} // beast
#include <beast/core/impl/streambuf.ipp>
#endif

View File

@@ -0,0 +1,151 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STRING_HPP
#define BEAST_STRING_HPP
#include <beast/config.hpp>
#include <boost/version.hpp>
#ifndef BEAST_NO_BOOST_STRING_VIEW
# if BOOST_VERSION >= 106400
# define BEAST_NO_BOOST_STRING_VIEW 0
# else
# define BEAST_NO_BOOST_STRING_VIEW 1
# endif
#endif
#if BEAST_NO_BOOST_STRING_VIEW
#include <boost/utility/string_ref.hpp>
#else
#include <boost/utility/string_view.hpp>
#endif
#include <algorithm>
namespace beast {
#if BEAST_NO_BOOST_STRING_VIEW
/// The type of string view used by the library
using string_view = boost::string_ref;
/// The type of basic string view used by the library
template<class CharT, class Traits>
using basic_string_view =
boost::basic_string_ref<CharT, Traits>;
#else
/// The type of string view used by the library
using string_view = boost::string_view;
/// The type of basic string view used by the library
template<class CharT, class Traits>
using basic_string_view =
boost::basic_string_view<CharT, Traits>;
#endif
namespace detail {
inline
char
ascii_tolower(char c)
{
if(c >= 'A' && c <= 'Z')
c += 'a' - 'A';
return c;
}
template<class = void>
bool
iequals(
beast::string_view const& lhs,
beast::string_view const& rhs)
{
auto n = lhs.size();
if(rhs.size() != n)
return false;
auto p1 = lhs.data();
auto p2 = rhs.data();
char a, b;
while(n--)
{
a = *p1++;
b = *p2++;
if(a != b)
goto slow;
}
return true;
while(n--)
{
slow:
if(ascii_tolower(a) != ascii_tolower(b))
return false;
a = *p1++;
b = *p2++;
}
return true;
}
} // detail
/** Returns `true` if two strings are equal, using a case-insensitive comparison.
The case-comparison operation is defined only for low-ASCII characters.
@param lhs The string on the left side of the equality
@param rhs The string on the right side of the equality
*/
inline
bool
iequals(
beast::string_view const& lhs,
beast::string_view const& rhs)
{
return detail::iequals(lhs, rhs);
}
/** A case-insensitive less predicate for strings.
The case-comparison operation is defined only for low-ASCII characters.
*/
struct iless
{
bool
operator()(
string_view const& lhs,
string_view const& rhs) const
{
using std::begin;
using std::end;
return std::lexicographical_compare(
begin(lhs), end(lhs), begin(rhs), end(rhs),
[](char c1, char c2)
{
return detail::ascii_tolower(c1) < detail::ascii_tolower(c2);
}
);
}
};
/** A case-insensitive equality predicate for strings.
The case-comparison operation is defined only for low-ASCII characters.
*/
struct iequal
{
bool
operator()(
string_view const& lhs,
string_view const& rhs) const
{
return iequals(lhs, rhs);
}
};
} // beast
#endif

View File

@@ -0,0 +1,109 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_STRING_PARAM_HPP
#define BEAST_STRING_PARAM_HPP
#include <beast/config.hpp>
#include <beast/core/string.hpp>
#include <beast/core/static_string.hpp>
#include <beast/core/detail/static_ostream.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/optional.hpp>
namespace beast {
/** A function parameter which efficiently converts to string.
This is used as a function parameter type to allow callers
notational convenience: objects other than strings may be
passed in contexts where a string is expected. The conversion
to string is made using `operator<<` to a non-dynamically
allocated static buffer if possible, else to a `std::string`
on overflow.
*/
class string_param
{
string_view sv_;
char buf_[128];
boost::optional<detail::static_ostream> os_;
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
print(T const&);
template<class T>
typename std::enable_if<
! std::is_integral<T>::value &&
! std::is_convertible<T, string_view>::value
>::type
print(T const&);
void
print(string_view const&);
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
print_1(T const&);
template<class T>
typename std::enable_if<
! std::is_integral<T>::value>::type
print_1(T const&);
void
print_n()
{
}
template<class T0, class... TN>
void
print_n(T0 const&, TN const&...);
template<class T0, class T1, class... TN>
void
print(T0 const&, T1 const&, TN const&...);
public:
/// Copy constructor (disallowed)
string_param(string_param const&) = delete;
/// Copy assignment (disallowed)
string_param& operator=(string_param const&) = delete;
/** Constructor
This function constructs a string as if by concatenating
the result of streaming each argument in order into an
output stream. It is used as a notational convenience
at call sites which expect a parameter with the semantics
of a @ref string_view.
The implementation uses a small, internal static buffer
to avoid memory allocations especially for the case where
the list of arguments to be converted consists of a single
integral type.
@param args One or more arguments to convert
*/
template<class... Args>
string_param(Args const&... args);
/// Implicit conversion to @ref string_view
operator string_view const() const
{
return sv_;
}
};
} // beast
#include <beast/core/impl/string_param.ipp>
#endif

View File

@@ -1,53 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TO_STRING_HPP
#define BEAST_TO_STRING_HPP
#include <beast/config.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
/** Convert a @b `ConstBufferSequence` to a `std::string`.
This function will convert the octets in a buffer sequence to a string.
All octets will be inserted into the resulting string, including null
or unprintable characters.
@param buffers The buffer sequence to convert.
@return A string representing the contents of the input area.
@note This function participates in overload resolution only if
the buffers parameter meets the requirements of @b `ConstBufferSequence`.
*/
template<class ConstBufferSequence>
#if GENERATING_DOCS
std::string
#else
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::string>::type
#endif
to_string(ConstBufferSequence const& buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(buffers));
for(auto const& buffer : buffers)
s.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return s;
}
} // beast
#endif

View File

@@ -0,0 +1,633 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TYPE_TRAITS_HPP
#define BEAST_TYPE_TRAITS_HPP
#include <beast/config.hpp>
#include <beast/core/file_base.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
namespace beast {
//------------------------------------------------------------------------------
//
// Buffer concepts
//
//------------------------------------------------------------------------------
/** Determine if `T` meets the requirements of @b ConstBufferSequence.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class ConstBufferSequence>
void f(ConstBufferSequence const& buffers)
{
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class ConstBufferSequence>
typename std::enable_if<is_const_buffer_sequence<ConstBufferSequence>::value>::type
f(ConstBufferSequence const& buffers);
@endcode
*/
template<class T>
#if BEAST_DOXYGEN
struct is_const_buffer_sequence : std::integral_constant<bool, ...>
#else
struct is_const_buffer_sequence :
detail::is_buffer_sequence<T, boost::asio::const_buffer>
#endif
{
};
/** Determine if `T` meets the requirements of @b MutableBufferSequence.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class MutableBufferSequence>
void f(MutableBufferSequence const& buffers)
{
static_assert(is_const_buffer_sequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class MutableBufferSequence>
typename std::enable_if<is_mutable_buffer_sequence<MutableBufferSequence>::value>::type
f(MutableBufferSequence const& buffers);
@endcode
*/
template<class T>
#if BEAST_DOXYGEN
struct is_mutable_buffer_sequence : std::integral_constant<bool, ...>
#else
struct is_mutable_buffer_sequence :
detail::is_buffer_sequence<T, boost::asio::mutable_buffer>
#endif
{
};
/** Determine if `T` meets the requirements of @b DynamicBuffer.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class DynamicBuffer>
void f(DynamicBuffer& buffer)
{
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class DynamicBuffer>
typename std::enable_if<is_dynamic_buffer<DynamicBuffer>::value>::type
f(DynamicBuffer const& buffer);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_dynamic_buffer : std::integral_constant<bool, ...> {};
#else
template<class T, class = void>
struct is_dynamic_buffer : std::false_type {};
template<class T>
struct is_dynamic_buffer<T, detail::void_t<decltype(
std::declval<std::size_t&>() =
std::declval<T const&>().size(),
std::declval<std::size_t&>() =
std::declval<T const&>().max_size(),
std::declval<std::size_t&>() =
std::declval<T const&>().capacity(),
std::declval<T&>().commit(std::declval<std::size_t>()),
std::declval<T&>().consume(std::declval<std::size_t>()),
(void)0)> > : std::integral_constant<bool,
is_const_buffer_sequence<
typename T::const_buffers_type>::value &&
is_mutable_buffer_sequence<
typename T::mutable_buffers_type>::value &&
std::is_same<typename T::const_buffers_type,
decltype(std::declval<T const&>().data())>::value &&
std::is_same<typename T::mutable_buffers_type,
decltype(std::declval<T&>().prepare(
std::declval<std::size_t>()))>::value
>
{
};
// Special case for Boost.Asio which doesn't adhere to
// net-ts but still provides a read_size_helper so things work
template<class Allocator>
struct is_dynamic_buffer<
boost::asio::basic_streambuf<Allocator>> : std::true_type
{
};
#endif
//------------------------------------------------------------------------------
//
// Handler concepts
//
//------------------------------------------------------------------------------
/** Determine if `T` meets the requirements of @b CompletionHandler.
This trait checks whether a type meets the requirements for a completion
handler, and is also callable with the specified signature.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
struct handler
{
void operator()(error_code&);
};
static_assert(is_completion_handler<handler, void(error_code&)>::value,
"Not a completion handler");
@endcode
*/
template<class T, class Signature>
#if BEAST_DOXYGEN
using is_completion_handler = std::integral_constant<bool, ...>;
#else
using is_completion_handler = std::integral_constant<bool,
std::is_copy_constructible<typename std::decay<T>::type>::value &&
detail::is_invocable<T, Signature>::value>;
#endif
//------------------------------------------------------------------------------
//
// Stream concepts
//
//------------------------------------------------------------------------------
/** Determine if `T` has the `get_io_service` member.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` has the member
function with the correct signature, else type will be `std::false_type`.
@par Example
Use with tag dispatching:
@code
template<class T>
void maybe_hello(T& t, std::true_type)
{
t.get_io_service().post([]{ std::cout << "Hello, world!" << std::endl; });
}
template<class T>
void maybe_hello(T&, std::false_type)
{
// T does not have get_io_service
}
template<class T>
void maybe_hello(T& t)
{
maybe_hello(t, has_get_io_service<T>{});
}
@endcode
Use with `static_assert`:
@code
struct stream
{
boost::asio::io_service& get_io_service();
};
static_assert(has_get_io_service<stream>::value,
"Missing get_io_service member");
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct has_get_io_service : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct has_get_io_service : std::false_type {};
template<class T>
struct has_get_io_service<T, beast::detail::void_t<decltype(
detail::accept_rv<boost::asio::io_service&>(
std::declval<T&>().get_io_service()),
(void)0)>> : std::true_type {};
#endif
/** Returns `T::lowest_layer_type` if it exists, else `T`
This will contain a nested `type` equal to `T::lowest_layer_type`
if it exists, else `type` will be equal to `T`.
@par Example
Declaring a wrapper:
@code
template<class Stream>
struct stream_wrapper
{
using next_layer_type = typename std::remove_reference<Stream>::type;
using lowest_layer_type = typename get_lowest_layer<stream_type>::type;
};
@endcode
Defining a metafunction:
@code
/// Alias for `std::true_type` if `T` wraps another stream
template<class T>
using is_stream_wrapper : std::integral_constant<bool,
! std::is_same<T, typename get_lowest_layer<T>::type>::value> {};
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct get_lowest_layer;
#else
template<class T, class = void>
struct get_lowest_layer
{
using type = T;
};
template<class T>
struct get_lowest_layer<T, detail::void_t<
typename T::lowest_layer_type>>
{
using type = typename T::lowest_layer_type;
};
#endif
/** Determine if `T` meets the requirements of @b AsyncReadStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class AsyncReadStream>
void f(AsyncReadStream& stream)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class AsyncReadStream>
typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type
f(AsyncReadStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_async_read_stream : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct is_async_read_stream : std::false_type {};
template<class T>
struct is_async_read_stream<T, detail::void_t<decltype(
std::declval<T>().async_read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<detail::ReadHandler>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/** Determine if `T` meets the requirements of @b AsyncWriteStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class AsyncWriteStream>
void f(AsyncWriteStream& stream)
{
static_assert(is_async_write_stream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class AsyncWriteStream>
typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type
f(AsyncWriteStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_async_write_stream : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct is_async_write_stream : std::false_type {};
template<class T>
struct is_async_write_stream<T, detail::void_t<decltype(
std::declval<T>().async_write_some(
std::declval<detail::ConstBufferSequence>(),
std::declval<detail::WriteHandler>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/** Determine if `T` meets the requirements of @b SyncReadStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class SyncReadStream>
void f(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class SyncReadStream>
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
f(SyncReadStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_sync_read_stream : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct is_sync_read_stream : std::false_type {};
template<class T>
struct is_sync_read_stream<T, detail::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<boost::system::error_code&>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/** Determine if `T` meets the requirements of @b SyncWriterStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class SyncReadStream>
void f(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class SyncReadStream>
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
f(SyncReadStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_sync_write_stream : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct is_sync_write_stream : std::false_type {};
template<class T>
struct is_sync_write_stream<T, detail::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>(),
std::declval<boost::system::error_code&>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/** Determine if `T` meets the requirements of @b AsyncStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class AsyncStream>
void f(AsyncStream& stream)
{
static_assert(is_async_stream<AsyncStream>::value,
"AsyncStream requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class AsyncStream>
typename std::enable_if<is_async_stream<AsyncStream>::value>::type
f(AsyncStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_async_stream : std::integral_constant<bool, ...>{};
#else
template<class T>
using is_async_stream = std::integral_constant<bool,
is_async_read_stream<T>::value && is_async_write_stream<T>::value>;
#endif
/** Determine if `T` meets the requirements of @b SyncStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class SyncStream>
void f(SyncStream& stream)
{
static_assert(is_sync_stream<SyncStream>::value,
"SyncStream requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class SyncStream>
typename std::enable_if<is_sync_stream<SyncStream>::value>::type
f(SyncStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_sync_stream : std::integral_constant<bool, ...>{};
#else
template<class T>
using is_sync_stream = std::integral_constant<bool,
is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>;
#endif
//------------------------------------------------------------------------------
//
// File concepts
//
//------------------------------------------------------------------------------
/** Determine if `T` meets the requirements of @b File.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class File>
void f(File& file)
{
static_assert(is_file<File>::value,
"File requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class File>
typename std::enable_if<is_file<File>::value>::type
f(File& file);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_file : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct is_file : std::false_type {};
template<class T>
struct is_file<T, detail::void_t<decltype(
std::declval<bool&>() = std::declval<T const&>().is_open(),
std::declval<T&>().close(std::declval<error_code&>()),
std::declval<T&>().open(
std::declval<char const*>(),
std::declval<file_mode>(),
std::declval<error_code&>()),
std::declval<std::uint64_t&>() = std::declval<T&>().size(
std::declval<error_code&>()),
std::declval<std::uint64_t&>() = std::declval<T&>().pos(
std::declval<error_code&>()),
std::declval<T&>().seek(
std::declval<std::uint64_t>(),
std::declval<error_code&>()),
std::declval<std::size_t&>() = std::declval<T&>().read(
std::declval<void*>(),
std::declval<std::size_t>(),
std::declval<error_code&>()),
std::declval<std::size_t&>() = std::declval<T&>().write(
std::declval<void const*>(),
std::declval<std::size_t>(),
std::declval<error_code&>()),
(void)0)>> : std::integral_constant<bool,
std::is_default_constructible<T>::value &&
std::is_destructible<T>::value
> {};
#endif
} // beast
#endif

View File

@@ -1,64 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_WRITE_DYNABUF_HPP
#define BEAST_WRITE_DYNABUF_HPP
#include <beast/config.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/write_dynabuf.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** Write to a @b `DynamicBuffer`.
This function appends the serialized representation of each provided
argument into the dynamic buffer. It is capable of converting the
following types of arguments:
@li `boost::asio::const_buffer`
@li `boost::asio::mutable_buffer`
@li A type meeting the requirements of @b `ConvertibleToConstBuffer`
@li A type meeting the requirements of @b `ConstBufferSequence`
@li A type meeting the requirements of @b `MutableBufferSequence`
For all types not listed above, the function will invoke
`boost::lexical_cast` on the argument in an attempt to convert to
a string, which is then appended to the dynamic buffer.
When this function serializes numbers, it converts them to
their text representation as if by a call to `std::to_string`.
@param dynabuf The dynamic buffer to write to.
@param args A list of one or more arguments to write.
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
@note This function participates in overload resolution only if
the `dynabuf` parameter meets the requirements of @b `DynamicBuffer`.
*/
template<class DynamicBuffer, class... Args>
#if GENERATING_DOCS
void
#else
typename std::enable_if<is_DynamicBuffer<DynamicBuffer>::value>::type
#endif
write(DynamicBuffer& dynabuf, Args const&... args)
{
detail::write_dynabuf(dynabuf, args...);
}
} // beast
#endif

View File

@@ -10,20 +10,25 @@
#include <beast/config.hpp>
#include <beast/http/basic_fields.hpp>
#include <beast/http/basic_parser_v1.hpp>
#include <beast/http/chunk_encode.hpp>
#include <beast/http/basic_parser.hpp>
#include <beast/http/buffer_body.hpp>
#include <beast/http/dynamic_body.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/error.hpp>
#include <beast/http/field.hpp>
#include <beast/http/fields.hpp>
#include <beast/http/file_body.hpp>
#include <beast/http/message.hpp>
#include <beast/http/parse.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/parser.hpp>
#include <beast/http/read.hpp>
#include <beast/http/reason.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/http/streambuf_body.hpp>
#include <beast/http/serializer.hpp>
#include <beast/http/span_body.hpp>
#include <beast/http/status.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/type_traits.hpp>
#include <beast/http/vector_body.hpp>
#include <beast/http/verb.hpp>
#include <beast/http/write.hpp>
#endif

View File

@@ -1,101 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
#define BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/http/message.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
namespace beast {
namespace http {
/** A message body represented by a @b `DynamicBuffer`
Meets the requirements of @b `Body`.
*/
template<class DynamicBuffer>
struct basic_dynabuf_body
{
/// The type of the `message::body` member
using value_type = DynamicBuffer;
#if GENERATING_DOCS
private:
#endif
class reader
{
value_type& sb_;
public:
template<bool isRequest, class Fields>
explicit
reader(message<isRequest,
basic_dynabuf_body, Fields>& m) noexcept
: sb_(m.body)
{
}
void
init(error_code&) noexcept
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
sb_.commit(buffer_copy(
sb_.prepare(size), buffer(data, size)));
}
};
class writer
{
DynamicBuffer const& body_;
public:
template<bool isRequest, class Fields>
explicit
writer(message<
isRequest, basic_dynabuf_body, Fields> const& m) noexcept
: body_(m.body)
{
}
void
init(error_code& ec) noexcept
{
beast::detail::ignore_unused(ec);
}
std::uint64_t
content_length() const noexcept
{
return body_.size();
}
template<class WriteFunction>
bool
write(error_code&, WriteFunction&& wf) noexcept
{
wf(body_.data());
return true;
}
};
};
} // http
} // beast
#endif

View File

@@ -1,307 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_BASIC_FIELDS_HPP
#define BEAST_HTTP_BASIC_FIELDS_HPP
#include <beast/config.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <beast/http/detail/basic_fields.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <cctype>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A container for storing HTTP header fields.
This container is designed to store the field value pairs that make
up the fields and trailers in a HTTP message. Objects of this type
are iterable, with each element holding the field name and field
value.
Field names are stored as-is, but comparisons are case-insensitive.
When the container is iterated, the fields are presented in the order
of insertion. For fields with the same name, the container behaves
as a `std::multiset`; there will be a separate value for each occurrence
of the field name.
@note Meets the requirements of @b FieldSequence.
*/
template<class Allocator>
class basic_fields :
#if ! GENERATING_DOCS
private beast::detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_fields_base::element>>,
#endif
public detail::basic_fields_base
{
using alloc_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_fields_base::element>;
using alloc_traits =
std::allocator_traits<alloc_type>;
using size_type =
typename std::allocator_traits<Allocator>::size_type;
void
delete_all();
void
move_assign(basic_fields&, std::false_type);
void
move_assign(basic_fields&, std::true_type);
void
copy_assign(basic_fields const&, std::false_type);
void
copy_assign(basic_fields const&, std::true_type);
template<class FieldSequence>
void
copy_from(FieldSequence const& fs)
{
for(auto const& e : fs)
insert(e.first, e.second);
}
public:
/// The type of allocator used.
using allocator_type = Allocator;
/** The value type of the field sequence.
Meets the requirements of @b Field.
*/
#if GENERATING_DOCS
using value_type = implementation_defined;
#endif
/// A const iterator to the field sequence
#if GENERATING_DOCS
using iterator = implementation_defined;
#endif
/// A const iterator to the field sequence
#if GENERATING_DOCS
using const_iterator = implementation_defined;
#endif
/// Default constructor.
basic_fields() = default;
/// Destructor
~basic_fields();
/** Construct the fields.
@param alloc The allocator to use.
*/
explicit
basic_fields(Allocator const& alloc);
/** Move constructor.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_fields(basic_fields&& other);
/** Move assignment.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_fields& operator=(basic_fields&& other);
/// Copy constructor.
basic_fields(basic_fields const&);
/// Copy assignment.
basic_fields& operator=(basic_fields const&);
/// Copy constructor.
template<class OtherAlloc>
basic_fields(basic_fields<OtherAlloc> const&);
/// Copy assignment.
template<class OtherAlloc>
basic_fields& operator=(basic_fields<OtherAlloc> const&);
/// Construct from a field sequence.
template<class FwdIt>
basic_fields(FwdIt first, FwdIt last);
/// Returns `true` if the field sequence contains no elements.
bool
empty() const
{
return set_.empty();
}
/// Returns the number of elements in the field sequence.
std::size_t
size() const
{
return set_.size();
}
/// Returns a const iterator to the beginning of the field sequence.
const_iterator
begin() const
{
return list_.cbegin();
}
/// Returns a const iterator to the end of the field sequence.
const_iterator
end() const
{
return list_.cend();
}
/// Returns a const iterator to the beginning of the field sequence.
const_iterator
cbegin() const
{
return list_.cbegin();
}
/// Returns a const iterator to the end of the field sequence.
const_iterator
cend() const
{
return list_.cend();
}
/// Returns `true` if the specified field exists.
bool
exists(boost::string_ref const& name) const
{
return set_.find(name, less{}) != set_.end();
}
/// Returns the number of values for the specified field.
std::size_t
count(boost::string_ref const& name) const;
/** Returns an iterator to the case-insensitive matching field name.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
*/
iterator
find(boost::string_ref const& name) const;
/** Returns the value for a case-insensitive matching header, or `""`.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
*/
boost::string_ref
operator[](boost::string_ref const& name) const;
/// Clear the contents of the basic_fields.
void
clear() noexcept;
/** Remove a field.
If more than one field with the specified name exists, all
matching fields will be removed.
@param name The name of the field(s) to remove.
@return The number of fields removed.
*/
std::size_t
erase(boost::string_ref const& name);
/** Insert a field value.
If a field with the same name already exists, the
existing field is untouched and a new field value pair
is inserted into the container.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
insert(boost::string_ref const& name, boost::string_ref value);
/** Insert a field value.
If a field with the same name already exists, the
existing field is untouched and a new field value pair
is inserted into the container.
@param name The name of the field
@param value The value of the field. The object will be
converted to a string using `boost::lexical_cast`.
*/
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
insert(boost::string_ref name, T const& value)
{
insert(name, boost::lexical_cast<std::string>(value));
}
/** Replace a field value.
First removes any values with matching field names, then
inserts the new field value.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
replace(boost::string_ref const& name, boost::string_ref value);
/** Replace a field value.
First removes any values with matching field names, then
inserts the new field value.
@param name The name of the field
@param value The value of the field. The object will be
converted to a string using `boost::lexical_cast`.
*/
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
replace(boost::string_ref const& name, T const& value)
{
replace(name,
boost::lexical_cast<std::string>(value));
}
};
} // http
} // beast
#include <beast/http/impl/basic_fields.ipp>
#endif

View File

@@ -0,0 +1,509 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
#define BEAST_HTTP_BASIC_PARSER_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/string.hpp>
#include <beast/http/field.hpp>
#include <beast/http/verb.hpp>
#include <beast/http/detail/basic_parser.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <boost/assert.hpp>
#include <limits>
#include <memory>
#include <utility>
namespace beast {
namespace http {
/** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the
HTTP/1 wire format. It allocates no memory when input is
presented as a single contiguous buffer, and uses minimal
state. It will handle chunked encoding and it understands
the semantics of the Connection, Content-Length, and Upgrade
fields.
The parser is optimized for the case where the input buffer
sequence consists of a single contiguous buffer. The
@ref beast::flat_buffer class is provided, which guarantees
that the input sequence of the stream buffer will be represented
by exactly one contiguous buffer. To ensure the optimum performance
of the parser, use @ref beast::flat_buffer with HTTP algorithms
such as @ref beast::http::read, @ref beast::http::read_some,
@ref beast::http::async_read, and @ref beast::http::async_read_some.
Alternatively, the caller may use custom techniques to ensure that
the structured portion of the HTTP message (header or chunk header)
is contained in a linear buffer.
The interface uses CRTP (Curiously Recurring Template Pattern).
To use this class directly, derive from @ref basic_parser. When
bytes are presented, the implementation will make a series of zero
or more calls to derived class members functions (termed "callbacks"
in this context) matching a specific signature.
Every callback must be provided by the derived class, or else
a compilation error will be generated. This exemplar shows
the signature and description of the callbacks required in
the derived class.
For each callback, the function will ensure that `!ec` is `true`
if there was no error or set to the appropriate error code if
there was one. If an error is set, the value is propagated to
the caller of the parser.
@tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message.
@tparam Derived The derived class type. This is part of the
Curiously Recurring Template Pattern interface.
@note If the parser encounters a field value with obs-fold
longer than 4 kilobytes in length, an error is generated.
*/
template<bool isRequest, class Derived>
class basic_parser
: private detail::basic_parser_base
{
template<bool OtherIsRequest, class OtherDerived>
friend class basic_parser;
// limit on the size of the stack flat buffer
static std::size_t constexpr max_stack_buffer = 8192;
// Message will be complete after reading header
static unsigned constexpr flagSkipBody = 1<< 0;
// Consume input buffers across semantic boundaries
static unsigned constexpr flagEager = 1<< 1;
// The parser has read at least one byte
static unsigned constexpr flagGotSome = 1<< 2;
// Message semantics indicate a body is expected.
// cleared if flagSkipBody set
//
static unsigned constexpr flagHasBody = 1<< 3;
static unsigned constexpr flagHTTP11 = 1<< 4;
static unsigned constexpr flagNeedEOF = 1<< 5;
static unsigned constexpr flagExpectCRLF = 1<< 6;
static unsigned constexpr flagConnectionClose = 1<< 7;
static unsigned constexpr flagConnectionUpgrade = 1<< 8;
static unsigned constexpr flagConnectionKeepAlive = 1<< 9;
static unsigned constexpr flagContentLength = 1<< 10;
static unsigned constexpr flagChunked = 1<< 11;
static unsigned constexpr flagUpgrade = 1<< 12;
static unsigned constexpr flagFinalChunk = 1<< 13;
static
std::uint64_t
default_body_limit(std::true_type)
{
// limit for requests
return 1 * 1024 * 1024; // 1MB
}
static
std::uint64_t
default_body_limit(std::false_type)
{
// limit for responses
return 8 * 1024 * 1024; // 8MB
}
std::uint64_t body_limit_; // max payload body
std::uint64_t len_; // size of chunk or body
std::unique_ptr<char[]> buf_; // temp storage
std::size_t buf_len_ = 0; // size of buf_
std::size_t skip_ = 0; // resume search here
std::uint32_t
header_limit_ = 8192; // max header size
unsigned short status_; // response status
state state_ = // initial state
state::nothing_yet;
unsigned f_ = 0; // flags
public:
/// `true` if this parser parses requests, `false` for responses.
using is_request =
std::integral_constant<bool, isRequest>;
/// Copy constructor (disallowed)
basic_parser(basic_parser const&) = delete;
/// Copy assignment (disallowed)
basic_parser& operator=(basic_parser const&) = delete;
/// Destructor
~basic_parser() = default;
/// Default constructor
basic_parser();
/** Move constructor
After the move, the only valid operation on the
moved-from object is destruction.
*/
template<class OtherDerived>
basic_parser(basic_parser<isRequest, OtherDerived>&&);
/** Returns a reference to this object as a @ref basic_parser.
This is used to pass a derived class where a base class is
expected, to choose a correct function overload when the
resolution would be ambiguous.
*/
basic_parser&
base()
{
return *this;
}
/** Returns a constant reference to this object as a @ref basic_parser.
This is used to pass a derived class where a base class is
expected, to choose a correct function overload when the
resolution would be ambiguous.
*/
basic_parser const&
base() const
{
return *this;
}
/// Returns `true` if the parser has received at least one byte of input.
bool
got_some() const
{
return state_ != state::nothing_yet;
}
/** Returns `true` if the message is complete.
The message is complete after the full header is prduced
and one of the following is true:
@li The skip body option was set.
@li The semantics of the message indicate there is no body.
@li The semantics of the message indicate a body is expected,
and the entire body was parsed.
*/
bool
is_done() const
{
return state_ == state::complete;
}
/** Returns `true` if a the parser has produced the full header.
*/
bool
is_header_done() const
{
return state_ > state::fields;
}
/** Returns `true` if the message is an upgrade message.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
bool
is_upgrade() const
{
return (f_ & flagConnectionUpgrade) != 0;
}
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
bool
is_chunked() const
{
return (f_ & flagChunked) != 0;
}
/** Returns `true` if the message has keep-alive connection semantics.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
bool
is_keep_alive() const;
/** Returns the optional value of Content-Length if known.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
boost::optional<std::uint64_t>
content_length() const;
/** Returns `true` if the message semantics require an end of file.
Depending on the contents of the header, the parser may
require and end of file notification to know where the end
of the body lies. If this function returns `true` it will be
necessary to call @ref put_eof when there will never be additional
data from the input.
*/
bool
need_eof() const
{
return (f_ & flagNeedEOF) != 0;
}
/** Set the limit on the payload body.
This function sets the maximum allowed size of the payload body,
before any encodings except chunked have been removed. Depending
on the message semantics, one of these cases will apply:
@li The Content-Length is specified and exceeds the limit. In
this case the result @ref error::body_limit is returned
immediately after the header is parsed.
@li The Content-Length is unspecified and the chunked encoding
is not specified as the last encoding. In this case the end of
message is determined by the end of file indicator on the
associated stream or input source. If a sufficient number of
body payload octets are presented to the parser to exceed the
configured limit, the parse fails with the result
@ref error::body_limit
@li The Transfer-Encoding specifies the chunked encoding as the
last encoding. In this case, when the number of payload body
octets produced by removing the chunked encoding exceeds
the configured limit, the parse fails with the result
@ref error::body_limit.
Setting the limit after any body octets have been parsed
results in undefined behavior.
The default limit is 1MB for requests and 8MB for responses.
@param v The payload body limit to set
*/
void
body_limit(std::uint64_t v)
{
body_limit_ = v;
}
/** Set a limit on the total size of the header.
This function sets the maximum allowed size of the header
including all field name, value, and delimiter characters
and also including the CRLF sequences in the serialized
input. If the end of the header is not found within the
limit of the header size, the error @ref error::header_limit
is returned by @ref put.
Setting the limit after any header octets have been parsed
results in undefined behavior.
*/
void
header_limit(std::uint32_t v)
{
header_limit_ = v;
}
/// Returns `true` if the eager parse option is set.
bool
eager() const
{
return (f_ & flagEager) != 0;
}
/** Set the eager parse option.
Normally the parser returns after successfully parsing a structured
element (header, chunk header, or chunk body) even if there are octets
remaining in the input. This is necessary when attempting to parse the
header first, or when the caller wants to inspect information which may
be invalidated by subsequent parsing, such as a chunk extension. The
`eager` option controls whether the parser keeps going after parsing
structured element if there are octets remaining in the buffer and no
error occurs. This option is automatically set or cleared during certain
stream operations to improve performance with no change in functionality.
The default setting is `false`.
@param v `true` to set the eager parse option or `false` to disable it.
*/
void
eager(bool v)
{
if(v)
f_ |= flagEager;
else
f_ &= ~flagEager;
}
/// Returns `true` if the skip parse option is set.
bool
skip()
{
return (f_ & flagSkipBody) != 0;
}
/** Set the skip parse option.
This option controls whether or not the parser expects to see an HTTP
body, regardless of the presence or absence of certain fields such as
Content-Length or a chunked Transfer-Encoding. Depending on the request,
some responses do not carry a body. For example, a 200 response to a
CONNECT request from a tunneling proxy, or a response to a HEAD request.
In these cases, callers may use this function inform the parser that
no body is expected. The parser will consider the message complete
after the header has been received.
@param v `true` to set the skip body option or `false` to disable it.
@note This function must called before any bytes are processed.
*/
void
skip(bool v);
/** Write a buffer sequence to the parser.
This function attempts to incrementally parse the HTTP
message data stored in the caller provided buffers. Upon
success, a positive return value indicates that the parser
made forward progress, consuming that number of
bytes.
In some cases there may be an insufficient number of octets
in the input buffer in order to make forward progress. This
is indicated by the code @ref error::need_more. When
this happens, the caller should place additional bytes into
the buffer sequence and call @ref put again.
The error code @ref error::need_more is special. When this
error is returned, a subsequent call to @ref put may succeed
if the buffers have been updated. Otherwise, upon error
the parser may not be restarted.
@param buffers An object meeting the requirements of
@b ConstBufferSequence that represents the next chunk of
message data. If the length of this buffer sequence is
one, the implementation will not allocate additional memory.
The class @ref flat_buffer is provided as one way to
meet this requirement
@param ec Set to the error, if any occurred.
@return The number of octets consumed in the buffer
sequence. The caller should remove these octets even if the
error is set.
*/
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const& buffers, error_code& ec);
#if ! BEAST_DOXYGEN
std::size_t
put(boost::asio::const_buffers_1 const& buffer,
error_code& ec);
#endif
/** Inform the parser that the end of stream was reached.
In certain cases, HTTP needs to know where the end of
the stream is. For example, sometimes servers send
responses without Content-Length and expect the client
to consume input (for the body) until EOF. Callbacks
and errors will still be processed as usual.
This is typically called when a read from the
underlying stream object sets the error code to
`boost::asio::error::eof`.
@note Only valid after parsing a complete header.
@param ec Set to the error, if any occurred.
*/
void
put_eof(error_code& ec);
private:
inline
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
template<class ConstBufferSequence>
std::size_t
put_from_stack(std::size_t size,
ConstBufferSequence const& buffers,
error_code& ec);
void
maybe_need_more(
char const* p, std::size_t n,
error_code& ec);
void
parse_start_line(
char const*& p, char const* last,
error_code& ec, std::true_type);
void
parse_start_line(
char const*& p, char const* last,
error_code& ec, std::false_type);
void
parse_fields(
char const*& p, char const* last,
error_code& ec);
void
finish_header(
error_code& ec, std::true_type);
void
finish_header(
error_code& ec, std::false_type);
void
parse_body(char const*& p,
std::size_t n, error_code& ec);
void
parse_body_to_eof(char const*& p,
std::size_t n, error_code& ec);
void
parse_chunk_header(char const*& p,
std::size_t n, error_code& ec);
void
parse_chunk_body(char const*& p,
std::size_t n, error_code& ec);
void
do_field(field f,
string_view value, error_code& ec);
};
} // http
} // beast
#include <beast/http/impl/basic_parser.ipp>
#endif

View File

@@ -1,856 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_BASIC_PARSER_v1_HPP
#define BEAST_HTTP_BASIC_PARSER_v1_HPP
#include <beast/config.hpp>
#include <beast/http/message.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/http/detail/basic_parser_v1.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <array>
#include <climits>
#include <cstdint>
#include <type_traits>
namespace beast {
namespace http {
/** Parse flags
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
*/
enum parse_flag
{
chunked = 1,
connection_keep_alive = 2,
connection_close = 4,
connection_upgrade = 8,
trailing = 16,
upgrade = 32,
skipbody = 64,
contentlength = 128,
paused = 256
};
/** Body maximum size option.
Sets the maximum number of cumulative bytes allowed including
all body octets. Octets in chunk-encoded bodies are counted
after decoding. A value of zero indicates no limit on
the number of body octets.
The default body maximum size for requests is 4MB (four
megabytes or 4,194,304 bytes) and unlimited for responses.
@note Objects of this type are used with @ref basic_parser_v1::set_option.
*/
struct body_max_size
{
std::size_t value;
explicit
body_max_size(std::size_t v)
: value(v)
{
}
};
/** Header maximum size option.
Sets the maximum number of cumulative bytes allowed
including all header octets. A value of zero indicates
no limit on the number of header octets.
The default header maximum size is 16KB (16,384 bytes).
@note Objects of this type are used with @ref basic_parser_v1::set_option.
*/
struct header_max_size
{
std::size_t value;
explicit
header_max_size(std::size_t v)
: value(v)
{
}
};
/** A value indicating how the parser should treat the body.
This value is returned from the `on_header` callback in
the derived class. It controls what the parser does next
in terms of the message body.
*/
enum class body_what
{
/** The parser should expect a body, keep reading.
*/
normal,
/** Skip parsing of the body.
When returned by `on_header` this causes parsing to
complete and control to return to the caller. This
could be used when sending a response to a HEAD
request, for example.
*/
skip,
/** The message represents an UPGRADE request.
When returned by `on_body_prepare` this causes parsing
to complete and control to return to the caller.
*/
upgrade,
/** Suspend parsing before reading the body.
When returned by `on_body_prepare` this causes parsing
to pause. Control is returned to the caller, and the
parser state is preserved such that a subsequent call
to the parser will begin reading the message body.
This could be used by callers to inspect the HTTP
header before committing to read the body. For example,
to choose the body type based on the fields. Or to
respond to an Expect: 100-continue request.
*/
pause
};
/// The value returned when no content length is known or applicable.
static std::uint64_t constexpr no_content_length =
(std::numeric_limits<std::uint64_t>::max)();
/** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the
HTTP/1 wire format. It allocates no memory and uses minimal
state. It will handle chunked encoding and it understands the
semantics of the Connection and Content-Length header fields.
The interface uses CRTP (Curiously Recurring Template Pattern).
To use this class, derive from basic_parser. When bytes are
presented, the implementation will make a series of zero or
more calls to derived class members functions (referred to as
"callbacks" from here on) matching a specific signature.
Every callback must be provided by the derived class, or else
a compilation error will be generated. This exemplar shows
the signature and description of the callbacks required in
the derived class.
@code
template<bool isRequest>
struct exemplar : basic_parser_v1<isRequest, exemplar>
{
// Called when the first valid octet of a new message is received
//
void on_start(error_code&);
// Called for each piece of the Request-Method
//
void on_method(boost::string_ref const&, error_code&);
// Called for each piece of the Request-URI
//
void on_uri(boost::string_ref const&, error_code&);
// Called for each piece of the reason-phrase
//
void on_reason(boost::string_ref const&, error_code&);
// Called after the entire Request-Line has been parsed successfully.
//
void on_request(error_code&);
// Called after the entire Response-Line has been parsed successfully.
//
void on_response(error_code&);
// Called for each piece of the current header field.
//
void on_field(boost::string_ref const&, error_code&);
// Called for each piece of the current header value.
//
void on_value(boost::string_ref const&, error_code&)
// Called when the entire header has been parsed successfully.
//
void
on_header(std::uint64_t content_length, error_code&);
// Called after on_header, before the body is parsed
//
body_what
on_body_what(std::uint64_t content_length, error_code&);
// Called for each piece of the body.
//
// If the header indicates chunk encoding, the chunk
// encoding is removed from the buffer before being
// passed to the callback.
//
void on_body(boost::string_ref const&, error_code&);
// Called when the entire message has been parsed successfully.
// At this point, @ref complete returns `true`, and the parser
// is ready to parse another message if `keep_alive` would
// return `true`.
//
void on_complete(error_code&) {}
};
@endcode
The return value of `on_body_what` is special, it controls
whether or not the parser should expect a body. See @ref body_what
for choices of the return value.
If a callback sets an error, parsing stops at the current octet
and the error is returned to the caller. Callbacks must not throw
exceptions.
@tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message.
@tparam Derived The derived class type. This is part of the
Curiously Recurring Template Pattern interface.
*/
template<bool isRequest, class Derived>
class basic_parser_v1 : public detail::parser_base
{
private:
template<bool, class>
friend class basic_parser_v1;
using self = basic_parser_v1;
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
enum field_state : std::uint8_t
{
h_general = 0,
h_C,
h_CO,
h_CON,
h_matching_connection,
h_matching_proxy_connection,
h_matching_content_length,
h_matching_transfer_encoding,
h_matching_upgrade,
h_connection,
h_content_length0,
h_content_length,
h_content_length_ows,
h_transfer_encoding,
h_upgrade,
h_matching_transfer_encoding_chunked,
h_matching_transfer_encoding_general,
h_matching_connection_keep_alive,
h_matching_connection_close,
h_matching_connection_upgrade,
h_transfer_encoding_chunked,
h_transfer_encoding_chunked_ows,
h_connection_keep_alive,
h_connection_keep_alive_ows,
h_connection_close,
h_connection_close_ows,
h_connection_upgrade,
h_connection_upgrade_ows,
h_connection_token,
h_connection_token_ows
};
std::size_t h_max_;
std::size_t h_left_;
std::size_t b_max_;
std::size_t b_left_;
std::uint64_t content_length_;
pmf_t cb_;
state s_ : 8;
unsigned fs_ : 8;
unsigned pos_ : 8; // position in field state
unsigned http_major_ : 16;
unsigned http_minor_ : 16;
unsigned status_code_ : 16;
unsigned flags_ : 9;
bool upgrade_ : 1; // true if parser exited for upgrade
public:
/// Default constructor
basic_parser_v1();
/// Copy constructor.
template<class OtherDerived>
basic_parser_v1(basic_parser_v1<
isRequest, OtherDerived> const& other);
/// Copy assignment.
template<class OtherDerived>
basic_parser_v1& operator=(basic_parser_v1<
isRequest, OtherDerived> const& other);
/** Set options on the parser.
@param args One or more parser options to set.
*/
#if GENERATING_DOCS
template<class... Args>
void
set_option(Args&&... args)
#else
template<class A1, class A2, class... An>
void
set_option(A1&& a1, A2&& a2, An&&... an)
#endif
{
set_option(std::forward<A1>(a1));
set_option(std::forward<A2>(a2),
std::forward<An>(an)...);
}
/// Set the header maximum size option
void
set_option(header_max_size const& o)
{
h_max_ = o.value;
h_left_ = h_max_;
}
/// Set the body maximum size option
void
set_option(body_max_size const& o)
{
b_max_ = o.value;
b_left_ = b_max_;
}
/// Returns internal flags associated with the parser.
unsigned
flags() const
{
return flags_;
}
/** Returns `true` if the message end is indicated by eof.
This function returns true if the semantics of the message require
that the end of the message is signaled by an end of file. For
example, if the message is a HTTP/1.0 message and the Content-Length
is unspecified, the end of the message is indicated by an end of file.
@return `true` if write_eof must be used to indicate the message end.
*/
bool
needs_eof() const
{
return needs_eof(
std::integral_constant<bool, isRequest>{});
}
/** Returns the major HTTP version number.
Examples:
* Returns 1 for HTTP/1.1
* Returns 1 for HTTP/1.0
@return The HTTP major version number.
*/
unsigned
http_major() const
{
return http_major_;
}
/** Returns the minor HTTP version number.
Examples:
* Returns 1 for HTTP/1.1
* Returns 0 for HTTP/1.0
@return The HTTP minor version number.
*/
unsigned
http_minor() const
{
return http_minor_;
}
/** Returns `true` if the message is an upgrade message.
A value of `true` indicates that the parser has successfully
completed parsing a HTTP upgrade message.
@return `true` if the message is an upgrade message.
*/
bool
upgrade() const
{
return upgrade_;
}
/** Returns the numeric HTTP Status-Code of a response.
@return The Status-Code.
*/
unsigned
status_code() const
{
return status_code_;
}
/** Returns `true` if the connection should be kept open.
@note This function is only valid to call when the parser
is complete.
*/
bool
keep_alive() const;
/** Returns `true` if the parse has completed succesfully.
When the parse has completed successfully, and the semantics
of the parsed message indicate that the connection is still
active, a subsequent call to `write` will begin parsing a
new message.
@return `true` If the parsing has completed successfully.
*/
bool
complete() const
{
return
s_ == s_restart ||
s_ == s_closed_complete ||
(flags_ & parse_flag::paused);
}
/** Write a sequence of buffers to the parser.
@param buffers An object meeting the requirements of
ConstBufferSequence that represents the input sequence.
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the input sequence.
*/
template<class ConstBufferSequence>
#if GENERATING_DOCS
std::size_t
#else
typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value,
std::size_t>::type
#endif
write(ConstBufferSequence const& buffers, error_code& ec);
/** Write a single buffer of data to the parser.
@param buffer The buffer to write.
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the buffer.
*/
std::size_t
write(boost::asio::const_buffer const& buffer, error_code& ec);
/** Called to indicate the end of file.
HTTP needs to know where the end of the stream is. For example,
sometimes servers send responses without Content-Length and
expect the client to consume input (for the body) until EOF.
Callbacks and errors will still be processed as usual.
@note This is typically called when a socket read returns eof.
*/
void
write_eof(error_code& ec);
protected:
/** Reset the parsing state.
The state of the parser is reset to expect the beginning of
a new request or response. The old state is discarded.
*/
void
reset();
private:
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
void
reset(std::true_type)
{
s_ = s_req_start;
}
void
reset(std::false_type)
{
s_ = s_res_start;
}
void
init(std::true_type)
{
// Request: 16KB max header, 4MB max body
h_max_ = 16 * 1024;
b_max_ = 4 * 1024 * 1024;
}
void
init(std::false_type)
{
// Response: 16KB max header, unlimited body
h_max_ = 16 * 1024;
b_max_ = 0;
}
void
init()
{
init(std::integral_constant<bool, isRequest>{});
reset();
}
bool
needs_eof(std::true_type) const;
bool
needs_eof(std::false_type) const;
template<class T, class = beast::detail::void_t<>>
struct check_on_start : std::false_type {};
template<class T>
struct check_on_start<T, beast::detail::void_t<decltype(
std::declval<T>().on_start(
std::declval<error_code&>())
)>> : std::true_type { };
template<class T, class = beast::detail::void_t<>>
struct check_on_method : std::false_type {};
template<class T>
struct check_on_method<T, beast::detail::void_t<decltype(
std::declval<T>().on_method(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_uri : std::false_type {};
template<class T>
struct check_on_uri<T, beast::detail::void_t<decltype(
std::declval<T>().on_uri(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_reason : std::false_type {};
template<class T>
struct check_on_reason<T, beast::detail::void_t<decltype(
std::declval<T>().on_reason(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_request : std::false_type {};
template<class T>
struct check_on_request<T, beast::detail::void_t<decltype(
std::declval<T>().on_request(
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_response : std::false_type {};
template<class T>
struct check_on_response<T, beast::detail::void_t<decltype(
std::declval<T>().on_response(
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_field : std::false_type {};
template<class T>
struct check_on_field<T, beast::detail::void_t<decltype(
std::declval<T>().on_field(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_value : std::false_type {};
template<class T>
struct check_on_value<T, beast::detail::void_t<decltype(
std::declval<T>().on_value(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_headers : std::false_type {};
template<class T>
struct check_on_headers<T, beast::detail::void_t<decltype(
std::declval<T>().on_header(
std::declval<std::uint64_t>(),
std::declval<error_code&>())
)>> : std::true_type {};
// VFALCO Can we use std::is_detected? Is C++11 capable?
template<class C>
class check_on_body_what_t
{
template<class T, class R = std::is_convertible<decltype(
std::declval<T>().on_body_what(
std::declval<std::uint64_t>(),
std::declval<error_code&>())),
body_what>>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using check_on_body_what =
std::integral_constant<bool, check_on_body_what_t<C>::value>;
template<class T, class = beast::detail::void_t<>>
struct check_on_body : std::false_type {};
template<class T>
struct check_on_body<T, beast::detail::void_t<decltype(
std::declval<T>().on_body(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_complete : std::false_type {};
template<class T>
struct check_on_complete<T, beast::detail::void_t<decltype(
std::declval<T>().on_complete(
std::declval<error_code&>())
)>> : std::true_type {};
void call_on_start(error_code& ec)
{
static_assert(check_on_start<Derived>::value,
"on_start requirements not met");
impl().on_start(ec);
}
void call_on_method(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_method<Derived>::value,
"on_method requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_method(s, ec);
}
void call_on_method(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_method(error_code& ec,
boost::string_ref const& s)
{
call_on_method(ec, s,
std::integral_constant<bool, isRequest>{});
}
void call_on_uri(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_uri<Derived>::value,
"on_uri requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_uri(s, ec);
}
void call_on_uri(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_uri(error_code& ec,
boost::string_ref const& s)
{
call_on_uri(ec, s,
std::integral_constant<bool, isRequest>{});
}
void call_on_reason(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_reason<Derived>::value,
"on_reason requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_reason(s, ec);
}
void call_on_reason(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_reason(error_code& ec, boost::string_ref const& s)
{
call_on_reason(ec, s,
std::integral_constant<bool, ! isRequest>{});
}
void call_on_request(error_code& ec, std::true_type)
{
static_assert(check_on_request<Derived>::value,
"on_request requirements not met");
impl().on_request(ec);
}
void call_on_request(error_code&, std::false_type)
{
}
void call_on_request(error_code& ec)
{
call_on_request(ec,
std::integral_constant<bool, isRequest>{});
}
void call_on_response(error_code& ec, std::true_type)
{
static_assert(check_on_response<Derived>::value,
"on_response requirements not met");
impl().on_response(ec);
}
void call_on_response(error_code&, std::false_type)
{
}
void call_on_response(error_code& ec)
{
call_on_response(ec,
std::integral_constant<bool, ! isRequest>{});
}
void call_on_field(error_code& ec,
boost::string_ref const& s)
{
static_assert(check_on_field<Derived>::value,
"on_field requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_field(s, ec);
}
void call_on_value(error_code& ec,
boost::string_ref const& s)
{
static_assert(check_on_value<Derived>::value,
"on_value requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_value(s, ec);
}
void
call_on_headers(error_code& ec)
{
static_assert(check_on_headers<Derived>::value,
"on_header requirements not met");
impl().on_header(content_length_, ec);
}
body_what
call_on_body_what(error_code& ec)
{
static_assert(check_on_body_what<Derived>::value,
"on_body_what requirements not met");
return impl().on_body_what(content_length_, ec);
}
void call_on_body(error_code& ec,
boost::string_ref const& s)
{
static_assert(check_on_body<Derived>::value,
"on_body requirements not met");
if(b_max_ && s.size() > b_left_)
{
ec = parse_error::body_too_big;
return;
}
b_left_ -= s.size();
impl().on_body(s, ec);
}
void call_on_complete(error_code& ec)
{
static_assert(check_on_complete<Derived>::value,
"on_complete requirements not met");
impl().on_complete(ec);
}
};
} // http
} // beast
#include <beast/http/impl/basic_parser_v1.ipp>
#endif

View File

@@ -0,0 +1,224 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_BUFFER_BODY_HPP
#define BEAST_HTTP_BUFFER_BODY_HPP
#include <beast/config.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <beast/http/type_traits.hpp>
#include <boost/optional.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A @b Body using a caller provided buffer
Messages using this body type may be serialized and parsed.
To use this class, the caller must initialize the members
of @ref buffer_body::value_type to appropriate values before
each call to read or write during a stream operation.
*/
struct buffer_body
{
/// The type of the body member when used in a message.
struct value_type
{
/** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
@par When Serializing
If this is `nullptr` and `more` is `true`, the error
@ref error::need_buffer will be returned from @ref serializer::get
Otherwise, the serializer will use the memory pointed to
by `data` having `size` octets of valid storage as the
next buffer representing the body.
@par When Parsing
If this is `nullptr`, the error @ref error::need_buffer
will be returned from @ref parser::put. Otherwise, the
parser will store body octets into the memory pointed to
by `data` having `size` octets of valid storage. After
octets are stored, the `data` and `size` members are
adjusted: `data` is incremented to point to the next
octet after the data written, while `size` is decremented
to reflect the remaining space at the memory location
pointed to by `data`.
*/
void* data = nullptr;
/** The number of octets in the buffer pointed to by @ref data.
@par When Serializing
If `data` is `nullptr` during serialization, this value
is ignored. Otherwise, it represents the number of valid
body octets pointed to by `data`.
@par When Parsing
The value of this field will be decremented during parsing
to indicate the number of remaining free octets in the
buffer pointed to by `data`. When it reaches zero, the
parser will return @ref error::need_buffer, indicating to
the caller that the values of `data` and `size` should be
updated to point to a new memory buffer.
*/
std::size_t size = 0;
/** `true` if this is not the last buffer.
@par When Serializing
If this is `true` and `data` is `nullptr`, the error
@ref error::need_buffer will be returned from @ref serializer::get
@par When Parsing
This field is not used during parsing.
*/
bool more = true;
};
/** The algorithm for serializing the body
Meets the requirements of @b BodyReader.
*/
#if BEAST_DOXYGEN
using reader = implementation_defined;
#else
class reader
{
bool toggle_ = false;
value_type const& body_;
public:
using const_buffers_type =
boost::asio::const_buffers_1;
template<bool isRequest, class Fields>
explicit
reader(message<isRequest,
buffer_body, Fields> const& msg)
: body_(msg.body)
{
}
void
init(error_code& ec)
{
ec.assign(0, ec.category());
}
boost::optional<
std::pair<const_buffers_type, bool>>
get(error_code& ec)
{
if(toggle_)
{
if(body_.more)
{
toggle_ = false;
ec = error::need_buffer;
}
else
{
ec.assign(0, ec.category());
}
return boost::none;
}
if(body_.data)
{
ec.assign(0, ec.category());
toggle_ = true;
return {{const_buffers_type{
body_.data, body_.size}, body_.more}};
}
if(body_.more)
ec = error::need_buffer;
else
ec.assign(0, ec.category());
return boost::none;
}
};
#endif
/** The algorithm for parsing the body
Meets the requirements of @b BodyReader.
*/
#if BEAST_DOXYGEN
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
public:
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, buffer_body, Fields>& m)
: body_(m.body)
{
}
void
init(boost::optional<std::uint64_t> const&, error_code& ec)
{
ec.assign(0, ec.category());
}
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const& buffers,
error_code& ec)
{
using boost::asio::buffer_size;
using boost::asio::buffer_copy;
if(! body_.data)
{
ec = error::need_buffer;
return 0;
}
auto const bytes_transferred =
buffer_copy(boost::asio::buffer(
body_.data, body_.size), buffers);
body_.data = reinterpret_cast<char*>(
body_.data) + bytes_transferred;
body_.size -= bytes_transferred;
if(bytes_transferred == buffer_size(buffers))
ec.assign(0, ec.category());
else
ec = error::need_buffer;
return bytes_transferred;
}
void
finish(error_code& ec)
{
ec.assign(0, ec.category());
}
};
#endif
};
#if ! BEAST_DOXYGEN
// operator<< is not supported for buffer_body
template<bool isRequest, class Fields>
std::ostream&
operator<<(std::ostream& os, message<isRequest,
buffer_body, Fields> const& msg) = delete;
#endif
} // http
} // beast
#endif

View File

@@ -1,75 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
#define BEAST_HTTP_CHUNK_ENCODE_HPP
#include <beast/config.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/http/detail/chunk_encode.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <type_traits>
namespace beast {
namespace http {
/** Returns a chunk-encoded ConstBufferSequence.
This returns a buffer sequence representing the
first chunk of a chunked transfer coded body.
@param fin `true` if this is the last chunk.
@param buffers The input buffer sequence.
@return A chunk-encoded ConstBufferSequence representing the input.
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
*/
template<class ConstBufferSequence>
#if GENERATING_DOCS
implementation_defined
#else
beast::detail::buffer_cat_helper<
detail::chunk_encode_delim,
ConstBufferSequence,
boost::asio::const_buffers_1>
#endif
chunk_encode(bool fin, ConstBufferSequence const& buffers)
{
using boost::asio::buffer_size;
return buffer_cat(
detail::chunk_encode_delim{buffer_size(buffers)},
buffers,
fin ? boost::asio::const_buffers_1{"\r\n0\r\n\r\n", 7}
: boost::asio::const_buffers_1{"\r\n", 2});
}
/** Returns a chunked encoding final chunk.
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
*/
inline
#if GENERATING_DOCS
implementation_defined
#else
boost::asio::const_buffers_1
#endif
chunk_encode_final()
{
return boost::asio::const_buffers_1{"0\r\n\r\n", 5};
}
} // http
} // beast
#endif

View File

@@ -1,231 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
struct write_function
{
template<class ConstBufferSequence>
void
operator()(ConstBufferSequence const&);
};
template<class T, class = beast::detail::void_t<>>
struct has_value_type : std::false_type {};
template<class T>
struct has_value_type<T, beast::detail::void_t<
typename T::value_type
> > : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct has_content_length : std::false_type {};
template<class T>
struct has_content_length<T, beast::detail::void_t<decltype(
std::declval<T>().content_length()
)> > : std::true_type
{
static_assert(std::is_convertible<
decltype(std::declval<T>().content_length()),
std::uint64_t>::value,
"Writer::content_length requirements not met");
};
template<class T, class M>
class is_Writer
{
template<class U, class R = decltype(
std::declval<U>().init(std::declval<error_code&>()),
std::true_type{})>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
// VFALCO This is unfortunate, we have to provide the template
// argument type because this is not a deduced context?
//
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().template write<detail::write_function>(
std::declval<error_code&>(),
std::declval<detail::write_function>()))
, bool>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
public:
static_assert(std::is_same<
typename M::body_type::writer, T>::value,
"Mismatched writer and message");
using type = std::integral_constant<bool,
std::is_nothrow_constructible<T, M const&>::value
&& type1::value
&& type2::value
>;
};
template<class T>
class is_Parser
{
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().complete()),
bool>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write(
std::declval<boost::asio::const_buffers_1 const&>(),
std::declval<error_code&>())),
std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = decltype(
std::declval<U>().write_eof(std::declval<error_code&>()),
std::true_type{})>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
type2::value &&
type3::value
>;
};
} // detail
/// Determine if `T` meets the requirements of @b Body.
template<class T>
#if GENERATING_DOCS
struct is_Body : std::integral_constant<bool, ...>{};
#else
using is_Body = detail::has_value_type<T>;
#endif
/** Determine if a @b Body has a nested type `reader`.
@tparam T The type to check, which must meet the
requirements of @b Body.
*/
#if GENERATING_DOCS
template<class T>
struct has_reader : std::integral_constant<bool, ...>{};
#else
template<class T, class = beast::detail::void_t<>>
struct has_reader : std::false_type {};
template<class T>
struct has_reader<T, beast::detail::void_t<
typename T::reader
> > : std::true_type {};
#endif
/** Determine if a @b Body has a nested type `writer`.
@tparam T The type to check, which must meet the
requirements of @b Body.
*/
#if GENERATING_DOCS
template<class T>
struct has_writer : std::integral_constant<bool, ...>{};
#else
template<class T, class = beast::detail::void_t<>>
struct has_writer : std::false_type {};
template<class T>
struct has_writer<T, beast::detail::void_t<
typename T::writer
> > : std::true_type {};
#endif
/** Determine if `T` meets the requirements of @b Reader for `M`.
@tparam T The type to test.
@tparam M The message type to test with, which must be of
type `message`.
*/
#if GENERATING_DOCS
template<class T, class M>
struct is_Reader : std::integral_constant<bool, ...> {};
#else
template<class T, class M, class = beast::detail::void_t<>>
struct is_Reader : std::false_type {};
template<class T, class M>
struct is_Reader<T, M, beast::detail::void_t<decltype(
std::declval<T>().init(
std::declval<error_code&>()),
std::declval<T>().write(
std::declval<void const*>(),
std::declval<std::size_t>(),
std::declval<error_code&>())
)> > : std::integral_constant<bool,
std::is_nothrow_constructible<T, M&>::value
>
{
static_assert(std::is_same<
typename M::body_type::reader, T>::value,
"Mismatched reader and message");
};
#endif
/** Determine if `T` meets the requirements of @b Writer for `M`.
@tparam T The type to test.
@tparam M The message type to test with, which must be of
type `message`.
*/
template<class T, class M>
#if GENERATING_DOCS
struct is_Writer : std::integral_constant<bool, ...> {};
#else
using is_Writer = typename detail::is_Writer<T, M>::type;
#endif
/// Determine if `T` meets the requirements of @b Parser.
template<class T>
#if GENERATING_DOCS
struct is_Parser : std::integral_constant<bool, ...>{};
#else
using is_Parser = typename detail::is_Parser<T>::type;
#endif
} // http
} // beast
#endif

View File

@@ -1,214 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
#define BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
#include <beast/core/detail/ci_char_traits.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/utility/string_ref.hpp>
namespace beast {
namespace http {
template<class Allocator>
class basic_fields;
namespace detail {
class basic_fields_base
{
public:
struct value_type
{
std::string first;
std::string second;
value_type(boost::string_ref const& name_,
boost::string_ref const& value_)
: first(name_)
, second(value_)
{
}
boost::string_ref
name() const
{
return first;
}
boost::string_ref
value() const
{
return second;
}
};
protected:
template<class Allocator>
friend class beast::http::basic_fields;
struct element
: boost::intrusive::set_base_hook <
boost::intrusive::link_mode <
boost::intrusive::normal_link>>
, boost::intrusive::list_base_hook <
boost::intrusive::link_mode <
boost::intrusive::normal_link>>
{
value_type data;
element(boost::string_ref const& name,
boost::string_ref const& value)
: data(name, value)
{
}
};
struct less : private beast::detail::ci_less
{
template<class String>
bool
operator()(String const& lhs, element const& rhs) const
{
return ci_less::operator()(lhs, rhs.data.first);
}
template<class String>
bool
operator()(element const& lhs, String const& rhs) const
{
return ci_less::operator()(lhs.data.first, rhs);
}
bool
operator()(element const& lhs, element const& rhs) const
{
return ci_less::operator()(
lhs.data.first, rhs.data.first);
}
};
using list_t = boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<false>>::type;
using set_t = boost::intrusive::make_multiset<element,
boost::intrusive::constant_time_size<true>,
boost::intrusive::compare<less>>::type;
// data
set_t set_;
list_t list_;
basic_fields_base(set_t&& set, list_t&& list)
: set_(std::move(set))
, list_(std::move(list))
{
}
public:
class const_iterator;
using iterator = const_iterator;
basic_fields_base() = default;
};
//------------------------------------------------------------------------------
class basic_fields_base::const_iterator
{
using iter_type = list_t::const_iterator;
iter_type it_;
template<class Allocator>
friend class beast::http::basic_fields;
friend class basic_fields_base;
const_iterator(iter_type it)
: it_(it)
{
}
public:
using value_type =
typename basic_fields_base::value_type;
using pointer = value_type const*;
using reference = value_type const&;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator&& other) = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator&& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const
{
return it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return it_->data;
}
pointer
operator->() const
{
return &**this;
}
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
} // detail
} // http
} // beast
#endif

View File

@@ -0,0 +1,194 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
#include <beast/core/string.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <cstddef>
#include <iterator>
namespace beast {
namespace http {
namespace detail {
/** A list parser which presents the sequence as a container.
*/
template<class Policy>
class basic_parsed_list
{
string_view s_;
public:
/// The type of policy this list uses for parsing.
using policy_type = Policy;
/// The type of each element in the list.
using value_type = typename Policy::value_type;
/// A constant iterator to a list element.
#if BEAST_DOXYGEN
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
class const_iterator
: private beast::detail::
empty_base_optimization<Policy>
{
basic_parsed_list const* list_ = nullptr;
char const* it_ = nullptr;
typename Policy::value_type v_;
bool error_ = false;
public:
using value_type =
typename Policy::value_type;
using reference = value_type const&;
using pointer = value_type const*;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::forward_iterator_tag;
const_iterator() = default;
bool
operator==(
const_iterator const& other) const
{
return
other.list_ == list_ &&
other.it_ == it_;
}
bool
operator!=(
const_iterator const& other) const
{
return ! (*this == other);
}
reference
operator*() const
{
return v_;
}
const_iterator&
operator++()
{
increment();
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
bool
error() const
{
return error_;
}
private:
friend class basic_parsed_list;
const_iterator(
basic_parsed_list const& list, bool at_end)
: list_(&list)
, it_(at_end ? nullptr :
list.s_.begin())
{
if(! at_end)
increment();
}
void
increment()
{
if(! this->member()(
v_, it_, list_->s_))
{
it_ = nullptr;
error_ = true;
}
}
};
/// Construct a list from a string
explicit
basic_parsed_list(string_view s)
: s_(s)
{
}
/// Return a const iterator to the beginning of the list
const_iterator begin() const;
/// Return a const iterator to the end of the list
const_iterator end() const;
/// Return a const iterator to the beginning of the list
const_iterator cbegin() const;
/// Return a const iterator to the end of the list
const_iterator cend() const;
};
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
begin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
end() const ->
const_iterator
{
return const_iterator{*this, true};
}
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
cbegin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
cend() const ->
const_iterator
{
return const_iterator{*this, true};
}
} // detail
} // http
} // beast
#endif

View File

@@ -0,0 +1,718 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
#include <beast/core/static_string.hpp>
#include <beast/core/string.hpp>
#include <beast/core/detail/cpu_info.hpp>
#include <beast/http/error.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <boost/config.hpp>
#include <boost/version.hpp>
#include <algorithm>
#include <cstddef>
#include <utility>
namespace beast {
namespace http {
namespace detail {
class basic_parser_base
{
protected:
// limit on the size of the obs-fold buffer
//
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
//
static std::size_t constexpr max_obs_fold = 4096;
enum class state
{
nothing_yet = 0,
start_line,
fields,
body0,
body,
body_to_eof0,
body_to_eof,
chunk_header0,
chunk_header,
chunk_body,
complete
};
static
bool
is_pathchar(char c)
{
// VFALCO This looks the same as the one below...
// TEXT = <any OCTET except CTLs, and excluding LWS>
static bool constexpr tab[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
return tab[static_cast<unsigned char>(c)];
}
static
inline
bool
unhex(unsigned char& d, char c)
{
static signed char constexpr tab[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
};
d = static_cast<unsigned char>(
tab[static_cast<unsigned char>(c)]);
return d != static_cast<unsigned char>(-1);
}
static
bool
is_digit(char c)
{
return static_cast<unsigned char>(c-'0') < 10;
}
static
bool
is_print(char c)
{
return static_cast<unsigned char>(c-32) < 95;
}
template<class FwdIt>
static
FwdIt
trim_front(FwdIt it, FwdIt const& end)
{
while(it != end)
{
if(*it != ' ' && *it != '\t')
break;
++it;
}
return it;
}
template<class RanIt>
static
RanIt
trim_back(
RanIt it, RanIt const& first)
{
while(it != first)
{
auto const c = it[-1];
if(c != ' ' && c != '\t')
break;
--it;
}
return it;
}
static
string_view
make_string(char const* first, char const* last)
{
return {first, static_cast<
std::size_t>(last - first)};
}
//--------------------------------------------------------------------------
std::pair<char const*, bool>
find_fast(
char const* buf,
char const* buf_end,
char const* ranges,
size_t ranges_size)
{
bool found = false;
boost::ignore_unused(buf_end, ranges, ranges_size);
return {buf, found};
}
// VFALCO Can SIMD help this?
static
char const*
find_eol(
char const* it, char const* last,
error_code& ec)
{
for(;;)
{
if(it == last)
{
ec.assign(0, ec.category());
return nullptr;
}
if(*it == '\r')
{
if(++it == last)
{
ec.assign(0, ec.category());
return nullptr;
}
if(*it != '\n')
{
ec = error::bad_line_ending;
return nullptr;
}
ec.assign(0, ec.category());
return ++it;
}
// VFALCO Should we handle the legacy case
// for lines terminated with a single '\n'?
++it;
}
}
// VFALCO Can SIMD help this?
static
char const*
find_eom(char const* p, char const* last)
{
for(;;)
{
if(p + 4 > last)
return nullptr;
if(p[3] != '\n')
{
if(p[3] == '\r')
++p;
else
p += 4;
}
else if(p[2] != '\r')
{
p += 4;
}
else if(p[1] != '\n')
{
p += 2;
}
else if(p[0] != '\r')
{
p += 2;
}
else
{
return p + 4;
}
}
}
//--------------------------------------------------------------------------
char const*
parse_token_to_eol(
char const* p,
char const* last,
char const*& token_last,
error_code& ec)
{
for(;; ++p)
{
if(p >= last)
{
ec = error::need_more;
return p;
}
if(BOOST_UNLIKELY(! is_print(*p)))
if((BOOST_LIKELY(static_cast<
unsigned char>(*p) < '\040') &&
BOOST_LIKELY(*p != '\011')) ||
BOOST_UNLIKELY(*p == '\177'))
goto found_control;
}
found_control:
if(BOOST_LIKELY(*p == '\r'))
{
if(++p >= last)
{
ec = error::need_more;
return last;
}
if(*p++ != '\n')
{
ec = error::bad_line_ending;
return last;
}
token_last = p - 2;
}
#if 0
// VFALCO This allows `\n` by itself
// to terminate a line
else if(*p == '\n')
{
token_last = p;
++p;
}
#endif
else
{
// invalid character
return nullptr;
}
return p;
}
template<class Iter, class Unsigned>
static
bool
parse_dec(Iter it, Iter last, Unsigned& v)
{
if(! is_digit(*it))
return false;
v = *it - '0';
for(;;)
{
if(! is_digit(*++it))
break;
auto const d = *it - '0';
if(v > ((std::numeric_limits<
Unsigned>::max)() - 10) / 10)
return false;
v = 10 * v + d;
}
return it == last;
}
template<class Iter, class Unsigned>
bool
parse_hex(Iter& it, Unsigned& v)
{
unsigned char d;
if(! unhex(d, *it))
return false;
v = d;
for(;;)
{
if(! unhex(d, *++it))
break;
auto const v0 = v;
v = 16 * v + d;
if(v < v0)
return false;
}
return true;
}
static
bool
parse_crlf(char const*& it)
{
if( it[0] != '\r' || it[1] != '\n')
return false;
it += 2;
return true;
}
static
void
parse_method(
char const*& it, char const* last,
string_view& result, error_code& ec)
{
// parse token SP
auto const first = it;
for(;; ++it)
{
if(it + 1 > last)
{
ec = error::need_more;
return;
}
if(! detail::is_tchar(*it))
break;
}
if(it + 1 > last)
{
ec = error::need_more;
return;
}
if(*it != ' ')
{
ec = error::bad_method;
return;
}
if(it == first)
{
// cannot be empty
ec = error::bad_method;
return;
}
result = make_string(first, it++);
}
static
void
parse_target(
char const*& it, char const* last,
string_view& result, error_code& ec)
{
// parse target SP
auto const first = it;
for(;; ++it)
{
if(it + 1 > last)
{
ec = error::need_more;
return;
}
if(! is_pathchar(*it))
break;
}
if(it + 1 > last)
{
ec = error::need_more;
return;
}
if(*it != ' ')
{
ec = error::bad_target;
return;
}
if(it == first)
{
// cannot be empty
ec = error::bad_target;
return;
}
result = make_string(first, it++);
}
static
void
parse_version(
char const*& it, char const* last,
int& result, error_code& ec)
{
if(it + 8 > last)
{
ec = error::need_more;
return;
}
if(*it++ != 'H')
{
ec = error::bad_version;
return;
}
if(*it++ != 'T')
{
ec = error::bad_version;
return;
}
if(*it++ != 'T')
{
ec = error::bad_version;
return;
}
if(*it++ != 'P')
{
ec = error::bad_version;
return;
}
if(*it++ != '/')
{
ec = error::bad_version;
return;
}
if(! is_digit(*it))
{
ec = error::bad_version;
return;
}
result = 10 * (*it++ - '0');
if(*it++ != '.')
{
ec = error::bad_version;
return;
}
if(! is_digit(*it))
{
ec = error::bad_version;
return;
}
result += *it++ - '0';
}
static
void
parse_status(
char const*& it, char const* last,
unsigned short& result, error_code& ec)
{
// parse 3(digit) SP
if(it + 4 > last)
{
ec = error::need_more;
return;
}
if(! is_digit(*it))
{
ec = error::bad_status;
return;
}
result = 100 * (*it++ - '0');
if(! is_digit(*it))
{
ec = error::bad_status;
return;
}
result += 10 * (*it++ - '0');
if(! is_digit(*it))
{
ec = error::bad_status;
return;
}
result += *it++ - '0';
if(*it++ != ' ')
{
ec = error::bad_status;
return;
}
}
void
parse_reason(
char const*& it, char const* last,
string_view& result, error_code& ec)
{
auto const first = it;
char const* token_last = nullptr;
auto p = parse_token_to_eol(
it, last, token_last, ec);
if(ec)
return;
if(! p)
{
ec = error::bad_reason;
return;
}
result = make_string(first, token_last);
it = p;
}
template<std::size_t N>
void
parse_field(
char const*& p,
char const* last,
string_view& name,
string_view& value,
static_string<N>& buf,
error_code& ec)
{
/* header-field = field-name ":" OWS field-value OWS
field-name = token
field-value = *( field-content / obs-fold )
field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
field-vchar = VCHAR / obs-text
obs-fold = CRLF 1*( SP / HTAB )
; obsolete line folding
; see Section 3.2.4
token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static char const* is_token =
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
"\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
// name
BOOST_ALIGNMENT(16) static const char ranges1[] =
"\x00 " /* control chars and up to SP */
"\"\"" /* 0x22 */
"()" /* 0x28,0x29 */
",," /* 0x2c */
"//" /* 0x2f */
":@" /* 0x3a-0x40 */
"[]" /* 0x5b-0x5d */
"{\377"; /* 0x7b-0xff */
auto first = p;
bool found;
std::tie(p, found) = find_fast(
p, last, ranges1, sizeof(ranges1)-1);
if(! found && p >= last)
{
ec = error::need_more;
return;
}
for(;;)
{
if(*p == ':')
break;
if(! is_token[static_cast<
unsigned char>(*p)])
{
ec = error::bad_field;
return;
}
++p;
if(p >= last)
{
ec = error::need_more;
return;
}
}
if(p == first)
{
// empty name
ec = error::bad_field;
return;
}
name = make_string(first, p);
++p; // eat ':'
char const* token_last;
for(;;)
{
// eat leading ' ' and '\t'
for(;;++p)
{
if(p + 1 > last)
{
ec = error::need_more;
return;
}
if(! (*p == ' ' || *p == '\t'))
break;
}
// parse to CRLF
first = p;
p = parse_token_to_eol(p, last, token_last, ec);
if(ec)
return;
if(! p)
{
ec = error::bad_value;
return;
}
// Look 1 char past the CRLF to handle obs-fold.
if(p + 1 > last)
{
ec = error::need_more;
return;
}
token_last =
trim_back(token_last, first);
if(*p != ' ' && *p != '\t')
{
value = make_string(first, token_last);
return;
}
++p;
if(token_last != first)
break;
}
buf.resize(0);
buf.append(first, token_last);
BOOST_ASSERT(! buf.empty());
try
{
for(;;)
{
// eat leading ' ' and '\t'
for(;;++p)
{
if(p + 1 > last)
{
ec = error::need_more;
return;
}
if(! (*p == ' ' || *p == '\t'))
break;
}
// parse to CRLF
first = p;
p = parse_token_to_eol(p, last, token_last, ec);
if(ec)
return;
if(! p)
{
ec = error::bad_value;
return;
}
// Look 1 char past the CRLF to handle obs-fold.
if(p + 1 > last)
{
ec = error::need_more;
return;
}
token_last = trim_back(token_last, first);
if(first != token_last)
{
buf.push_back(' ');
buf.append(first, token_last);
}
if(*p != ' ' && *p != '\t')
{
value = {buf.data(), buf.size()};
return;
}
++p;
}
}
catch(std::length_error const&)
{
ec = error::header_limit;
return;
}
}
};
} // detail
} // http
} // beast
#endif

View File

@@ -1,146 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
#include <cstdint>
namespace beast {
namespace http {
namespace detail {
template<class = void>
struct parser_str_t
{
static char constexpr close[6] = "close";
static char constexpr chunked[8] = "chunked";
static char constexpr keep_alive[11] = "keep-alive";
static char constexpr upgrade[8] = "upgrade";
static char constexpr connection[11] = "connection";
static char constexpr content_length[15] = "content-length";
static char constexpr proxy_connection[17] = "proxy-connection";
static char constexpr transfer_encoding[18] = "transfer-encoding";
};
template<class _>
char constexpr
parser_str_t<_>::close[6];
template<class _>
char constexpr
parser_str_t<_>::chunked[8];
template<class _>
char constexpr
parser_str_t<_>::keep_alive[11];
template<class _>
char constexpr
parser_str_t<_>::upgrade[8];
template<class _>
char constexpr
parser_str_t<_>::connection[11];
template<class _>
char constexpr
parser_str_t<_>::content_length[15];
template<class _>
char constexpr
parser_str_t<_>::proxy_connection[17];
template<class _>
char constexpr
parser_str_t<_>::transfer_encoding[18];
using parser_str = parser_str_t<>;
class parser_base
{
protected:
enum state : std::uint8_t
{
s_dead = 1,
s_req_start,
s_req_method0,
s_req_method,
s_req_url0,
s_req_url,
s_req_http,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_major,
s_req_dot,
s_req_minor,
s_req_cr,
s_req_lf,
s_res_start,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_major,
s_res_dot,
s_res_minor,
s_res_space_1,
s_res_status0,
s_res_status1,
s_res_status2,
s_res_space_2,
s_res_reason0,
s_res_reason,
s_res_line_lf,
s_res_line_done,
s_header_name0,
s_header_name,
s_header_value0_lf,
s_header_value0_almost_done,
s_header_value0,
s_header_value,
s_header_value_lf,
s_header_value_almost_done,
s_header_value_unfold,
s_headers_almost_done,
s_headers_done,
s_chunk_size0,
s_chunk_size,
s_chunk_ext_name0,
s_chunk_ext_name,
s_chunk_ext_val,
s_chunk_size_lf,
s_chunk_data0,
s_chunk_data,
s_chunk_data_cr,
s_chunk_data_lf,
s_body_pause,
s_body_identity0,
s_body_identity,
s_body_identity_eof0,
s_body_identity_eof,
s_complete,
s_restart,
s_closed_complete
};
};
} // detail
} // http
} // beast
#endif

View File

@@ -17,20 +17,22 @@ namespace beast {
namespace http {
namespace detail {
class chunk_encode_delim
/** A buffer sequence containing a chunk-encoding header
*/
class chunk_header
{
boost::asio::const_buffer cb_;
// Storage for the longest hex string we might need, plus delimiters.
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
// Storage for the longest hex string we might need
char buf_[2 * sizeof(std::size_t)];
template<class = void>
void
copy(chunk_encode_delim const& other);
copy(chunk_header const& other);
template<class = void>
void
setup(std::size_t n);
prepare_impl(std::size_t n);
template<class OutIter>
static
@@ -55,15 +57,27 @@ public:
using const_iterator = value_type const*;
chunk_encode_delim(chunk_encode_delim const& other)
/** Constructor (default)
Default-constructed chunk headers are in an
undefined state.
*/
chunk_header() = default;
/// Copy constructor
chunk_header(chunk_header const& other)
{
copy(other);
}
/** Construct a chunk header
@param n The number of octets in this chunk.
*/
explicit
chunk_encode_delim(std::size_t n)
chunk_header(std::size_t n)
{
setup(n);
prepare_impl(n);
}
const_iterator
@@ -77,31 +91,55 @@ public:
{
return begin() + 1;
}
void
prepare(std::size_t n)
{
prepare_impl(n);
}
};
template<class>
void
chunk_encode_delim::
copy(chunk_encode_delim const& other)
chunk_header::
copy(chunk_header const& other)
{
using boost::asio::buffer_copy;
auto const n =
boost::asio::buffer_size(other.cb_);
buf_ = other.buf_;
cb_ = boost::asio::const_buffer(
&buf_[buf_.size() - n], n);
auto const mb = boost::asio::mutable_buffers_1(
&buf_[sizeof(buf_) - n], n);
cb_ = *mb.begin();
buffer_copy(mb,
boost::asio::const_buffers_1(other.cb_));
}
template<class>
void
chunk_encode_delim::
setup(std::size_t n)
chunk_header::
prepare_impl(std::size_t n)
{
buf_[buf_.size() - 2] = '\r';
buf_[buf_.size() - 1] = '\n';
auto it = to_hex(buf_.end() - 2, n);
auto const end = &buf_[sizeof(buf_)];
auto it = to_hex(end, n);
cb_ = boost::asio::const_buffer{&*it,
static_cast<std::size_t>(
std::distance(it, buf_.end()))};
std::distance(it, end))};
}
/// Returns a buffer sequence holding a CRLF for chunk encoding
inline
boost::asio::const_buffers_1
chunk_crlf()
{
return {"\r\n", 2};
}
/// Returns a buffer sequence holding a final chunk header
inline
boost::asio::const_buffers_1
chunk_final()
{
return {"0\r\n", 3};
}
} // detail

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_HTTP_DETAIL_RFC7230_HPP
#define BEAST_HTTP_DETAIL_RFC7230_HPP
#include <boost/utility/string_ref.hpp>
#include <beast/core/string.hpp>
#include <iterator>
#include <utility>
@@ -45,8 +45,8 @@ is_alpha(char c)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return tab[static_cast<unsigned char>(c)];
}
inline
@@ -72,8 +72,8 @@ is_text(char c)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return tab[static_cast<unsigned char>(c)];
}
inline
@@ -104,8 +104,8 @@ is_tchar(char c)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return tab[static_cast<unsigned char>(c)];
}
inline
@@ -133,8 +133,8 @@ is_qdchar(char c)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return tab[static_cast<unsigned char>(c)];
}
inline
@@ -163,44 +163,8 @@ is_qpchar(char c)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
}
// converts to lower case,
// returns 0 if not a valid token char
//
inline
char
to_field_char(char c)
{
/* token = 1*<any CHAR except CTLs or separators>
CHAR = <any US-ASCII character (octets 0 - 127)>
sep = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
*/
static char constexpr tab[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return tab[static_cast<unsigned char>(c)];
}
// converts to lower case,
@@ -229,15 +193,16 @@ to_value_char(char c)
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
};
static_assert(sizeof(tab) == 256, "");
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return static_cast<char>(tab[static_cast<unsigned char>(c)]);
}
// VFALCO TODO Make this return unsigned?
inline
std::int8_t
unhex(char c)
{
static char constexpr tab[] = {
static signed char constexpr tab[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
@@ -255,23 +220,69 @@ unhex(char c)
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
return tab[static_cast<unsigned char>(c)];
}
template<class FwdIt>
inline
void
skip_ows(FwdIt& it, FwdIt const& end)
{
while(it != end)
{
auto const c = *it;
if(c != ' ' && c != '\t')
if(*it != ' ' && *it != '\t')
break;
++it;
}
}
template<class RanIt>
inline
void
skip_ows_rev(
RanIt& it, RanIt const& first)
{
while(it != first)
{
auto const c = it[-1];
if(c != ' ' && c != '\t')
break;
--it;
}
}
// obs-fold = CRLF 1*( SP / HTAB )
// return `false` on parse error
//
template<class FwdIt>
inline
bool
skip_obs_fold(
FwdIt& it, FwdIt const& last)
{
for(;;)
{
if(*it != '\r')
return true;
if(++it == last)
return false;
if(*it != '\n')
return false;
if(++it == last)
return false;
if(*it != ' ' && *it != '\t')
return false;
for(;;)
{
if(++it == last)
return true;
if(*it != ' ' && *it != '\t')
return true;
}
}
}
template<class FwdIt>
void
skip_token(FwdIt& it, FwdIt const& last)
@@ -281,8 +292,8 @@ skip_token(FwdIt& it, FwdIt const& last)
}
inline
boost::string_ref
trim(boost::string_ref const& s)
string_view
trim(string_view s)
{
auto first = s.begin();
auto last = s.end();
@@ -302,12 +313,12 @@ trim(boost::string_ref const& s)
struct param_iter
{
using iter_type = boost::string_ref::const_iterator;
using iter_type = string_view::const_iterator;
iter_type it;
iter_type first;
iter_type last;
std::pair<boost::string_ref, boost::string_ref> v;
std::pair<string_view, string_view> v;
bool
empty() const
@@ -403,6 +414,53 @@ increment()
}
}
/*
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
*/
struct opt_token_list_policy
{
using value_type = string_view;
bool
operator()(value_type& v,
char const*& it, string_view s) const
{
v = {};
auto need_comma = it != s.begin();
for(;;)
{
detail::skip_ows(it, s.end());
if(it == s.end())
{
it = nullptr;
return true;
}
auto const c = *it;
if(detail::is_tchar(c))
{
if(need_comma)
return false;
auto const p0 = it;
for(;;)
{
++it;
if(it == s.end())
break;
if(! detail::is_tchar(*it))
break;
}
v = string_view{&*p0,
static_cast<std::size_t>(it - p0)};
return true;
}
if(c != ',')
return false;
need_comma = false;
++it;
}
}
};
} // detail
} // http
} // beast

View File

@@ -0,0 +1,188 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
#define BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
#include <beast/core/detail/type_traits.hpp>
#include <boost/optional.hpp>
#include <cstdint>
namespace beast {
namespace http {
template<bool isRequest, class Fields>
struct header;
template<bool, class, class>
struct message;
template<bool isRequest,class Body, class Fields>
class parser;
namespace detail {
template<class T>
class is_header_impl
{
template<bool b, class F>
static std::true_type check(
header<b, F> const*);
static std::false_type check(...);
public:
using type = decltype(check((T*)0));
};
template<class T>
using is_header = typename is_header_impl<T>::type;
template<class T>
struct is_parser : std::false_type {};
template<bool isRequest, class Body, class Fields>
struct is_parser<parser<isRequest, Body, Fields>> : std::true_type {};
struct fields_model
{
string_view method() const;
string_view reason() const;
string_view target() const;
protected:
string_view get_method_impl() const;
string_view get_target_impl() const;
string_view get_reason_impl() const;
bool get_chunked_impl() const;
bool get_keep_alive_impl(unsigned) const;
void set_method_impl(string_view);
void set_target_impl(string_view);
void set_reason_impl(string_view);
void set_chunked_impl(bool);
void set_content_length_impl(boost::optional<std::uint64_t>);
void set_keep_alive_impl(unsigned, bool);
};
template<class T, class = beast::detail::void_t<>>
struct has_value_type : std::false_type {};
template<class T>
struct has_value_type<T, beast::detail::void_t<
typename T::value_type
> > : std::true_type {};
/** Determine if a @b Body type has a size
This metafunction is equivalent to `std::true_type` if
Body contains a static member function called `size`.
*/
template<class T, class = void>
struct is_body_sized : std::false_type {};
template<class T>
struct is_body_sized<T, beast::detail::void_t<
typename T::value_type,
decltype(
std::declval<std::uint64_t&>() =
T::size(std::declval<typename T::value_type const&>()),
(void)0)>> : std::true_type {};
template<class T>
struct is_fields_helper : T
{
template<class U = is_fields_helper>
static auto f1(int) -> decltype(
std::declval<string_view&>() = std::declval<U const&>().get_method_impl(),
std::true_type());
static auto f1(...) -> std::false_type;
using t1 = decltype(f1(0));
template<class U = is_fields_helper>
static auto f2(int) -> decltype(
std::declval<string_view&>() = std::declval<U const&>().get_target_impl(),
std::true_type());
static auto f2(...) -> std::false_type;
using t2 = decltype(f2(0));
template<class U = is_fields_helper>
static auto f3(int) -> decltype(
std::declval<string_view&>() = std::declval<U const&>().get_reason_impl(),
std::true_type());
static auto f3(...) -> std::false_type;
using t3 = decltype(f3(0));
template<class U = is_fields_helper>
static auto f4(int) -> decltype(
std::declval<bool&>() = std::declval<U const&>().get_chunked_impl(),
std::true_type());
static auto f4(...) -> std::false_type;
using t4 = decltype(f4(0));
template<class U = is_fields_helper>
static auto f5(int) -> decltype(
std::declval<bool&>() = std::declval<U const&>().get_keep_alive_impl(
std::declval<unsigned>()),
std::true_type());
static auto f5(...) -> std::false_type;
using t5 = decltype(f5(0));
template<class U = is_fields_helper>
static auto f6(int) -> decltype(
void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
std::true_type());
static auto f6(...) -> std::false_type;
using t6 = decltype(f6(0));
template<class U = is_fields_helper>
static auto f7(int) -> decltype(
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
std::true_type());
static auto f7(...) -> std::false_type;
using t7 = decltype(f7(0));
template<class U = is_fields_helper>
static auto f8(int) -> decltype(
void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
std::true_type());
static auto f8(...) -> std::false_type;
using t8 = decltype(f8(0));
template<class U = is_fields_helper>
static auto f9(int) -> decltype(
void(std::declval<U&>().set_chunked_impl(std::declval<bool>())),
std::true_type());
static auto f9(...) -> std::false_type;
using t9 = decltype(f9(0));
template<class U = is_fields_helper>
static auto f10(int) -> decltype(
void(std::declval<U&>().set_content_length_impl(
std::declval<boost::optional<std::uint64_t>>())),
std::true_type());
static auto f10(...) -> std::false_type;
using t10 = decltype(f10(0));
template<class U = is_fields_helper>
static auto f11(int) -> decltype(
void(std::declval<U&>().set_keep_alive_impl(
std::declval<unsigned>(),
std::declval<bool>())),
std::true_type());
static auto f11(...) -> std::false_type;
using t11 = decltype(f11(0));
using type = std::integral_constant<bool,
t1::value && t2::value && t3::value &&
t4::value && t5::value && t6::value &&
t7::value && t8::value && t9::value &&
t10::value && t11::value>;
};
} // detail
} // http
} // beast
#endif

View File

@@ -0,0 +1,168 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_DYNAMIC_BODY_HPP
#define BEAST_HTTP_DYNAMIC_BODY_HPP
#include <beast/config.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <utility>
namespace beast {
namespace http {
/** A @b Body using a @b DynamicBuffer
This body uses a @b DynamicBuffer as a memory-based container
for holding message payloads. Messages using this body type
may be serialized and parsed.
*/
template<class DynamicBuffer>
struct basic_dynamic_body
{
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
/** The type of container used for the body
This determines the type of @ref message::body
when this body type is used with a message container.
*/
using value_type = DynamicBuffer;
/** Returns the payload size of the body
When this body is used with @ref message::prepare_payload,
the Content-Length will be set to the payload size, and
any chunked Transfer-Encoding will be removed.
*/
static
std::uint64_t
size(value_type const& v)
{
return v.size();
}
/** The algorithm for serializing the body
Meets the requirements of @b BodyReader.
*/
#if BEAST_DOXYGEN
using reader = implementation_defined;
#else
class reader
{
DynamicBuffer const& body_;
public:
using const_buffers_type =
typename DynamicBuffer::const_buffers_type;
template<bool isRequest, class Fields>
explicit
reader(message<isRequest,
basic_dynamic_body, Fields> const& m)
: body_(m.body)
{
}
void
init(error_code& ec)
{
ec.assign(0, ec.category());
}
boost::optional<std::pair<const_buffers_type, bool>>
get(error_code& ec)
{
ec.assign(0, ec.category());
return {{body_.data(), false}};
}
};
#endif
/** The algorithm for parsing the body
Meets the requirements of @b BodyReader.
*/
#if BEAST_DOXYGEN
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
public:
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, basic_dynamic_body, Fields>& msg)
: body_(msg.body)
{
}
void
init(boost::optional<std::uint64_t> const&, error_code& ec)
{
ec.assign(0, ec.category());
}
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const& buffers,
error_code& ec)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
auto const n = buffer_size(buffers);
if(body_.size() > body_.max_size() - n)
{
ec = error::buffer_overflow;
return 0;
}
boost::optional<typename
DynamicBuffer::mutable_buffers_type> b;
try
{
b.emplace(body_.prepare((std::min)(n,
body_.max_size() - body_.size())));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return 0;
}
ec.assign(0, ec.category());
auto const bytes_transferred =
buffer_copy(*b, buffers);
body_.commit(bytes_transferred);
return bytes_transferred;
}
void
finish(error_code& ec)
{
ec.assign(0, ec.category());
}
};
#endif
};
/** A dynamic message body represented by a @ref multi_buffer
Meets the requirements of @b Body.
*/
using dynamic_body = basic_dynamic_body<multi_buffer>;
} // http
} // beast
#endif

View File

@@ -9,62 +9,117 @@
#define BEAST_HTTP_EMPTY_BODY_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <memory>
#include <string>
#include <boost/optional.hpp>
namespace beast {
namespace http {
/** An empty content-body.
/** An empty @b Body
Meets the requirements of @b `Body`.
This body is used to represent messages which do not have a
message body. If this body is used with a parser, and the
parser encounters octets corresponding to a message body,
the parser will fail with the error @ref http::unexpected_body.
The Content-Length of this body is always 0.
*/
struct empty_body
{
#if GENERATING_DOCS
/// The type of the `message::body` member
using value_type = void;
/** The type of container used for the body
This determines the type of @ref message::body
when this body type is used with a message container.
*/
struct value_type
{
};
/** Returns the payload size of the body
When this body is used with @ref message::prepare_payload,
the Content-Length will be set to the payload size, and
any chunked Transfer-Encoding will be removed.
*/
static
std::uint64_t
size(value_type)
{
return 0;
}
/** The algorithm for serializing the body
Meets the requirements of @b BodyReader.
*/
#if BEAST_DOXYGEN
using reader = implementation_defined;
#else
struct value_type {};
struct reader
{
using const_buffers_type =
boost::asio::null_buffers;
template<bool isRequest, class Fields>
explicit
reader(message<isRequest,
empty_body, Fields> const&)
{
}
void
init(error_code& ec)
{
ec.assign(0, ec.category());
}
boost::optional<std::pair<const_buffers_type, bool>>
get(error_code& ec)
{
ec.assign(0, ec.category());
return boost::none;
}
};
#endif
#if GENERATING_DOCS
private:
#endif
/** The algorithm for parsing the body
Meets the requirements of @b BodyReader.
*/
#if BEAST_DOXYGEN
using writer = implementation_defined;
#else
struct writer
{
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, empty_body, Fields> const& m) noexcept
writer(message<isRequest, empty_body, Fields>&)
{
beast::detail::ignore_unused(m);
}
void
init(error_code& ec) noexcept
init(boost::optional<std::uint64_t> const&, error_code& ec)
{
beast::detail::ignore_unused(ec);
ec.assign(0, ec.category());
}
std::uint64_t
content_length() const noexcept
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const&,
error_code& ec)
{
ec = error::unexpected_body;
return 0;
}
template<class WriteFunction>
bool
write(error_code&, WriteFunction&& wf) noexcept
void
finish(error_code& ec)
{
wf(boost::asio::null_buffers{});
return true;
ec.assign(0, ec.category());
}
};
#endif
};
} // http

View File

@@ -0,0 +1,150 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_ERROR_HPP
#define BEAST_HTTP_ERROR_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
namespace beast {
namespace http {
/// Error codes returned from HTTP algorithms and operations.
enum class error
{
/** The end of the stream was reached.
This error is returned under the following conditions:
@li When attempting to read HTTP data from a stream and the stream
read returns the error `boost::asio::error::eof` before any new octets
have been received.
@li When sending a complete HTTP message at once and the semantics of
the message are that the connection should be closed to indicate the
end of the message.
*/
end_of_stream = 1,
/** The incoming message is incomplete.
This happens when the end of stream is reached during
parsing and some octets have been received, but not the
entire message.
*/
partial_message,
/** Additional buffers are required.
This error is returned during parsing when additional
octets are needed. The caller should append more data
to the existing buffer and retry the parse operaetion.
*/
need_more,
/** An unexpected body was encountered during parsing.
This error is returned when attempting to parse body
octets into a message container which has the
@ref empty_body body type.
@see @ref empty_body
*/
unexpected_body,
/** Additional buffers are required.
This error is returned under the following conditions:
@li During serialization when using @ref buffer_body.
The caller should update the body to point to a new
buffer or indicate that there are no more octets in
the body.
@li During parsing when using @ref buffer_body.
The caller should update the body to point to a new
storage area to receive additional body octets.
*/
need_buffer,
/** Buffer maximum exceeded.
This error is returned when reading HTTP content
into a dynamic buffer, and the operation would
exceed the maximum size of the buffer.
*/
buffer_overflow,
/** Header limit exceeded.
The parser detected an incoming message header which
exceeded a configured limit.
*/
header_limit,
/** Body limit exceeded.
The parser detected an incoming message body which
exceeded a configured limit.
*/
body_limit,
/** A memory allocation failed.
When basic_fields throws std::bad_alloc, it is
converted into this error by @ref parser.
*/
bad_alloc,
//
// (parser errors)
//
/// The line ending was malformed
bad_line_ending,
/// The method is invalid.
bad_method,
/// The request-target is invalid.
bad_target,
/// The HTTP-version is invalid.
bad_version,
/// The status-code is invalid.
bad_status,
/// The reason-phrase is invalid.
bad_reason,
/// The field name is invalid.
bad_field,
/// The field value is invalid.
bad_value,
/// The Content-Length is invalid.
bad_content_length,
/// The Transfer-Encoding is invalid.
bad_transfer_encoding,
/// The chunk syntax is invalid.
bad_chunk,
/// An obs-fold exceeded an internal limit.
bad_obs_fold
};
} // http
} // beast
#include <beast/http/impl/error.ipp>
#endif

View File

@@ -0,0 +1,405 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_FIELD_HPP
#define BEAST_HTTP_FIELD_HPP
#include <beast/config.hpp>
#include <beast/core/string.hpp>
#include <iosfwd>
namespace beast {
namespace http {
enum class field : unsigned short
{
unknown = 0,
a_im,
accept,
accept_additions,
accept_charset,
accept_datetime,
accept_encoding,
accept_features,
accept_language,
accept_patch,
accept_post,
accept_ranges,
access_control,
access_control_allow_credentials,
access_control_allow_headers,
access_control_allow_methods,
access_control_allow_origin,
access_control_max_age,
access_control_request_headers,
access_control_request_method,
age,
allow,
alpn,
also_control,
alt_svc,
alt_used,
alternate_recipient,
alternates,
apparently_to,
apply_to_redirect_ref,
approved,
archive,
archived_at,
article_names,
article_updates,
authentication_control,
authentication_info,
authentication_results,
authorization,
auto_submitted,
autoforwarded,
autosubmitted,
base,
bcc,
body,
c_ext,
c_man,
c_opt,
c_pep,
c_pep_info,
cache_control,
caldav_timezones,
cancel_key,
cancel_lock,
cc,
close,
comments,
compliance,
connection,
content_alternative,
content_base,
content_description,
content_disposition,
content_duration,
content_encoding,
content_features,
content_id,
content_identifier,
content_language,
content_length,
content_location,
content_md5,
content_range,
content_return,
content_script_type,
content_style_type,
content_transfer_encoding,
content_type,
content_version,
control,
conversion,
conversion_with_loss,
cookie,
cookie2,
cost,
dasl,
date,
date_received,
dav,
default_style,
deferred_delivery,
delivery_date,
delta_base,
depth,
derived_from,
destination,
differential_id,
digest,
discarded_x400_ipms_extensions,
discarded_x400_mts_extensions,
disclose_recipients,
disposition_notification_options,
disposition_notification_to,
distribution,
dkim_signature,
dl_expansion_history,
downgraded_bcc,
downgraded_cc,
downgraded_disposition_notification_to,
downgraded_final_recipient,
downgraded_from,
downgraded_in_reply_to,
downgraded_mail_from,
downgraded_message_id,
downgraded_original_recipient,
downgraded_rcpt_to,
downgraded_references,
downgraded_reply_to,
downgraded_resent_bcc,
downgraded_resent_cc,
downgraded_resent_from,
downgraded_resent_reply_to,
downgraded_resent_sender,
downgraded_resent_to,
downgraded_return_path,
downgraded_sender,
downgraded_to,
ediint_features,
eesst_version,
encoding,
encrypted,
errors_to,
etag,
expect,
expires,
expiry_date,
ext,
followup_to,
forwarded,
from,
generate_delivery_report,
getprofile,
hobareg,
host,
http2_settings,
if_,
if_match,
if_modified_since,
if_none_match,
if_range,
if_schedule_tag_match,
if_unmodified_since,
im,
importance,
in_reply_to,
incomplete_copy,
injection_date,
injection_info,
jabber_id,
keep_alive,
keywords,
label,
language,
last_modified,
latest_delivery_time,
lines,
link,
list_archive,
list_help,
list_id,
list_owner,
list_post,
list_subscribe,
list_unsubscribe,
list_unsubscribe_post,
location,
lock_token,
man,
max_forwards,
memento_datetime,
message_context,
message_id,
message_type,
meter,
method_check,
method_check_expires,
mime_version,
mmhs_acp127_message_identifier,
mmhs_authorizing_users,
mmhs_codress_message_indicator,
mmhs_copy_precedence,
mmhs_exempted_address,
mmhs_extended_authorisation_info,
mmhs_handling_instructions,
mmhs_message_instructions,
mmhs_message_type,
mmhs_originator_plad,
mmhs_originator_reference,
mmhs_other_recipients_indicator_cc,
mmhs_other_recipients_indicator_to,
mmhs_primary_precedence,
mmhs_subject_indicator_codes,
mt_priority,
negotiate,
newsgroups,
nntp_posting_date,
nntp_posting_host,
non_compliance,
obsoletes,
opt,
optional,
optional_www_authenticate,
ordering_type,
organization,
origin,
original_encoded_information_types,
original_from,
original_message_id,
original_recipient,
original_sender,
original_subject,
originator_return_address,
overwrite,
p3p,
path,
pep,
pep_info,
pics_label,
position,
posting_version,
pragma,
prefer,
preference_applied,
prevent_nondelivery_report,
priority,
privicon,
profileobject,
protocol,
protocol_info,
protocol_query,
protocol_request,
proxy_authenticate,
proxy_authentication_info,
proxy_authorization,
proxy_connection,
proxy_features,
proxy_instruction,
public_,
public_key_pins,
public_key_pins_report_only,
range,
received,
received_spf,
redirect_ref,
references,
referer,
referer_root,
relay_version,
reply_by,
reply_to,
require_recipient_valid_since,
resent_bcc,
resent_cc,
resent_date,
resent_from,
resent_message_id,
resent_reply_to,
resent_sender,
resent_to,
resolution_hint,
resolver_location,
retry_after,
return_path,
safe,
schedule_reply,
schedule_tag,
sec_websocket_accept,
sec_websocket_extensions,
sec_websocket_key,
sec_websocket_protocol,
sec_websocket_version,
security_scheme,
see_also,
sender,
sensitivity,
server,
set_cookie,
set_cookie2,
setprofile,
sio_label,
sio_label_history,
slug,
soapaction,
solicitation,
status_uri,
strict_transport_security,
subject,
subok,
subst,
summary,
supersedes,
surrogate_capability,
surrogate_control,
tcn,
te,
timeout,
title,
to,
topic,
trailer,
transfer_encoding,
ttl,
ua_color,
ua_media,
ua_pixels,
ua_resolution,
ua_windowpixels,
upgrade,
urgency,
uri,
user_agent,
variant_vary,
vary,
vbr_info,
version,
via,
want_digest,
warning,
www_authenticate,
x_archived_at,
x_device_accept,
x_device_accept_charset,
x_device_accept_encoding,
x_device_accept_language,
x_device_user_agent,
x_frame_options,
x_mittente,
x_pgp_sig,
x_ricevuta,
x_riferimento_message_id,
x_tiporicevuta,
x_trasporto,
x_verificasicurezza,
x400_content_identifier,
x400_content_return,
x400_content_type,
x400_mts_identifier,
x400_originator,
x400_received,
x400_recipients,
x400_trace,
xref,
};
/** Convert a field enum to a string.
@param f The field to convert
*/
string_view
to_string(field f);
/** Attempt to convert a string to a field enum.
The string comparison is case-insensitive.
@return The corresponding field, or @ref field::unknown
if no known field matches.
*/
field
string_to_field(string_view s);
/// Write the text for a field name to an output stream.
inline
std::ostream&
operator<<(std::ostream& os, field f)
{
return os << to_string(f);
}
} // http
} // beast
#include <beast/http/impl/field.ipp>
#endif

View File

@@ -9,17 +9,709 @@
#define BEAST_HTTP_FIELDS_HPP
#include <beast/config.hpp>
#include <beast/http/basic_fields.hpp>
#include <beast/core/string_param.hpp>
#include <beast/core/string.hpp>
#include <beast/http/field.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A container for storing HTTP header fields.
This container is designed to store the field value pairs that make
up the fields and trailers in an HTTP message. Objects of this type
are iterable, with each element holding the field name and field
value.
Field names are stored as-is, but comparisons are case-insensitive.
The container behaves as a `std::multiset`; there will be a separate
value for each occurrence of the same field name. When the container
is iterated the fields are presented in the order of insertion, with
fields having the same name following each other consecutively.
Meets the requirements of @b Fields
@tparam Allocator The allocator to use. This must meet the
requirements of @b Allocator.
*/
template<class Allocator>
class basic_fields
{
static std::size_t constexpr max_static_buffer = 4096;
using off_t = std::uint16_t;
public:
/// The type of allocator used.
using allocator_type = Allocator;
/// The type of element used to represent a field
class value_type
{
friend class basic_fields;
boost::asio::const_buffer
buffer() const;
value_type(field name,
string_view sname, string_view value);
boost::intrusive::list_member_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
list_hook_;
boost::intrusive::set_member_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
set_hook_;
off_t off_;
off_t len_;
field f_;
public:
/// Returns the field enum, which can be @ref field::unknown
field
name() const;
/// Returns the field name as a string
string_view
name_string() const;
/// Returns the value of the field
string_view
value() const;
};
/** A strictly less predicate for comparing keys, using a case-insensitive comparison.
The case-comparison operation is defined only for low-ASCII characters.
*/
struct key_compare : beast::iless
{
/// Returns `true` if lhs is less than rhs using a strict ordering
template<class String>
bool
operator()(String const& lhs, value_type const& rhs) const
{
if(lhs.size() < rhs.name_string().size())
return true;
if(lhs.size() > rhs.name_string().size())
return false;
return iless::operator()(lhs, rhs.name_string());
}
/// Returns `true` if lhs is less than rhs using a strict ordering
template<class String>
bool
operator()(value_type const& lhs, String const& rhs) const
{
if(lhs.name_string().size() < rhs.size())
return true;
if(lhs.name_string().size() > rhs.size())
return false;
return iless::operator()(lhs.name_string(), rhs);
}
/// Returns `true` if lhs is less than rhs using a strict ordering
bool
operator()(value_type const& lhs, value_type const& rhs) const
{
if(lhs.name_string().size() < rhs.name_string().size())
return true;
if(lhs.name_string().size() > rhs.name_string().size())
return false;
return iless::operator()(lhs.name_string(), rhs.name_string());
}
};
/// The algorithm used to serialize the header
#if BEAST_DOXYGEN
using reader = implementation_defined;
#else
class reader;
#endif
private:
using list_t = typename boost::intrusive::make_list<
value_type, boost::intrusive::member_hook<
value_type, boost::intrusive::list_member_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>,
&value_type::list_hook_>,
boost::intrusive::constant_time_size<
false>>::type;
using set_t = typename boost::intrusive::make_multiset<
value_type, boost::intrusive::member_hook<value_type,
boost::intrusive::set_member_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>,
&value_type::set_hook_>,
boost::intrusive::constant_time_size<true>,
boost::intrusive::compare<key_compare>>::type;
protected:
friend class fields_test; // for `header`
/// Destructor
~basic_fields();
/// Constructor.
basic_fields() = default;
/** Constructor.
@param alloc The allocator to use.
*/
explicit
basic_fields(Allocator const& alloc);
/** Move constructor.
The state of the moved-from object is
as if constructed using the same allocator.
*/
basic_fields(basic_fields&&);
/** Move constructor.
The state of the moved-from object is
as if constructed using the same allocator.
@param alloc The allocator to use.
*/
basic_fields(basic_fields&&, Allocator const& alloc);
/// Copy constructor.
basic_fields(basic_fields const&);
/** Copy constructor.
@param alloc The allocator to use.
*/
basic_fields(basic_fields const&, Allocator const& alloc);
/// Copy constructor.
template<class OtherAlloc>
basic_fields(basic_fields<OtherAlloc> const&);
/** Copy constructor.
@param alloc The allocator to use.
*/
template<class OtherAlloc>
basic_fields(basic_fields<OtherAlloc> const&,
Allocator const& alloc);
/** Move assignment.
The state of the moved-from object is
as if constructed using the same allocator.
*/
basic_fields& operator=(basic_fields&&);
/// Copy assignment.
basic_fields& operator=(basic_fields const&);
/// Copy assignment.
template<class OtherAlloc>
basic_fields& operator=(basic_fields<OtherAlloc> const&);
public:
/// A constant iterator to the field sequence.
#if BEAST_DOXYGEN
using const_iterator = implementation_defined;
#else
using const_iterator = typename list_t::const_iterator;
#endif
/// A constant iterator to the field sequence.
using iterator = const_iterator;
/// Return a copy of the allocator associated with the container.
allocator_type
get_allocator() const
{
return typename std::allocator_traits<
Allocator>::template rebind_alloc<
value_type>(alloc_);
}
//--------------------------------------------------------------------------
//
// Element access
//
//--------------------------------------------------------------------------
/** Returns the value for a field, or throws an exception.
@param name The name of the field.
@return The field value.
@throws std::out_of_range if the field is not found.
*/
string_view
at(field name) const;
/** Returns the value for a field, or throws an exception.
@param name The name of the field.
@return The field value.
@throws std::out_of_range if the field is not found.
*/
string_view
at(string_view name) const;
/** Returns the value for a field, or `""` if it does not exist.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
@param name The name of the field.
*/
string_view
operator[](field name) const;
/** Returns the value for a case-insensitive matching header, or `""` if it does not exist.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
@param name The name of the field.
*/
string_view
operator[](string_view name) const;
//--------------------------------------------------------------------------
//
// Iterators
//
//--------------------------------------------------------------------------
/// Return a const iterator to the beginning of the field sequence.
const_iterator
begin() const
{
return list_.cbegin();
}
/// Return a const iterator to the end of the field sequence.
const_iterator
end() const
{
return list_.cend();
}
/// Return a const iterator to the beginning of the field sequence.
const_iterator
cbegin() const
{
return list_.cbegin();
}
/// Return a const iterator to the end of the field sequence.
const_iterator
cend() const
{
return list_.cend();
}
//--------------------------------------------------------------------------
//
// Capacity
//
//--------------------------------------------------------------------------
private:
// VFALCO Since the header and message derive from Fields,
// what does the expression m.empty() mean? Its confusing.
bool
empty() const
{
return list_.empty();
}
public:
//--------------------------------------------------------------------------
//
// Modifiers
//
//--------------------------------------------------------------------------
private:
// VFALCO But this leaves behind the method, target, and reason!
/** Remove all fields from the container
All references, pointers, or iterators referring to contained
elements are invalidated. All past-the-end iterators are also
invalidated.
*/
void
clear();
public:
/** Insert a field.
If one or more fields with the same name already exist,
the new field will be inserted after the last field with
the matching name, in serialization order.
@param name The field name.
@param value The value of the field, as a @ref string_param
*/
void
insert(field name, string_param const& value);
/** Insert a field.
If one or more fields with the same name already exist,
the new field will be inserted after the last field with
the matching name, in serialization order.
@param name The field name.
@param value The value of the field, as a @ref string_param
*/
void
insert(string_view name, string_param const& value);
/** Insert a field.
If one or more fields with the same name already exist,
the new field will be inserted after the last field with
the matching name, in serialization order.
@param name The field name.
@param name_string The literal text corresponding to the
field name. If `name != field::unknown`, then this value
must be equal to `to_string(name)` using a case-insensitive
comparison, otherwise the behavior is undefined.
@param value The value of the field, as a @ref string_param
*/
void
insert(field name, string_view name_string,
string_param const& value);
/** Set a field value, removing any other instances of that field.
First removes any values with matching field names, then
inserts the new field value.
@param name The field name.
@param value The value of the field, as a @ref string_param
@return The field value.
*/
void
set(field name, string_param const& value);
/** Set a field value, removing any other instances of that field.
First removes any values with matching field names, then
inserts the new field value.
@param name The field name.
@param value The value of the field, as a @ref string_param
*/
void
set(string_view name, string_param const& value);
/** Remove a field.
References and iterators to the erased elements are
invalidated. Other references and iterators are not
affected.
@param pos An iterator to the element to remove.
@return An iterator following the last removed element.
If the iterator refers to the last element, the end()
iterator is returned.
*/
const_iterator
erase(const_iterator pos);
/** Remove all fields with the specified name.
All fields with the same field name are erased from the
container.
References and iterators to the erased elements are
invalidated. Other references and iterators are not
affected.
@param name The field name.
@return The number of fields removed.
*/
std::size_t
erase(field name);
/** Remove all fields with the specified name.
All fields with the same field name are erased from the
container.
References and iterators to the erased elements are
invalidated. Other references and iterators are not
affected.
@param name The field name.
@return The number of fields removed.
*/
std::size_t
erase(string_view name);
/// Swap this container with another
void
swap(basic_fields& other);
/// Swap two field containers
template<class Alloc>
friend
void
swap(basic_fields<Alloc>& lhs, basic_fields<Alloc>& rhs);
//--------------------------------------------------------------------------
//
// Lookup
//
//--------------------------------------------------------------------------
/** Return the number of fields with the specified name.
@param name The field name.
*/
std::size_t
count(field name) const;
/** Return the number of fields with the specified name.
@param name The field name.
*/
std::size_t
count(string_view name) const;
/** Returns an iterator to the case-insensitive matching field.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
@param name The field name.
@return An iterator to the matching field, or `end()` if
no match was found.
*/
const_iterator
find(field name) const;
/** Returns an iterator to the case-insensitive matching field name.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
@param name The field name.
@return An iterator to the matching field, or `end()` if
no match was found.
*/
const_iterator
find(string_view name) const;
/** Returns a range of iterators to the fields with the specified name.
@param name The field name.
@return A range of iterators to fields with the same name,
otherwise an empty range.
*/
std::pair<const_iterator, const_iterator>
equal_range(field name) const;
/** Returns a range of iterators to the fields with the specified name.
@param name The field name.
@return A range of iterators to fields with the same name,
otherwise an empty range.
*/
std::pair<const_iterator, const_iterator>
equal_range(string_view name) const;
//--------------------------------------------------------------------------
//
// Observers
//
//--------------------------------------------------------------------------
/// Returns a copy of the key comparison function
key_compare
key_comp() const
{
return key_compare{};
}
protected:
/** Returns the request-method string.
@note Only called for requests.
*/
string_view
get_method_impl() const;
/** Returns the request-target string.
@note Only called for requests.
*/
string_view
get_target_impl() const;
/** Returns the response reason-phrase string.
@note Only called for responses.
*/
string_view
get_reason_impl() const;
/** Returns the chunked Transfer-Encoding setting
*/
bool
get_chunked_impl() const;
/** Returns the keep-alive setting
*/
bool
get_keep_alive_impl(unsigned version) const;
/** Set or clear the method string.
@note Only called for requests.
*/
void
set_method_impl(string_view s);
/** Set or clear the target string.
@note Only called for requests.
*/
void
set_target_impl(string_view s);
/** Set or clear the reason string.
@note Only called for responses.
*/
void
set_reason_impl(string_view s);
/** Adjusts the chunked Transfer-Encoding value
*/
void
set_chunked_impl(bool value);
/** Sets or clears the Content-Length field
*/
void
set_content_length_impl(
boost::optional<std::uint64_t> const& value);
/** Adjusts the Connection field
*/
void
set_keep_alive_impl(
unsigned version, bool keep_alive);
private:
template<class OtherAlloc>
friend class basic_fields;
using alloc_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<value_type>;
using alloc_traits =
std::allocator_traits<alloc_type>;
using size_type =
typename std::allocator_traits<Allocator>::size_type;
value_type&
new_element(field name,
string_view sname, string_view value);
void
delete_element(value_type& e);
void
set_element(value_type& e);
void
realloc_string(string_view& dest, string_view s);
void
realloc_target(
string_view& dest, string_view s);
template<class OtherAlloc>
void
copy_all(basic_fields<OtherAlloc> const&);
void
clear_all();
void
delete_list();
void
move_assign(basic_fields&, std::true_type);
void
move_assign(basic_fields&, std::false_type);
void
copy_assign(basic_fields const&, std::true_type);
void
copy_assign(basic_fields const&, std::false_type);
void
swap(basic_fields& other, std::true_type);
void
swap(basic_fields& other, std::false_type);
alloc_type alloc_;
set_t set_;
list_t list_;
string_view method_;
string_view target_or_reason_;
};
/// A typical HTTP header fields container
using fields =
basic_fields<std::allocator<char>>;
using fields = basic_fields<std::allocator<char>>;
} // http
} // beast
#include <beast/http/impl/fields.ipp>
#endif

View File

@@ -0,0 +1,522 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_FILE_BODY_HPP
#define BEAST_HTTP_FILE_BODY_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/file.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/http/message.hpp>
#include <boost/assert.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <cstdio>
#include <cstdint>
#include <utility>
namespace beast {
namespace http {
//[example_http_file_body_1
/** A message body represented by a file on the filesystem.
Messages with this type have bodies represented by a
file on the file system. When parsing a message using
this body type, the data is stored in the file pointed
to by the path, which must be writable. When serializing,
the implementation will read the file and present those
octets as the body content. This may be used to serve
content from a directory as part of a web service.
@tparam File The implementation to use for accessing files.
This type must meet the requirements of @b File.
*/
template<class File>
struct basic_file_body
{
static_assert(is_file<File>::value,
"File requirements not met");
/// The type of File this body uses
using file_type = File;
/** Algorithm for retrieving buffers when serializing.
Objects of this type are created during serialization
to extract the buffers representing the body.
*/
class reader;
/** Algorithm for storing buffers when parsing.
Objects of this type are created during parsing
to store incoming buffers representing the body.
*/
class writer;
/** The type of the @ref message::body member.
Messages declared using `basic_file_body` will have this
type for the body member. This rich class interface
allow the file to be opened with the file handle
maintained directly in the object, which is attached
to the message.
*/
class value_type;
/** Returns the size of the body
@param v The file body to use
*/
static
std::uint64_t
size(value_type const& v);
};
//]
//[example_http_file_body_2
// The body container holds a handle to the file when
// it is open, and also caches the size when set.
//
template<class File>
class basic_file_body<File>::value_type
{
friend class reader;
friend class writer;
friend struct basic_file_body;
// This represents the open file
File file_;
// The cached file size
std::uint64_t file_size_ = 0;
public:
/** Destructor.
If the file is open, it is closed first.
*/
~value_type() = default;
/// Constructor
value_type() = default;
/// Constructor
value_type(value_type&& other) = default;
/// Move assignment
value_type& operator=(value_type&& other) = default;
/// Returns `true` if the file is open
bool
is_open() const
{
return file_.is_open();
}
/// Returns the size of the file if open
std::uint64_t
size() const
{
return file_size_;
}
/// Close the file if open
void
close();
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
void
open(char const* path, file_mode mode, error_code& ec);
/** Set the open file
This function is used to set the open
*/
void
reset(File&& file, error_code& ec);
};
template<class File>
void
basic_file_body<File>::
value_type::
close()
{
error_code ignored;
file_.close(ignored);
}
template<class File>
void
basic_file_body<File>::
value_type::
open(char const* path, file_mode mode, error_code& ec)
{
// Open the file
file_.open(path, mode, ec);
if(ec)
return;
// Cache the size
file_size_ = file_.size(ec);
if(ec)
{
close();
return;
}
}
template<class File>
void
basic_file_body<File>::
value_type::
reset(File&& file, error_code& ec)
{
// First close the file if open
if(file_.is_open())
{
error_code ignored;
file_.close(ignored);
}
// Take ownership of the new file
file_ = std::move(file);
// Cache the size
file_size_ = file_.size(ec);
}
// This is called from message::payload_size
template<class File>
std::uint64_t
basic_file_body<File>::
size(value_type const& v)
{
// Forward the call to the body
return v.size();
}
//]
//[example_http_file_body_3
template<class File>
class basic_file_body<File>::reader
{
value_type& body_; // The body we are reading from
std::uint64_t remain_; // The number of unread bytes
char buf_[4096]; // Small buffer for reading
public:
// The type of buffer sequence returned by `get`.
//
using const_buffers_type =
boost::asio::const_buffers_1;
// Constructor.
//
// `m` holds the message we are sending, which will
// always have the `file_body` as the body type.
//
// Note that the message is passed by non-const reference.
// This is intentional, because reading from the file
// changes its "current position" which counts makes the
// operation logically not-const (although it is bitwise
// const).
//
// The BodyReader concept allows the reader to choose
// whether to take the message by const reference or
// non-const reference. Depending on the choice, a
// serializer constructed using that body type will
// require the same const or non-const reference to
// construct.
//
// Readers which accept const messages usually allow
// the same body to be serialized by multiple threads
// concurrently, while readers accepting non-const
// messages may only be serialized by one thread at
// a time.
//
template<bool isRequest, class Fields>
reader(message<
isRequest, basic_file_body, Fields>& m);
// Initializer
//
// This is called before the body is serialized and
// gives the reader a chance to do something that might
// need to return an error code.
//
void
init(error_code& ec);
// This function is called zero or more times to
// retrieve buffers. A return value of `boost::none`
// means there are no more buffers. Otherwise,
// the contained pair will have the next buffer
// to serialize, and a `bool` indicating whether
// or not there may be additional buffers.
boost::optional<std::pair<const_buffers_type, bool>>
get(error_code& ec);
};
//]
//[example_http_file_body_4
// Here we just stash a reference to the path for later.
// Rather than dealing with messy constructor exceptions,
// we save the things that might fail for the call to `init`.
//
template<class File>
template<bool isRequest, class Fields>
basic_file_body<File>::
reader::
reader(message<isRequest, basic_file_body, Fields>& m)
: body_(m.body)
{
// The file must already be open
BOOST_ASSERT(body_.file_.is_open());
// Get the size of the file
remain_ = body_.file_size_;
}
// Initializer
template<class File>
void
basic_file_body<File>::
reader::
init(error_code& ec)
{
// The error_code specification requires that we
// either set the error to some value, or set it
// to indicate no error.
//
// We don't do anything fancy so set "no error"
ec.assign(0, ec.category());
}
// This function is called repeatedly by the serializer to
// retrieve the buffers representing the body. Our strategy
// is to read into our buffer and return it until we have
// read through the whole file.
//
template<class File>
auto
basic_file_body<File>::
reader::
get(error_code& ec) ->
boost::optional<std::pair<const_buffers_type, bool>>
{
// Calculate the smaller of our buffer size,
// or the amount of unread data in the file.
auto const amount = remain_ > sizeof(buf_) ?
sizeof(buf_) : static_cast<std::size_t>(remain_);
// Handle the case where the file is zero length
if(amount == 0)
{
// Modify the error code to indicate success
// This is required by the error_code specification.
//
// NOTE We use the existing category instead of calling
// into the library to get the generic category because
// that saves us a possibly expensive atomic operation.
//
ec.assign(0, ec.category());
return boost::none;
}
// Now read the next buffer
auto const nread = body_.file_.read(buf_, amount, ec);
if(ec)
return boost::none;
// Make sure there is forward progress
BOOST_ASSERT(nread != 0);
BOOST_ASSERT(nread <= remain_);
// Update the amount remaining based on what we got
remain_ -= nread;
// Return the buffer to the caller.
//
// The second element of the pair indicates whether or
// not there is more data. As long as there is some
// unread bytes, there will be more data. Otherwise,
// we set this bool to `false` so we will not be called
// again.
//
ec.assign(0, ec.category());
return {{
const_buffers_type{buf_, nread}, // buffer to return.
remain_ > 0 // `true` if there are more buffers.
}};
}
//]
//[example_http_file_body_5
template<class File>
class basic_file_body<File>::writer
{
value_type& body_; // The body we are writing to
public:
// Constructor.
//
// This is called after the header is parsed and
// indicates that a non-zero sized body may be present.
// `m` holds the message we are receiving, which will
// always have the `file_body` as the body type.
//
template<bool isRequest, class Fields>
explicit
writer(
message<isRequest, basic_file_body, Fields>& m);
// Initializer
//
// This is called before the body is parsed and
// gives the writer a chance to do something that might
// need to return an error code. It informs us of
// the payload size (`content_length`) which we can
// optionally use for optimization.
//
void
init(boost::optional<std::uint64_t> const&, error_code& ec);
// This function is called one or more times to store
// buffer sequences corresponding to the incoming body.
//
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const& buffers,
error_code& ec);
// This function is called when writing is complete.
// It is an opportunity to perform any final actions
// which might fail, in order to return an error code.
// Operations that might fail should not be attemped in
// destructors, since an exception thrown from there
// would terminate the program.
//
void
finish(error_code& ec);
};
//]
//[example_http_file_body_6
// We don't do much in the writer constructor since the
// file is already open.
//
template<class File>
template<bool isRequest, class Fields>
basic_file_body<File>::
writer::
writer(message<isRequest, basic_file_body, Fields>& m)
: body_(m.body)
{
}
template<class File>
void
basic_file_body<File>::
writer::
init(
boost::optional<std::uint64_t> const& content_length,
error_code& ec)
{
// The file must already be open for writing
BOOST_ASSERT(body_.file_.is_open());
// We don't do anything with this but a sophisticated
// application might check available space on the device
// to see if there is enough room to store the body.
boost::ignore_unused(content_length);
// The error_code specification requires that we
// either set the error to some value, or set it
// to indicate no error.
//
// We don't do anything fancy so set "no error"
ec.assign(0, ec.category());
}
// This will get called one or more times with body buffers
//
template<class File>
template<class ConstBufferSequence>
std::size_t
basic_file_body<File>::
writer::
put(ConstBufferSequence const& buffers, error_code& ec)
{
// This function must return the total number of
// bytes transferred from the input buffers.
std::size_t nwritten = 0;
// Loop over all the buffers in the sequence,
// and write each one to the file.
for(boost::asio::const_buffer buffer : buffers)
{
// Write this buffer to the file
nwritten += body_.file_.write(
boost::asio::buffer_cast<void const*>(buffer),
boost::asio::buffer_size(buffer),
ec);
if(ec)
return nwritten;
}
// Indicate success
// This is required by the error_code specification
ec.assign(0, ec.category());
return nwritten;
}
// Called after writing is done when there's no error.
template<class File>
void
basic_file_body<File>::
writer::
finish(error_code& ec)
{
// This has to be cleared before returning, to
// indicate no error. The specification requires it.
ec.assign(0, ec.category());
}
//]
/// A message body represented by a file on the filesystem.
using file_body = basic_file_body<file>;
} // http
} // beast
#include <beast/http/impl/file_body_win32.ipp>
#endif

View File

@@ -1,233 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_HEADERS_PARSER_V1_HPP
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
#include <beast/config.hpp>
#include <beast/http/basic_parser_v1.hpp>
#include <beast/http/concepts.hpp>
#include <beast/http/message.hpp>
#include <beast/core/error.hpp>
#include <boost/assert.hpp>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
struct request_parser_base
{
std::string method_;
std::string uri_;
};
struct response_parser_base
{
std::string reason_;
};
} // detail
/** A parser for a HTTP/1 request or response header.
This class uses the HTTP/1 wire format parser to
convert a series of octets into a request or
response @ref header.
@note A new instance of the parser is required for each message.
*/
template<bool isRequest, class Fields>
class header_parser_v1
: public basic_parser_v1<isRequest,
header_parser_v1<isRequest, Fields>>
, private std::conditional<isRequest,
detail::request_parser_base,
detail::response_parser_base>::type
{
public:
/// The type of the header this parser produces.
using header_type = header<isRequest, Fields>;
private:
// VFALCO Check Fields requirements?
std::string field_;
std::string value_;
header_type h_;
bool flush_ = false;
public:
/// Default constructor
header_parser_v1() = default;
/// Move constructor
header_parser_v1(header_parser_v1&&) = default;
/// Copy constructor (disallowed)
header_parser_v1(header_parser_v1 const&) = delete;
/// Move assignment (disallowed)
header_parser_v1& operator=(header_parser_v1&&) = delete;
/// Copy assignment (disallowed)
header_parser_v1& operator=(header_parser_v1 const&) = delete;
/** Construct the parser.
@param args Forwarded to the header constructor.
*/
#if GENERATING_DOCS
template<class... Args>
explicit
header_parser_v1(Args&&... args);
#else
template<class Arg1, class... ArgN,
class = typename std::enable_if<! std::is_same<
typename std::decay<Arg1>::type, header_parser_v1>::value>>
explicit
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
: h_(std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...)
{
}
#endif
/** Returns the parsed header
Only valid if @ref complete would return `true`.
*/
header_type const&
get() const
{
return h_;
}
/** Returns the parsed header.
Only valid if @ref complete would return `true`.
*/
header_type&
get()
{
return h_;
}
/** Returns ownership of the parsed header.
Ownership is transferred to the caller. Only
valid if @ref complete would return `true`.
Requires:
@ref header_type is @b MoveConstructible
*/
header_type
release()
{
static_assert(std::is_move_constructible<decltype(h_)>::value,
"MoveConstructible requirements not met");
return std::move(h_);
}
private:
friend class basic_parser_v1<isRequest, header_parser_v1>;
void flush()
{
if(! flush_)
return;
flush_ = false;
BOOST_ASSERT(! field_.empty());
h_.fields.insert(field_, value_);
field_.clear();
value_.clear();
}
void on_start(error_code&)
{
}
void on_method(boost::string_ref const& s, error_code&)
{
this->method_.append(s.data(), s.size());
}
void on_uri(boost::string_ref const& s, error_code&)
{
this->uri_.append(s.data(), s.size());
}
void on_reason(boost::string_ref const& s, error_code&)
{
this->reason_.append(s.data(), s.size());
}
void on_request_or_response(std::true_type)
{
h_.method = std::move(this->method_);
h_.url = std::move(this->uri_);
}
void on_request_or_response(std::false_type)
{
h_.status = this->status_code();
h_.reason = std::move(this->reason_);
}
void on_request(error_code& ec)
{
on_request_or_response(
std::integral_constant<bool, isRequest>{});
}
void on_response(error_code& ec)
{
on_request_or_response(
std::integral_constant<bool, isRequest>{});
}
void on_field(boost::string_ref const& s, error_code&)
{
flush();
field_.append(s.data(), s.size());
}
void on_value(boost::string_ref const& s, error_code&)
{
value_.append(s.data(), s.size());
flush_ = true;
}
void
on_header(std::uint64_t, error_code&)
{
flush();
h_.version = 10 * this->http_major() + this->http_minor();
}
body_what
on_body_what(std::uint64_t, error_code&)
{
return body_what::pause;
}
void on_body(boost::string_ref const&, error_code&)
{
}
void on_complete(error_code&)
{
}
};
} // http
} // beast
#endif

View File

@@ -1,266 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
#define BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
#include <beast/http/detail/rfc7230.hpp>
#include <algorithm>
namespace beast {
namespace http {
template<class Allocator>
void
basic_fields<Allocator>::
delete_all()
{
for(auto it = list_.begin(); it != list_.end();)
{
auto& e = *it++;
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(
this->member(), &e, 1);
}
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
move_assign(basic_fields& other, std::false_type)
{
if(this->member() != other.member())
{
copy_from(other);
other.clear();
}
else
{
set_ = std::move(other.set_);
list_ = std::move(other.list_);
}
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
move_assign(basic_fields& other, std::true_type)
{
this->member() = std::move(other.member());
set_ = std::move(other.set_);
list_ = std::move(other.list_);
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
copy_assign(basic_fields const& other, std::false_type)
{
copy_from(other);
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
copy_assign(basic_fields const& other, std::true_type)
{
this->member() = other.member();
copy_from(other);
}
//------------------------------------------------------------------------------
template<class Allocator>
basic_fields<Allocator>::
~basic_fields()
{
delete_all();
}
template<class Allocator>
basic_fields<Allocator>::
basic_fields(Allocator const& alloc)
: beast::detail::empty_base_optimization<
alloc_type>(alloc)
{
}
template<class Allocator>
basic_fields<Allocator>::
basic_fields(basic_fields&& other)
: beast::detail::empty_base_optimization<alloc_type>(
std::move(other.member()))
, detail::basic_fields_base(
std::move(other.set_), std::move(other.list_))
{
}
template<class Allocator>
auto
basic_fields<Allocator>::
operator=(basic_fields&& other) ->
basic_fields&
{
if(this == &other)
return *this;
clear();
move_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_move_assignment::value>{});
return *this;
}
template<class Allocator>
basic_fields<Allocator>::
basic_fields(basic_fields const& other)
: basic_fields(alloc_traits::
select_on_container_copy_construction(other.member()))
{
copy_from(other);
}
template<class Allocator>
auto
basic_fields<Allocator>::
operator=(basic_fields const& other) ->
basic_fields&
{
clear();
copy_assign(other, std::integral_constant<bool,
alloc_traits::propagate_on_container_copy_assignment::value>{});
return *this;
}
template<class Allocator>
template<class OtherAlloc>
basic_fields<Allocator>::
basic_fields(basic_fields<OtherAlloc> const& other)
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_fields<Allocator>::
operator=(basic_fields<OtherAlloc> const& other) ->
basic_fields&
{
clear();
copy_from(other);
return *this;
}
template<class Allocator>
template<class FwdIt>
basic_fields<Allocator>::
basic_fields(FwdIt first, FwdIt last)
{
for(;first != last; ++first)
insert(first->name(), first->value());
}
template<class Allocator>
std::size_t
basic_fields<Allocator>::
count(boost::string_ref const& name) const
{
auto const it = set_.find(name, less{});
if(it == set_.end())
return 0;
auto const last = set_.upper_bound(name, less{});
return static_cast<std::size_t>(std::distance(it, last));
}
template<class Allocator>
auto
basic_fields<Allocator>::
find(boost::string_ref const& name) const ->
iterator
{
auto const it = set_.find(name, less{});
if(it == set_.end())
return list_.end();
return list_.iterator_to(*it);
}
template<class Allocator>
boost::string_ref
basic_fields<Allocator>::
operator[](boost::string_ref const& name) const
{
auto const it = find(name);
if(it == end())
return {};
return it->second;
}
template<class Allocator>
void
basic_fields<Allocator>::
clear() noexcept
{
delete_all();
list_.clear();
set_.clear();
}
template<class Allocator>
std::size_t
basic_fields<Allocator>::
erase(boost::string_ref const& name)
{
auto it = set_.find(name, less{});
if(it == set_.end())
return 0;
auto const last = set_.upper_bound(name, less{});
std::size_t n = 1;
for(;;)
{
auto& e = *it++;
set_.erase(set_.iterator_to(e));
list_.erase(list_.iterator_to(e));
alloc_traits::destroy(this->member(), &e);
alloc_traits::deallocate(this->member(), &e, 1);
if(it == last)
break;
++n;
}
return n;
}
template<class Allocator>
void
basic_fields<Allocator>::
insert(boost::string_ref const& name,
boost::string_ref value)
{
value = detail::trim(value);
auto const p = alloc_traits::allocate(this->member(), 1);
alloc_traits::construct(this->member(), p, name, value);
set_.insert_before(set_.upper_bound(name, less{}), *p);
list_.push_back(*p);
}
template<class Allocator>
void
basic_fields<Allocator>::
replace(boost::string_ref const& name,
boost::string_ref value)
{
value = detail::trim(value);
erase(name);
insert(name, value);
}
} // http
} // beast
#endif

View File

@@ -0,0 +1,932 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP
#define BEAST_HTTP_IMPL_BASIC_PARSER_IPP
#include <beast/core/static_string.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/core/detail/clamp.hpp>
#include <beast/core/detail/config.hpp>
#include <beast/http/error.hpp>
#include <beast/http/rfc7230.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/make_unique.hpp>
#include <algorithm>
#include <utility>
namespace beast {
namespace http {
template<bool isRequest, class Derived>
basic_parser<isRequest, Derived>::
basic_parser()
: body_limit_(
default_body_limit(is_request{}))
{
}
template<bool isRequest, class Derived>
template<class OtherDerived>
basic_parser<isRequest, Derived>::
basic_parser(basic_parser<
isRequest, OtherDerived>&& other)
: body_limit_(other.body_limit_)
, len_(other.len_)
, buf_(std::move(other.buf_))
, buf_len_(other.buf_len_)
, skip_(other.skip_)
, state_(other.state_)
, f_(other.f_)
{
}
template<bool isRequest, class Derived>
bool
basic_parser<isRequest, Derived>::
is_keep_alive() const
{
BOOST_ASSERT(is_header_done());
if(f_ & flagHTTP11)
{
if(f_ & flagConnectionClose)
return false;
}
else
{
if(! (f_ & flagConnectionKeepAlive))
return false;
}
return (f_ & flagNeedEOF) == 0;
}
template<bool isRequest, class Derived>
boost::optional<std::uint64_t>
basic_parser<isRequest, Derived>::
content_length() const
{
BOOST_ASSERT(is_header_done());
if(! (f_ & flagContentLength))
return boost::none;
return len_;
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
skip(bool v)
{
BOOST_ASSERT(! got_some());
if(v)
f_ |= flagSkipBody;
else
f_ &= ~flagSkipBody;
}
template<bool isRequest, class Derived>
template<class ConstBufferSequence>
std::size_t
basic_parser<isRequest, Derived>::
put(ConstBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_cast;
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
auto const p = buffers.begin();
auto const last = buffers.end();
if(p == last)
{
ec.assign(0, ec.category());
return 0;
}
if(std::next(p) == last)
{
// single buffer
auto const b = *p;
return put(boost::asio::const_buffers_1{
buffer_cast<char const*>(b),
buffer_size(b)}, ec);
}
auto const size = buffer_size(buffers);
if(size <= max_stack_buffer)
return put_from_stack(size, buffers, ec);
if(size > buf_len_)
{
// reallocate
buf_ = boost::make_unique_noinit<char[]>(size);
buf_len_ = size;
}
// flatten
buffer_copy(boost::asio::buffer(
buf_.get(), buf_len_), buffers);
return put(boost::asio::const_buffers_1{
buf_.get(), buf_len_}, ec);
}
template<bool isRequest, class Derived>
std::size_t
basic_parser<isRequest, Derived>::
put(boost::asio::const_buffers_1 const& buffer,
error_code& ec)
{
BOOST_ASSERT(state_ != state::complete);
using boost::asio::buffer_size;
auto p = boost::asio::buffer_cast<
char const*>(*buffer.begin());
auto n = buffer_size(*buffer.begin());
auto const p0 = p;
auto const p1 = p0 + n;
ec.assign(0, ec.category());
loop:
switch(state_)
{
case state::nothing_yet:
if(n == 0)
{
ec = error::need_more;
return 0;
}
state_ = state::start_line;
BEAST_FALLTHROUGH;
case state::start_line:
{
maybe_need_more(p, n, ec);
if(ec)
goto done;
parse_start_line(p, p + std::min<std::size_t>(
header_limit_, n), ec, is_request{});
if(ec)
{
if(ec == error::need_more)
{
if(n >= header_limit_)
{
ec = error::header_limit;
goto done;
}
if(p + 3 <= p1)
skip_ = static_cast<
std::size_t>(p1 - p - 3);
}
goto done;
}
BOOST_ASSERT(! is_done());
n = static_cast<std::size_t>(p1 - p);
if(p >= p1)
{
ec = error::need_more;
goto done;
}
BEAST_FALLTHROUGH;
}
case state::fields:
maybe_need_more(p, n, ec);
if(ec)
goto done;
parse_fields(p, p + std::min<std::size_t>(
header_limit_, n), ec);
if(ec)
{
if(ec == error::need_more)
{
if(n >= header_limit_)
{
ec = error::header_limit;
goto done;
}
if(p + 3 <= p1)
skip_ = static_cast<
std::size_t>(p1 - p - 3);
}
goto done;
}
finish_header(ec, is_request{});
break;
case state::body0:
BOOST_ASSERT(! skip_);
impl().on_body(content_length(), ec);
if(ec)
goto done;
state_ = state::body;
BEAST_FALLTHROUGH;
case state::body:
BOOST_ASSERT(! skip_);
parse_body(p, n, ec);
if(ec)
goto done;
break;
case state::body_to_eof0:
BOOST_ASSERT(! skip_);
impl().on_body(content_length(), ec);
if(ec)
goto done;
state_ = state::body_to_eof;
BEAST_FALLTHROUGH;
case state::body_to_eof:
BOOST_ASSERT(! skip_);
parse_body_to_eof(p, n, ec);
if(ec)
goto done;
break;
case state::chunk_header0:
impl().on_body(content_length(), ec);
if(ec)
goto done;
state_ = state::chunk_header;
BEAST_FALLTHROUGH;
case state::chunk_header:
parse_chunk_header(p, n, ec);
if(ec)
goto done;
break;
case state::chunk_body:
parse_chunk_body(p, n, ec);
if(ec)
goto done;
break;
case state::complete:
ec.assign(0, ec.category());
goto done;
}
if(p < p1 && ! is_done() && eager())
{
n = static_cast<std::size_t>(p1 - p);
goto loop;
}
done:
return static_cast<std::size_t>(p - p0);
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
put_eof(error_code& ec)
{
BOOST_ASSERT(got_some());
if( state_ == state::start_line ||
state_ == state::fields)
{
ec = error::partial_message;
return;
}
if(f_ & (flagContentLength | flagChunked))
{
if(state_ != state::complete)
{
ec = error::partial_message;
return;
}
ec.assign(0, ec.category());
return;
}
impl().on_complete(ec);
if(ec)
return;
state_ = state::complete;
}
template<bool isRequest, class Derived>
template<class ConstBufferSequence>
std::size_t
basic_parser<isRequest, Derived>::
put_from_stack(std::size_t size,
ConstBufferSequence const& buffers,
error_code& ec)
{
char buf[max_stack_buffer];
using boost::asio::buffer;
using boost::asio::buffer_copy;
buffer_copy(buffer(buf, sizeof(buf)), buffers);
return put(boost::asio::const_buffers_1{
buf, size}, ec);
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
maybe_need_more(
char const* p, std::size_t n,
error_code& ec)
{
if(skip_ == 0)
return;
if( n > header_limit_)
n = header_limit_;
if(n < skip_ + 4)
{
ec = error::need_more;
return;
}
auto const term =
find_eom(p + skip_, p + n);
if(! term)
{
skip_ = n - 3;
if(skip_ + 4 > header_limit_)
{
ec = error::header_limit;
return;
}
ec = error::need_more;
return;
}
skip_ = 0;
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
parse_start_line(
char const*& in, char const* last,
error_code& ec, std::true_type)
{
/*
request-line = method SP request-target SP HTTP-version CRLF
method = token
*/
auto p = in;
string_view method;
parse_method(p, last, method, ec);
if(ec)
return;
string_view target;
parse_target(p, last, target, ec);
if(ec)
return;
int version = 0;
parse_version(p, last, version, ec);
if(ec)
return;
if(version < 10 || version > 11)
{
ec = error::bad_version;
return;
}
if(p + 2 > last)
{
ec = error::need_more;
return;
}
if(p[0] != '\r' || p[1] != '\n')
{
ec = error::bad_version;
return;
}
p += 2;
if(version >= 11)
f_ |= flagHTTP11;
impl().on_request(string_to_verb(method),
method, target, version, ec);
if(ec)
return;
in = p;
state_ = state::fields;
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
parse_start_line(
char const*& in, char const* last,
error_code& ec, std::false_type)
{
/*
status-line = HTTP-version SP status-code SP reason-phrase CRLF
status-code = 3*DIGIT
reason-phrase = *( HTAB / SP / VCHAR / obs-text )
*/
auto p = in;
int version = 0;
parse_version(p, last, version, ec);
if(ec)
return;
if(version < 10 || version > 11)
{
ec = error::bad_version;
return;
}
// SP
if(p + 1 > last)
{
ec = error::need_more;
return;
}
if(*p++ != ' ')
{
ec = error::bad_version;
return;
}
parse_status(p, last, status_, ec);
if(ec)
return;
// parse reason CRLF
string_view reason;
parse_reason(p, last, reason, ec);
if(ec)
return;
if(version >= 11)
f_ |= flagHTTP11;
impl().on_response(
status_, reason, version, ec);
if(ec)
return;
in = p;
state_ = state::fields;
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
parse_fields(char const*& in,
char const* last, error_code& ec)
{
string_view name;
string_view value;
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
static_string<max_obs_fold> buf;
auto p = in;
for(;;)
{
if(p + 2 > last)
{
ec = error::need_more;
return;
}
if(p[0] == '\r')
{
if(p[1] != '\n')
ec = error::bad_line_ending;
in = p + 2;
return;
}
parse_field(p, last, name, value, buf, ec);
if(ec)
return;
auto const f = string_to_field(name);
do_field(f, value, ec);
if(ec)
return;
impl().on_field(f, name, value, ec);
if(ec)
return;
in = p;
}
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
finish_header(error_code& ec, std::true_type)
{
// RFC 7230 section 3.3
// https://tools.ietf.org/html/rfc7230#section-3.3
if(f_ & flagSkipBody)
{
state_ = state::complete;
}
else if(f_ & flagContentLength)
{
if(len_ > 0)
{
f_ |= flagHasBody;
state_ = state::body0;
}
else
{
state_ = state::complete;
}
}
else if(f_ & flagChunked)
{
f_ |= flagHasBody;
state_ = state::chunk_header0;
}
else
{
len_ = 0;
state_ = state::complete;
}
impl().on_header(ec);
if(ec)
return;
if(state_ == state::complete)
{
impl().on_complete(ec);
if(ec)
return;
}
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
finish_header(error_code& ec, std::false_type)
{
// RFC 7230 section 3.3
// https://tools.ietf.org/html/rfc7230#section-3.3
if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
status_ / 100 == 1 || // 1xx e.g. Continue
status_ == 204 || // No Content
status_ == 304) // Not Modified
{
state_ = state::complete;
return;
}
if(f_ & flagContentLength)
{
if(len_ > 0)
{
f_ |= flagHasBody;
state_ = state::body0;
}
else
{
state_ = state::complete;
}
}
else if(f_ & flagChunked)
{
f_ |= flagHasBody;
state_ = state::chunk_header0;
}
else
{
f_ |= flagHasBody;
f_ |= flagNeedEOF;
state_ = state::body_to_eof0;
}
impl().on_header(ec);
if(ec)
return;
if(state_ == state::complete)
{
impl().on_complete(ec);
if(ec)
return;
}
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
parse_body(char const*& p,
std::size_t n, error_code& ec)
{
n = impl().on_data(string_view{p,
beast::detail::clamp(len_, n)}, ec);
p += n;
len_ -= n;
if(ec)
return;
if(len_ > 0)
return;
impl().on_complete(ec);
if(ec)
return;
state_ = state::complete;
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
parse_body_to_eof(char const*& p,
std::size_t n, error_code& ec)
{
if(n > body_limit_)
{
ec = error::body_limit;
return;
}
body_limit_ = body_limit_ - n;
n = impl().on_data(string_view{p, n}, ec);
p += n;
if(ec)
return;
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
parse_chunk_header(char const*& p0,
std::size_t n, error_code& ec)
{
/*
chunked-body = *chunk last-chunk trailer-part CRLF
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
last-chunk = 1*("0") [ chunk-ext ] CRLF
trailer-part = *( header-field CRLF )
chunk-size = 1*HEXDIG
chunk-data = 1*OCTET ; a sequence of chunk-size octets
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token / quoted-string
*/
auto p = p0;
auto const pend = p + n;
char const* eol;
if(! (f_ & flagFinalChunk))
{
if(n < skip_ + 2)
{
ec = error::need_more;
return;
}
if(f_ & flagExpectCRLF)
{
// Treat the last CRLF in a chunk as
// part of the next chunk, so p can
// be parsed in one call instead of two.
if(! parse_crlf(p))
{
ec = error::bad_chunk;
return;
}
}
eol = find_eol(p0 + skip_, pend, ec);
if(ec)
return;
if(! eol)
{
ec = error::need_more;
skip_ = n - 1;
return;
}
skip_ = static_cast<
std::size_t>(eol - 2 - p0);
std::uint64_t v;
if(! parse_hex(p, v))
{
ec = error::bad_chunk;
return;
}
if(v != 0)
{
if(v > body_limit_)
{
ec = error::body_limit;
return;
}
body_limit_ -= v;
if(*p == ';')
{
// VFALCO TODO Validate extension
impl().on_chunk(v, make_string(
p, eol - 2), ec);
if(ec)
return;
}
else if(p == eol - 2)
{
impl().on_chunk(v, {}, ec);
if(ec)
return;
}
else
{
ec = error::bad_chunk;
return;
}
len_ = v;
skip_ = 2;
p0 = eol;
f_ |= flagExpectCRLF;
state_ = state::chunk_body;
return;
}
f_ |= flagFinalChunk;
}
else
{
BOOST_ASSERT(n >= 5);
if(f_ & flagExpectCRLF)
BOOST_VERIFY(parse_crlf(p));
std::uint64_t v;
BOOST_VERIFY(parse_hex(p, v));
eol = find_eol(p, pend, ec);
BOOST_ASSERT(! ec);
}
auto eom = find_eom(p0 + skip_, pend);
if(! eom)
{
BOOST_ASSERT(n >= 3);
skip_ = n - 3;
ec = error::need_more;
return;
}
if(*p == ';')
{
// VFALCO TODO Validate extension
impl().on_chunk(0, make_string(
p, eol - 2), ec);
if(ec)
return;
}
p = eol;
parse_fields(p, eom, ec);
if(ec)
return;
BOOST_ASSERT(p == eom);
p0 = eom;
impl().on_complete(ec);
if(ec)
return;
state_ = state::complete;
}
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
parse_chunk_body(char const*& p,
std::size_t n, error_code& ec)
{
n = impl().on_data(string_view{p,
beast::detail::clamp(len_, n)}, ec);
p += n;
len_ -= n;
if(ec)
return;
if(len_ > 0)
return;
state_ = state::chunk_header;
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
do_field(field f,
string_view value, error_code& ec)
{
// Connection
if(f == field::connection ||
f == field::proxy_connection)
{
auto const list = opt_token_list{value};
if(! validate_list(list))
{
// VFALCO Should this be a field specific error?
ec = error::bad_value;
return;
}
for(auto const& s : list)
{
if(iequals({"close", 5}, s))
{
f_ |= flagConnectionClose;
continue;
}
if(iequals({"keep-alive", 10}, s))
{
f_ |= flagConnectionKeepAlive;
continue;
}
if(iequals({"upgrade", 7}, s))
{
f_ |= flagConnectionUpgrade;
continue;
}
}
ec.assign(0, ec.category());
return;
}
// Content-Length
if(f == field::content_length)
{
if(f_ & flagContentLength)
{
// duplicate
ec = error::bad_content_length;
return;
}
if(f_ & flagChunked)
{
// conflicting field
ec = error::bad_content_length;
return;
}
std::uint64_t v;
if(! parse_dec(
value.begin(), value.end(), v))
{
ec = error::bad_content_length;
return;
}
if(v > body_limit_)
{
ec = error::body_limit;
return;
}
ec.assign(0, ec.category());
len_ = v;
f_ |= flagContentLength;
return;
}
// Transfer-Encoding
if(f == field::transfer_encoding)
{
if(f_ & flagChunked)
{
// duplicate
ec = error::bad_transfer_encoding;
return;
}
if(f_ & flagContentLength)
{
// conflicting field
ec = error::bad_transfer_encoding;
return;
}
ec.assign(0, ec.category());
auto const v = token_list{value};
auto const p = std::find_if(v.begin(), v.end(),
[&](typename token_list::value_type const& s)
{
return iequals({"chunked", 7}, s);
});
if(p == v.end())
return;
if(std::next(p) != v.end())
return;
len_ = 0;
f_ |= flagChunked;
return;
}
// Upgrade
if(f == field::upgrade)
{
ec.assign(0, ec.category());
f_ |= flagUpgrade;
return;
}
ec.assign(0, ec.category());
}
} // http
} // beast
#endif

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More