├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── examples ├── CMakeLists.txt └── simple │ ├── CMakeLists.txt │ └── main.cpp ├── include ├── asio_http │ ├── asio │ │ └── placeholders.hpp │ ├── aux │ │ ├── logging.hpp │ │ └── url_parser.hpp │ ├── detail │ │ └── status_codes.hpp │ ├── http_client-inl.hpp │ ├── http_client.hpp │ ├── http_client_connection-inl.hpp │ ├── http_client_connection.hpp │ ├── http_server-inl.hpp │ ├── http_server.hpp │ ├── http_server_connection-inl.hpp │ └── http_server_connection.hpp └── http_parser.h ├── src └── http_parser.c └── tests ├── CMakeLists.txt ├── json ├── json-forwards.h └── json.h ├── jsoncpp.cpp ├── test.c ├── test_server.cpp ├── test_server.py └── test_url_parser.cpp /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/asio"] 2 | path = vendor/asio 3 | url = https://github.com/chriskohlhoff/asio.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: 2 | - cpp 3 | compiler: 4 | - gcc 5 | - clang 6 | before_install: 7 | - sudo apt-get update -qq 8 | - sudo apt-get install -qq -y cmake libboost-all-dev 9 | env: 10 | - BUILD_TYPE="Debug" 11 | - BUILD_TYPE="Release" 12 | - BUILD_TYPE="RelWithDebInfo" 13 | - BUILD_TYPE="MinSizeRel" 14 | script: 15 | - mkdir build 16 | - cd build 17 | - cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" -DASIO_HTTP_BUILD_EXAMPLES=ON -DASIO_HTTP_BUILD_TESTS=ON .. 18 | - make 19 | - ctest -V 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.3) 2 | project(asio_http) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | 8 | option(ASIO_HTTP_BUILD_EXAMPLES "Build asio_http examples" ON) 9 | #option(ASIO_HTTP_BUILD_TESTS "Build asio_http tests" OFF) 10 | 11 | add_definitions(-DASIO_STANDALONE -DASIO_SEPARATE_COMPILATION) 12 | 13 | include_directories(asio_http PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/vendor/asio/asio/include") 14 | 15 | add_library(asio_http src/http_parser.c vendor/asio/asio/src/asio.cpp) 16 | 17 | install(DIRECTORY include/ DESTINATION include) 18 | install(TARGETS asio_http 19 | RUNTIME DESTINATION bin 20 | LIBRARY DESTINATION lib 21 | ARCHIVE DESTINATION lib) 22 | 23 | if(ASIO_HTTP_BUILD_EXAMPLES) 24 | add_subdirectory(examples) 25 | endif() 26 | 27 | if(ASIO_HTTP_BUILD_TESTS) 28 | enable_testing() 29 | add_subdirectory(tests) 30 | endif() 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Embeddable HTTP Server 2 | Asio + http-parser from Joyent = fun. 3 | 4 | 5 | # Introduction 6 | You can create your own high performance HTTP based services in seconds. This library uses `http-parser` library from Joyent which is based on the `nginx` http parser and powers `nodejs`. 7 | 8 | Uses stand-alone [ASIO](http://think-async.com/Asio) library and C++11 without the requirement of boost like the original implementation. 9 | 10 | # Building 11 | 12 | git clone https://github.com/thejustinwalsh/asio_http.git 13 | cd asio_http 14 | git submodule update --init 15 | mkdir build 16 | cd build 17 | cmake .. 18 | make 19 | 20 | Still Considering using [Conan](https://conan.io) package manager. 21 | 22 | # License 23 | The MIT License (MIT) 24 | Copyright (c) 2016 thejustinwalsh LLC 25 | 26 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | 32 | *Portions of this code (http_parser.c) are based on code from NGINX copyright Igor Sysoev with additional changes licensed under the same terms as NGINX and copyright Joyent, Inc. and other Node contributors.* 33 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(simple) 2 | -------------------------------------------------------------------------------- /examples/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.3) 2 | project(http_server) 3 | 4 | add_executable(http_server main.cpp) 5 | 6 | target_link_libraries (http_server asio_http) 7 | -------------------------------------------------------------------------------- /examples/simple/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace asio::ip; 7 | 8 | struct http_request_handler 9 | { 10 | typedef basic_http_connection connection; 11 | void operator()(const connection::pointer & ptr) 12 | { 13 | ptr->send_response(200, "Hello world!"); 14 | } 15 | }; 16 | 17 | struct client_body_handler 18 | { 19 | typedef void result_type; 20 | std::string & str_; 21 | client_body_handler(std::string & str) 22 | : str_(str) 23 | { 24 | } 25 | void operator()(const std::error_code & ec, const asio::const_buffer & buffer) 26 | { 27 | const char * data = asio::buffer_cast(buffer); 28 | std::size_t size = asio::buffer_size(buffer); 29 | std::string chunk(data, data + size); 30 | std::cout << "chunk[" << chunk << "]" << std::endl; 31 | str_ += chunk; 32 | } 33 | }; 34 | 35 | struct client_done_handler 36 | { 37 | std::string & str_; 38 | client_done_handler(std::string & str) 39 | : str_(str) 40 | { 41 | } 42 | void operator()(const std::error_code & ec) 43 | { 44 | std::cout << "done [" << str_ << "]" << std::endl; 45 | } 46 | }; 47 | 48 | typedef http_client_connection< 49 | http_client::protocol_type, 50 | client_body_handler, 51 | client_done_handler> client_connection; 52 | 53 | int 54 | main(int argc, char * argv[]) 55 | { 56 | asio::io_service io_svc; 57 | 58 | // Server 59 | http_server server(io_svc, tcp::endpoint(tcp::v4(), 5000)); 60 | 61 | // Client 62 | http_client & client = asio::use_service(io_svc); 63 | std::string data; 64 | client_body_handler body(data); 65 | client_done_handler done(data); 66 | client_connection::pointer connection = client.create_request("http://127.0.0.1:5000/", body, done); 67 | connection->start(); 68 | 69 | 70 | io_svc.run(); 71 | return 0; 72 | } -------------------------------------------------------------------------------- /include/asio_http/asio/placeholders.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_ASIO_PLACEHOLDERS_INCLUDED_H_) 2 | #define ASIO_HTTP_ASIO_PLACEHOLDERS_INCLUDED_H_ 3 | 4 | #include 5 | 6 | namespace asio { 7 | 8 | // In standalone mode, ASIO doesn't define placeholders. 9 | // https://github.com/chriskohlhoff/asio/issues/83 10 | namespace placeholders { 11 | 12 | const decltype(std::placeholders::_1) error{}; 13 | const decltype(std::placeholders::_2) bytes_transferred{}; 14 | const decltype(std::placeholders::_2) iterator{}; 15 | const decltype(std::placeholders::_2) results{}; 16 | const decltype(std::placeholders::_2) endpoint{}; 17 | const decltype(std::placeholders::_2) signal_number{}; 18 | 19 | } // end namespace placeholders 20 | 21 | } // end namespace asio 22 | 23 | #endif /* ASIO_HTTP_ASIO_PLACEHOLDERS_INCLUDED_H_ */ -------------------------------------------------------------------------------- /include/asio_http/aux/logging.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_AUX_LOGGING_INCLUDED_H_) 2 | #define ASIO_HTTP_AUX_LOGGING_INCLUDED_H_ 3 | 4 | /* 5 | * Internal logging function that just prints data to stdout. 6 | * TODO: Make this method pluggable into instance. 7 | */ 8 | #if !defined(NDEBUG) || defined(HTTP_SERVER_VERBOSE) 9 | #define HTTP_SERVER_DEBUG_OUTPUT(message, ...) \ 10 | do \ 11 | { \ 12 | std::fprintf(stdout, message, ##__VA_ARGS__); \ 13 | std::fflush(stdout); \ 14 | } while (0) 15 | #else 16 | #define HTTP_SERVER_DEBUG_OUTPUT(...) do {} while (0) 17 | #endif 18 | 19 | #endif /* ASIO_HTTP_AUX_LOGGING_INCLUDED_H_ */ -------------------------------------------------------------------------------- /include/asio_http/aux/url_parser.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_AUX_HTTP_PARSER_INCLUDED_H_) 2 | #define ASIO_HTTP_AUX_HTTP_PARSER_INCLUDED_H_ 3 | 4 | #include 5 | #include "http_parser.h" 6 | 7 | struct url_parser 8 | { 9 | http_parser_url url_; 10 | std::string input_; 11 | bool is_connect_; 12 | url_parser(const std::string & input, bool is_connect = false) 13 | : url_() 14 | , input_(input) 15 | , is_connect_(is_connect) 16 | { 17 | if (::http_parser_parse_url(input_.c_str(), input_.size(), is_connect_, &url_) != 0) 18 | { 19 | throw std::runtime_error("Unable to parser url"); 20 | } 21 | } 22 | std::string get_schema() const 23 | { 24 | assert(url_.field_set & (1 << UF_SCHEMA)); 25 | uint16_t off = url_.field_data[UF_SCHEMA].off, len = url_.field_data[UF_SCHEMA].len; 26 | return input_.substr(off, len); 27 | } 28 | std::string get_host() const 29 | { 30 | assert(url_.field_set & (1 << UF_HOST)); 31 | uint16_t off = url_.field_data[UF_HOST].off, len = url_.field_data[UF_HOST].len; 32 | return input_.substr(off, len); 33 | } 34 | int get_port() const 35 | { 36 | assert(url_.field_set & (1 << UF_PORT)); 37 | return url_.port; 38 | } 39 | std::string get_path() const 40 | { 41 | assert(url_.field_set & (1 << UF_PATH)); 42 | uint16_t off = url_.field_data[UF_PATH].off, len = url_.field_data[UF_PATH].len; 43 | return input_.substr(off, len); 44 | } 45 | std::string get_query() const 46 | { 47 | assert(url_.field_set & (1 << UF_QUERY)); 48 | uint16_t off = url_.field_data[UF_QUERY].off, len = url_.field_data[UF_QUERY].len; 49 | return input_.substr(off, len); 50 | } 51 | std::string get_fragment() const 52 | { 53 | assert(url_.field_set & (1 << UF_FRAGMENT)); 54 | uint16_t off = url_.field_data[UF_FRAGMENT].off, len = url_.field_data[UF_FRAGMENT].len; 55 | return input_.substr(off, len); 56 | } 57 | std::string get_userinfo() const 58 | { 59 | assert(url_.field_set & (1 << UF_USERINFO)); 60 | uint16_t off = url_.field_data[UF_USERINFO].off, len = url_.field_data[UF_USERINFO].len; 61 | return input_.substr(off, len); 62 | } 63 | }; 64 | 65 | #endif /* ASIO_HTTP_AUX_HTTP_PARSER_INCLUDED_H_ */ -------------------------------------------------------------------------------- /include/asio_http/detail/status_codes.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_SERVER_DETAIL_STATUS_CODES_HPP_INCLUDED_) 2 | #define ASIO_HTTP_HTTP_SERVER_DETAIL_STATUS_CODES_HPP_INCLUDED_ 3 | 4 | // http://wiki.splunk.com/Http_status.csv 5 | #define HTTP_STATUS_CODE_MAP(XX) \ 6 | XX(100, "Continue", "Informational") \ 7 | XX(101, "Switching Protocols", "Informational") \ 8 | XX(200, "OK", "Successful") \ 9 | XX(201, "Created", "Successful") \ 10 | XX(202, "Accepted", "Successful") \ 11 | XX(203, "Non-Authoritative Information", "Successful") \ 12 | XX(204, "No Content", "Successful") \ 13 | XX(205, "Reset Content", "Successful") \ 14 | XX(206, "Partial Content", "Successful") \ 15 | XX(300, "Multiple Choices", "Redirection") \ 16 | XX(301, "Moved Permanently", "Redirection") \ 17 | XX(302, "Found", "Redirection") \ 18 | XX(303, "See Other", "Redirection") \ 19 | XX(304, "Not Modified", "Redirection") \ 20 | XX(305, "Use Proxy", "Redirection") \ 21 | XX(307, "Temporary Redirect", "Redirection") \ 22 | XX(400, "Bad Request", "Client Error") \ 23 | XX(401, "Unauthorized", "Client Error") \ 24 | XX(402, "Payment Required", "Client Error") \ 25 | XX(403, "Forbidden", "Client Error") \ 26 | XX(404, "Not Found", "Client Error") \ 27 | XX(405, "Method Not Allowed", "Client Error") \ 28 | XX(406, "Not Acceptable", "Client Error") \ 29 | XX(407, "Proxy Authentication Required", "Client Error") \ 30 | XX(408, "Request Timeout", "Client Error") \ 31 | XX(409, "Conflict", "Client Error") \ 32 | XX(410, "Gone", "Client Error") \ 33 | XX(411, "Length Required", "Client Error") \ 34 | XX(412, "Precondition Failed", "Client Error") \ 35 | XX(413, "Request Entity Too Large", "Client Error") \ 36 | XX(414, "Request-URI Too Long", "Client Error") \ 37 | XX(415, "Unsupported Media Type", "Client Error") \ 38 | XX(416, "Requested Range Not Satisfiable", "Client Error") \ 39 | XX(417, "Expectation Failed", "Client Error") \ 40 | XX(500, "Internal Server Error", "Server Error") \ 41 | XX(501, "Not Implemented", "Server Error") \ 42 | XX(502, "Bad Gateway", "Server Error") \ 43 | XX(503, "Service Unavailable", "Server Error") \ 44 | XX(504, "Gateway Timeout", "Server Error") \ 45 | XX(505, "HTTP Version Not Supported", "Server Error") 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /include/asio_http/http_client-inl.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_CLIENT_INCLUDED_H_) 2 | #error "Invalid include order" 3 | #endif 4 | 5 | template 6 | asio::io_service::id basic_http_client::id; 7 | 8 | template 9 | basic_http_client::basic_http_client(asio::io_service & io_service) 10 | : asio::io_service::service(io_service) 11 | { 12 | } 13 | 14 | template 15 | void basic_http_client::shutdown_service() 16 | { 17 | } 18 | 19 | template 20 | template 21 | std::shared_ptr< 22 | http_client_connection< 23 | Protocol, 24 | BodyHandler, 25 | DoneHandler 26 | > 27 | > basic_http_client::create_request(const std::string & url, BodyHandler body_handler, DoneHandler done_handler) 28 | { 29 | typedef http_client_connection result_type; 30 | return std::make_shared( 31 | std::ref(get_io_service()), 32 | url, 33 | body_handler, 34 | done_handler); 35 | } -------------------------------------------------------------------------------- /include/asio_http/http_client.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_CLIENT_INCLUDED_H_) 2 | #define ASIO_HTTP_HTTP_CLIENT_INCLUDED_H_ 3 | 4 | #include 5 | #include "http_client_connection.hpp" 6 | 7 | template 8 | struct basic_http_client 9 | : asio::io_service::service 10 | { 11 | static asio::io_service::id id; 12 | typedef Protocol protocol_type; 13 | basic_http_client(asio::io_service & io_service); 14 | void shutdown_service(); 15 | template 16 | std::shared_ptr< 17 | http_client_connection< 18 | Protocol, 19 | BodyHandler, 20 | DoneHandler 21 | > 22 | > create_request(const std::string & url, BodyHandler body_handler, DoneHandler done_handler); 23 | }; 24 | 25 | #include "http_client-inl.hpp" 26 | 27 | typedef basic_http_client http_client; 28 | 29 | #endif -------------------------------------------------------------------------------- /include/asio_http/http_client_connection-inl.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_CLIENT_INCLUDED_H_) 2 | #error "Invalid include order" 3 | #endif 4 | 5 | template 6 | http_client_connection::http_client_connection(asio::io_service & io_service, 7 | std::string url, 8 | BodyHandler body_handler, DoneHandler done_handler) 9 | : io_service_(io_service) 10 | , resolver_(io_service_) 11 | , socket_(io_service_) 12 | , url_(url) 13 | , parsed_url_() 14 | , body_handler_(body_handler) 15 | , done_handler_(done_handler) 16 | , settings_() 17 | , parser_() 18 | { 19 | HTTP_SERVER_DEBUG_OUTPUT("New connection %s\n", url_.c_str()); 20 | int result = http_parser_parse_url(url_.c_str(), url_.size(), 0, &parsed_url_); 21 | assert(result == 0); 22 | http_parser_init(&parser_, HTTP_RESPONSE); 23 | parser_.data = this; 24 | settings_.on_body = &http_client_connection::on_body; 25 | settings_.on_message_complete = &http_client_connection::on_message_complete; 26 | settings_.on_status = &http_client_connection::on_status; 27 | } 28 | 29 | template 30 | http_client_connection::~http_client_connection() 31 | { 32 | HTTP_SERVER_DEBUG_OUTPUT("~http_client_connection\n"); 33 | } 34 | 35 | template 36 | void http_client_connection::start() 37 | { 38 | std::string port = parsed_url_.port 39 | ? url_.substr( 40 | parsed_url_.field_data[UF_PORT].off, 41 | parsed_url_.field_data[UF_PORT].len) 42 | : "80"; 43 | std::string addr = url_.substr( 44 | parsed_url_.field_data[UF_HOST].off, 45 | parsed_url_.field_data[UF_HOST].len); 46 | asio::ip::tcp::resolver::query q(addr, port); 47 | resolver_.async_resolve(q, 48 | std::bind(&http_client_connection::resolve_handler, this->shared_from_this(), 49 | asio::placeholders::error, 50 | asio::placeholders::iterator)); 51 | } 52 | 53 | template 54 | void http_client_connection::resolve_handler(const std::error_code& ec, 55 | asio::ip::tcp::resolver::iterator i) 56 | { 57 | if (ec) 58 | { 59 | io_service_.post(std::bind(done_handler_, ec)); 60 | return; 61 | } 62 | assert(i != asio::ip::tcp::resolver::iterator()); 63 | asio::ip::tcp::endpoint ep = *i; 64 | socket_.async_connect(ep, 65 | std::bind(&http_client_connection::connect_handler, this->shared_from_this(), 66 | asio::placeholders::error, 67 | ++i)); 68 | } 69 | 70 | template 71 | void http_client_connection::connect_handler( 72 | const std::error_code& ec, 73 | asio::ip::tcp::resolver::iterator i) 74 | { 75 | if (!ec) 76 | { 77 | // An error occurred. 78 | HTTP_SERVER_DEBUG_OUTPUT("connected to %s:%d\n", socket_.local_endpoint().address().to_string().c_str(), socket_.local_endpoint().port()); 79 | std::string path = url_.substr( 80 | parsed_url_.field_data[UF_PATH].off, 81 | url_.size()); 82 | std::ostream o(&request_buffer_); 83 | o << "GET " << path << " HTTP/1.1\r\n\r\n"; 84 | asio::async_write(socket_, request_buffer_, 85 | std::bind(&http_client_connection::write_handler, this->shared_from_this(), 86 | asio::placeholders::error, 87 | asio::placeholders::bytes_transferred)); 88 | return; 89 | } 90 | else if (i != asio::ip::tcp::resolver::iterator()) 91 | { 92 | HTTP_SERVER_DEBUG_OUTPUT("Trying to connect to %s\n", i->endpoint().address().to_string().c_str()); 93 | socket_.close(); 94 | asio::ip::tcp::endpoint ep = *i; 95 | socket_.async_connect(ep, 96 | std::bind(&http_client_connection::connect_handler, this->shared_from_this(), 97 | asio::placeholders::error, 98 | ++i)); 99 | } 100 | else 101 | { 102 | io_service_.post(std::bind(done_handler_, ec)); 103 | } 104 | } 105 | 106 | template 107 | void http_client_connection::start_read() 108 | { 109 | HTTP_SERVER_DEBUG_OUTPUT("Reading...\n"); 110 | socket_.async_read_some(response_buffer_.prepare(1024), 111 | std::bind(&http_client_connection::read_handler, 112 | this->shared_from_this(), 113 | asio::placeholders::error, 114 | asio::placeholders::bytes_transferred)); 115 | } 116 | 117 | template 118 | void http_client_connection::write_handler( 119 | const std::error_code& error, 120 | std::size_t bytes_transferred) 121 | { 122 | HTTP_SERVER_DEBUG_OUTPUT("Write %lu: %s\n", 123 | bytes_transferred, 124 | error.message().c_str()); 125 | if (error) 126 | { 127 | io_service_.post(std::bind(done_handler_, error)); 128 | return; 129 | } 130 | request_buffer_.consume(bytes_transferred); 131 | start_read(); 132 | } 133 | 134 | template 135 | void http_client_connection::read_handler( 136 | const std::error_code& error, 137 | std::size_t bytes_transferred) 138 | { 139 | HTTP_SERVER_DEBUG_OUTPUT("Received data %s [%lu bytes]\n", error.message().c_str(), bytes_transferred); 140 | if (!error && bytes_transferred) 141 | { 142 | const char * data = asio::buffer_cast(response_buffer_.data()); 143 | std::size_t nsize = http_parser_execute(&parser_, &settings_, data, bytes_transferred); 144 | // std::cout << "nsize = " << nsize << std::endl; 145 | if (nsize != bytes_transferred) 146 | { 147 | HTTP_SERVER_DEBUG_OUTPUT("http parser execute fail %lu/%lu\n", nsize, bytes_transferred); 148 | socket_.close(); 149 | return; 150 | } 151 | response_buffer_.consume(nsize); 152 | start_read(); 153 | } 154 | else 155 | { 156 | socket_.close(); 157 | } 158 | } 159 | 160 | template 161 | int http_client_connection::on_body(http_parser * parser, const char * at, size_t length) 162 | { 163 | assert(parser->data); 164 | http_client_connection * obj = static_cast(parser->data); 165 | obj->body_handler_(std::error_code(), asio::const_buffer(at, length)); 166 | return 0; 167 | } 168 | 169 | template 170 | int http_client_connection::on_message_complete(http_parser * parser) 171 | { 172 | assert(parser->data); 173 | http_client_connection * obj = static_cast(parser->data); 174 | obj->io_service_.post(std::bind(obj->done_handler_, std::error_code())); 175 | obj->socket_.close(); 176 | return 0; 177 | } 178 | 179 | template 180 | int http_client_connection::on_status(http_parser * parser, const char * at, size_t length) 181 | { 182 | assert(parser->data); 183 | http_client_connection * obj = static_cast(parser->data); 184 | obj->status_.append(at, length); 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /include/asio_http/http_client_connection.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_CLIENT_CONNECTION_INCLUDED_H_) 2 | #define ASIO_HTTP_HTTP_CLIENT_CONNECTION_INCLUDED_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | struct http_client_connection 11 | : std::enable_shared_from_this< 12 | http_client_connection< 13 | T, 14 | BodyHandler, 15 | DoneHandler 16 | > 17 | > 18 | { 19 | typedef std::shared_ptr pointer; 20 | http_client_connection(asio::io_service & io_service, 21 | std::string url, 22 | BodyHandler body_handler, DoneHandler done_handler); 23 | ~http_client_connection(); 24 | void start(); 25 | void resolve_handler(const std::error_code& ec, 26 | asio::ip::tcp::resolver::iterator i); 27 | void connect_handler(const std::error_code& ec, 28 | asio::ip::tcp::resolver::iterator i); 29 | void start_read(); 30 | void write_handler(const std::error_code& error, 31 | std::size_t bytes_transferred); 32 | void read_handler(const std::error_code& error, 33 | std::size_t bytes_transferred); 34 | static int on_body(http_parser * parser, const char * at, size_t length); 35 | static int on_message_complete(http_parser * parser); 36 | static int on_status(http_parser * parser, const char * at, size_t length); 37 | asio::io_service & io_service_; 38 | asio::ip::tcp::resolver resolver_; 39 | T socket_; 40 | std::string url_; 41 | http_parser_url parsed_url_; 42 | BodyHandler body_handler_; 43 | DoneHandler done_handler_; 44 | asio::streambuf request_buffer_; 45 | asio::streambuf response_buffer_; 46 | http_parser_settings settings_; 47 | http_parser parser_; 48 | /** 49 | * Status string. For example "OK" 50 | */ 51 | std::string status_; 52 | inline 53 | unsigned int get_status_code() const 54 | { 55 | assert(parser_.type == HTTP_RESPONSE); 56 | return parser_.status_code; 57 | } 58 | inline 59 | const std::string & get_status() const 60 | { 61 | return status_; 62 | } 63 | }; 64 | 65 | #include "http_client_connection-inl.hpp" 66 | 67 | #endif -------------------------------------------------------------------------------- /include/asio_http/http_server-inl.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_SERVER_INCLUDED_H_) 2 | #error "Invalid include order" 3 | #endif 4 | 5 | template 6 | http_server::http_server(asio::io_service & io_svc, 7 | asio::ip::tcp::endpoint endpoint_, 8 | RequestHandler handler) 9 | : io_svc_(io_svc) 10 | , acceptor_(io_svc_, endpoint_) 11 | , request_handler_(handler) 12 | { 13 | start_accept(); 14 | } 15 | 16 | template 17 | void http_server::start_accept() 18 | { 19 | typename connection_type::pointer new_connection = 20 | connection_type::create(io_svc_, &request_handler_); 21 | acceptor_.async_accept(new_connection->get_socket(), 22 | std::bind(&http_server::handle_accept, this, new_connection, 23 | asio::placeholders::error)); 24 | } 25 | 26 | template 27 | void http_server::stop_accept() 28 | { 29 | acceptor_.cancel(); 30 | } 31 | 32 | template 33 | void http_server::handle_accept(typename connection_type::pointer new_connection, 34 | const std::error_code& error) 35 | { 36 | if (error) 37 | { 38 | HTTP_SERVER_DEBUG_OUTPUT("Failed to accept new server connection: %s [Errno %d]", 39 | error.message().c_str(), 40 | error.value()); 41 | return; 42 | } 43 | new_connection->start(); 44 | start_accept(); 45 | return; 46 | } 47 | 48 | template 49 | void http_server::handle_request(typename connection_type::pointer connection) 50 | { 51 | 52 | } -------------------------------------------------------------------------------- /include/asio_http/http_server.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_SERVER_INCLUDED_H_) 2 | #define ASIO_HTTP_HTTP_SERVER_INCLUDED_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /** 12 | * HTTP server implementation 13 | */ 14 | template 15 | class http_server 16 | { 17 | private: 18 | asio::io_service & io_svc_; 19 | /** 20 | * It waits for sockets 21 | */ 22 | 23 | asio::ip::tcp::acceptor acceptor_; 24 | RequestHandler request_handler_; 25 | public: 26 | typedef basic_http_connection connection_type; 27 | http_server(asio::io_service & io_svc, 28 | asio::ip::tcp::endpoint endpoint_, 29 | RequestHandler request_handler = RequestHandler()); 30 | /** 31 | * Start asynchronous accept. 32 | */ 33 | void start_accept(); 34 | /** 35 | * Stop accepting new connections 36 | */ 37 | void stop_accept(); 38 | /** 39 | * New client connected 40 | */ 41 | void handle_accept(typename connection_type::pointer new_connection, 42 | const std::error_code& error); 43 | void handle_request(typename connection_type::pointer connection); 44 | inline asio::ip::tcp::acceptor & get_acceptor() 45 | { 46 | return acceptor_; 47 | } 48 | }; 49 | 50 | #include "http_server-inl.hpp" 51 | 52 | #endif /* ASIO_HTTP_HTTP_SERVER_INCLUDED_H_ */ -------------------------------------------------------------------------------- /include/asio_http/http_server_connection-inl.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_SERVER_CONNECTION_H_INCLUDED_) 2 | #error "Invalid include order" 3 | #endif 4 | 5 | template 6 | basic_http_connection::basic_http_connection(asio::io_service& io_service, 7 | SocketType * handler) 8 | : handler_(handler) 9 | , socket_(io_service) 10 | , parser_() 11 | , header_state_(HEADER_START) 12 | { 13 | // Initialize parser 14 | http_parser_init(&parser_, HTTP_REQUEST); 15 | parser_.data = this; 16 | std::memset(&settings_, 0, sizeof(settings_)); 17 | settings_.on_url = &basic_http_connection::on_url; 18 | settings_.on_message_begin = &basic_http_connection::on_message_begin; 19 | settings_.on_header_field = &basic_http_connection::on_header_field; 20 | settings_.on_header_value = &basic_http_connection::on_header_value; 21 | settings_.on_headers_complete = &basic_http_connection::on_headers_complete; 22 | settings_.on_body = &basic_http_connection::on_body; 23 | settings_.on_message_complete = &basic_http_connection::on_message_complete; 24 | } 25 | 26 | template 27 | basic_http_connection::~basic_http_connection() 28 | { 29 | HTTP_SERVER_DEBUG_OUTPUT("~basic_http_connection\n"); 30 | } 31 | 32 | template 33 | void basic_http_connection::start() 34 | { 35 | socket_.async_read_some(buffer_.prepare(8), 36 | bind(&basic_http_connection::handler, 37 | this->shared_from_this(), 38 | asio::placeholders::error, 39 | asio::placeholders::bytes_transferred)); 40 | } 41 | 42 | template 43 | int basic_http_connection::on_message_begin(http_parser * parser) 44 | { 45 | basic_http_connection * conn = static_cast(parser->data); 46 | conn->request_url_.clear(); 47 | conn->headers_.clear(); 48 | return 0; 49 | } 50 | 51 | template 52 | int basic_http_connection::on_header_field(http_parser * parser, const char * at, size_t length) 53 | { 54 | basic_http_connection * conn = static_cast(parser->data); 55 | if (conn->header_state_ == HEADER_START) 56 | { 57 | conn->header_field_.append(at, at + length); 58 | } 59 | else if (conn->header_state_ == HEADER_VALUE) 60 | { 61 | conn->headers_.insert(std::make_pair(conn->header_field_, conn->header_value_)); 62 | conn->header_field_.clear(); 63 | conn->header_value_.clear(); 64 | conn->header_field_.append(at, at + length); 65 | } 66 | else if (conn->header_state_ == HEADER_FIELD) 67 | { 68 | conn->header_field_.append(at, at + length); 69 | } 70 | conn->header_state_ = HEADER_FIELD; 71 | return 0; 72 | } 73 | template 74 | int basic_http_connection::on_header_value(http_parser * parser, const char * at, size_t length) 75 | { 76 | basic_http_connection * conn = static_cast(parser->data); 77 | if (conn->header_state_ == HEADER_FIELD) 78 | { 79 | conn->header_value_.clear(); 80 | conn->header_value_.append(at, at + length); 81 | } 82 | else if (conn->header_state_ == HEADER_VALUE) 83 | { 84 | conn->header_value_.append(at, at + length); 85 | } 86 | conn->header_state_ = HEADER_VALUE; 87 | return 0; 88 | } 89 | template 90 | int basic_http_connection::on_headers_complete(http_parser * parser) 91 | { 92 | basic_http_connection * conn = static_cast(parser->data); 93 | if (conn->header_state_ == HEADER_VALUE) 94 | { 95 | conn->headers_.insert(std::make_pair(conn->header_field_, conn->header_value_)); 96 | } 97 | return 0; 98 | } 99 | template 100 | int basic_http_connection::on_body(http_parser * parser, const char * at, size_t length) 101 | { 102 | basic_http_connection * conn = static_cast(parser->data); 103 | conn->request_body_.append(at, at + length); 104 | return 0; 105 | } 106 | 107 | template 108 | int basic_http_connection::on_url(http_parser* parser, const char *at, size_t length) 109 | { 110 | basic_http_connection * conn = static_cast(parser->data); 111 | conn->request_url_.append(at, at + length); 112 | return 0; 113 | } 114 | 115 | template 116 | int basic_http_connection::on_message_complete(http_parser * parser) 117 | { 118 | basic_http_connection * conn = static_cast(parser->data); 119 | conn->socket_.get_io_service().post( 120 | bind(&basic_http_connection::process_request, conn->shared_from_this())); 121 | return 0; 122 | } 123 | 124 | template 125 | void basic_http_connection::handler(const std::error_code& error, std::size_t bytes_transferred) 126 | { 127 | if (!error && bytes_transferred) 128 | { 129 | const char * data = asio::buffer_cast(buffer_.data()); 130 | std::size_t nsize = http_parser_execute(&parser_, &settings_, data, bytes_transferred); 131 | if (nsize != bytes_transferred) 132 | { 133 | HTTP_SERVER_DEBUG_OUTPUT("http parser execute fail %lu/%lu\n", nsize, bytes_transferred); 134 | socket_.close(); 135 | return; 136 | } 137 | buffer_.consume(nsize); 138 | start(); 139 | } 140 | else 141 | { 142 | socket_.close(); 143 | } 144 | } 145 | 146 | template 147 | void basic_http_connection::handle_write(const std::error_code& error, 148 | size_t bytes_transferred) 149 | { 150 | if (error) 151 | { 152 | HTTP_SERVER_DEBUG_OUTPUT("Unable to handle request: %s [Errno %d]\n", 153 | error.message().c_str(), 154 | error.value()); 155 | return; 156 | } 157 | HTTP_SERVER_DEBUG_OUTPUT("Response sent with %lu bytes\n", bytes_transferred); 158 | } 159 | 160 | template 161 | void basic_http_connection::send_response(int status_code, std::string message) 162 | { 163 | std::string status_text; 164 | #define HTTP_STATUS_CODE(code, descr, type) \ 165 | case (code): \ 166 | status_text = (descr); \ 167 | break; 168 | switch (status_code) 169 | { 170 | HTTP_STATUS_CODE_MAP(HTTP_STATUS_CODE) 171 | default: 172 | status_text = "UNKNOWN"; 173 | break; 174 | } 175 | #undef HTTP_STATUS_CODE 176 | 177 | std::ostream o(&outgoing_buffer_); 178 | o << "HTTP/1.1 " << status_code << " " << status_text << "\r\n" 179 | << "Content-Length: " << message.length() << "\r\n"; 180 | if (http_should_keep_alive(&parser_) == 1) 181 | { 182 | o << "Connection: keep-alive\r\n"; 183 | } 184 | o << "\r\n"; 185 | o << message; 186 | asio::async_write(socket_, outgoing_buffer_, 187 | bind(&basic_http_connection::handle_write, this->shared_from_this(), 188 | asio::placeholders::error, 189 | asio::placeholders::bytes_transferred)); 190 | } 191 | 192 | template 193 | void basic_http_connection::process_request() 194 | { 195 | (*handler_)(this->shared_from_this()); 196 | // Re-initialize parser 197 | http_parser_init(&parser_, HTTP_REQUEST); 198 | parser_.data = this; 199 | } 200 | -------------------------------------------------------------------------------- /include/asio_http/http_server_connection.hpp: -------------------------------------------------------------------------------- 1 | #if !defined(ASIO_HTTP_HTTP_SERVER_CONNECTION_H_INCLUDED_) 2 | #define ASIO_HTTP_HTTP_SERVER_CONNECTION_H_INCLUDED_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "http_parser.h" 12 | #include "detail/status_codes.hpp" 13 | 14 | template 15 | class basic_http_connection 16 | : public std::enable_shared_from_this > 17 | { 18 | public: 19 | typedef std::shared_ptr > pointer; 20 | 21 | static pointer create(asio::io_service& io_service, SocketType * handler) 22 | { 23 | return pointer(new basic_http_connection(io_service, handler)); 24 | } 25 | 26 | asio::ip::tcp::socket& get_socket() 27 | { 28 | return socket_; 29 | } 30 | void start(); 31 | http_parser_settings settings_; 32 | /** 33 | * Send HTTP response. 34 | */ 35 | void send_response(int status_code, std::string message); 36 | inline const std::string & get_request_url() const 37 | { 38 | return request_url_; 39 | } 40 | typedef std::multimap headers_type; 41 | const headers_type & get_headers() const 42 | { 43 | return headers_; 44 | } 45 | int get_request_method() const 46 | { 47 | return parser_.method; 48 | } 49 | const std::string & get_request_body() const 50 | { 51 | return request_body_; 52 | } 53 | ~basic_http_connection(); 54 | private: 55 | SocketType * handler_; 56 | basic_http_connection(asio::io_service& io_service, 57 | SocketType * handler); 58 | 59 | void handle_write(const std::error_code& error_code /*error*/, 60 | size_t /*bytes_transferred*/); 61 | asio::ip::tcp::socket socket_; 62 | /* 63 | * HTTP stuff 64 | */ 65 | http_parser parser_; 66 | static int on_message_begin(http_parser * parser); 67 | static int on_header_field(http_parser * parser, const char * at, size_t length); 68 | static int on_header_value(http_parser * parser, const char * at, size_t length); 69 | static int on_headers_complete(http_parser * parser); 70 | std::string request_body_; 71 | static int on_body(http_parser * parser, const char * at, size_t length); 72 | /** 73 | * HTTP parser encountered something that appears to be URL. 74 | * This callback might be called multiple times, but in our 75 | * case this gets called just once. 76 | */ 77 | static int on_url(http_parser* parser, const char *at, size_t length); 78 | /** 79 | * Received complete HTTP request 80 | */ 81 | static int on_message_complete(http_parser * parser); 82 | /** 83 | * Temporary socket data is stored here. 84 | */ 85 | asio::streambuf buffer_; 86 | /** 87 | * Temporary buffer for static responses 88 | */ 89 | asio::streambuf outgoing_buffer_; 90 | /** 91 | * Received HTTP header. 92 | */ 93 | void handler(const std::error_code& e, std::size_t size); 94 | /** 95 | * Executes request handler after successful request 96 | */ 97 | void process_request(); 98 | /** 99 | * Full URL of the current request 100 | */ 101 | std::string request_url_; 102 | enum 103 | { 104 | HEADER_START, 105 | HEADER_FIELD, 106 | HEADER_VALUE 107 | }; 108 | int header_state_; 109 | std::string header_field_; 110 | std::string header_value_; 111 | headers_type headers_; 112 | }; 113 | 114 | #include "http_server_connection-inl.hpp" 115 | 116 | typedef basic_http_connection http_connection; 117 | 118 | #endif /* ASIO_HTTP_HTTP_SERVER_CONNECTION_H_INCLUDED_ */ -------------------------------------------------------------------------------- /include/http_parser.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | #ifndef http_parser_h 22 | #define http_parser_h 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | /* Also update SONAME in the Makefile whenever you change these. */ 28 | #define HTTP_PARSER_VERSION_MAJOR 2 29 | #define HTTP_PARSER_VERSION_MINOR 3 30 | #define HTTP_PARSER_VERSION_PATCH 0 31 | 32 | #include 33 | #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) 34 | #include 35 | #include 36 | typedef __int8 int8_t; 37 | typedef unsigned __int8 uint8_t; 38 | typedef __int16 int16_t; 39 | typedef unsigned __int16 uint16_t; 40 | typedef __int32 int32_t; 41 | typedef unsigned __int32 uint32_t; 42 | typedef __int64 int64_t; 43 | typedef unsigned __int64 uint64_t; 44 | #else 45 | #include 46 | #endif 47 | 48 | /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run 49 | * faster 50 | */ 51 | #ifndef HTTP_PARSER_STRICT 52 | # define HTTP_PARSER_STRICT 1 53 | #endif 54 | 55 | /* Maximium header size allowed. If the macro is not defined 56 | * before including this header then the default is used. To 57 | * change the maximum header size, define the macro in the build 58 | * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove 59 | * the effective limit on the size of the header, define the macro 60 | * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) 61 | */ 62 | #ifndef HTTP_MAX_HEADER_SIZE 63 | # define HTTP_MAX_HEADER_SIZE (80*1024) 64 | #endif 65 | 66 | typedef struct http_parser http_parser; 67 | typedef struct http_parser_settings http_parser_settings; 68 | 69 | 70 | /* Callbacks should return non-zero to indicate an error. The parser will 71 | * then halt execution. 72 | * 73 | * The one exception is on_headers_complete. In a HTTP_RESPONSE parser 74 | * returning '1' from on_headers_complete will tell the parser that it 75 | * should not expect a body. This is used when receiving a response to a 76 | * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: 77 | * chunked' headers that indicate the presence of a body. 78 | * 79 | * http_data_cb does not return data chunks. It will be call arbitrarally 80 | * many times for each string. E.G. you might get 10 callbacks for "on_url" 81 | * each providing just a few characters more data. 82 | */ 83 | typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); 84 | typedef int (*http_cb) (http_parser*); 85 | 86 | 87 | /* Request Methods */ 88 | #define HTTP_METHOD_MAP(XX) \ 89 | XX(0, DELETE, DELETE) \ 90 | XX(1, GET, GET) \ 91 | XX(2, HEAD, HEAD) \ 92 | XX(3, POST, POST) \ 93 | XX(4, PUT, PUT) \ 94 | /* pathological */ \ 95 | XX(5, CONNECT, CONNECT) \ 96 | XX(6, OPTIONS, OPTIONS) \ 97 | XX(7, TRACE, TRACE) \ 98 | /* webdav */ \ 99 | XX(8, COPY, COPY) \ 100 | XX(9, LOCK, LOCK) \ 101 | XX(10, MKCOL, MKCOL) \ 102 | XX(11, MOVE, MOVE) \ 103 | XX(12, PROPFIND, PROPFIND) \ 104 | XX(13, PROPPATCH, PROPPATCH) \ 105 | XX(14, SEARCH, SEARCH) \ 106 | XX(15, UNLOCK, UNLOCK) \ 107 | /* subversion */ \ 108 | XX(16, REPORT, REPORT) \ 109 | XX(17, MKACTIVITY, MKACTIVITY) \ 110 | XX(18, CHECKOUT, CHECKOUT) \ 111 | XX(19, MERGE, MERGE) \ 112 | /* upnp */ \ 113 | XX(20, MSEARCH, M-SEARCH) \ 114 | XX(21, NOTIFY, NOTIFY) \ 115 | XX(22, SUBSCRIBE, SUBSCRIBE) \ 116 | XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ 117 | /* RFC-5789 */ \ 118 | XX(24, PATCH, PATCH) \ 119 | XX(25, PURGE, PURGE) \ 120 | /* CalDAV */ \ 121 | XX(26, MKCALENDAR, MKCALENDAR) \ 122 | 123 | enum http_method 124 | { 125 | #define XX(num, name, string) HTTP_##name = num, 126 | HTTP_METHOD_MAP(XX) 127 | #undef XX 128 | }; 129 | 130 | 131 | enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; 132 | 133 | 134 | /* Flag values for http_parser.flags field */ 135 | enum flags 136 | { F_CHUNKED = 1 << 0 137 | , F_CONNECTION_KEEP_ALIVE = 1 << 1 138 | , F_CONNECTION_CLOSE = 1 << 2 139 | , F_TRAILING = 1 << 3 140 | , F_UPGRADE = 1 << 4 141 | , F_SKIPBODY = 1 << 5 142 | }; 143 | 144 | 145 | /* Map for errno-related constants 146 | * 147 | * The provided argument should be a macro that takes 2 arguments. 148 | */ 149 | #define HTTP_ERRNO_MAP(XX) \ 150 | /* No error */ \ 151 | XX(OK, "success") \ 152 | \ 153 | /* Callback-related errors */ \ 154 | XX(CB_message_begin, "the on_message_begin callback failed") \ 155 | XX(CB_url, "the on_url callback failed") \ 156 | XX(CB_header_field, "the on_header_field callback failed") \ 157 | XX(CB_header_value, "the on_header_value callback failed") \ 158 | XX(CB_headers_complete, "the on_headers_complete callback failed") \ 159 | XX(CB_body, "the on_body callback failed") \ 160 | XX(CB_message_complete, "the on_message_complete callback failed") \ 161 | XX(CB_status, "the on_status callback failed") \ 162 | \ 163 | /* Parsing-related errors */ \ 164 | XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ 165 | XX(HEADER_OVERFLOW, \ 166 | "too many header bytes seen; overflow detected") \ 167 | XX(CLOSED_CONNECTION, \ 168 | "data received after completed connection: close message") \ 169 | XX(INVALID_VERSION, "invalid HTTP version") \ 170 | XX(INVALID_STATUS, "invalid HTTP status code") \ 171 | XX(INVALID_METHOD, "invalid HTTP method") \ 172 | XX(INVALID_URL, "invalid URL") \ 173 | XX(INVALID_HOST, "invalid host") \ 174 | XX(INVALID_PORT, "invalid port") \ 175 | XX(INVALID_PATH, "invalid path") \ 176 | XX(INVALID_QUERY_STRING, "invalid query string") \ 177 | XX(INVALID_FRAGMENT, "invalid fragment") \ 178 | XX(LF_EXPECTED, "LF character expected") \ 179 | XX(INVALID_HEADER_TOKEN, "invalid character in header") \ 180 | XX(INVALID_CONTENT_LENGTH, \ 181 | "invalid character in content-length header") \ 182 | XX(INVALID_CHUNK_SIZE, \ 183 | "invalid character in chunk size header") \ 184 | XX(INVALID_CONSTANT, "invalid constant string") \ 185 | XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ 186 | XX(STRICT, "strict mode assertion failed") \ 187 | XX(PAUSED, "parser is paused") \ 188 | XX(UNKNOWN, "an unknown error occurred") 189 | 190 | 191 | /* Define HPE_* values for each errno value above */ 192 | #define HTTP_ERRNO_GEN(n, s) HPE_##n, 193 | enum http_errno { 194 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) 195 | }; 196 | #undef HTTP_ERRNO_GEN 197 | 198 | 199 | /* Get an http_errno value from an http_parser */ 200 | #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) 201 | 202 | 203 | struct http_parser { 204 | /** PRIVATE **/ 205 | unsigned int type : 2; /* enum http_parser_type */ 206 | unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ 207 | unsigned int state : 8; /* enum state from http_parser.c */ 208 | unsigned int header_state : 8; /* enum header_state from http_parser.c */ 209 | unsigned int index : 8; /* index into current matcher */ 210 | 211 | uint32_t nread; /* # bytes read in various scenarios */ 212 | uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ 213 | 214 | /** READ-ONLY **/ 215 | unsigned short http_major; 216 | unsigned short http_minor; 217 | unsigned int status_code : 16; /* responses only */ 218 | unsigned int method : 8; /* requests only */ 219 | unsigned int http_errno : 7; 220 | 221 | /* 1 = Upgrade header was present and the parser has exited because of that. 222 | * 0 = No upgrade header present. 223 | * Should be checked when http_parser_execute() returns in addition to 224 | * error checking. 225 | */ 226 | unsigned int upgrade : 1; 227 | 228 | /** PUBLIC **/ 229 | void *data; /* A pointer to get hook to the "connection" or "socket" object */ 230 | }; 231 | 232 | 233 | struct http_parser_settings { 234 | http_cb on_message_begin; 235 | http_data_cb on_url; 236 | http_data_cb on_status; 237 | http_data_cb on_header_field; 238 | http_data_cb on_header_value; 239 | http_cb on_headers_complete; 240 | http_data_cb on_body; 241 | http_cb on_message_complete; 242 | }; 243 | 244 | 245 | enum http_parser_url_fields 246 | { UF_SCHEMA = 0 247 | , UF_HOST = 1 248 | , UF_PORT = 2 249 | , UF_PATH = 3 250 | , UF_QUERY = 4 251 | , UF_FRAGMENT = 5 252 | , UF_USERINFO = 6 253 | , UF_MAX = 7 254 | }; 255 | 256 | 257 | /* Result structure for http_parser_parse_url(). 258 | * 259 | * Callers should index into field_data[] with UF_* values iff field_set 260 | * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and 261 | * because we probably have padding left over), we convert any port to 262 | * a uint16_t. 263 | */ 264 | struct http_parser_url { 265 | uint16_t field_set; /* Bitmask of (1 << UF_*) values */ 266 | uint16_t port; /* Converted UF_PORT string */ 267 | 268 | struct { 269 | uint16_t off; /* Offset into buffer in which field starts */ 270 | uint16_t len; /* Length of run in buffer */ 271 | } field_data[UF_MAX]; 272 | }; 273 | 274 | 275 | /* Returns the library version. Bits 16-23 contain the major version number, 276 | * bits 8-15 the minor version number and bits 0-7 the patch level. 277 | * Usage example: 278 | * 279 | * unsigned long version = http_parser_version(); 280 | * unsigned major = (version >> 16) & 255; 281 | * unsigned minor = (version >> 8) & 255; 282 | * unsigned patch = version & 255; 283 | * printf("http_parser v%u.%u.%u\n", major, minor, version); 284 | */ 285 | unsigned long http_parser_version(void); 286 | 287 | void http_parser_init(http_parser *parser, enum http_parser_type type); 288 | 289 | 290 | size_t http_parser_execute(http_parser *parser, 291 | const http_parser_settings *settings, 292 | const char *data, 293 | size_t len); 294 | 295 | 296 | /* If http_should_keep_alive() in the on_headers_complete or 297 | * on_message_complete callback returns 0, then this should be 298 | * the last message on the connection. 299 | * If you are the server, respond with the "Connection: close" header. 300 | * If you are the client, close the connection. 301 | */ 302 | int http_should_keep_alive(const http_parser *parser); 303 | 304 | /* Returns a string version of the HTTP method. */ 305 | const char *http_method_str(enum http_method m); 306 | 307 | /* Return a string name of the given error */ 308 | const char *http_errno_name(enum http_errno err); 309 | 310 | /* Return a string description of the given error */ 311 | const char *http_errno_description(enum http_errno err); 312 | 313 | /* Parse a URL; return nonzero on failure */ 314 | int http_parser_parse_url(const char *buf, size_t buflen, 315 | int is_connect, 316 | struct http_parser_url *u); 317 | 318 | /* Pause or un-pause the parser; a nonzero value pauses */ 319 | void http_parser_pause(http_parser *parser, int paused); 320 | 321 | /* Checks if this is the final chunk of the body. */ 322 | int http_body_is_final(const http_parser *parser); 323 | 324 | #ifdef __cplusplus 325 | } 326 | #endif 327 | #endif 328 | -------------------------------------------------------------------------------- /src/http_parser.c: -------------------------------------------------------------------------------- 1 | /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev 2 | * 3 | * Additional changes are licensed under the same terms as NGINX and 4 | * copyright Joyent, Inc. and other Node contributors. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | #include "http_parser.h" 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef ULLONG_MAX 33 | # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ 34 | #endif 35 | 36 | #ifndef MIN 37 | # define MIN(a,b) ((a) < (b) ? (a) : (b)) 38 | #endif 39 | 40 | #ifndef ARRAY_SIZE 41 | # define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 42 | #endif 43 | 44 | #ifndef BIT_AT 45 | # define BIT_AT(a, i) \ 46 | (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ 47 | (1 << ((unsigned int) (i) & 7)))) 48 | #endif 49 | 50 | #ifndef ELEM_AT 51 | # define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) 52 | #endif 53 | 54 | #define SET_ERRNO(e) \ 55 | do { \ 56 | parser->http_errno = (e); \ 57 | } while(0) 58 | 59 | 60 | /* Run the notify callback FOR, returning ER if it fails */ 61 | #define CALLBACK_NOTIFY_(FOR, ER) \ 62 | do { \ 63 | assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ 64 | \ 65 | if (settings->on_##FOR) { \ 66 | if (0 != settings->on_##FOR(parser)) { \ 67 | SET_ERRNO(HPE_CB_##FOR); \ 68 | } \ 69 | \ 70 | /* We either errored above or got paused; get out */ \ 71 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ 72 | return (ER); \ 73 | } \ 74 | } \ 75 | } while (0) 76 | 77 | /* Run the notify callback FOR and consume the current byte */ 78 | #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) 79 | 80 | /* Run the notify callback FOR and don't consume the current byte */ 81 | #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) 82 | 83 | /* Run data callback FOR with LEN bytes, returning ER if it fails */ 84 | #define CALLBACK_DATA_(FOR, LEN, ER) \ 85 | do { \ 86 | assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ 87 | \ 88 | if (FOR##_mark) { \ 89 | if (settings->on_##FOR) { \ 90 | if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ 91 | SET_ERRNO(HPE_CB_##FOR); \ 92 | } \ 93 | \ 94 | /* We either errored above or got paused; get out */ \ 95 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ 96 | return (ER); \ 97 | } \ 98 | } \ 99 | FOR##_mark = NULL; \ 100 | } \ 101 | } while (0) 102 | 103 | /* Run the data callback FOR and consume the current byte */ 104 | #define CALLBACK_DATA(FOR) \ 105 | CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) 106 | 107 | /* Run the data callback FOR and don't consume the current byte */ 108 | #define CALLBACK_DATA_NOADVANCE(FOR) \ 109 | CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) 110 | 111 | /* Set the mark FOR; non-destructive if mark is already set */ 112 | #define MARK(FOR) \ 113 | do { \ 114 | if (!FOR##_mark) { \ 115 | FOR##_mark = p; \ 116 | } \ 117 | } while (0) 118 | 119 | 120 | #define PROXY_CONNECTION "proxy-connection" 121 | #define CONNECTION "connection" 122 | #define CONTENT_LENGTH "content-length" 123 | #define TRANSFER_ENCODING "transfer-encoding" 124 | #define UPGRADE "upgrade" 125 | #define CHUNKED "chunked" 126 | #define KEEP_ALIVE "keep-alive" 127 | #define CLOSE "close" 128 | 129 | 130 | static const char *method_strings[] = 131 | { 132 | #define XX(num, name, string) #string, 133 | HTTP_METHOD_MAP(XX) 134 | #undef XX 135 | }; 136 | 137 | 138 | /* Tokens as defined by rfc 2616. Also lowercases them. 139 | * token = 1* 140 | * separators = "(" | ")" | "<" | ">" | "@" 141 | * | "," | ";" | ":" | "\" | <"> 142 | * | "/" | "[" | "]" | "?" | "=" 143 | * | "{" | "}" | SP | HT 144 | */ 145 | static const char tokens[256] = { 146 | /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 147 | 0, 0, 0, 0, 0, 0, 0, 0, 148 | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 149 | 0, 0, 0, 0, 0, 0, 0, 0, 150 | /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 151 | 0, 0, 0, 0, 0, 0, 0, 0, 152 | /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 153 | 0, 0, 0, 0, 0, 0, 0, 0, 154 | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 155 | 0, '!', 0, '#', '$', '%', '&', '\'', 156 | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 157 | 0, 0, '*', '+', 0, '-', '.', 0, 158 | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 159 | '0', '1', '2', '3', '4', '5', '6', '7', 160 | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 161 | '8', '9', 0, 0, 0, 0, 0, 0, 162 | /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 163 | 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 164 | /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 165 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 166 | /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 167 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 168 | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 169 | 'x', 'y', 'z', 0, 0, 0, '^', '_', 170 | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 171 | '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 172 | /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 173 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 174 | /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 175 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 176 | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 177 | 'x', 'y', 'z', 0, '|', 0, '~', 0 }; 178 | 179 | 180 | static const int8_t unhex[256] = 181 | {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 182 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 183 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 184 | , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 185 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 186 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 187 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 188 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 189 | }; 190 | 191 | 192 | #if HTTP_PARSER_STRICT 193 | # define T(v) 0 194 | #else 195 | # define T(v) v 196 | #endif 197 | 198 | 199 | static const uint8_t normal_url_char[32] = { 200 | /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 201 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 202 | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 203 | 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, 204 | /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 205 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 206 | /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 207 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 208 | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 209 | 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, 210 | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 211 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 212 | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 213 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 214 | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 215 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, 216 | /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 217 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 218 | /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 219 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 220 | /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 221 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 222 | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 223 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 224 | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 225 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 226 | /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 227 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 228 | /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 229 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 230 | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 231 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; 232 | 233 | #undef T 234 | 235 | enum state 236 | { s_dead = 1 /* important that this is > 0 */ 237 | 238 | , s_start_req_or_res 239 | , s_res_or_resp_H 240 | , s_start_res 241 | , s_res_H 242 | , s_res_HT 243 | , s_res_HTT 244 | , s_res_HTTP 245 | , s_res_first_http_major 246 | , s_res_http_major 247 | , s_res_first_http_minor 248 | , s_res_http_minor 249 | , s_res_first_status_code 250 | , s_res_status_code 251 | , s_res_status_start 252 | , s_res_status 253 | , s_res_line_almost_done 254 | 255 | , s_start_req 256 | 257 | , s_req_method 258 | , s_req_spaces_before_url 259 | , s_req_schema 260 | , s_req_schema_slash 261 | , s_req_schema_slash_slash 262 | , s_req_server_start 263 | , s_req_server 264 | , s_req_server_with_at 265 | , s_req_path 266 | , s_req_query_string_start 267 | , s_req_query_string 268 | , s_req_fragment_start 269 | , s_req_fragment 270 | , s_req_http_start 271 | , s_req_http_H 272 | , s_req_http_HT 273 | , s_req_http_HTT 274 | , s_req_http_HTTP 275 | , s_req_first_http_major 276 | , s_req_http_major 277 | , s_req_first_http_minor 278 | , s_req_http_minor 279 | , s_req_line_almost_done 280 | 281 | , s_header_field_start 282 | , s_header_field 283 | , s_header_value_discard_ws 284 | , s_header_value_discard_ws_almost_done 285 | , s_header_value_discard_lws 286 | , s_header_value_start 287 | , s_header_value 288 | , s_header_value_lws 289 | 290 | , s_header_almost_done 291 | 292 | , s_chunk_size_start 293 | , s_chunk_size 294 | , s_chunk_parameters 295 | , s_chunk_size_almost_done 296 | 297 | , s_headers_almost_done 298 | , s_headers_done 299 | 300 | /* Important: 's_headers_done' must be the last 'header' state. All 301 | * states beyond this must be 'body' states. It is used for overflow 302 | * checking. See the PARSING_HEADER() macro. 303 | */ 304 | 305 | , s_chunk_data 306 | , s_chunk_data_almost_done 307 | , s_chunk_data_done 308 | 309 | , s_body_identity 310 | , s_body_identity_eof 311 | 312 | , s_message_done 313 | }; 314 | 315 | 316 | #define PARSING_HEADER(state) (state <= s_headers_done) 317 | 318 | 319 | enum header_states 320 | { h_general = 0 321 | , h_C 322 | , h_CO 323 | , h_CON 324 | 325 | , h_matching_connection 326 | , h_matching_proxy_connection 327 | , h_matching_content_length 328 | , h_matching_transfer_encoding 329 | , h_matching_upgrade 330 | 331 | , h_connection 332 | , h_content_length 333 | , h_transfer_encoding 334 | , h_upgrade 335 | 336 | , h_matching_transfer_encoding_chunked 337 | , h_matching_connection_keep_alive 338 | , h_matching_connection_close 339 | 340 | , h_transfer_encoding_chunked 341 | , h_connection_keep_alive 342 | , h_connection_close 343 | }; 344 | 345 | enum http_host_state 346 | { 347 | s_http_host_dead = 1 348 | , s_http_userinfo_start 349 | , s_http_userinfo 350 | , s_http_host_start 351 | , s_http_host_v6_start 352 | , s_http_host 353 | , s_http_host_v6 354 | , s_http_host_v6_end 355 | , s_http_host_port_start 356 | , s_http_host_port 357 | }; 358 | 359 | /* Macros for character classes; depends on strict-mode */ 360 | #define CR '\r' 361 | #define LF '\n' 362 | #define LOWER(c) (unsigned char)(c | 0x20) 363 | #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') 364 | #define IS_NUM(c) ((c) >= '0' && (c) <= '9') 365 | #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) 366 | #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) 367 | #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ 368 | (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ 369 | (c) == ')') 370 | #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ 371 | (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ 372 | (c) == '$' || (c) == ',') 373 | 374 | #if HTTP_PARSER_STRICT 375 | #define TOKEN(c) (tokens[(unsigned char)c]) 376 | #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) 377 | #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') 378 | #else 379 | #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) 380 | #define IS_URL_CHAR(c) \ 381 | (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) 382 | #define IS_HOST_CHAR(c) \ 383 | (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') 384 | #endif 385 | 386 | 387 | #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) 388 | 389 | 390 | #if HTTP_PARSER_STRICT 391 | # define STRICT_CHECK(cond) \ 392 | do { \ 393 | if (cond) { \ 394 | SET_ERRNO(HPE_STRICT); \ 395 | goto error; \ 396 | } \ 397 | } while (0) 398 | # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) 399 | #else 400 | # define STRICT_CHECK(cond) 401 | # define NEW_MESSAGE() start_state 402 | #endif 403 | 404 | 405 | /* Map errno values to strings for human-readable output */ 406 | #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, 407 | static struct { 408 | const char *name; 409 | const char *description; 410 | } http_strerror_tab[] = { 411 | HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) 412 | }; 413 | #undef HTTP_STRERROR_GEN 414 | 415 | int http_message_needs_eof(const http_parser *parser); 416 | 417 | /* Our URL parser. 418 | * 419 | * This is designed to be shared by http_parser_execute() for URL validation, 420 | * hence it has a state transition + byte-for-byte interface. In addition, it 421 | * is meant to be embedded in http_parser_parse_url(), which does the dirty 422 | * work of turning state transitions URL components for its API. 423 | * 424 | * This function should only be invoked with non-space characters. It is 425 | * assumed that the caller cares about (and can detect) the transition between 426 | * URL and non-URL states by looking for these. 427 | */ 428 | static enum state 429 | parse_url_char(enum state s, const char ch) 430 | { 431 | if (ch == ' ' || ch == '\r' || ch == '\n') { 432 | return s_dead; 433 | } 434 | 435 | #if HTTP_PARSER_STRICT 436 | if (ch == '\t' || ch == '\f') { 437 | return s_dead; 438 | } 439 | #endif 440 | 441 | switch (s) { 442 | case s_req_spaces_before_url: 443 | /* Proxied requests are followed by scheme of an absolute URI (alpha). 444 | * All methods except CONNECT are followed by '/' or '*'. 445 | */ 446 | 447 | if (ch == '/' || ch == '*') { 448 | return s_req_path; 449 | } 450 | 451 | if (IS_ALPHA(ch)) { 452 | return s_req_schema; 453 | } 454 | 455 | break; 456 | 457 | case s_req_schema: 458 | if (IS_ALPHA(ch)) { 459 | return s; 460 | } 461 | 462 | if (ch == ':') { 463 | return s_req_schema_slash; 464 | } 465 | 466 | break; 467 | 468 | case s_req_schema_slash: 469 | if (ch == '/') { 470 | return s_req_schema_slash_slash; 471 | } 472 | 473 | break; 474 | 475 | case s_req_schema_slash_slash: 476 | if (ch == '/') { 477 | return s_req_server_start; 478 | } 479 | 480 | break; 481 | 482 | case s_req_server_with_at: 483 | if (ch == '@') { 484 | return s_dead; 485 | } 486 | 487 | /* FALLTHROUGH */ 488 | case s_req_server_start: 489 | case s_req_server: 490 | if (ch == '/') { 491 | return s_req_path; 492 | } 493 | 494 | if (ch == '?') { 495 | return s_req_query_string_start; 496 | } 497 | 498 | if (ch == '@') { 499 | return s_req_server_with_at; 500 | } 501 | 502 | if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { 503 | return s_req_server; 504 | } 505 | 506 | break; 507 | 508 | case s_req_path: 509 | if (IS_URL_CHAR(ch)) { 510 | return s; 511 | } 512 | 513 | switch (ch) { 514 | case '?': 515 | return s_req_query_string_start; 516 | 517 | case '#': 518 | return s_req_fragment_start; 519 | } 520 | 521 | break; 522 | 523 | case s_req_query_string_start: 524 | case s_req_query_string: 525 | if (IS_URL_CHAR(ch)) { 526 | return s_req_query_string; 527 | } 528 | 529 | switch (ch) { 530 | case '?': 531 | /* allow extra '?' in query string */ 532 | return s_req_query_string; 533 | 534 | case '#': 535 | return s_req_fragment_start; 536 | } 537 | 538 | break; 539 | 540 | case s_req_fragment_start: 541 | if (IS_URL_CHAR(ch)) { 542 | return s_req_fragment; 543 | } 544 | 545 | switch (ch) { 546 | case '?': 547 | return s_req_fragment; 548 | 549 | case '#': 550 | return s; 551 | } 552 | 553 | break; 554 | 555 | case s_req_fragment: 556 | if (IS_URL_CHAR(ch)) { 557 | return s; 558 | } 559 | 560 | switch (ch) { 561 | case '?': 562 | case '#': 563 | return s; 564 | } 565 | 566 | break; 567 | 568 | default: 569 | break; 570 | } 571 | 572 | /* We should never fall out of the switch above unless there's an error */ 573 | return s_dead; 574 | } 575 | 576 | size_t http_parser_execute (http_parser *parser, 577 | const http_parser_settings *settings, 578 | const char *data, 579 | size_t len) 580 | { 581 | char c, ch; 582 | int8_t unhex_val; 583 | const char *p = data; 584 | const char *header_field_mark = 0; 585 | const char *header_value_mark = 0; 586 | const char *url_mark = 0; 587 | const char *body_mark = 0; 588 | const char *status_mark = 0; 589 | 590 | /* We're in an error state. Don't bother doing anything. */ 591 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { 592 | return 0; 593 | } 594 | 595 | if (len == 0) { 596 | switch (parser->state) { 597 | case s_body_identity_eof: 598 | /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if 599 | * we got paused. 600 | */ 601 | CALLBACK_NOTIFY_NOADVANCE(message_complete); 602 | return 0; 603 | 604 | case s_dead: 605 | case s_start_req_or_res: 606 | case s_start_res: 607 | case s_start_req: 608 | return 0; 609 | 610 | default: 611 | SET_ERRNO(HPE_INVALID_EOF_STATE); 612 | return 1; 613 | } 614 | } 615 | 616 | 617 | if (parser->state == s_header_field) 618 | header_field_mark = data; 619 | if (parser->state == s_header_value) 620 | header_value_mark = data; 621 | switch (parser->state) { 622 | case s_req_path: 623 | case s_req_schema: 624 | case s_req_schema_slash: 625 | case s_req_schema_slash_slash: 626 | case s_req_server_start: 627 | case s_req_server: 628 | case s_req_server_with_at: 629 | case s_req_query_string_start: 630 | case s_req_query_string: 631 | case s_req_fragment_start: 632 | case s_req_fragment: 633 | url_mark = data; 634 | break; 635 | case s_res_status: 636 | status_mark = data; 637 | break; 638 | } 639 | 640 | for (p=data; p != data + len; p++) { 641 | ch = *p; 642 | 643 | if (PARSING_HEADER(parser->state)) { 644 | ++parser->nread; 645 | /* Don't allow the total size of the HTTP headers (including the status 646 | * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect 647 | * embedders against denial-of-service attacks where the attacker feeds 648 | * us a never-ending header that the embedder keeps buffering. 649 | * 650 | * This check is arguably the responsibility of embedders but we're doing 651 | * it on the embedder's behalf because most won't bother and this way we 652 | * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger 653 | * than any reasonable request or response so this should never affect 654 | * day-to-day operation. 655 | */ 656 | if (parser->nread > (HTTP_MAX_HEADER_SIZE)) { 657 | SET_ERRNO(HPE_HEADER_OVERFLOW); 658 | goto error; 659 | } 660 | } 661 | 662 | reexecute_byte: 663 | switch (parser->state) { 664 | 665 | case s_dead: 666 | /* this state is used after a 'Connection: close' message 667 | * the parser will error out if it reads another message 668 | */ 669 | if (ch == CR || ch == LF) 670 | break; 671 | 672 | SET_ERRNO(HPE_CLOSED_CONNECTION); 673 | goto error; 674 | 675 | case s_start_req_or_res: 676 | { 677 | if (ch == CR || ch == LF) 678 | break; 679 | parser->flags = 0; 680 | parser->content_length = ULLONG_MAX; 681 | 682 | if (ch == 'H') { 683 | parser->state = s_res_or_resp_H; 684 | 685 | CALLBACK_NOTIFY(message_begin); 686 | } else { 687 | parser->type = HTTP_REQUEST; 688 | parser->state = s_start_req; 689 | goto reexecute_byte; 690 | } 691 | 692 | break; 693 | } 694 | 695 | case s_res_or_resp_H: 696 | if (ch == 'T') { 697 | parser->type = HTTP_RESPONSE; 698 | parser->state = s_res_HT; 699 | } else { 700 | if (ch != 'E') { 701 | SET_ERRNO(HPE_INVALID_CONSTANT); 702 | goto error; 703 | } 704 | 705 | parser->type = HTTP_REQUEST; 706 | parser->method = HTTP_HEAD; 707 | parser->index = 2; 708 | parser->state = s_req_method; 709 | } 710 | break; 711 | 712 | case s_start_res: 713 | { 714 | parser->flags = 0; 715 | parser->content_length = ULLONG_MAX; 716 | 717 | switch (ch) { 718 | case 'H': 719 | parser->state = s_res_H; 720 | break; 721 | 722 | case CR: 723 | case LF: 724 | break; 725 | 726 | default: 727 | SET_ERRNO(HPE_INVALID_CONSTANT); 728 | goto error; 729 | } 730 | 731 | CALLBACK_NOTIFY(message_begin); 732 | break; 733 | } 734 | 735 | case s_res_H: 736 | STRICT_CHECK(ch != 'T'); 737 | parser->state = s_res_HT; 738 | break; 739 | 740 | case s_res_HT: 741 | STRICT_CHECK(ch != 'T'); 742 | parser->state = s_res_HTT; 743 | break; 744 | 745 | case s_res_HTT: 746 | STRICT_CHECK(ch != 'P'); 747 | parser->state = s_res_HTTP; 748 | break; 749 | 750 | case s_res_HTTP: 751 | STRICT_CHECK(ch != '/'); 752 | parser->state = s_res_first_http_major; 753 | break; 754 | 755 | case s_res_first_http_major: 756 | if (ch < '0' || ch > '9') { 757 | SET_ERRNO(HPE_INVALID_VERSION); 758 | goto error; 759 | } 760 | 761 | parser->http_major = ch - '0'; 762 | parser->state = s_res_http_major; 763 | break; 764 | 765 | /* major HTTP version or dot */ 766 | case s_res_http_major: 767 | { 768 | if (ch == '.') { 769 | parser->state = s_res_first_http_minor; 770 | break; 771 | } 772 | 773 | if (!IS_NUM(ch)) { 774 | SET_ERRNO(HPE_INVALID_VERSION); 775 | goto error; 776 | } 777 | 778 | parser->http_major *= 10; 779 | parser->http_major += ch - '0'; 780 | 781 | if (parser->http_major > 999) { 782 | SET_ERRNO(HPE_INVALID_VERSION); 783 | goto error; 784 | } 785 | 786 | break; 787 | } 788 | 789 | /* first digit of minor HTTP version */ 790 | case s_res_first_http_minor: 791 | if (!IS_NUM(ch)) { 792 | SET_ERRNO(HPE_INVALID_VERSION); 793 | goto error; 794 | } 795 | 796 | parser->http_minor = ch - '0'; 797 | parser->state = s_res_http_minor; 798 | break; 799 | 800 | /* minor HTTP version or end of request line */ 801 | case s_res_http_minor: 802 | { 803 | if (ch == ' ') { 804 | parser->state = s_res_first_status_code; 805 | break; 806 | } 807 | 808 | if (!IS_NUM(ch)) { 809 | SET_ERRNO(HPE_INVALID_VERSION); 810 | goto error; 811 | } 812 | 813 | parser->http_minor *= 10; 814 | parser->http_minor += ch - '0'; 815 | 816 | if (parser->http_minor > 999) { 817 | SET_ERRNO(HPE_INVALID_VERSION); 818 | goto error; 819 | } 820 | 821 | break; 822 | } 823 | 824 | case s_res_first_status_code: 825 | { 826 | if (!IS_NUM(ch)) { 827 | if (ch == ' ') { 828 | break; 829 | } 830 | 831 | SET_ERRNO(HPE_INVALID_STATUS); 832 | goto error; 833 | } 834 | parser->status_code = ch - '0'; 835 | parser->state = s_res_status_code; 836 | break; 837 | } 838 | 839 | case s_res_status_code: 840 | { 841 | if (!IS_NUM(ch)) { 842 | switch (ch) { 843 | case ' ': 844 | parser->state = s_res_status_start; 845 | break; 846 | case CR: 847 | parser->state = s_res_line_almost_done; 848 | break; 849 | case LF: 850 | parser->state = s_header_field_start; 851 | break; 852 | default: 853 | SET_ERRNO(HPE_INVALID_STATUS); 854 | goto error; 855 | } 856 | break; 857 | } 858 | 859 | parser->status_code *= 10; 860 | parser->status_code += ch - '0'; 861 | 862 | if (parser->status_code > 999) { 863 | SET_ERRNO(HPE_INVALID_STATUS); 864 | goto error; 865 | } 866 | 867 | break; 868 | } 869 | 870 | case s_res_status_start: 871 | { 872 | if (ch == CR) { 873 | parser->state = s_res_line_almost_done; 874 | break; 875 | } 876 | 877 | if (ch == LF) { 878 | parser->state = s_header_field_start; 879 | break; 880 | } 881 | 882 | MARK(status); 883 | parser->state = s_res_status; 884 | parser->index = 0; 885 | break; 886 | } 887 | 888 | case s_res_status: 889 | if (ch == CR) { 890 | parser->state = s_res_line_almost_done; 891 | CALLBACK_DATA(status); 892 | break; 893 | } 894 | 895 | if (ch == LF) { 896 | parser->state = s_header_field_start; 897 | CALLBACK_DATA(status); 898 | break; 899 | } 900 | 901 | break; 902 | 903 | case s_res_line_almost_done: 904 | STRICT_CHECK(ch != LF); 905 | parser->state = s_header_field_start; 906 | break; 907 | 908 | case s_start_req: 909 | { 910 | if (ch == CR || ch == LF) 911 | break; 912 | parser->flags = 0; 913 | parser->content_length = ULLONG_MAX; 914 | 915 | if (!IS_ALPHA(ch)) { 916 | SET_ERRNO(HPE_INVALID_METHOD); 917 | goto error; 918 | } 919 | 920 | parser->method = (enum http_method) 0; 921 | parser->index = 1; 922 | switch (ch) { 923 | case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; 924 | case 'D': parser->method = HTTP_DELETE; break; 925 | case 'G': parser->method = HTTP_GET; break; 926 | case 'H': parser->method = HTTP_HEAD; break; 927 | case 'L': parser->method = HTTP_LOCK; break; 928 | case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; 929 | case 'N': parser->method = HTTP_NOTIFY; break; 930 | case 'O': parser->method = HTTP_OPTIONS; break; 931 | case 'P': parser->method = HTTP_POST; 932 | /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ 933 | break; 934 | case 'R': parser->method = HTTP_REPORT; break; 935 | case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; 936 | case 'T': parser->method = HTTP_TRACE; break; 937 | case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; 938 | default: 939 | SET_ERRNO(HPE_INVALID_METHOD); 940 | goto error; 941 | } 942 | parser->state = s_req_method; 943 | 944 | CALLBACK_NOTIFY(message_begin); 945 | 946 | break; 947 | } 948 | 949 | case s_req_method: 950 | { 951 | const char *matcher; 952 | if (ch == '\0') { 953 | SET_ERRNO(HPE_INVALID_METHOD); 954 | goto error; 955 | } 956 | 957 | matcher = method_strings[parser->method]; 958 | if (ch == ' ' && matcher[parser->index] == '\0') { 959 | parser->state = s_req_spaces_before_url; 960 | } else if (ch == matcher[parser->index]) { 961 | ; /* nada */ 962 | } else if (parser->method == HTTP_CONNECT) { 963 | if (parser->index == 1 && ch == 'H') { 964 | parser->method = HTTP_CHECKOUT; 965 | } else if (parser->index == 2 && ch == 'P') { 966 | parser->method = HTTP_COPY; 967 | } else { 968 | SET_ERRNO(HPE_INVALID_METHOD); 969 | goto error; 970 | } 971 | } else if (parser->method == HTTP_MKCOL) { 972 | if (parser->index == 1 && ch == 'O') { 973 | parser->method = HTTP_MOVE; 974 | } else if (parser->index == 1 && ch == 'E') { 975 | parser->method = HTTP_MERGE; 976 | } else if (parser->index == 1 && ch == '-') { 977 | parser->method = HTTP_MSEARCH; 978 | } else if (parser->index == 2 && ch == 'A') { 979 | parser->method = HTTP_MKACTIVITY; 980 | } else if (parser->index == 3 && ch == 'A') { 981 | parser->method = HTTP_MKCALENDAR; 982 | } else { 983 | SET_ERRNO(HPE_INVALID_METHOD); 984 | goto error; 985 | } 986 | } else if (parser->method == HTTP_SUBSCRIBE) { 987 | if (parser->index == 1 && ch == 'E') { 988 | parser->method = HTTP_SEARCH; 989 | } else { 990 | SET_ERRNO(HPE_INVALID_METHOD); 991 | goto error; 992 | } 993 | } else if (parser->index == 1 && parser->method == HTTP_POST) { 994 | if (ch == 'R') { 995 | parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ 996 | } else if (ch == 'U') { 997 | parser->method = HTTP_PUT; /* or HTTP_PURGE */ 998 | } else if (ch == 'A') { 999 | parser->method = HTTP_PATCH; 1000 | } else { 1001 | SET_ERRNO(HPE_INVALID_METHOD); 1002 | goto error; 1003 | } 1004 | } else if (parser->index == 2) { 1005 | if (parser->method == HTTP_PUT) { 1006 | if (ch == 'R') { 1007 | parser->method = HTTP_PURGE; 1008 | } else { 1009 | SET_ERRNO(HPE_INVALID_METHOD); 1010 | goto error; 1011 | } 1012 | } else if (parser->method == HTTP_UNLOCK) { 1013 | if (ch == 'S') { 1014 | parser->method = HTTP_UNSUBSCRIBE; 1015 | } else { 1016 | SET_ERRNO(HPE_INVALID_METHOD); 1017 | goto error; 1018 | } 1019 | } else { 1020 | SET_ERRNO(HPE_INVALID_METHOD); 1021 | goto error; 1022 | } 1023 | } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { 1024 | parser->method = HTTP_PROPPATCH; 1025 | } else { 1026 | SET_ERRNO(HPE_INVALID_METHOD); 1027 | goto error; 1028 | } 1029 | 1030 | ++parser->index; 1031 | break; 1032 | } 1033 | 1034 | case s_req_spaces_before_url: 1035 | { 1036 | if (ch == ' ') break; 1037 | 1038 | MARK(url); 1039 | if (parser->method == HTTP_CONNECT) { 1040 | parser->state = s_req_server_start; 1041 | } 1042 | 1043 | parser->state = parse_url_char((enum state)parser->state, ch); 1044 | if (parser->state == s_dead) { 1045 | SET_ERRNO(HPE_INVALID_URL); 1046 | goto error; 1047 | } 1048 | 1049 | break; 1050 | } 1051 | 1052 | case s_req_schema: 1053 | case s_req_schema_slash: 1054 | case s_req_schema_slash_slash: 1055 | case s_req_server_start: 1056 | { 1057 | switch (ch) { 1058 | /* No whitespace allowed here */ 1059 | case ' ': 1060 | case CR: 1061 | case LF: 1062 | SET_ERRNO(HPE_INVALID_URL); 1063 | goto error; 1064 | default: 1065 | parser->state = parse_url_char((enum state)parser->state, ch); 1066 | if (parser->state == s_dead) { 1067 | SET_ERRNO(HPE_INVALID_URL); 1068 | goto error; 1069 | } 1070 | } 1071 | 1072 | break; 1073 | } 1074 | 1075 | case s_req_server: 1076 | case s_req_server_with_at: 1077 | case s_req_path: 1078 | case s_req_query_string_start: 1079 | case s_req_query_string: 1080 | case s_req_fragment_start: 1081 | case s_req_fragment: 1082 | { 1083 | switch (ch) { 1084 | case ' ': 1085 | parser->state = s_req_http_start; 1086 | CALLBACK_DATA(url); 1087 | break; 1088 | case CR: 1089 | case LF: 1090 | parser->http_major = 0; 1091 | parser->http_minor = 9; 1092 | parser->state = (ch == CR) ? 1093 | s_req_line_almost_done : 1094 | s_header_field_start; 1095 | CALLBACK_DATA(url); 1096 | break; 1097 | default: 1098 | parser->state = parse_url_char((enum state)parser->state, ch); 1099 | if (parser->state == s_dead) { 1100 | SET_ERRNO(HPE_INVALID_URL); 1101 | goto error; 1102 | } 1103 | } 1104 | break; 1105 | } 1106 | 1107 | case s_req_http_start: 1108 | switch (ch) { 1109 | case 'H': 1110 | parser->state = s_req_http_H; 1111 | break; 1112 | case ' ': 1113 | break; 1114 | default: 1115 | SET_ERRNO(HPE_INVALID_CONSTANT); 1116 | goto error; 1117 | } 1118 | break; 1119 | 1120 | case s_req_http_H: 1121 | STRICT_CHECK(ch != 'T'); 1122 | parser->state = s_req_http_HT; 1123 | break; 1124 | 1125 | case s_req_http_HT: 1126 | STRICT_CHECK(ch != 'T'); 1127 | parser->state = s_req_http_HTT; 1128 | break; 1129 | 1130 | case s_req_http_HTT: 1131 | STRICT_CHECK(ch != 'P'); 1132 | parser->state = s_req_http_HTTP; 1133 | break; 1134 | 1135 | case s_req_http_HTTP: 1136 | STRICT_CHECK(ch != '/'); 1137 | parser->state = s_req_first_http_major; 1138 | break; 1139 | 1140 | /* first digit of major HTTP version */ 1141 | case s_req_first_http_major: 1142 | if (ch < '1' || ch > '9') { 1143 | SET_ERRNO(HPE_INVALID_VERSION); 1144 | goto error; 1145 | } 1146 | 1147 | parser->http_major = ch - '0'; 1148 | parser->state = s_req_http_major; 1149 | break; 1150 | 1151 | /* major HTTP version or dot */ 1152 | case s_req_http_major: 1153 | { 1154 | if (ch == '.') { 1155 | parser->state = s_req_first_http_minor; 1156 | break; 1157 | } 1158 | 1159 | if (!IS_NUM(ch)) { 1160 | SET_ERRNO(HPE_INVALID_VERSION); 1161 | goto error; 1162 | } 1163 | 1164 | parser->http_major *= 10; 1165 | parser->http_major += ch - '0'; 1166 | 1167 | if (parser->http_major > 999) { 1168 | SET_ERRNO(HPE_INVALID_VERSION); 1169 | goto error; 1170 | } 1171 | 1172 | break; 1173 | } 1174 | 1175 | /* first digit of minor HTTP version */ 1176 | case s_req_first_http_minor: 1177 | if (!IS_NUM(ch)) { 1178 | SET_ERRNO(HPE_INVALID_VERSION); 1179 | goto error; 1180 | } 1181 | 1182 | parser->http_minor = ch - '0'; 1183 | parser->state = s_req_http_minor; 1184 | break; 1185 | 1186 | /* minor HTTP version or end of request line */ 1187 | case s_req_http_minor: 1188 | { 1189 | if (ch == CR) { 1190 | parser->state = s_req_line_almost_done; 1191 | break; 1192 | } 1193 | 1194 | if (ch == LF) { 1195 | parser->state = s_header_field_start; 1196 | break; 1197 | } 1198 | 1199 | /* XXX allow spaces after digit? */ 1200 | 1201 | if (!IS_NUM(ch)) { 1202 | SET_ERRNO(HPE_INVALID_VERSION); 1203 | goto error; 1204 | } 1205 | 1206 | parser->http_minor *= 10; 1207 | parser->http_minor += ch - '0'; 1208 | 1209 | if (parser->http_minor > 999) { 1210 | SET_ERRNO(HPE_INVALID_VERSION); 1211 | goto error; 1212 | } 1213 | 1214 | break; 1215 | } 1216 | 1217 | /* end of request line */ 1218 | case s_req_line_almost_done: 1219 | { 1220 | if (ch != LF) { 1221 | SET_ERRNO(HPE_LF_EXPECTED); 1222 | goto error; 1223 | } 1224 | 1225 | parser->state = s_header_field_start; 1226 | break; 1227 | } 1228 | 1229 | case s_header_field_start: 1230 | { 1231 | if (ch == CR) { 1232 | parser->state = s_headers_almost_done; 1233 | break; 1234 | } 1235 | 1236 | if (ch == LF) { 1237 | /* they might be just sending \n instead of \r\n so this would be 1238 | * the second \n to denote the end of headers*/ 1239 | parser->state = s_headers_almost_done; 1240 | goto reexecute_byte; 1241 | } 1242 | 1243 | c = TOKEN(ch); 1244 | 1245 | if (!c) { 1246 | SET_ERRNO(HPE_INVALID_HEADER_TOKEN); 1247 | goto error; 1248 | } 1249 | 1250 | MARK(header_field); 1251 | 1252 | parser->index = 0; 1253 | parser->state = s_header_field; 1254 | 1255 | switch (c) { 1256 | case 'c': 1257 | parser->header_state = h_C; 1258 | break; 1259 | 1260 | case 'p': 1261 | parser->header_state = h_matching_proxy_connection; 1262 | break; 1263 | 1264 | case 't': 1265 | parser->header_state = h_matching_transfer_encoding; 1266 | break; 1267 | 1268 | case 'u': 1269 | parser->header_state = h_matching_upgrade; 1270 | break; 1271 | 1272 | default: 1273 | parser->header_state = h_general; 1274 | break; 1275 | } 1276 | break; 1277 | } 1278 | 1279 | case s_header_field: 1280 | { 1281 | c = TOKEN(ch); 1282 | 1283 | if (c) { 1284 | switch (parser->header_state) { 1285 | case h_general: 1286 | break; 1287 | 1288 | case h_C: 1289 | parser->index++; 1290 | parser->header_state = (c == 'o' ? h_CO : h_general); 1291 | break; 1292 | 1293 | case h_CO: 1294 | parser->index++; 1295 | parser->header_state = (c == 'n' ? h_CON : h_general); 1296 | break; 1297 | 1298 | case h_CON: 1299 | parser->index++; 1300 | switch (c) { 1301 | case 'n': 1302 | parser->header_state = h_matching_connection; 1303 | break; 1304 | case 't': 1305 | parser->header_state = h_matching_content_length; 1306 | break; 1307 | default: 1308 | parser->header_state = h_general; 1309 | break; 1310 | } 1311 | break; 1312 | 1313 | /* connection */ 1314 | 1315 | case h_matching_connection: 1316 | parser->index++; 1317 | if (parser->index > sizeof(CONNECTION)-1 1318 | || c != CONNECTION[parser->index]) { 1319 | parser->header_state = h_general; 1320 | } else if (parser->index == sizeof(CONNECTION)-2) { 1321 | parser->header_state = h_connection; 1322 | } 1323 | break; 1324 | 1325 | /* proxy-connection */ 1326 | 1327 | case h_matching_proxy_connection: 1328 | parser->index++; 1329 | if (parser->index > sizeof(PROXY_CONNECTION)-1 1330 | || c != PROXY_CONNECTION[parser->index]) { 1331 | parser->header_state = h_general; 1332 | } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { 1333 | parser->header_state = h_connection; 1334 | } 1335 | break; 1336 | 1337 | /* content-length */ 1338 | 1339 | case h_matching_content_length: 1340 | parser->index++; 1341 | if (parser->index > sizeof(CONTENT_LENGTH)-1 1342 | || c != CONTENT_LENGTH[parser->index]) { 1343 | parser->header_state = h_general; 1344 | } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { 1345 | parser->header_state = h_content_length; 1346 | } 1347 | break; 1348 | 1349 | /* transfer-encoding */ 1350 | 1351 | case h_matching_transfer_encoding: 1352 | parser->index++; 1353 | if (parser->index > sizeof(TRANSFER_ENCODING)-1 1354 | || c != TRANSFER_ENCODING[parser->index]) { 1355 | parser->header_state = h_general; 1356 | } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { 1357 | parser->header_state = h_transfer_encoding; 1358 | } 1359 | break; 1360 | 1361 | /* upgrade */ 1362 | 1363 | case h_matching_upgrade: 1364 | parser->index++; 1365 | if (parser->index > sizeof(UPGRADE)-1 1366 | || c != UPGRADE[parser->index]) { 1367 | parser->header_state = h_general; 1368 | } else if (parser->index == sizeof(UPGRADE)-2) { 1369 | parser->header_state = h_upgrade; 1370 | } 1371 | break; 1372 | 1373 | case h_connection: 1374 | case h_content_length: 1375 | case h_transfer_encoding: 1376 | case h_upgrade: 1377 | if (ch != ' ') parser->header_state = h_general; 1378 | break; 1379 | 1380 | default: 1381 | assert(0 && "Unknown header_state"); 1382 | break; 1383 | } 1384 | break; 1385 | } 1386 | 1387 | if (ch == ':') { 1388 | parser->state = s_header_value_discard_ws; 1389 | CALLBACK_DATA(header_field); 1390 | break; 1391 | } 1392 | 1393 | if (ch == CR) { 1394 | parser->state = s_header_almost_done; 1395 | CALLBACK_DATA(header_field); 1396 | break; 1397 | } 1398 | 1399 | if (ch == LF) { 1400 | parser->state = s_header_field_start; 1401 | CALLBACK_DATA(header_field); 1402 | break; 1403 | } 1404 | 1405 | SET_ERRNO(HPE_INVALID_HEADER_TOKEN); 1406 | goto error; 1407 | } 1408 | 1409 | case s_header_value_discard_ws: 1410 | if (ch == ' ' || ch == '\t') break; 1411 | 1412 | if (ch == CR) { 1413 | parser->state = s_header_value_discard_ws_almost_done; 1414 | break; 1415 | } 1416 | 1417 | if (ch == LF) { 1418 | parser->state = s_header_value_discard_lws; 1419 | break; 1420 | } 1421 | 1422 | /* FALLTHROUGH */ 1423 | 1424 | case s_header_value_start: 1425 | { 1426 | MARK(header_value); 1427 | 1428 | parser->state = s_header_value; 1429 | parser->index = 0; 1430 | 1431 | c = LOWER(ch); 1432 | 1433 | switch (parser->header_state) { 1434 | case h_upgrade: 1435 | parser->flags |= F_UPGRADE; 1436 | parser->header_state = h_general; 1437 | break; 1438 | 1439 | case h_transfer_encoding: 1440 | /* looking for 'Transfer-Encoding: chunked' */ 1441 | if ('c' == c) { 1442 | parser->header_state = h_matching_transfer_encoding_chunked; 1443 | } else { 1444 | parser->header_state = h_general; 1445 | } 1446 | break; 1447 | 1448 | case h_content_length: 1449 | if (!IS_NUM(ch)) { 1450 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1451 | goto error; 1452 | } 1453 | 1454 | parser->content_length = ch - '0'; 1455 | break; 1456 | 1457 | case h_connection: 1458 | /* looking for 'Connection: keep-alive' */ 1459 | if (c == 'k') { 1460 | parser->header_state = h_matching_connection_keep_alive; 1461 | /* looking for 'Connection: close' */ 1462 | } else if (c == 'c') { 1463 | parser->header_state = h_matching_connection_close; 1464 | } else { 1465 | parser->header_state = h_general; 1466 | } 1467 | break; 1468 | 1469 | default: 1470 | parser->header_state = h_general; 1471 | break; 1472 | } 1473 | break; 1474 | } 1475 | 1476 | case s_header_value: 1477 | { 1478 | 1479 | if (ch == CR) { 1480 | parser->state = s_header_almost_done; 1481 | CALLBACK_DATA(header_value); 1482 | break; 1483 | } 1484 | 1485 | if (ch == LF) { 1486 | parser->state = s_header_almost_done; 1487 | CALLBACK_DATA_NOADVANCE(header_value); 1488 | goto reexecute_byte; 1489 | } 1490 | 1491 | c = LOWER(ch); 1492 | 1493 | switch (parser->header_state) { 1494 | case h_general: 1495 | break; 1496 | 1497 | case h_connection: 1498 | case h_transfer_encoding: 1499 | assert(0 && "Shouldn't get here."); 1500 | break; 1501 | 1502 | case h_content_length: 1503 | { 1504 | uint64_t t; 1505 | 1506 | if (ch == ' ') break; 1507 | 1508 | if (!IS_NUM(ch)) { 1509 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1510 | goto error; 1511 | } 1512 | 1513 | t = parser->content_length; 1514 | t *= 10; 1515 | t += ch - '0'; 1516 | 1517 | /* Overflow? Test against a conservative limit for simplicity. */ 1518 | if ((ULLONG_MAX - 10) / 10 < parser->content_length) { 1519 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1520 | goto error; 1521 | } 1522 | 1523 | parser->content_length = t; 1524 | break; 1525 | } 1526 | 1527 | /* Transfer-Encoding: chunked */ 1528 | case h_matching_transfer_encoding_chunked: 1529 | parser->index++; 1530 | if (parser->index > sizeof(CHUNKED)-1 1531 | || c != CHUNKED[parser->index]) { 1532 | parser->header_state = h_general; 1533 | } else if (parser->index == sizeof(CHUNKED)-2) { 1534 | parser->header_state = h_transfer_encoding_chunked; 1535 | } 1536 | break; 1537 | 1538 | /* looking for 'Connection: keep-alive' */ 1539 | case h_matching_connection_keep_alive: 1540 | parser->index++; 1541 | if (parser->index > sizeof(KEEP_ALIVE)-1 1542 | || c != KEEP_ALIVE[parser->index]) { 1543 | parser->header_state = h_general; 1544 | } else if (parser->index == sizeof(KEEP_ALIVE)-2) { 1545 | parser->header_state = h_connection_keep_alive; 1546 | } 1547 | break; 1548 | 1549 | /* looking for 'Connection: close' */ 1550 | case h_matching_connection_close: 1551 | parser->index++; 1552 | if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { 1553 | parser->header_state = h_general; 1554 | } else if (parser->index == sizeof(CLOSE)-2) { 1555 | parser->header_state = h_connection_close; 1556 | } 1557 | break; 1558 | 1559 | case h_transfer_encoding_chunked: 1560 | case h_connection_keep_alive: 1561 | case h_connection_close: 1562 | if (ch != ' ') parser->header_state = h_general; 1563 | break; 1564 | 1565 | default: 1566 | parser->state = s_header_value; 1567 | parser->header_state = h_general; 1568 | break; 1569 | } 1570 | break; 1571 | } 1572 | 1573 | case s_header_almost_done: 1574 | { 1575 | STRICT_CHECK(ch != LF); 1576 | 1577 | parser->state = s_header_value_lws; 1578 | break; 1579 | } 1580 | 1581 | case s_header_value_lws: 1582 | { 1583 | if (ch == ' ' || ch == '\t') { 1584 | parser->state = s_header_value_start; 1585 | goto reexecute_byte; 1586 | } 1587 | 1588 | /* finished the header */ 1589 | switch (parser->header_state) { 1590 | case h_connection_keep_alive: 1591 | parser->flags |= F_CONNECTION_KEEP_ALIVE; 1592 | break; 1593 | case h_connection_close: 1594 | parser->flags |= F_CONNECTION_CLOSE; 1595 | break; 1596 | case h_transfer_encoding_chunked: 1597 | parser->flags |= F_CHUNKED; 1598 | break; 1599 | default: 1600 | break; 1601 | } 1602 | 1603 | parser->state = s_header_field_start; 1604 | goto reexecute_byte; 1605 | } 1606 | 1607 | case s_header_value_discard_ws_almost_done: 1608 | { 1609 | STRICT_CHECK(ch != LF); 1610 | parser->state = s_header_value_discard_lws; 1611 | break; 1612 | } 1613 | 1614 | case s_header_value_discard_lws: 1615 | { 1616 | if (ch == ' ' || ch == '\t') { 1617 | parser->state = s_header_value_discard_ws; 1618 | break; 1619 | } else { 1620 | /* header value was empty */ 1621 | MARK(header_value); 1622 | parser->state = s_header_field_start; 1623 | CALLBACK_DATA_NOADVANCE(header_value); 1624 | goto reexecute_byte; 1625 | } 1626 | } 1627 | 1628 | case s_headers_almost_done: 1629 | { 1630 | STRICT_CHECK(ch != LF); 1631 | 1632 | if (parser->flags & F_TRAILING) { 1633 | /* End of a chunked request */ 1634 | parser->state = NEW_MESSAGE(); 1635 | CALLBACK_NOTIFY(message_complete); 1636 | break; 1637 | } 1638 | 1639 | parser->state = s_headers_done; 1640 | 1641 | /* Set this here so that on_headers_complete() callbacks can see it */ 1642 | parser->upgrade = 1643 | (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); 1644 | 1645 | /* Here we call the headers_complete callback. This is somewhat 1646 | * different than other callbacks because if the user returns 1, we 1647 | * will interpret that as saying that this message has no body. This 1648 | * is needed for the annoying case of recieving a response to a HEAD 1649 | * request. 1650 | * 1651 | * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so 1652 | * we have to simulate it by handling a change in errno below. 1653 | */ 1654 | if (settings->on_headers_complete) { 1655 | switch (settings->on_headers_complete(parser)) { 1656 | case 0: 1657 | break; 1658 | 1659 | case 1: 1660 | parser->flags |= F_SKIPBODY; 1661 | break; 1662 | 1663 | default: 1664 | SET_ERRNO(HPE_CB_headers_complete); 1665 | return p - data; /* Error */ 1666 | } 1667 | } 1668 | 1669 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { 1670 | return p - data; 1671 | } 1672 | 1673 | goto reexecute_byte; 1674 | } 1675 | 1676 | case s_headers_done: 1677 | { 1678 | STRICT_CHECK(ch != LF); 1679 | 1680 | parser->nread = 0; 1681 | 1682 | /* Exit, the rest of the connect is in a different protocol. */ 1683 | if (parser->upgrade) { 1684 | parser->state = NEW_MESSAGE(); 1685 | CALLBACK_NOTIFY(message_complete); 1686 | return (p - data) + 1; 1687 | } 1688 | 1689 | if (parser->flags & F_SKIPBODY) { 1690 | parser->state = NEW_MESSAGE(); 1691 | CALLBACK_NOTIFY(message_complete); 1692 | } else if (parser->flags & F_CHUNKED) { 1693 | /* chunked encoding - ignore Content-Length header */ 1694 | parser->state = s_chunk_size_start; 1695 | } else { 1696 | if (parser->content_length == 0) { 1697 | /* Content-Length header given but zero: Content-Length: 0\r\n */ 1698 | parser->state = NEW_MESSAGE(); 1699 | CALLBACK_NOTIFY(message_complete); 1700 | } else if (parser->content_length != ULLONG_MAX) { 1701 | /* Content-Length header given and non-zero */ 1702 | parser->state = s_body_identity; 1703 | } else { 1704 | if (parser->type == HTTP_REQUEST || 1705 | !http_message_needs_eof(parser)) { 1706 | /* Assume content-length 0 - read the next */ 1707 | parser->state = NEW_MESSAGE(); 1708 | CALLBACK_NOTIFY(message_complete); 1709 | } else { 1710 | /* Read body until EOF */ 1711 | parser->state = s_body_identity_eof; 1712 | } 1713 | } 1714 | } 1715 | 1716 | break; 1717 | } 1718 | 1719 | case s_body_identity: 1720 | { 1721 | uint64_t to_read = MIN(parser->content_length, 1722 | (uint64_t) ((data + len) - p)); 1723 | 1724 | assert(parser->content_length != 0 1725 | && parser->content_length != ULLONG_MAX); 1726 | 1727 | /* The difference between advancing content_length and p is because 1728 | * the latter will automaticaly advance on the next loop iteration. 1729 | * Further, if content_length ends up at 0, we want to see the last 1730 | * byte again for our message complete callback. 1731 | */ 1732 | MARK(body); 1733 | parser->content_length -= to_read; 1734 | p += to_read - 1; 1735 | 1736 | if (parser->content_length == 0) { 1737 | parser->state = s_message_done; 1738 | 1739 | /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. 1740 | * 1741 | * The alternative to doing this is to wait for the next byte to 1742 | * trigger the data callback, just as in every other case. The 1743 | * problem with this is that this makes it difficult for the test 1744 | * harness to distinguish between complete-on-EOF and 1745 | * complete-on-length. It's not clear that this distinction is 1746 | * important for applications, but let's keep it for now. 1747 | */ 1748 | CALLBACK_DATA_(body, p - body_mark + 1, p - data); 1749 | goto reexecute_byte; 1750 | } 1751 | 1752 | break; 1753 | } 1754 | 1755 | /* read until EOF */ 1756 | case s_body_identity_eof: 1757 | MARK(body); 1758 | p = data + len - 1; 1759 | 1760 | break; 1761 | 1762 | case s_message_done: 1763 | parser->state = NEW_MESSAGE(); 1764 | CALLBACK_NOTIFY(message_complete); 1765 | break; 1766 | 1767 | case s_chunk_size_start: 1768 | { 1769 | assert(parser->nread == 1); 1770 | assert(parser->flags & F_CHUNKED); 1771 | 1772 | unhex_val = unhex[(unsigned char)ch]; 1773 | if (unhex_val == -1) { 1774 | SET_ERRNO(HPE_INVALID_CHUNK_SIZE); 1775 | goto error; 1776 | } 1777 | 1778 | parser->content_length = unhex_val; 1779 | parser->state = s_chunk_size; 1780 | break; 1781 | } 1782 | 1783 | case s_chunk_size: 1784 | { 1785 | uint64_t t; 1786 | 1787 | assert(parser->flags & F_CHUNKED); 1788 | 1789 | if (ch == CR) { 1790 | parser->state = s_chunk_size_almost_done; 1791 | break; 1792 | } 1793 | 1794 | unhex_val = unhex[(unsigned char)ch]; 1795 | 1796 | if (unhex_val == -1) { 1797 | if (ch == ';' || ch == ' ') { 1798 | parser->state = s_chunk_parameters; 1799 | break; 1800 | } 1801 | 1802 | SET_ERRNO(HPE_INVALID_CHUNK_SIZE); 1803 | goto error; 1804 | } 1805 | 1806 | t = parser->content_length; 1807 | t *= 16; 1808 | t += unhex_val; 1809 | 1810 | /* Overflow? Test against a conservative limit for simplicity. */ 1811 | if ((ULLONG_MAX - 16) / 16 < parser->content_length) { 1812 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1813 | goto error; 1814 | } 1815 | 1816 | parser->content_length = t; 1817 | break; 1818 | } 1819 | 1820 | case s_chunk_parameters: 1821 | { 1822 | assert(parser->flags & F_CHUNKED); 1823 | /* just ignore this shit. TODO check for overflow */ 1824 | if (ch == CR) { 1825 | parser->state = s_chunk_size_almost_done; 1826 | break; 1827 | } 1828 | break; 1829 | } 1830 | 1831 | case s_chunk_size_almost_done: 1832 | { 1833 | assert(parser->flags & F_CHUNKED); 1834 | STRICT_CHECK(ch != LF); 1835 | 1836 | parser->nread = 0; 1837 | 1838 | if (parser->content_length == 0) { 1839 | parser->flags |= F_TRAILING; 1840 | parser->state = s_header_field_start; 1841 | } else { 1842 | parser->state = s_chunk_data; 1843 | } 1844 | break; 1845 | } 1846 | 1847 | case s_chunk_data: 1848 | { 1849 | uint64_t to_read = MIN(parser->content_length, 1850 | (uint64_t) ((data + len) - p)); 1851 | 1852 | assert(parser->flags & F_CHUNKED); 1853 | assert(parser->content_length != 0 1854 | && parser->content_length != ULLONG_MAX); 1855 | 1856 | /* See the explanation in s_body_identity for why the content 1857 | * length and data pointers are managed this way. 1858 | */ 1859 | MARK(body); 1860 | parser->content_length -= to_read; 1861 | p += to_read - 1; 1862 | 1863 | if (parser->content_length == 0) { 1864 | parser->state = s_chunk_data_almost_done; 1865 | } 1866 | 1867 | break; 1868 | } 1869 | 1870 | case s_chunk_data_almost_done: 1871 | assert(parser->flags & F_CHUNKED); 1872 | assert(parser->content_length == 0); 1873 | STRICT_CHECK(ch != CR); 1874 | parser->state = s_chunk_data_done; 1875 | CALLBACK_DATA(body); 1876 | break; 1877 | 1878 | case s_chunk_data_done: 1879 | assert(parser->flags & F_CHUNKED); 1880 | STRICT_CHECK(ch != LF); 1881 | parser->nread = 0; 1882 | parser->state = s_chunk_size_start; 1883 | break; 1884 | 1885 | default: 1886 | assert(0 && "unhandled state"); 1887 | SET_ERRNO(HPE_INVALID_INTERNAL_STATE); 1888 | goto error; 1889 | } 1890 | } 1891 | 1892 | /* Run callbacks for any marks that we have leftover after we ran our of 1893 | * bytes. There should be at most one of these set, so it's OK to invoke 1894 | * them in series (unset marks will not result in callbacks). 1895 | * 1896 | * We use the NOADVANCE() variety of callbacks here because 'p' has already 1897 | * overflowed 'data' and this allows us to correct for the off-by-one that 1898 | * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' 1899 | * value that's in-bounds). 1900 | */ 1901 | 1902 | assert(((header_field_mark ? 1 : 0) + 1903 | (header_value_mark ? 1 : 0) + 1904 | (url_mark ? 1 : 0) + 1905 | (body_mark ? 1 : 0) + 1906 | (status_mark ? 1 : 0)) <= 1); 1907 | 1908 | CALLBACK_DATA_NOADVANCE(header_field); 1909 | CALLBACK_DATA_NOADVANCE(header_value); 1910 | CALLBACK_DATA_NOADVANCE(url); 1911 | CALLBACK_DATA_NOADVANCE(body); 1912 | CALLBACK_DATA_NOADVANCE(status); 1913 | 1914 | return len; 1915 | 1916 | error: 1917 | if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { 1918 | SET_ERRNO(HPE_UNKNOWN); 1919 | } 1920 | 1921 | return (p - data); 1922 | } 1923 | 1924 | 1925 | /* Does the parser need to see an EOF to find the end of the message? */ 1926 | int 1927 | http_message_needs_eof (const http_parser *parser) 1928 | { 1929 | if (parser->type == HTTP_REQUEST) { 1930 | return 0; 1931 | } 1932 | 1933 | /* See RFC 2616 section 4.4 */ 1934 | if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ 1935 | parser->status_code == 204 || /* No Content */ 1936 | parser->status_code == 304 || /* Not Modified */ 1937 | parser->flags & F_SKIPBODY) { /* response to a HEAD request */ 1938 | return 0; 1939 | } 1940 | 1941 | if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { 1942 | return 0; 1943 | } 1944 | 1945 | return 1; 1946 | } 1947 | 1948 | 1949 | int 1950 | http_should_keep_alive (const http_parser *parser) 1951 | { 1952 | if (parser->http_major > 0 && parser->http_minor > 0) { 1953 | /* HTTP/1.1 */ 1954 | if (parser->flags & F_CONNECTION_CLOSE) { 1955 | return 0; 1956 | } 1957 | } else { 1958 | /* HTTP/1.0 or earlier */ 1959 | if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { 1960 | return 0; 1961 | } 1962 | } 1963 | 1964 | return !http_message_needs_eof(parser); 1965 | } 1966 | 1967 | 1968 | const char * 1969 | http_method_str (enum http_method m) 1970 | { 1971 | return ELEM_AT(method_strings, m, ""); 1972 | } 1973 | 1974 | 1975 | void 1976 | http_parser_init (http_parser *parser, enum http_parser_type t) 1977 | { 1978 | void *data = parser->data; /* preserve application data */ 1979 | memset(parser, 0, sizeof(*parser)); 1980 | parser->data = data; 1981 | parser->type = t; 1982 | parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); 1983 | parser->http_errno = HPE_OK; 1984 | } 1985 | 1986 | const char * 1987 | http_errno_name(enum http_errno err) { 1988 | assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); 1989 | return http_strerror_tab[err].name; 1990 | } 1991 | 1992 | const char * 1993 | http_errno_description(enum http_errno err) { 1994 | assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); 1995 | return http_strerror_tab[err].description; 1996 | } 1997 | 1998 | static enum http_host_state 1999 | http_parse_host_char(enum http_host_state s, const char ch) { 2000 | switch(s) { 2001 | case s_http_userinfo: 2002 | case s_http_userinfo_start: 2003 | if (ch == '@') { 2004 | return s_http_host_start; 2005 | } 2006 | 2007 | if (IS_USERINFO_CHAR(ch)) { 2008 | return s_http_userinfo; 2009 | } 2010 | break; 2011 | 2012 | case s_http_host_start: 2013 | if (ch == '[') { 2014 | return s_http_host_v6_start; 2015 | } 2016 | 2017 | if (IS_HOST_CHAR(ch)) { 2018 | return s_http_host; 2019 | } 2020 | 2021 | break; 2022 | 2023 | case s_http_host: 2024 | if (IS_HOST_CHAR(ch)) { 2025 | return s_http_host; 2026 | } 2027 | 2028 | /* FALLTHROUGH */ 2029 | case s_http_host_v6_end: 2030 | if (ch == ':') { 2031 | return s_http_host_port_start; 2032 | } 2033 | 2034 | break; 2035 | 2036 | case s_http_host_v6: 2037 | if (ch == ']') { 2038 | return s_http_host_v6_end; 2039 | } 2040 | 2041 | /* FALLTHROUGH */ 2042 | case s_http_host_v6_start: 2043 | if (IS_HEX(ch) || ch == ':' || ch == '.') { 2044 | return s_http_host_v6; 2045 | } 2046 | 2047 | break; 2048 | 2049 | case s_http_host_port: 2050 | case s_http_host_port_start: 2051 | if (IS_NUM(ch)) { 2052 | return s_http_host_port; 2053 | } 2054 | 2055 | break; 2056 | 2057 | default: 2058 | break; 2059 | } 2060 | return s_http_host_dead; 2061 | } 2062 | 2063 | static int 2064 | http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { 2065 | enum http_host_state s; 2066 | 2067 | const char *p; 2068 | size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; 2069 | 2070 | u->field_data[UF_HOST].len = 0; 2071 | 2072 | s = found_at ? s_http_userinfo_start : s_http_host_start; 2073 | 2074 | for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { 2075 | enum http_host_state new_s = http_parse_host_char(s, *p); 2076 | 2077 | if (new_s == s_http_host_dead) { 2078 | return 1; 2079 | } 2080 | 2081 | switch(new_s) { 2082 | case s_http_host: 2083 | if (s != s_http_host) { 2084 | u->field_data[UF_HOST].off = p - buf; 2085 | } 2086 | u->field_data[UF_HOST].len++; 2087 | break; 2088 | 2089 | case s_http_host_v6: 2090 | if (s != s_http_host_v6) { 2091 | u->field_data[UF_HOST].off = p - buf; 2092 | } 2093 | u->field_data[UF_HOST].len++; 2094 | break; 2095 | 2096 | case s_http_host_port: 2097 | if (s != s_http_host_port) { 2098 | u->field_data[UF_PORT].off = p - buf; 2099 | u->field_data[UF_PORT].len = 0; 2100 | u->field_set |= (1 << UF_PORT); 2101 | } 2102 | u->field_data[UF_PORT].len++; 2103 | break; 2104 | 2105 | case s_http_userinfo: 2106 | if (s != s_http_userinfo) { 2107 | u->field_data[UF_USERINFO].off = p - buf ; 2108 | u->field_data[UF_USERINFO].len = 0; 2109 | u->field_set |= (1 << UF_USERINFO); 2110 | } 2111 | u->field_data[UF_USERINFO].len++; 2112 | break; 2113 | 2114 | default: 2115 | break; 2116 | } 2117 | s = new_s; 2118 | } 2119 | 2120 | /* Make sure we don't end somewhere unexpected */ 2121 | switch (s) { 2122 | case s_http_host_start: 2123 | case s_http_host_v6_start: 2124 | case s_http_host_v6: 2125 | case s_http_host_port_start: 2126 | case s_http_userinfo: 2127 | case s_http_userinfo_start: 2128 | return 1; 2129 | default: 2130 | break; 2131 | } 2132 | 2133 | return 0; 2134 | } 2135 | 2136 | int 2137 | http_parser_parse_url(const char *buf, size_t buflen, int is_connect, 2138 | struct http_parser_url *u) 2139 | { 2140 | enum state s; 2141 | const char *p; 2142 | enum http_parser_url_fields uf, old_uf; 2143 | int found_at = 0; 2144 | 2145 | u->port = u->field_set = 0; 2146 | s = is_connect ? s_req_server_start : s_req_spaces_before_url; 2147 | uf = old_uf = UF_MAX; 2148 | 2149 | for (p = buf; p < buf + buflen; p++) { 2150 | s = parse_url_char(s, *p); 2151 | 2152 | /* Figure out the next field that we're operating on */ 2153 | switch (s) { 2154 | case s_dead: 2155 | return 1; 2156 | 2157 | /* Skip delimeters */ 2158 | case s_req_schema_slash: 2159 | case s_req_schema_slash_slash: 2160 | case s_req_server_start: 2161 | case s_req_query_string_start: 2162 | case s_req_fragment_start: 2163 | continue; 2164 | 2165 | case s_req_schema: 2166 | uf = UF_SCHEMA; 2167 | break; 2168 | 2169 | case s_req_server_with_at: 2170 | found_at = 1; 2171 | 2172 | /* FALLTROUGH */ 2173 | case s_req_server: 2174 | uf = UF_HOST; 2175 | break; 2176 | 2177 | case s_req_path: 2178 | uf = UF_PATH; 2179 | break; 2180 | 2181 | case s_req_query_string: 2182 | uf = UF_QUERY; 2183 | break; 2184 | 2185 | case s_req_fragment: 2186 | uf = UF_FRAGMENT; 2187 | break; 2188 | 2189 | default: 2190 | assert(!"Unexpected state"); 2191 | return 1; 2192 | } 2193 | 2194 | /* Nothing's changed; soldier on */ 2195 | if (uf == old_uf) { 2196 | u->field_data[uf].len++; 2197 | continue; 2198 | } 2199 | 2200 | u->field_data[uf].off = p - buf; 2201 | u->field_data[uf].len = 1; 2202 | 2203 | u->field_set |= (1 << uf); 2204 | old_uf = uf; 2205 | } 2206 | 2207 | /* host must be present if there is a schema */ 2208 | /* parsing http:///toto will fail */ 2209 | if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { 2210 | if (http_parse_host(buf, u, found_at) != 0) { 2211 | return 1; 2212 | } 2213 | } 2214 | 2215 | /* CONNECT requests can only contain "hostname:port" */ 2216 | if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { 2217 | return 1; 2218 | } 2219 | 2220 | if (u->field_set & (1 << UF_PORT)) { 2221 | /* Don't bother with endp; we've already validated the string */ 2222 | unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); 2223 | 2224 | /* Ports have a max value of 2^16 */ 2225 | if (v > 0xffff) { 2226 | return 1; 2227 | } 2228 | 2229 | u->port = (uint16_t) v; 2230 | } 2231 | 2232 | return 0; 2233 | } 2234 | 2235 | void 2236 | http_parser_pause(http_parser *parser, int paused) { 2237 | /* Users should only be pausing/unpausing a parser that is not in an error 2238 | * state. In non-debug builds, there's not much that we can do about this 2239 | * other than ignore it. 2240 | */ 2241 | if (HTTP_PARSER_ERRNO(parser) == HPE_OK || 2242 | HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { 2243 | SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); 2244 | } else { 2245 | assert(0 && "Attempting to pause parser in error state"); 2246 | } 2247 | } 2248 | 2249 | int 2250 | http_body_is_final(const struct http_parser *parser) { 2251 | return parser->state == s_message_done; 2252 | } 2253 | 2254 | unsigned long 2255 | http_parser_version(void) { 2256 | return HTTP_PARSER_VERSION_MAJOR * 0x10000 | 2257 | HTTP_PARSER_VERSION_MINOR * 0x00100 | 2258 | HTTP_PARSER_VERSION_PATCH * 0x00001; 2259 | } 2260 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED (VERSION 2.8) 2 | PROJECT (tests) 3 | 4 | FIND_PACKAGE (Threads) 5 | FIND_PACKAGE (Boost REQUIRED COMPONENTS 6 | system 7 | thread) 8 | FIND_PACKAGE (PythonInterp REQUIRED) 9 | 10 | INCLUDE_DIRECTORIES ( 11 | ${CMAKE_CURRENT_SOURCE_DIR} 12 | ${Boost_INCLUDE_DIRS}) 13 | ADD_EXECUTABLE (test_server 14 | test_server.cpp 15 | jsoncpp.cpp) 16 | TARGET_LINK_LIBRARIES (test_server 17 | asio_http 18 | ${Boost_LIBRARIES} 19 | ${CMAKE_THREAD_LIBS_INIT}) 20 | ADD_TEST (NAME test_server 21 | COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_server.py $) 22 | 23 | ADD_EXECUTABLE (test_url_parser 24 | test_url_parser.cpp) 25 | TARGET_LINK_LIBRARIES (test_url_parser 26 | asio_http 27 | ${Boost_LIBRARIES} 28 | ${CMAKE_THREAD_LIBS_INIT}) 29 | ADD_TEST (NAME test_url_parser 30 | COMMAND test_url_parser) 31 | #ADD_EXECUTABLE (http_parser_test 32 | # test.c) 33 | #TARGET_LINK_LIBRARIES (http_parser_test 34 | # asio_http) 35 | #ADD_TEST (http_parser 36 | # http_parser_test) 37 | -------------------------------------------------------------------------------- /tests/json/json-forwards.h: -------------------------------------------------------------------------------- 1 | /// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/). 2 | /// It is intented to be used with #include 3 | /// This header provides forward declaration for all JsonCpp types. 4 | 5 | // ////////////////////////////////////////////////////////////////////// 6 | // Beginning of content of file: LICENSE 7 | // ////////////////////////////////////////////////////////////////////// 8 | 9 | /* 10 | The JsonCpp library's source code, including accompanying documentation, 11 | tests and demonstration applications, are licensed under the following 12 | conditions... 13 | 14 | The author (Baptiste Lepilleur) explicitly disclaims copyright in all 15 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 16 | this software is released into the Public Domain. 17 | 18 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 19 | 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is 20 | released under the terms of the MIT License (see below). 21 | 22 | In jurisdictions which recognize Public Domain property, the user of this 23 | software may choose to accept it either as 1) Public Domain, 2) under the 24 | conditions of the MIT License (see below), or 3) under the terms of dual 25 | Public Domain/MIT License conditions described here, as they choose. 26 | 27 | The MIT License is about as close to Public Domain as a license can get, and is 28 | described in clear, concise terms at: 29 | 30 | http://en.wikipedia.org/wiki/MIT_License 31 | 32 | The full text of the MIT License follows: 33 | 34 | ======================================================================== 35 | Copyright (c) 2007-2010 Baptiste Lepilleur 36 | 37 | Permission is hereby granted, free of charge, to any person 38 | obtaining a copy of this software and associated documentation 39 | files (the "Software"), to deal in the Software without 40 | restriction, including without limitation the rights to use, copy, 41 | modify, merge, publish, distribute, sublicense, and/or sell copies 42 | of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be 46 | included in all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 49 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 50 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 51 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 52 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 53 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 54 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | ======================================================================== 57 | (END LICENSE TEXT) 58 | 59 | The MIT license is compatible with both the GPL and commercial 60 | software, affording one all of the rights of Public Domain with the 61 | minor nuisance of being required to keep the above copyright notice 62 | and license text in the source code. Note also that by accepting the 63 | Public Domain "license" you can re-license your copy using whatever 64 | license you like. 65 | 66 | */ 67 | 68 | // ////////////////////////////////////////////////////////////////////// 69 | // End of content of file: LICENSE 70 | // ////////////////////////////////////////////////////////////////////// 71 | 72 | 73 | 74 | 75 | 76 | #ifndef JSON_FORWARD_AMALGATED_H_INCLUDED 77 | # define JSON_FORWARD_AMALGATED_H_INCLUDED 78 | /// If defined, indicates that the source file is amalgated 79 | /// to prevent private header inclusion. 80 | #define JSON_IS_AMALGAMATION 81 | 82 | // ////////////////////////////////////////////////////////////////////// 83 | // Beginning of content of file: include/json/config.h 84 | // ////////////////////////////////////////////////////////////////////// 85 | 86 | // Copyright 2007-2010 Baptiste Lepilleur 87 | // Distributed under MIT license, or public domain if desired and 88 | // recognized in your jurisdiction. 89 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 90 | 91 | #ifndef JSON_CONFIG_H_INCLUDED 92 | #define JSON_CONFIG_H_INCLUDED 93 | 94 | /// If defined, indicates that json library is embedded in CppTL library. 95 | //# define JSON_IN_CPPTL 1 96 | 97 | /// If defined, indicates that json may leverage CppTL library 98 | //# define JSON_USE_CPPTL 1 99 | /// If defined, indicates that cpptl vector based map should be used instead of 100 | /// std::map 101 | /// as Value container. 102 | //# define JSON_USE_CPPTL_SMALLMAP 1 103 | /// If defined, indicates that Json specific container should be used 104 | /// (hash table & simple deque container with customizable allocator). 105 | /// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 106 | //# define JSON_VALUE_USE_INTERNAL_MAP 1 107 | /// Force usage of standard new/malloc based allocator instead of memory pool 108 | /// based allocator. 109 | /// The memory pools allocator used optimization (initializing Value and 110 | /// ValueInternalLink 111 | /// as if it was a POD) that may cause some validation tool to report errors. 112 | /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. 113 | //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 114 | 115 | // If non-zero, the library uses exceptions to report bad input instead of C 116 | // assertion macros. The default is to use exceptions. 117 | #ifndef JSON_USE_EXCEPTION 118 | #define JSON_USE_EXCEPTION 1 119 | #endif 120 | 121 | /// If defined, indicates that the source file is amalgated 122 | /// to prevent private header inclusion. 123 | /// Remarks: it is automatically defined in the generated amalgated header. 124 | // #define JSON_IS_AMALGAMATION 125 | 126 | #ifdef JSON_IN_CPPTL 127 | #include 128 | #ifndef JSON_USE_CPPTL 129 | #define JSON_USE_CPPTL 1 130 | #endif 131 | #endif 132 | 133 | #ifdef JSON_IN_CPPTL 134 | #define JSON_API CPPTL_API 135 | #elif defined(JSON_DLL_BUILD) 136 | #if defined(_MSC_VER) 137 | #define JSON_API __declspec(dllexport) 138 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 139 | #endif // if defined(_MSC_VER) 140 | #elif defined(JSON_DLL) 141 | #if defined(_MSC_VER) 142 | #define JSON_API __declspec(dllimport) 143 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 144 | #endif // if defined(_MSC_VER) 145 | #endif // ifdef JSON_IN_CPPTL 146 | #if !defined(JSON_API) 147 | #define JSON_API 148 | #endif 149 | 150 | // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for 151 | // integer 152 | // Storages, and 64 bits integer support is disabled. 153 | // #define JSON_NO_INT64 1 154 | 155 | #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 156 | // Microsoft Visual Studio 6 only support conversion from __int64 to double 157 | // (no conversion from unsigned __int64). 158 | #define JSON_USE_INT64_DOUBLE_CONVERSION 1 159 | // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' 160 | // characters in the debug information) 161 | // All projects I've ever seen with VS6 were using this globally (not bothering 162 | // with pragma push/pop). 163 | #pragma warning(disable : 4786) 164 | #endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 165 | 166 | #if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 167 | /// Indicates that the following function is deprecated. 168 | #define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) 169 | #endif 170 | 171 | #if !defined(JSONCPP_DEPRECATED) 172 | #define JSONCPP_DEPRECATED(message) 173 | #endif // if !defined(JSONCPP_DEPRECATED) 174 | 175 | namespace Json { 176 | typedef int Int; 177 | typedef unsigned int UInt; 178 | #if defined(JSON_NO_INT64) 179 | typedef int LargestInt; 180 | typedef unsigned int LargestUInt; 181 | #undef JSON_HAS_INT64 182 | #else // if defined(JSON_NO_INT64) 183 | // For Microsoft Visual use specific types as long long is not supported 184 | #if defined(_MSC_VER) // Microsoft Visual Studio 185 | typedef __int64 Int64; 186 | typedef unsigned __int64 UInt64; 187 | #else // if defined(_MSC_VER) // Other platforms, use long long 188 | typedef long long int Int64; 189 | typedef unsigned long long int UInt64; 190 | #endif // if defined(_MSC_VER) 191 | typedef Int64 LargestInt; 192 | typedef UInt64 LargestUInt; 193 | #define JSON_HAS_INT64 194 | #endif // if defined(JSON_NO_INT64) 195 | } // end namespace Json 196 | 197 | #endif // JSON_CONFIG_H_INCLUDED 198 | 199 | // ////////////////////////////////////////////////////////////////////// 200 | // End of content of file: include/json/config.h 201 | // ////////////////////////////////////////////////////////////////////// 202 | 203 | 204 | 205 | 206 | 207 | 208 | // ////////////////////////////////////////////////////////////////////// 209 | // Beginning of content of file: include/json/forwards.h 210 | // ////////////////////////////////////////////////////////////////////// 211 | 212 | // Copyright 2007-2010 Baptiste Lepilleur 213 | // Distributed under MIT license, or public domain if desired and 214 | // recognized in your jurisdiction. 215 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 216 | 217 | #ifndef JSON_FORWARDS_H_INCLUDED 218 | #define JSON_FORWARDS_H_INCLUDED 219 | 220 | #if !defined(JSON_IS_AMALGAMATION) 221 | #include "config.h" 222 | #endif // if !defined(JSON_IS_AMALGAMATION) 223 | 224 | namespace Json { 225 | 226 | // writer.h 227 | class FastWriter; 228 | class StyledWriter; 229 | 230 | // reader.h 231 | class Reader; 232 | 233 | // features.h 234 | class Features; 235 | 236 | // value.h 237 | typedef unsigned int ArrayIndex; 238 | class StaticString; 239 | class Path; 240 | class PathArgument; 241 | class Value; 242 | class ValueIteratorBase; 243 | class ValueIterator; 244 | class ValueConstIterator; 245 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 246 | class ValueMapAllocator; 247 | class ValueInternalLink; 248 | class ValueInternalArray; 249 | class ValueInternalMap; 250 | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP 251 | 252 | } // namespace Json 253 | 254 | #endif // JSON_FORWARDS_H_INCLUDED 255 | 256 | // ////////////////////////////////////////////////////////////////////// 257 | // End of content of file: include/json/forwards.h 258 | // ////////////////////////////////////////////////////////////////////// 259 | 260 | 261 | 262 | 263 | 264 | #endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED 265 | -------------------------------------------------------------------------------- /tests/json/json.h: -------------------------------------------------------------------------------- 1 | /// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). 2 | /// It is intented to be used with #include 3 | 4 | // ////////////////////////////////////////////////////////////////////// 5 | // Beginning of content of file: LICENSE 6 | // ////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | The JsonCpp library's source code, including accompanying documentation, 10 | tests and demonstration applications, are licensed under the following 11 | conditions... 12 | 13 | The author (Baptiste Lepilleur) explicitly disclaims copyright in all 14 | jurisdictions which recognize such a disclaimer. In such jurisdictions, 15 | this software is released into the Public Domain. 16 | 17 | In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 18 | 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is 19 | released under the terms of the MIT License (see below). 20 | 21 | In jurisdictions which recognize Public Domain property, the user of this 22 | software may choose to accept it either as 1) Public Domain, 2) under the 23 | conditions of the MIT License (see below), or 3) under the terms of dual 24 | Public Domain/MIT License conditions described here, as they choose. 25 | 26 | The MIT License is about as close to Public Domain as a license can get, and is 27 | described in clear, concise terms at: 28 | 29 | http://en.wikipedia.org/wiki/MIT_License 30 | 31 | The full text of the MIT License follows: 32 | 33 | ======================================================================== 34 | Copyright (c) 2007-2010 Baptiste Lepilleur 35 | 36 | Permission is hereby granted, free of charge, to any person 37 | obtaining a copy of this software and associated documentation 38 | files (the "Software"), to deal in the Software without 39 | restriction, including without limitation the rights to use, copy, 40 | modify, merge, publish, distribute, sublicense, and/or sell copies 41 | of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be 45 | included in all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 48 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 50 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 51 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 52 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 53 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | ======================================================================== 56 | (END LICENSE TEXT) 57 | 58 | The MIT license is compatible with both the GPL and commercial 59 | software, affording one all of the rights of Public Domain with the 60 | minor nuisance of being required to keep the above copyright notice 61 | and license text in the source code. Note also that by accepting the 62 | Public Domain "license" you can re-license your copy using whatever 63 | license you like. 64 | 65 | */ 66 | 67 | // ////////////////////////////////////////////////////////////////////// 68 | // End of content of file: LICENSE 69 | // ////////////////////////////////////////////////////////////////////// 70 | 71 | 72 | 73 | 74 | 75 | #ifndef JSON_AMALGATED_H_INCLUDED 76 | # define JSON_AMALGATED_H_INCLUDED 77 | /// If defined, indicates that the source file is amalgated 78 | /// to prevent private header inclusion. 79 | #define JSON_IS_AMALGAMATION 80 | 81 | // ////////////////////////////////////////////////////////////////////// 82 | // Beginning of content of file: include/json/version.h 83 | // ////////////////////////////////////////////////////////////////////// 84 | 85 | // DO NOT EDIT. This file is generated by CMake from "version" 86 | // and "version.h.in" files. 87 | // Run CMake configure step to update it. 88 | #ifndef JSON_VERSION_H_INCLUDED 89 | #define JSON_VERSION_H_INCLUDED 90 | 91 | #define JSONCPP_VERSION_STRING "0.6.0-dev" 92 | #define JSONCPP_VERSION_MAJOR 0 93 | #define JSONCPP_VERSION_MINOR 6 94 | #define JSONCPP_VERSION_PATCH 0 95 | #define JSONCPP_VERSION_QUALIFIER -dev 96 | #define JSONCPP_VERSION_HEXA \ 97 | ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \ 98 | (JSONCPP_VERSION_PATCH << 8)) 99 | 100 | #endif // JSON_VERSION_H_INCLUDED 101 | 102 | // ////////////////////////////////////////////////////////////////////// 103 | // End of content of file: include/json/version.h 104 | // ////////////////////////////////////////////////////////////////////// 105 | 106 | 107 | 108 | 109 | 110 | 111 | // ////////////////////////////////////////////////////////////////////// 112 | // Beginning of content of file: include/json/config.h 113 | // ////////////////////////////////////////////////////////////////////// 114 | 115 | // Copyright 2007-2010 Baptiste Lepilleur 116 | // Distributed under MIT license, or public domain if desired and 117 | // recognized in your jurisdiction. 118 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 119 | 120 | #ifndef JSON_CONFIG_H_INCLUDED 121 | #define JSON_CONFIG_H_INCLUDED 122 | 123 | /// If defined, indicates that json library is embedded in CppTL library. 124 | //# define JSON_IN_CPPTL 1 125 | 126 | /// If defined, indicates that json may leverage CppTL library 127 | //# define JSON_USE_CPPTL 1 128 | /// If defined, indicates that cpptl vector based map should be used instead of 129 | /// std::map 130 | /// as Value container. 131 | //# define JSON_USE_CPPTL_SMALLMAP 1 132 | /// If defined, indicates that Json specific container should be used 133 | /// (hash table & simple deque container with customizable allocator). 134 | /// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 135 | //# define JSON_VALUE_USE_INTERNAL_MAP 1 136 | /// Force usage of standard new/malloc based allocator instead of memory pool 137 | /// based allocator. 138 | /// The memory pools allocator used optimization (initializing Value and 139 | /// ValueInternalLink 140 | /// as if it was a POD) that may cause some validation tool to report errors. 141 | /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. 142 | //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 143 | 144 | // If non-zero, the library uses exceptions to report bad input instead of C 145 | // assertion macros. The default is to use exceptions. 146 | #ifndef JSON_USE_EXCEPTION 147 | #define JSON_USE_EXCEPTION 1 148 | #endif 149 | 150 | /// If defined, indicates that the source file is amalgated 151 | /// to prevent private header inclusion. 152 | /// Remarks: it is automatically defined in the generated amalgated header. 153 | // #define JSON_IS_AMALGAMATION 154 | 155 | #ifdef JSON_IN_CPPTL 156 | #include 157 | #ifndef JSON_USE_CPPTL 158 | #define JSON_USE_CPPTL 1 159 | #endif 160 | #endif 161 | 162 | #ifdef JSON_IN_CPPTL 163 | #define JSON_API CPPTL_API 164 | #elif defined(JSON_DLL_BUILD) 165 | #if defined(_MSC_VER) 166 | #define JSON_API __declspec(dllexport) 167 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 168 | #endif // if defined(_MSC_VER) 169 | #elif defined(JSON_DLL) 170 | #if defined(_MSC_VER) 171 | #define JSON_API __declspec(dllimport) 172 | #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING 173 | #endif // if defined(_MSC_VER) 174 | #endif // ifdef JSON_IN_CPPTL 175 | #if !defined(JSON_API) 176 | #define JSON_API 177 | #endif 178 | 179 | // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for 180 | // integer 181 | // Storages, and 64 bits integer support is disabled. 182 | // #define JSON_NO_INT64 1 183 | 184 | #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 185 | // Microsoft Visual Studio 6 only support conversion from __int64 to double 186 | // (no conversion from unsigned __int64). 187 | #define JSON_USE_INT64_DOUBLE_CONVERSION 1 188 | // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' 189 | // characters in the debug information) 190 | // All projects I've ever seen with VS6 were using this globally (not bothering 191 | // with pragma push/pop). 192 | #pragma warning(disable : 4786) 193 | #endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 194 | 195 | #if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 196 | /// Indicates that the following function is deprecated. 197 | #define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) 198 | #endif 199 | 200 | #if !defined(JSONCPP_DEPRECATED) 201 | #define JSONCPP_DEPRECATED(message) 202 | #endif // if !defined(JSONCPP_DEPRECATED) 203 | 204 | namespace Json { 205 | typedef int Int; 206 | typedef unsigned int UInt; 207 | #if defined(JSON_NO_INT64) 208 | typedef int LargestInt; 209 | typedef unsigned int LargestUInt; 210 | #undef JSON_HAS_INT64 211 | #else // if defined(JSON_NO_INT64) 212 | // For Microsoft Visual use specific types as long long is not supported 213 | #if defined(_MSC_VER) // Microsoft Visual Studio 214 | typedef __int64 Int64; 215 | typedef unsigned __int64 UInt64; 216 | #else // if defined(_MSC_VER) // Other platforms, use long long 217 | typedef long long int Int64; 218 | typedef unsigned long long int UInt64; 219 | #endif // if defined(_MSC_VER) 220 | typedef Int64 LargestInt; 221 | typedef UInt64 LargestUInt; 222 | #define JSON_HAS_INT64 223 | #endif // if defined(JSON_NO_INT64) 224 | } // end namespace Json 225 | 226 | #endif // JSON_CONFIG_H_INCLUDED 227 | 228 | // ////////////////////////////////////////////////////////////////////// 229 | // End of content of file: include/json/config.h 230 | // ////////////////////////////////////////////////////////////////////// 231 | 232 | 233 | 234 | 235 | 236 | 237 | // ////////////////////////////////////////////////////////////////////// 238 | // Beginning of content of file: include/json/forwards.h 239 | // ////////////////////////////////////////////////////////////////////// 240 | 241 | // Copyright 2007-2010 Baptiste Lepilleur 242 | // Distributed under MIT license, or public domain if desired and 243 | // recognized in your jurisdiction. 244 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 245 | 246 | #ifndef JSON_FORWARDS_H_INCLUDED 247 | #define JSON_FORWARDS_H_INCLUDED 248 | 249 | #if !defined(JSON_IS_AMALGAMATION) 250 | #include "config.h" 251 | #endif // if !defined(JSON_IS_AMALGAMATION) 252 | 253 | namespace Json { 254 | 255 | // writer.h 256 | class FastWriter; 257 | class StyledWriter; 258 | 259 | // reader.h 260 | class Reader; 261 | 262 | // features.h 263 | class Features; 264 | 265 | // value.h 266 | typedef unsigned int ArrayIndex; 267 | class StaticString; 268 | class Path; 269 | class PathArgument; 270 | class Value; 271 | class ValueIteratorBase; 272 | class ValueIterator; 273 | class ValueConstIterator; 274 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 275 | class ValueMapAllocator; 276 | class ValueInternalLink; 277 | class ValueInternalArray; 278 | class ValueInternalMap; 279 | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP 280 | 281 | } // namespace Json 282 | 283 | #endif // JSON_FORWARDS_H_INCLUDED 284 | 285 | // ////////////////////////////////////////////////////////////////////// 286 | // End of content of file: include/json/forwards.h 287 | // ////////////////////////////////////////////////////////////////////// 288 | 289 | 290 | 291 | 292 | 293 | 294 | // ////////////////////////////////////////////////////////////////////// 295 | // Beginning of content of file: include/json/features.h 296 | // ////////////////////////////////////////////////////////////////////// 297 | 298 | // Copyright 2007-2010 Baptiste Lepilleur 299 | // Distributed under MIT license, or public domain if desired and 300 | // recognized in your jurisdiction. 301 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 302 | 303 | #ifndef CPPTL_JSON_FEATURES_H_INCLUDED 304 | #define CPPTL_JSON_FEATURES_H_INCLUDED 305 | 306 | #if !defined(JSON_IS_AMALGAMATION) 307 | #include "forwards.h" 308 | #endif // if !defined(JSON_IS_AMALGAMATION) 309 | 310 | namespace Json { 311 | 312 | /** \brief Configuration passed to reader and writer. 313 | * This configuration object can be used to force the Reader or Writer 314 | * to behave in a standard conforming way. 315 | */ 316 | class JSON_API Features { 317 | public: 318 | /** \brief A configuration that allows all features and assumes all strings 319 | * are UTF-8. 320 | * - C & C++ comments are allowed 321 | * - Root object can be any JSON value 322 | * - Assumes Value strings are encoded in UTF-8 323 | */ 324 | static Features all(); 325 | 326 | /** \brief A configuration that is strictly compatible with the JSON 327 | * specification. 328 | * - Comments are forbidden. 329 | * - Root object must be either an array or an object value. 330 | * - Assumes Value strings are encoded in UTF-8 331 | */ 332 | static Features strictMode(); 333 | 334 | /** \brief Initialize the configuration like JsonConfig::allFeatures; 335 | */ 336 | Features(); 337 | 338 | /// \c true if comments are allowed. Default: \c true. 339 | bool allowComments_; 340 | 341 | /// \c true if root must be either an array or an object value. Default: \c 342 | /// false. 343 | bool strictRoot_; 344 | 345 | /// \c true if dropped null placeholders are allowed. Default: \c false. 346 | bool allowDroppedNullPlaceholders_; 347 | 348 | /// \c true if numeric object key are allowed. Default: \c false. 349 | bool allowNumericKeys_; 350 | }; 351 | 352 | } // namespace Json 353 | 354 | #endif // CPPTL_JSON_FEATURES_H_INCLUDED 355 | 356 | // ////////////////////////////////////////////////////////////////////// 357 | // End of content of file: include/json/features.h 358 | // ////////////////////////////////////////////////////////////////////// 359 | 360 | 361 | 362 | 363 | 364 | 365 | // ////////////////////////////////////////////////////////////////////// 366 | // Beginning of content of file: include/json/value.h 367 | // ////////////////////////////////////////////////////////////////////// 368 | 369 | // Copyright 2007-2010 Baptiste Lepilleur 370 | // Distributed under MIT license, or public domain if desired and 371 | // recognized in your jurisdiction. 372 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 373 | 374 | #ifndef CPPTL_JSON_H_INCLUDED 375 | #define CPPTL_JSON_H_INCLUDED 376 | 377 | #if !defined(JSON_IS_AMALGAMATION) 378 | #include "forwards.h" 379 | #endif // if !defined(JSON_IS_AMALGAMATION) 380 | #include 381 | #include 382 | 383 | #ifndef JSON_USE_CPPTL_SMALLMAP 384 | #include 385 | #else 386 | #include 387 | #endif 388 | #ifdef JSON_USE_CPPTL 389 | #include 390 | #endif 391 | 392 | // Disable warning C4251: : needs to have dll-interface to 393 | // be used by... 394 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 395 | #pragma warning(push) 396 | #pragma warning(disable : 4251) 397 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 398 | 399 | /** \brief JSON (JavaScript Object Notation). 400 | */ 401 | namespace Json { 402 | 403 | /** \brief Type of the value held by a Value object. 404 | */ 405 | enum ValueType { 406 | nullValue = 0, ///< 'null' value 407 | intValue, ///< signed integer value 408 | uintValue, ///< unsigned integer value 409 | realValue, ///< double value 410 | stringValue, ///< UTF-8 string value 411 | booleanValue, ///< bool value 412 | arrayValue, ///< array value (ordered list) 413 | objectValue ///< object value (collection of name/value pairs). 414 | }; 415 | 416 | enum CommentPlacement { 417 | commentBefore = 0, ///< a comment placed on the line before a value 418 | commentAfterOnSameLine, ///< a comment just after a value on the same line 419 | commentAfter, ///< a comment on the line after a value (only make sense for 420 | /// root value) 421 | numberOfCommentPlacement 422 | }; 423 | 424 | //# ifdef JSON_USE_CPPTL 425 | // typedef CppTL::AnyEnumerator EnumMemberNames; 426 | // typedef CppTL::AnyEnumerator EnumValues; 427 | //# endif 428 | 429 | /** \brief Lightweight wrapper to tag static string. 430 | * 431 | * Value constructor and objectValue member assignement takes advantage of the 432 | * StaticString and avoid the cost of string duplication when storing the 433 | * string or the member name. 434 | * 435 | * Example of usage: 436 | * \code 437 | * Json::Value aValue( StaticString("some text") ); 438 | * Json::Value object; 439 | * static const StaticString code("code"); 440 | * object[code] = 1234; 441 | * \endcode 442 | */ 443 | class JSON_API StaticString { 444 | public: 445 | explicit StaticString(const char *czstring) : str_(czstring) {} 446 | 447 | operator const char *() const { return str_; } 448 | 449 | const char *c_str() const { return str_; } 450 | 451 | private: 452 | const char *str_; 453 | }; 454 | 455 | /** \brief Represents a JSON value. 456 | * 457 | * This class is a discriminated union wrapper that can represents a: 458 | * - signed integer [range: Value::minInt - Value::maxInt] 459 | * - unsigned integer (range: 0 - Value::maxUInt) 460 | * - double 461 | * - UTF-8 string 462 | * - boolean 463 | * - 'null' 464 | * - an ordered list of Value 465 | * - collection of name/value pairs (javascript object) 466 | * 467 | * The type of the held value is represented by a #ValueType and 468 | * can be obtained using type(). 469 | * 470 | * values of an #objectValue or #arrayValue can be accessed using operator[]() 471 | *methods. 472 | * Non const methods will automatically create the a #nullValue element 473 | * if it does not exist. 474 | * The sequence of an #arrayValue will be automatically resize and initialized 475 | * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. 476 | * 477 | * The get() methods can be used to obtanis default value in the case the 478 | *required element 479 | * does not exist. 480 | * 481 | * It is possible to iterate over the list of a #objectValue values using 482 | * the getMemberNames() method. 483 | */ 484 | class JSON_API Value { 485 | friend class ValueIteratorBase; 486 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 487 | friend class ValueInternalLink; 488 | friend class ValueInternalMap; 489 | #endif 490 | public: 491 | typedef std::vector Members; 492 | typedef ValueIterator iterator; 493 | typedef ValueConstIterator const_iterator; 494 | typedef Json::UInt UInt; 495 | typedef Json::Int Int; 496 | #if defined(JSON_HAS_INT64) 497 | typedef Json::UInt64 UInt64; 498 | typedef Json::Int64 Int64; 499 | #endif // defined(JSON_HAS_INT64) 500 | typedef Json::LargestInt LargestInt; 501 | typedef Json::LargestUInt LargestUInt; 502 | typedef Json::ArrayIndex ArrayIndex; 503 | 504 | static const Value null; 505 | /// Minimum signed integer value that can be stored in a Json::Value. 506 | static const LargestInt minLargestInt; 507 | /// Maximum signed integer value that can be stored in a Json::Value. 508 | static const LargestInt maxLargestInt; 509 | /// Maximum unsigned integer value that can be stored in a Json::Value. 510 | static const LargestUInt maxLargestUInt; 511 | 512 | /// Minimum signed int value that can be stored in a Json::Value. 513 | static const Int minInt; 514 | /// Maximum signed int value that can be stored in a Json::Value. 515 | static const Int maxInt; 516 | /// Maximum unsigned int value that can be stored in a Json::Value. 517 | static const UInt maxUInt; 518 | 519 | #if defined(JSON_HAS_INT64) 520 | /// Minimum signed 64 bits int value that can be stored in a Json::Value. 521 | static const Int64 minInt64; 522 | /// Maximum signed 64 bits int value that can be stored in a Json::Value. 523 | static const Int64 maxInt64; 524 | /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. 525 | static const UInt64 maxUInt64; 526 | #endif // defined(JSON_HAS_INT64) 527 | 528 | private: 529 | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 530 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 531 | class CZString { 532 | public: 533 | enum DuplicationPolicy { 534 | noDuplication = 0, 535 | duplicate, 536 | duplicateOnCopy 537 | }; 538 | CZString(ArrayIndex index); 539 | CZString(const char *cstr, DuplicationPolicy allocate); 540 | CZString(const CZString &other); 541 | ~CZString(); 542 | CZString &operator=(const CZString &other); 543 | bool operator<(const CZString &other) const; 544 | bool operator==(const CZString &other) const; 545 | ArrayIndex index() const; 546 | const char *c_str() const; 547 | bool isStaticString() const; 548 | 549 | private: 550 | void swap(CZString &other); 551 | const char *cstr_; 552 | ArrayIndex index_; 553 | }; 554 | 555 | public: 556 | #ifndef JSON_USE_CPPTL_SMALLMAP 557 | typedef std::map ObjectValues; 558 | #else 559 | typedef CppTL::SmallMap ObjectValues; 560 | #endif // ifndef JSON_USE_CPPTL_SMALLMAP 561 | #endif // ifndef JSON_VALUE_USE_INTERNAL_MAP 562 | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 563 | 564 | public: 565 | /** \brief Create a default Value of the given type. 566 | 567 | This is a very useful constructor. 568 | To create an empty array, pass arrayValue. 569 | To create an empty object, pass objectValue. 570 | Another Value can then be set to this one by assignment. 571 | This is useful since clear() and resize() will not alter types. 572 | 573 | Examples: 574 | \code 575 | Json::Value null_value; // null 576 | Json::Value arr_value(Json::arrayValue); // [] 577 | Json::Value obj_value(Json::objectValue); // {} 578 | \endcode 579 | */ 580 | Value(ValueType type = nullValue); 581 | Value(Int value); 582 | Value(UInt value); 583 | #if defined(JSON_HAS_INT64) 584 | Value(Int64 value); 585 | Value(UInt64 value); 586 | #endif // if defined(JSON_HAS_INT64) 587 | Value(double value); 588 | Value(const char *value); 589 | Value(const char *beginValue, const char *endValue); 590 | /** \brief Constructs a value from a static string. 591 | 592 | * Like other value string constructor but do not duplicate the string for 593 | * internal storage. The given string must remain alive after the call to this 594 | * constructor. 595 | * Example of usage: 596 | * \code 597 | * Json::Value aValue( StaticString("some text") ); 598 | * \endcode 599 | */ 600 | Value(const StaticString &value); 601 | Value(const std::string &value); 602 | #ifdef JSON_USE_CPPTL 603 | Value(const CppTL::ConstString &value); 604 | #endif 605 | Value(bool value); 606 | Value(const Value &other); 607 | ~Value(); 608 | 609 | Value &operator=(const Value &other); 610 | /// Swap values. 611 | /// \note Currently, comments are intentionally not swapped, for 612 | /// both logic and efficiency. 613 | void swap(Value &other); 614 | 615 | ValueType type() const; 616 | 617 | bool operator<(const Value &other) const; 618 | bool operator<=(const Value &other) const; 619 | bool operator>=(const Value &other) const; 620 | bool operator>(const Value &other) const; 621 | 622 | bool operator==(const Value &other) const; 623 | bool operator!=(const Value &other) const; 624 | 625 | int compare(const Value &other) const; 626 | 627 | const char *asCString() const; 628 | std::string asString() const; 629 | #ifdef JSON_USE_CPPTL 630 | CppTL::ConstString asConstString() const; 631 | #endif 632 | Int asInt() const; 633 | UInt asUInt() const; 634 | #if defined(JSON_HAS_INT64) 635 | Int64 asInt64() const; 636 | UInt64 asUInt64() const; 637 | #endif // if defined(JSON_HAS_INT64) 638 | LargestInt asLargestInt() const; 639 | LargestUInt asLargestUInt() const; 640 | float asFloat() const; 641 | double asDouble() const; 642 | bool asBool() const; 643 | 644 | bool isNull() const; 645 | bool isBool() const; 646 | bool isInt() const; 647 | bool isInt64() const; 648 | bool isUInt() const; 649 | bool isUInt64() const; 650 | bool isIntegral() const; 651 | bool isDouble() const; 652 | bool isNumeric() const; 653 | bool isString() const; 654 | bool isArray() const; 655 | bool isObject() const; 656 | 657 | bool isConvertibleTo(ValueType other) const; 658 | 659 | /// Number of values in array or object 660 | ArrayIndex size() const; 661 | 662 | /// \brief Return true if empty array, empty object, or null; 663 | /// otherwise, false. 664 | bool empty() const; 665 | 666 | /// Return isNull() 667 | bool operator!() const; 668 | 669 | /// Remove all object members and array elements. 670 | /// \pre type() is arrayValue, objectValue, or nullValue 671 | /// \post type() is unchanged 672 | void clear(); 673 | 674 | /// Resize the array to size elements. 675 | /// New elements are initialized to null. 676 | /// May only be called on nullValue or arrayValue. 677 | /// \pre type() is arrayValue or nullValue 678 | /// \post type() is arrayValue 679 | void resize(ArrayIndex size); 680 | 681 | /// Access an array element (zero based index ). 682 | /// If the array contains less than index element, then null value are 683 | /// inserted 684 | /// in the array so that its size is index+1. 685 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 686 | /// this from the operator[] which takes a string.) 687 | Value &operator[](ArrayIndex index); 688 | 689 | /// Access an array element (zero based index ). 690 | /// If the array contains less than index element, then null value are 691 | /// inserted 692 | /// in the array so that its size is index+1. 693 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 694 | /// this from the operator[] which takes a string.) 695 | Value &operator[](int index); 696 | 697 | /// Access an array element (zero based index ) 698 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 699 | /// this from the operator[] which takes a string.) 700 | const Value &operator[](ArrayIndex index) const; 701 | 702 | /// Access an array element (zero based index ) 703 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 704 | /// this from the operator[] which takes a string.) 705 | const Value &operator[](int index) const; 706 | 707 | /// If the array contains at least index+1 elements, returns the element 708 | /// value, 709 | /// otherwise returns defaultValue. 710 | Value get(ArrayIndex index, const Value &defaultValue) const; 711 | /// Return true if index < size(). 712 | bool isValidIndex(ArrayIndex index) const; 713 | /// \brief Append value to array at the end. 714 | /// 715 | /// Equivalent to jsonvalue[jsonvalue.size()] = value; 716 | Value &append(const Value &value); 717 | 718 | /// Access an object value by name, create a null member if it does not exist. 719 | Value &operator[](const char *key); 720 | /// Access an object value by name, returns null if there is no member with 721 | /// that name. 722 | const Value &operator[](const char *key) const; 723 | /// Access an object value by name, create a null member if it does not exist. 724 | Value &operator[](const std::string &key); 725 | /// Access an object value by name, returns null if there is no member with 726 | /// that name. 727 | const Value &operator[](const std::string &key) const; 728 | /** \brief Access an object value by name, create a null member if it does not 729 | exist. 730 | 731 | * If the object as no entry for that name, then the member name used to store 732 | * the new entry is not duplicated. 733 | * Example of use: 734 | * \code 735 | * Json::Value object; 736 | * static const StaticString code("code"); 737 | * object[code] = 1234; 738 | * \endcode 739 | */ 740 | Value &operator[](const StaticString &key); 741 | #ifdef JSON_USE_CPPTL 742 | /// Access an object value by name, create a null member if it does not exist. 743 | Value &operator[](const CppTL::ConstString &key); 744 | /// Access an object value by name, returns null if there is no member with 745 | /// that name. 746 | const Value &operator[](const CppTL::ConstString &key) const; 747 | #endif 748 | /// Return the member named key if it exist, defaultValue otherwise. 749 | Value get(const char *key, const Value &defaultValue) const; 750 | /// Return the member named key if it exist, defaultValue otherwise. 751 | Value get(const std::string &key, const Value &defaultValue) const; 752 | #ifdef JSON_USE_CPPTL 753 | /// Return the member named key if it exist, defaultValue otherwise. 754 | Value get(const CppTL::ConstString &key, const Value &defaultValue) const; 755 | #endif 756 | /// \brief Remove and return the named member. 757 | /// 758 | /// Do nothing if it did not exist. 759 | /// \return the removed Value, or null. 760 | /// \pre type() is objectValue or nullValue 761 | /// \post type() is unchanged 762 | Value removeMember(const char *key); 763 | /// Same as removeMember(const char*) 764 | Value removeMember(const std::string &key); 765 | 766 | /// Return true if the object has a member named key. 767 | bool isMember(const char *key) const; 768 | /// Return true if the object has a member named key. 769 | bool isMember(const std::string &key) const; 770 | #ifdef JSON_USE_CPPTL 771 | /// Return true if the object has a member named key. 772 | bool isMember(const CppTL::ConstString &key) const; 773 | #endif 774 | 775 | /// \brief Return a list of the member names. 776 | /// 777 | /// If null, return an empty list. 778 | /// \pre type() is objectValue or nullValue 779 | /// \post if type() was nullValue, it remains nullValue 780 | Members getMemberNames() const; 781 | 782 | //# ifdef JSON_USE_CPPTL 783 | // EnumMemberNames enumMemberNames() const; 784 | // EnumValues enumValues() const; 785 | //# endif 786 | 787 | /// Comments must be //... or /* ... */ 788 | void setComment(const char *comment, CommentPlacement placement); 789 | /// Comments must be //... or /* ... */ 790 | void setComment(const std::string &comment, CommentPlacement placement); 791 | bool hasComment(CommentPlacement placement) const; 792 | /// Include delimiters and embedded newlines. 793 | std::string getComment(CommentPlacement placement) const; 794 | 795 | std::string toStyledString() const; 796 | 797 | const_iterator begin() const; 798 | const_iterator end() const; 799 | 800 | iterator begin(); 801 | iterator end(); 802 | 803 | // Accessors for the [start, limit) range of bytes within the JSON text from 804 | // which this value was parsed, if any. 805 | void setOffsetStart(size_t start); 806 | void setOffsetLimit(size_t limit); 807 | size_t getOffsetStart() const; 808 | size_t getOffsetLimit() const; 809 | 810 | private: 811 | Value &resolveReference(const char *key, bool isStatic); 812 | 813 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 814 | inline bool isItemAvailable() const { return itemIsUsed_ == 0; } 815 | 816 | inline void setItemUsed(bool isUsed = true) { itemIsUsed_ = isUsed ? 1 : 0; } 817 | 818 | inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; } 819 | 820 | inline void setMemberNameIsStatic(bool isStatic) { 821 | memberNameIsStatic_ = isStatic ? 1 : 0; 822 | } 823 | #endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP 824 | 825 | private: 826 | struct CommentInfo { 827 | CommentInfo(); 828 | ~CommentInfo(); 829 | 830 | void setComment(const char *text); 831 | 832 | char *comment_; 833 | }; 834 | 835 | // struct MemberNamesTransform 836 | //{ 837 | // typedef const char *result_type; 838 | // const char *operator()( const CZString &name ) const 839 | // { 840 | // return name.c_str(); 841 | // } 842 | //}; 843 | 844 | union ValueHolder { 845 | LargestInt int_; 846 | LargestUInt uint_; 847 | double real_; 848 | bool bool_; 849 | char *string_; 850 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 851 | ValueInternalArray *array_; 852 | ValueInternalMap *map_; 853 | #else 854 | ObjectValues *map_; 855 | #endif 856 | } value_; 857 | ValueType type_ : 8; 858 | int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. 859 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 860 | unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. 861 | int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. 862 | #endif 863 | CommentInfo *comments_; 864 | 865 | // [start, limit) byte offsets in the source JSON text from which this Value 866 | // was extracted. 867 | size_t start_; 868 | size_t limit_; 869 | }; 870 | 871 | /** \brief Experimental and untested: represents an element of the "path" to 872 | * access a node. 873 | */ 874 | class JSON_API PathArgument { 875 | public: 876 | friend class Path; 877 | 878 | PathArgument(); 879 | PathArgument(ArrayIndex index); 880 | PathArgument(const char *key); 881 | PathArgument(const std::string &key); 882 | 883 | private: 884 | enum Kind { 885 | kindNone = 0, 886 | kindIndex, 887 | kindKey 888 | }; 889 | std::string key_; 890 | ArrayIndex index_; 891 | Kind kind_; 892 | }; 893 | 894 | /** \brief Experimental and untested: represents a "path" to access a node. 895 | * 896 | * Syntax: 897 | * - "." => root node 898 | * - ".[n]" => elements at index 'n' of root node (an array value) 899 | * - ".name" => member named 'name' of root node (an object value) 900 | * - ".name1.name2.name3" 901 | * - ".[0][1][2].name1[3]" 902 | * - ".%" => member name is provided as parameter 903 | * - ".[%]" => index is provied as parameter 904 | */ 905 | class JSON_API Path { 906 | public: 907 | Path(const std::string &path, 908 | const PathArgument &a1 = PathArgument(), 909 | const PathArgument &a2 = PathArgument(), 910 | const PathArgument &a3 = PathArgument(), 911 | const PathArgument &a4 = PathArgument(), 912 | const PathArgument &a5 = PathArgument()); 913 | 914 | const Value &resolve(const Value &root) const; 915 | Value resolve(const Value &root, const Value &defaultValue) const; 916 | /// Creates the "path" to access the specified node and returns a reference on 917 | /// the node. 918 | Value &make(Value &root) const; 919 | 920 | private: 921 | typedef std::vector InArgs; 922 | typedef std::vector Args; 923 | 924 | void makePath(const std::string &path, const InArgs &in); 925 | void addPathInArg(const std::string &path, 926 | const InArgs &in, 927 | InArgs::const_iterator &itInArg, 928 | PathArgument::Kind kind); 929 | void invalidPath(const std::string &path, int location); 930 | 931 | Args args_; 932 | }; 933 | 934 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 935 | /** \brief Allocator to customize Value internal map. 936 | * Below is an example of a simple implementation (default implementation 937 | actually 938 | * use memory pool for speed). 939 | * \code 940 | class DefaultValueMapAllocator : public ValueMapAllocator 941 | { 942 | public: // overridden from ValueMapAllocator 943 | virtual ValueInternalMap *newMap() 944 | { 945 | return new ValueInternalMap(); 946 | } 947 | 948 | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) 949 | { 950 | return new ValueInternalMap( other ); 951 | } 952 | 953 | virtual void destructMap( ValueInternalMap *map ) 954 | { 955 | delete map; 956 | } 957 | 958 | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) 959 | { 960 | return new ValueInternalLink[size]; 961 | } 962 | 963 | virtual void releaseMapBuckets( ValueInternalLink *links ) 964 | { 965 | delete [] links; 966 | } 967 | 968 | virtual ValueInternalLink *allocateMapLink() 969 | { 970 | return new ValueInternalLink(); 971 | } 972 | 973 | virtual void releaseMapLink( ValueInternalLink *link ) 974 | { 975 | delete link; 976 | } 977 | }; 978 | * \endcode 979 | */ 980 | class JSON_API ValueMapAllocator { 981 | public: 982 | virtual ~ValueMapAllocator(); 983 | virtual ValueInternalMap *newMap() = 0; 984 | virtual ValueInternalMap *newMapCopy(const ValueInternalMap &other) = 0; 985 | virtual void destructMap(ValueInternalMap *map) = 0; 986 | virtual ValueInternalLink *allocateMapBuckets(unsigned int size) = 0; 987 | virtual void releaseMapBuckets(ValueInternalLink *links) = 0; 988 | virtual ValueInternalLink *allocateMapLink() = 0; 989 | virtual void releaseMapLink(ValueInternalLink *link) = 0; 990 | }; 991 | 992 | /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). 993 | * \internal previous_ & next_ allows for bidirectional traversal. 994 | */ 995 | class JSON_API ValueInternalLink { 996 | public: 997 | enum { 998 | itemPerLink = 6 999 | }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. 1000 | enum InternalFlags { 1001 | flagAvailable = 0, 1002 | flagUsed = 1 1003 | }; 1004 | 1005 | ValueInternalLink(); 1006 | 1007 | ~ValueInternalLink(); 1008 | 1009 | Value items_[itemPerLink]; 1010 | char *keys_[itemPerLink]; 1011 | ValueInternalLink *previous_; 1012 | ValueInternalLink *next_; 1013 | }; 1014 | 1015 | /** \brief A linked page based hash-table implementation used internally by 1016 | *Value. 1017 | * \internal ValueInternalMap is a tradional bucket based hash-table, with a 1018 | *linked 1019 | * list in each bucket to handle collision. There is an addional twist in that 1020 | * each node of the collision linked list is a page containing a fixed amount of 1021 | * value. This provides a better compromise between memory usage and speed. 1022 | * 1023 | * Each bucket is made up of a chained list of ValueInternalLink. The last 1024 | * link of a given bucket can be found in the 'previous_' field of the following 1025 | *bucket. 1026 | * The last link of the last bucket is stored in tailLink_ as it has no 1027 | *following bucket. 1028 | * Only the last link of a bucket may contains 'available' item. The last link 1029 | *always 1030 | * contains at least one element unless is it the bucket one very first link. 1031 | */ 1032 | class JSON_API ValueInternalMap { 1033 | friend class ValueIteratorBase; 1034 | friend class Value; 1035 | 1036 | public: 1037 | typedef unsigned int HashKey; 1038 | typedef unsigned int BucketIndex; 1039 | 1040 | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 1041 | struct IteratorState { 1042 | IteratorState() : map_(0), link_(0), itemIndex_(0), bucketIndex_(0) {} 1043 | ValueInternalMap *map_; 1044 | ValueInternalLink *link_; 1045 | BucketIndex itemIndex_; 1046 | BucketIndex bucketIndex_; 1047 | }; 1048 | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 1049 | 1050 | ValueInternalMap(); 1051 | ValueInternalMap(const ValueInternalMap &other); 1052 | ValueInternalMap &operator=(const ValueInternalMap &other); 1053 | ~ValueInternalMap(); 1054 | 1055 | void swap(ValueInternalMap &other); 1056 | 1057 | BucketIndex size() const; 1058 | 1059 | void clear(); 1060 | 1061 | bool reserveDelta(BucketIndex growth); 1062 | 1063 | bool reserve(BucketIndex newItemCount); 1064 | 1065 | const Value *find(const char *key) const; 1066 | 1067 | Value *find(const char *key); 1068 | 1069 | Value &resolveReference(const char *key, bool isStatic); 1070 | 1071 | void remove(const char *key); 1072 | 1073 | void doActualRemove(ValueInternalLink *link, 1074 | BucketIndex index, 1075 | BucketIndex bucketIndex); 1076 | 1077 | ValueInternalLink *&getLastLinkInBucket(BucketIndex bucketIndex); 1078 | 1079 | Value &setNewItem(const char *key, 1080 | bool isStatic, 1081 | ValueInternalLink *link, 1082 | BucketIndex index); 1083 | 1084 | Value &unsafeAdd(const char *key, bool isStatic, HashKey hashedKey); 1085 | 1086 | HashKey hash(const char *key) const; 1087 | 1088 | int compare(const ValueInternalMap &other) const; 1089 | 1090 | private: 1091 | void makeBeginIterator(IteratorState &it) const; 1092 | void makeEndIterator(IteratorState &it) const; 1093 | static bool equals(const IteratorState &x, const IteratorState &other); 1094 | static void increment(IteratorState &iterator); 1095 | static void incrementBucket(IteratorState &iterator); 1096 | static void decrement(IteratorState &iterator); 1097 | static const char *key(const IteratorState &iterator); 1098 | static const char *key(const IteratorState &iterator, bool &isStatic); 1099 | static Value &value(const IteratorState &iterator); 1100 | static int distance(const IteratorState &x, const IteratorState &y); 1101 | 1102 | private: 1103 | ValueInternalLink *buckets_; 1104 | ValueInternalLink *tailLink_; 1105 | BucketIndex bucketsSize_; 1106 | BucketIndex itemCount_; 1107 | }; 1108 | 1109 | /** \brief A simplified deque implementation used internally by Value. 1110 | * \internal 1111 | * It is based on a list of fixed "page", each page contains a fixed number of 1112 | *items. 1113 | * Instead of using a linked-list, a array of pointer is used for fast item 1114 | *look-up. 1115 | * Look-up for an element is as follow: 1116 | * - compute page index: pageIndex = itemIndex / itemsPerPage 1117 | * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] 1118 | * 1119 | * Insertion is amortized constant time (only the array containing the index of 1120 | *pointers 1121 | * need to be reallocated when items are appended). 1122 | */ 1123 | class JSON_API ValueInternalArray { 1124 | friend class Value; 1125 | friend class ValueIteratorBase; 1126 | 1127 | public: 1128 | enum { 1129 | itemsPerPage = 8 1130 | }; // should be a power of 2 for fast divide and modulo. 1131 | typedef Value::ArrayIndex ArrayIndex; 1132 | typedef unsigned int PageIndex; 1133 | 1134 | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 1135 | struct IteratorState // Must be a POD 1136 | { 1137 | IteratorState() : array_(0), currentPageIndex_(0), currentItemIndex_(0) {} 1138 | ValueInternalArray *array_; 1139 | Value **currentPageIndex_; 1140 | unsigned int currentItemIndex_; 1141 | }; 1142 | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 1143 | 1144 | ValueInternalArray(); 1145 | ValueInternalArray(const ValueInternalArray &other); 1146 | ValueInternalArray &operator=(const ValueInternalArray &other); 1147 | ~ValueInternalArray(); 1148 | void swap(ValueInternalArray &other); 1149 | 1150 | void clear(); 1151 | void resize(ArrayIndex newSize); 1152 | 1153 | Value &resolveReference(ArrayIndex index); 1154 | 1155 | Value *find(ArrayIndex index) const; 1156 | 1157 | ArrayIndex size() const; 1158 | 1159 | int compare(const ValueInternalArray &other) const; 1160 | 1161 | private: 1162 | static bool equals(const IteratorState &x, const IteratorState &other); 1163 | static void increment(IteratorState &iterator); 1164 | static void decrement(IteratorState &iterator); 1165 | static Value &dereference(const IteratorState &iterator); 1166 | static Value &unsafeDereference(const IteratorState &iterator); 1167 | static int distance(const IteratorState &x, const IteratorState &y); 1168 | static ArrayIndex indexOf(const IteratorState &iterator); 1169 | void makeBeginIterator(IteratorState &it) const; 1170 | void makeEndIterator(IteratorState &it) const; 1171 | void makeIterator(IteratorState &it, ArrayIndex index) const; 1172 | 1173 | void makeIndexValid(ArrayIndex index); 1174 | 1175 | Value **pages_; 1176 | ArrayIndex size_; 1177 | PageIndex pageCount_; 1178 | }; 1179 | 1180 | /** \brief Experimental: do not use. Allocator to customize Value internal 1181 | array. 1182 | * Below is an example of a simple implementation (actual implementation use 1183 | * memory pool). 1184 | \code 1185 | class DefaultValueArrayAllocator : public ValueArrayAllocator 1186 | { 1187 | public: // overridden from ValueArrayAllocator 1188 | virtual ~DefaultValueArrayAllocator() 1189 | { 1190 | } 1191 | 1192 | virtual ValueInternalArray *newArray() 1193 | { 1194 | return new ValueInternalArray(); 1195 | } 1196 | 1197 | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) 1198 | { 1199 | return new ValueInternalArray( other ); 1200 | } 1201 | 1202 | virtual void destruct( ValueInternalArray *array ) 1203 | { 1204 | delete array; 1205 | } 1206 | 1207 | virtual void reallocateArrayPageIndex( Value **&indexes, 1208 | ValueInternalArray::PageIndex 1209 | &indexCount, 1210 | ValueInternalArray::PageIndex 1211 | minNewIndexCount ) 1212 | { 1213 | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; 1214 | if ( minNewIndexCount > newIndexCount ) 1215 | newIndexCount = minNewIndexCount; 1216 | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); 1217 | if ( !newIndexes ) 1218 | throw std::bad_alloc(); 1219 | indexCount = newIndexCount; 1220 | indexes = static_cast( newIndexes ); 1221 | } 1222 | virtual void releaseArrayPageIndex( Value **indexes, 1223 | ValueInternalArray::PageIndex indexCount ) 1224 | { 1225 | if ( indexes ) 1226 | free( indexes ); 1227 | } 1228 | 1229 | virtual Value *allocateArrayPage() 1230 | { 1231 | return static_cast( malloc( sizeof(Value) * 1232 | ValueInternalArray::itemsPerPage ) ); 1233 | } 1234 | 1235 | virtual void releaseArrayPage( Value *value ) 1236 | { 1237 | if ( value ) 1238 | free( value ); 1239 | } 1240 | }; 1241 | \endcode 1242 | */ 1243 | class JSON_API ValueArrayAllocator { 1244 | public: 1245 | virtual ~ValueArrayAllocator(); 1246 | virtual ValueInternalArray *newArray() = 0; 1247 | virtual ValueInternalArray *newArrayCopy(const ValueInternalArray &other) = 0; 1248 | virtual void destructArray(ValueInternalArray *array) = 0; 1249 | /** \brief Reallocate array page index. 1250 | * Reallocates an array of pointer on each page. 1251 | * \param indexes [input] pointer on the current index. May be \c NULL. 1252 | * [output] pointer on the new index of at least 1253 | * \a minNewIndexCount pages. 1254 | * \param indexCount [input] current number of pages in the index. 1255 | * [output] number of page the reallocated index can handle. 1256 | * \b MUST be >= \a minNewIndexCount. 1257 | * \param minNewIndexCount Minimum number of page the new index must be able 1258 | * to 1259 | * handle. 1260 | */ 1261 | virtual void 1262 | reallocateArrayPageIndex(Value **&indexes, 1263 | ValueInternalArray::PageIndex &indexCount, 1264 | ValueInternalArray::PageIndex minNewIndexCount) = 0; 1265 | virtual void 1266 | releaseArrayPageIndex(Value **indexes, 1267 | ValueInternalArray::PageIndex indexCount) = 0; 1268 | virtual Value *allocateArrayPage() = 0; 1269 | virtual void releaseArrayPage(Value *value) = 0; 1270 | }; 1271 | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP 1272 | 1273 | /** \brief base class for Value iterators. 1274 | * 1275 | */ 1276 | class JSON_API ValueIteratorBase { 1277 | public: 1278 | typedef std::bidirectional_iterator_tag iterator_category; 1279 | typedef unsigned int size_t; 1280 | typedef int difference_type; 1281 | typedef ValueIteratorBase SelfType; 1282 | 1283 | ValueIteratorBase(); 1284 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 1285 | explicit ValueIteratorBase(const Value::ObjectValues::iterator ¤t); 1286 | #else 1287 | ValueIteratorBase(const ValueInternalArray::IteratorState &state); 1288 | ValueIteratorBase(const ValueInternalMap::IteratorState &state); 1289 | #endif 1290 | 1291 | bool operator==(const SelfType &other) const { return isEqual(other); } 1292 | 1293 | bool operator!=(const SelfType &other) const { return !isEqual(other); } 1294 | 1295 | difference_type operator-(const SelfType &other) const { 1296 | return computeDistance(other); 1297 | } 1298 | 1299 | /// Return either the index or the member name of the referenced value as a 1300 | /// Value. 1301 | Value key() const; 1302 | 1303 | /// Return the index of the referenced Value. -1 if it is not an arrayValue. 1304 | UInt index() const; 1305 | 1306 | /// Return the member name of the referenced Value. "" if it is not an 1307 | /// objectValue. 1308 | const char *memberName() const; 1309 | 1310 | protected: 1311 | Value &deref() const; 1312 | 1313 | void increment(); 1314 | 1315 | void decrement(); 1316 | 1317 | difference_type computeDistance(const SelfType &other) const; 1318 | 1319 | bool isEqual(const SelfType &other) const; 1320 | 1321 | void copy(const SelfType &other); 1322 | 1323 | private: 1324 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 1325 | Value::ObjectValues::iterator current_; 1326 | // Indicates that iterator is for a null value. 1327 | bool isNull_; 1328 | #else 1329 | union { 1330 | ValueInternalArray::IteratorState array_; 1331 | ValueInternalMap::IteratorState map_; 1332 | } iterator_; 1333 | bool isArray_; 1334 | #endif 1335 | }; 1336 | 1337 | /** \brief const iterator for object and array value. 1338 | * 1339 | */ 1340 | class JSON_API ValueConstIterator : public ValueIteratorBase { 1341 | friend class Value; 1342 | 1343 | public: 1344 | typedef const Value value_type; 1345 | typedef unsigned int size_t; 1346 | typedef int difference_type; 1347 | typedef const Value &reference; 1348 | typedef const Value *pointer; 1349 | typedef ValueConstIterator SelfType; 1350 | 1351 | ValueConstIterator(); 1352 | 1353 | private: 1354 | /*! \internal Use by Value to create an iterator. 1355 | */ 1356 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 1357 | explicit ValueConstIterator(const Value::ObjectValues::iterator ¤t); 1358 | #else 1359 | ValueConstIterator(const ValueInternalArray::IteratorState &state); 1360 | ValueConstIterator(const ValueInternalMap::IteratorState &state); 1361 | #endif 1362 | public: 1363 | SelfType &operator=(const ValueIteratorBase &other); 1364 | 1365 | SelfType operator++(int) { 1366 | SelfType temp(*this); 1367 | ++*this; 1368 | return temp; 1369 | } 1370 | 1371 | SelfType operator--(int) { 1372 | SelfType temp(*this); 1373 | --*this; 1374 | return temp; 1375 | } 1376 | 1377 | SelfType &operator--() { 1378 | decrement(); 1379 | return *this; 1380 | } 1381 | 1382 | SelfType &operator++() { 1383 | increment(); 1384 | return *this; 1385 | } 1386 | 1387 | reference operator*() const { return deref(); } 1388 | }; 1389 | 1390 | /** \brief Iterator for object and array value. 1391 | */ 1392 | class JSON_API ValueIterator : public ValueIteratorBase { 1393 | friend class Value; 1394 | 1395 | public: 1396 | typedef Value value_type; 1397 | typedef unsigned int size_t; 1398 | typedef int difference_type; 1399 | typedef Value &reference; 1400 | typedef Value *pointer; 1401 | typedef ValueIterator SelfType; 1402 | 1403 | ValueIterator(); 1404 | ValueIterator(const ValueConstIterator &other); 1405 | ValueIterator(const ValueIterator &other); 1406 | 1407 | private: 1408 | /*! \internal Use by Value to create an iterator. 1409 | */ 1410 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 1411 | explicit ValueIterator(const Value::ObjectValues::iterator ¤t); 1412 | #else 1413 | ValueIterator(const ValueInternalArray::IteratorState &state); 1414 | ValueIterator(const ValueInternalMap::IteratorState &state); 1415 | #endif 1416 | public: 1417 | SelfType &operator=(const SelfType &other); 1418 | 1419 | SelfType operator++(int) { 1420 | SelfType temp(*this); 1421 | ++*this; 1422 | return temp; 1423 | } 1424 | 1425 | SelfType operator--(int) { 1426 | SelfType temp(*this); 1427 | --*this; 1428 | return temp; 1429 | } 1430 | 1431 | SelfType &operator--() { 1432 | decrement(); 1433 | return *this; 1434 | } 1435 | 1436 | SelfType &operator++() { 1437 | increment(); 1438 | return *this; 1439 | } 1440 | 1441 | reference operator*() const { return deref(); } 1442 | }; 1443 | 1444 | } // namespace Json 1445 | 1446 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1447 | #pragma warning(pop) 1448 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1449 | 1450 | #endif // CPPTL_JSON_H_INCLUDED 1451 | 1452 | // ////////////////////////////////////////////////////////////////////// 1453 | // End of content of file: include/json/value.h 1454 | // ////////////////////////////////////////////////////////////////////// 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | // ////////////////////////////////////////////////////////////////////// 1462 | // Beginning of content of file: include/json/reader.h 1463 | // ////////////////////////////////////////////////////////////////////// 1464 | 1465 | // Copyright 2007-2010 Baptiste Lepilleur 1466 | // Distributed under MIT license, or public domain if desired and 1467 | // recognized in your jurisdiction. 1468 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1469 | 1470 | #ifndef CPPTL_JSON_READER_H_INCLUDED 1471 | #define CPPTL_JSON_READER_H_INCLUDED 1472 | 1473 | #if !defined(JSON_IS_AMALGAMATION) 1474 | #include "features.h" 1475 | #include "value.h" 1476 | #endif // if !defined(JSON_IS_AMALGAMATION) 1477 | #include 1478 | #include 1479 | #include 1480 | #include 1481 | 1482 | // Disable warning C4251: : needs to have dll-interface to 1483 | // be used by... 1484 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1485 | #pragma warning(push) 1486 | #pragma warning(disable : 4251) 1487 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1488 | 1489 | namespace Json { 1490 | 1491 | /** \brief Unserialize a JSON document into a 1492 | *Value. 1493 | * 1494 | */ 1495 | class JSON_API Reader { 1496 | public: 1497 | typedef char Char; 1498 | typedef const Char *Location; 1499 | 1500 | /** \brief An error tagged with where in the JSON text it was encountered. 1501 | * 1502 | * The offsets give the [start, limit) range of bytes within the text. Note 1503 | * that this is bytes, not codepoints. 1504 | * 1505 | */ 1506 | struct StructuredError { 1507 | size_t offset_start; 1508 | size_t offset_limit; 1509 | std::string message; 1510 | }; 1511 | 1512 | /** \brief Constructs a Reader allowing all features 1513 | * for parsing. 1514 | */ 1515 | Reader(); 1516 | 1517 | /** \brief Constructs a Reader allowing the specified feature set 1518 | * for parsing. 1519 | */ 1520 | Reader(const Features &features); 1521 | 1522 | /** \brief Read a Value from a JSON 1523 | * document. 1524 | * \param document UTF-8 encoded string containing the document to read. 1525 | * \param root [out] Contains the root value of the document if it was 1526 | * successfully parsed. 1527 | * \param collectComments \c true to collect comment and allow writing them 1528 | * back during 1529 | * serialization, \c false to discard comments. 1530 | * This parameter is ignored if 1531 | * Features::allowComments_ 1532 | * is \c false. 1533 | * \return \c true if the document was successfully parsed, \c false if an 1534 | * error occurred. 1535 | */ 1536 | bool 1537 | parse(const std::string &document, Value &root, bool collectComments = true); 1538 | 1539 | /** \brief Read a Value from a JSON 1540 | document. 1541 | * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the 1542 | document to read. 1543 | * \param endDoc Pointer on the end of the UTF-8 encoded string of the 1544 | document to read. 1545 | \ Must be >= beginDoc. 1546 | * \param root [out] Contains the root value of the document if it was 1547 | * successfully parsed. 1548 | * \param collectComments \c true to collect comment and allow writing them 1549 | back during 1550 | * serialization, \c false to discard comments. 1551 | * This parameter is ignored if 1552 | Features::allowComments_ 1553 | * is \c false. 1554 | * \return \c true if the document was successfully parsed, \c false if an 1555 | error occurred. 1556 | */ 1557 | bool parse(const char *beginDoc, 1558 | const char *endDoc, 1559 | Value &root, 1560 | bool collectComments = true); 1561 | 1562 | /// \brief Parse from input stream. 1563 | /// \see Json::operator>>(std::istream&, Json::Value&). 1564 | bool parse(std::istream &is, Value &root, bool collectComments = true); 1565 | 1566 | /** \brief Returns a user friendly string that list errors in the parsed 1567 | * document. 1568 | * \return Formatted error message with the list of errors with their location 1569 | * in 1570 | * the parsed document. An empty string is returned if no error 1571 | * occurred 1572 | * during parsing. 1573 | * \deprecated Use getFormattedErrorMessages() instead (typo fix). 1574 | */ 1575 | JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") 1576 | std::string getFormatedErrorMessages() const; 1577 | 1578 | /** \brief Returns a user friendly string that list errors in the parsed 1579 | * document. 1580 | * \return Formatted error message with the list of errors with their location 1581 | * in 1582 | * the parsed document. An empty string is returned if no error 1583 | * occurred 1584 | * during parsing. 1585 | */ 1586 | std::string getFormattedErrorMessages() const; 1587 | 1588 | /** \brief Returns a vector of structured erros encounted while parsing. 1589 | * \return A (possibly empty) vector of StructuredError objects. Currently 1590 | * only one error can be returned, but the caller should tolerate 1591 | * multiple 1592 | * errors. This can occur if the parser recovers from a non-fatal 1593 | * parse error and then encounters additional errors. 1594 | */ 1595 | std::vector getStructuredErrors() const; 1596 | 1597 | private: 1598 | enum TokenType { 1599 | tokenEndOfStream = 0, 1600 | tokenObjectBegin, 1601 | tokenObjectEnd, 1602 | tokenArrayBegin, 1603 | tokenArrayEnd, 1604 | tokenString, 1605 | tokenNumber, 1606 | tokenTrue, 1607 | tokenFalse, 1608 | tokenNull, 1609 | tokenArraySeparator, 1610 | tokenMemberSeparator, 1611 | tokenComment, 1612 | tokenError 1613 | }; 1614 | 1615 | class Token { 1616 | public: 1617 | TokenType type_; 1618 | Location start_; 1619 | Location end_; 1620 | }; 1621 | 1622 | class ErrorInfo { 1623 | public: 1624 | Token token_; 1625 | std::string message_; 1626 | Location extra_; 1627 | }; 1628 | 1629 | typedef std::deque Errors; 1630 | 1631 | bool expectToken(TokenType type, Token &token, const char *message); 1632 | bool readToken(Token &token); 1633 | void skipSpaces(); 1634 | bool match(Location pattern, int patternLength); 1635 | bool readComment(); 1636 | bool readCStyleComment(); 1637 | bool readCppStyleComment(); 1638 | bool readString(); 1639 | void readNumber(); 1640 | bool readValue(); 1641 | bool readObject(Token &token); 1642 | bool readArray(Token &token); 1643 | bool decodeNumber(Token &token); 1644 | bool decodeNumber(Token &token, Value &decoded); 1645 | bool decodeString(Token &token); 1646 | bool decodeString(Token &token, std::string &decoded); 1647 | bool decodeDouble(Token &token); 1648 | bool decodeDouble(Token &token, Value &decoded); 1649 | bool decodeUnicodeCodePoint(Token &token, 1650 | Location ¤t, 1651 | Location end, 1652 | unsigned int &unicode); 1653 | bool decodeUnicodeEscapeSequence(Token &token, 1654 | Location ¤t, 1655 | Location end, 1656 | unsigned int &unicode); 1657 | bool addError(const std::string &message, Token &token, Location extra = 0); 1658 | bool recoverFromError(TokenType skipUntilToken); 1659 | bool addErrorAndRecover(const std::string &message, 1660 | Token &token, 1661 | TokenType skipUntilToken); 1662 | void skipUntilSpace(); 1663 | Value ¤tValue(); 1664 | Char getNextChar(); 1665 | void 1666 | getLocationLineAndColumn(Location location, int &line, int &column) const; 1667 | std::string getLocationLineAndColumn(Location location) const; 1668 | void addComment(Location begin, Location end, CommentPlacement placement); 1669 | void skipCommentTokens(Token &token); 1670 | 1671 | typedef std::stack Nodes; 1672 | Nodes nodes_; 1673 | Errors errors_; 1674 | std::string document_; 1675 | Location begin_; 1676 | Location end_; 1677 | Location current_; 1678 | Location lastValueEnd_; 1679 | Value *lastValue_; 1680 | std::string commentsBefore_; 1681 | Features features_; 1682 | bool collectComments_; 1683 | }; 1684 | 1685 | /** \brief Read from 'sin' into 'root'. 1686 | 1687 | Always keep comments from the input JSON. 1688 | 1689 | This can be used to read a file into a particular sub-object. 1690 | For example: 1691 | \code 1692 | Json::Value root; 1693 | cin >> root["dir"]["file"]; 1694 | cout << root; 1695 | \endcode 1696 | Result: 1697 | \verbatim 1698 | { 1699 | "dir": { 1700 | "file": { 1701 | // The input stream JSON would be nested here. 1702 | } 1703 | } 1704 | } 1705 | \endverbatim 1706 | \throw std::exception on parse error. 1707 | \see Json::operator<<() 1708 | */ 1709 | JSON_API std::istream &operator>>(std::istream &, Value &); 1710 | 1711 | } // namespace Json 1712 | 1713 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1714 | #pragma warning(pop) 1715 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1716 | 1717 | #endif // CPPTL_JSON_READER_H_INCLUDED 1718 | 1719 | // ////////////////////////////////////////////////////////////////////// 1720 | // End of content of file: include/json/reader.h 1721 | // ////////////////////////////////////////////////////////////////////// 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | 1728 | // ////////////////////////////////////////////////////////////////////// 1729 | // Beginning of content of file: include/json/writer.h 1730 | // ////////////////////////////////////////////////////////////////////// 1731 | 1732 | // Copyright 2007-2010 Baptiste Lepilleur 1733 | // Distributed under MIT license, or public domain if desired and 1734 | // recognized in your jurisdiction. 1735 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1736 | 1737 | #ifndef JSON_WRITER_H_INCLUDED 1738 | #define JSON_WRITER_H_INCLUDED 1739 | 1740 | #if !defined(JSON_IS_AMALGAMATION) 1741 | #include "value.h" 1742 | #endif // if !defined(JSON_IS_AMALGAMATION) 1743 | #include 1744 | #include 1745 | 1746 | // Disable warning C4251: : needs to have dll-interface to 1747 | // be used by... 1748 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1749 | #pragma warning(push) 1750 | #pragma warning(disable : 4251) 1751 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1752 | 1753 | namespace Json { 1754 | 1755 | class Value; 1756 | 1757 | /** \brief Abstract class for writers. 1758 | */ 1759 | class JSON_API Writer { 1760 | public: 1761 | virtual ~Writer(); 1762 | 1763 | virtual std::string write(const Value &root) = 0; 1764 | }; 1765 | 1766 | /** \brief Outputs a Value in JSON format 1767 | *without formatting (not human friendly). 1768 | * 1769 | * The JSON document is written in a single line. It is not intended for 'human' 1770 | *consumption, 1771 | * but may be usefull to support feature such as RPC where bandwith is limited. 1772 | * \sa Reader, Value 1773 | */ 1774 | class JSON_API FastWriter : public Writer { 1775 | public: 1776 | FastWriter(); 1777 | virtual ~FastWriter() {} 1778 | 1779 | void enableYAMLCompatibility(); 1780 | 1781 | /** \brief Drop the "null" string from the writer's output for nullValues. 1782 | * Strictly speaking, this is not valid JSON. But when the output is being 1783 | * fed to a browser's Javascript, it makes for smaller output and the 1784 | * browser can handle the output just fine. 1785 | */ 1786 | void dropNullPlaceholders(); 1787 | 1788 | public: // overridden from Writer 1789 | virtual std::string write(const Value &root); 1790 | 1791 | private: 1792 | void writeValue(const Value &value); 1793 | 1794 | std::string document_; 1795 | bool yamlCompatiblityEnabled_; 1796 | bool dropNullPlaceholders_; 1797 | }; 1798 | 1799 | /** \brief Writes a Value in JSON format in a 1800 | *human friendly way. 1801 | * 1802 | * The rules for line break and indent are as follow: 1803 | * - Object value: 1804 | * - if empty then print {} without indent and line break 1805 | * - if not empty the print '{', line break & indent, print one value per 1806 | *line 1807 | * and then unindent and line break and print '}'. 1808 | * - Array value: 1809 | * - if empty then print [] without indent and line break 1810 | * - if the array contains no object value, empty array or some other value 1811 | *types, 1812 | * and all the values fit on one lines, then print the array on a single 1813 | *line. 1814 | * - otherwise, it the values do not fit on one line, or the array contains 1815 | * object or non empty array, then print one value per line. 1816 | * 1817 | * If the Value have comments then they are outputed according to their 1818 | *#CommentPlacement. 1819 | * 1820 | * \sa Reader, Value, Value::setComment() 1821 | */ 1822 | class JSON_API StyledWriter : public Writer { 1823 | public: 1824 | StyledWriter(); 1825 | virtual ~StyledWriter() {} 1826 | 1827 | public: // overridden from Writer 1828 | /** \brief Serialize a Value in JSON format. 1829 | * \param root Value to serialize. 1830 | * \return String containing the JSON document that represents the root value. 1831 | */ 1832 | virtual std::string write(const Value &root); 1833 | 1834 | private: 1835 | void writeValue(const Value &value); 1836 | void writeArrayValue(const Value &value); 1837 | bool isMultineArray(const Value &value); 1838 | void pushValue(const std::string &value); 1839 | void writeIndent(); 1840 | void writeWithIndent(const std::string &value); 1841 | void indent(); 1842 | void unindent(); 1843 | void writeCommentBeforeValue(const Value &root); 1844 | void writeCommentAfterValueOnSameLine(const Value &root); 1845 | bool hasCommentForValue(const Value &value); 1846 | static std::string normalizeEOL(const std::string &text); 1847 | 1848 | typedef std::vector ChildValues; 1849 | 1850 | ChildValues childValues_; 1851 | std::string document_; 1852 | std::string indentString_; 1853 | int rightMargin_; 1854 | int indentSize_; 1855 | bool addChildValues_; 1856 | }; 1857 | 1858 | /** \brief Writes a Value in JSON format in a 1859 | human friendly way, 1860 | to a stream rather than to a string. 1861 | * 1862 | * The rules for line break and indent are as follow: 1863 | * - Object value: 1864 | * - if empty then print {} without indent and line break 1865 | * - if not empty the print '{', line break & indent, print one value per 1866 | line 1867 | * and then unindent and line break and print '}'. 1868 | * - Array value: 1869 | * - if empty then print [] without indent and line break 1870 | * - if the array contains no object value, empty array or some other value 1871 | types, 1872 | * and all the values fit on one lines, then print the array on a single 1873 | line. 1874 | * - otherwise, it the values do not fit on one line, or the array contains 1875 | * object or non empty array, then print one value per line. 1876 | * 1877 | * If the Value have comments then they are outputed according to their 1878 | #CommentPlacement. 1879 | * 1880 | * \param indentation Each level will be indented by this amount extra. 1881 | * \sa Reader, Value, Value::setComment() 1882 | */ 1883 | class JSON_API StyledStreamWriter { 1884 | public: 1885 | StyledStreamWriter(std::string indentation = "\t"); 1886 | ~StyledStreamWriter() {} 1887 | 1888 | public: 1889 | /** \brief Serialize a Value in JSON format. 1890 | * \param out Stream to write to. (Can be ostringstream, e.g.) 1891 | * \param root Value to serialize. 1892 | * \note There is no point in deriving from Writer, since write() should not 1893 | * return a value. 1894 | */ 1895 | void write(std::ostream &out, const Value &root); 1896 | 1897 | private: 1898 | void writeValue(const Value &value); 1899 | void writeArrayValue(const Value &value); 1900 | bool isMultineArray(const Value &value); 1901 | void pushValue(const std::string &value); 1902 | void writeIndent(); 1903 | void writeWithIndent(const std::string &value); 1904 | void indent(); 1905 | void unindent(); 1906 | void writeCommentBeforeValue(const Value &root); 1907 | void writeCommentAfterValueOnSameLine(const Value &root); 1908 | bool hasCommentForValue(const Value &value); 1909 | static std::string normalizeEOL(const std::string &text); 1910 | 1911 | typedef std::vector ChildValues; 1912 | 1913 | ChildValues childValues_; 1914 | std::ostream *document_; 1915 | std::string indentString_; 1916 | int rightMargin_; 1917 | std::string indentation_; 1918 | bool addChildValues_; 1919 | }; 1920 | 1921 | #if defined(JSON_HAS_INT64) 1922 | std::string JSON_API valueToString(Int value); 1923 | std::string JSON_API valueToString(UInt value); 1924 | #endif // if defined(JSON_HAS_INT64) 1925 | std::string JSON_API valueToString(LargestInt value); 1926 | std::string JSON_API valueToString(LargestUInt value); 1927 | std::string JSON_API valueToString(double value); 1928 | std::string JSON_API valueToString(bool value); 1929 | std::string JSON_API valueToQuotedString(const char *value); 1930 | 1931 | /// \brief Output using the StyledStreamWriter. 1932 | /// \see Json::operator>>() 1933 | JSON_API std::ostream &operator<<(std::ostream &, const Value &root); 1934 | 1935 | } // namespace Json 1936 | 1937 | #if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1938 | #pragma warning(pop) 1939 | #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) 1940 | 1941 | #endif // JSON_WRITER_H_INCLUDED 1942 | 1943 | // ////////////////////////////////////////////////////////////////////// 1944 | // End of content of file: include/json/writer.h 1945 | // ////////////////////////////////////////////////////////////////////// 1946 | 1947 | 1948 | 1949 | 1950 | 1951 | 1952 | // ////////////////////////////////////////////////////////////////////// 1953 | // Beginning of content of file: include/json/assertions.h 1954 | // ////////////////////////////////////////////////////////////////////// 1955 | 1956 | // Copyright 2007-2010 Baptiste Lepilleur 1957 | // Distributed under MIT license, or public domain if desired and 1958 | // recognized in your jurisdiction. 1959 | // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 1960 | 1961 | #ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED 1962 | #define CPPTL_JSON_ASSERTIONS_H_INCLUDED 1963 | 1964 | #include 1965 | 1966 | #if !defined(JSON_IS_AMALGAMATION) 1967 | #include 1968 | #endif // if !defined(JSON_IS_AMALGAMATION) 1969 | 1970 | #if JSON_USE_EXCEPTION 1971 | #include 1972 | #define JSON_ASSERT(condition) \ 1973 | assert(condition); // @todo <= change this into an exception throw 1974 | #define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message); 1975 | #else // JSON_USE_EXCEPTION 1976 | #define JSON_ASSERT(condition) assert(condition); 1977 | 1978 | // The call to assert() will show the failure message in debug builds. In 1979 | // release bugs we write to invalid memory in order to crash hard, so that a 1980 | // debugger or crash reporter gets the chance to take over. We still call exit() 1981 | // afterward in order to tell the compiler that this macro doesn't return. 1982 | #define JSON_FAIL_MESSAGE(message) \ 1983 | { \ 1984 | assert(false &&message); \ 1985 | strcpy(reinterpret_cast(666), message); \ 1986 | exit(123); \ 1987 | } 1988 | 1989 | #endif 1990 | 1991 | #define JSON_ASSERT_MESSAGE(condition, message) \ 1992 | if (!(condition)) { \ 1993 | JSON_FAIL_MESSAGE(message) \ 1994 | } 1995 | 1996 | #endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED 1997 | 1998 | // ////////////////////////////////////////////////////////////////////// 1999 | // End of content of file: include/json/assertions.h 2000 | // ////////////////////////////////////////////////////////////////////// 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | #endif //ifndef JSON_AMALGATED_H_INCLUDED 2007 | -------------------------------------------------------------------------------- /tests/test_server.cpp: -------------------------------------------------------------------------------- 1 | #if 0 2 | #define BOOST_TEST_MODULE example 3 | #include 4 | #include 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "json/json.h" 15 | 16 | using namespace boost::asio::ip; 17 | 18 | #define HTTP_404_TEMPLATE "\n" \ 19 | "404 Not Found\n" \ 20 | "

Not Found

\n" \ 21 | "

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

\n" 22 | 23 | 24 | #define HTTP_405_TEMPLATE "\n" \ 25 | "405 Method Not Allowed\n" \ 26 | "

Method Not Allowed

\n" \ 27 | "

The method is not allowed for the requested URL.

\n" \ 28 | 29 | struct http_request_handler 30 | { 31 | typedef basic_http_connection connection; 32 | Json::Value get_json_data(const connection::pointer & ptr) 33 | { 34 | Json::Value result; 35 | result["url"] = ptr->get_request_url(); 36 | Json::Value headers(Json::objectValue); 37 | std::ostringstream oss; 38 | for (connection::headers_type::const_iterator it = ptr->get_headers().begin(), end = ptr->get_headers().end(); 39 | it != end; ++it) 40 | { 41 | headers[it->first] = it->second; 42 | } 43 | result["headers"] = headers; 44 | result["origin"] = boost::lexical_cast(ptr->get_socket().local_endpoint().address()); 45 | return result; 46 | } 47 | void operator()(const connection::pointer & ptr) 48 | { 49 | std::cout << "Request URL: " << ptr->get_request_url() << std::endl; 50 | if (ptr->get_request_url() == "/get") 51 | { 52 | if (ptr->get_request_method() != HTTP_GET) 53 | { 54 | ptr->send_response(405, "Method Not Allowed"); 55 | return; 56 | } 57 | std::cout << "Request handler" << std::endl; 58 | Json::Value result = get_json_data(ptr); 59 | ptr->send_response(200, result.toStyledString()); 60 | return; 61 | } 62 | if (ptr->get_request_url() == "/post") 63 | { 64 | if (ptr->get_request_method() != HTTP_POST) 65 | { 66 | ptr->send_response(405, "Method Not Allowed"); 67 | return; 68 | } 69 | Json::Value result = get_json_data(ptr); 70 | result["data"] = ptr->get_request_body(); 71 | ptr->send_response(200, result.toStyledString()); 72 | return; 73 | } 74 | ptr->send_response(404, HTTP_404_TEMPLATE); 75 | } 76 | }; 77 | 78 | struct F 79 | { 80 | F() 81 | : io_service() 82 | , server(io_service, tcp::endpoint(tcp::v4(), 0)) 83 | , client(boost::asio::use_service(io_service)) 84 | { 85 | std::ostringstream oss; 86 | oss << "http://127.0.0.1:" << server.get_acceptor().local_endpoint().port(); 87 | base_url = oss.str(); 88 | } 89 | ~F() 90 | { 91 | } 92 | boost::asio::io_service io_service; 93 | typedef http_server server_type; 94 | server_type server; 95 | http_client & client; 96 | std::string base_url; 97 | }; 98 | 99 | struct client_body_handler 100 | { 101 | typedef void result_type; 102 | std::string & str_; 103 | client_body_handler(std::string & str) 104 | : str_(str) 105 | { 106 | } 107 | void operator()(const boost::system::error_code & ec, const boost::asio::const_buffer & buffer) 108 | { 109 | const char * data = boost::asio::buffer_cast(buffer); 110 | std::size_t size = boost::asio::buffer_size(buffer); 111 | std::string chunk(data, data + size); 112 | std::cout << "chunk[" << chunk << "]" << std::endl; 113 | str_ += chunk; 114 | } 115 | }; 116 | 117 | struct client_done_handler 118 | { 119 | typedef void result_type; 120 | F::server_type & server_; 121 | boost::system::error_code & ec_; 122 | client_done_handler(F::server_type & server, boost::system::error_code & ec) 123 | : server_(server) 124 | , ec_(ec) 125 | { 126 | } 127 | void operator()(const boost::system::error_code & ec) 128 | { 129 | std::cout << "done handler " << ec.message() << std::endl; 130 | ec_ = ec; 131 | server_.stop_accept(); 132 | } 133 | }; 134 | 135 | typedef http_client_connection< 136 | http_client::protocol_type, 137 | client_body_handler, 138 | client_done_handler> client_connection; 139 | 140 | int 141 | main(int argc, char * argv[]) 142 | { 143 | try 144 | { 145 | typedef http_server server_type; 146 | boost::asio::io_service io_service; 147 | server_type srv(io_service, tcp::endpoint(tcp::v4(), 0)); 148 | Json::Value message; 149 | message["type"] = "listen"; 150 | message["data"]["port"] = srv.get_acceptor().local_endpoint().address().to_string(); 151 | message["data"]["port"] = srv.get_acceptor().local_endpoint().port(); 152 | std::string json_data = Json::FastWriter().write(message); 153 | std::fprintf(stdout, "%s", json_data.c_str()); 154 | std::fflush(stdout); 155 | io_service.run(); 156 | } 157 | catch (std::exception & e) 158 | { 159 | Json::Value message; 160 | message["type"] = "exception"; 161 | message["data"]["what"] = e.what(); 162 | std::cout << Json::FastWriter().write(message); 163 | return 1; 164 | } 165 | } 166 | 167 | #if 0 168 | 169 | BOOST_FIXTURE_TEST_SUITE( s, F ) 170 | 171 | BOOST_AUTO_TEST_CASE( test_get1 ) 172 | { 173 | std::string data; 174 | boost::system::error_code ec; 175 | client_body_handler body(data); 176 | client_done_handler done(server, ec); 177 | client_connection::pointer connection = client.create_request(base_url + "/get", body, done); 178 | connection->start(); 179 | io_service.run(); 180 | Json::Reader reader; 181 | Json::Value json_data; 182 | BOOST_REQUIRE(reader.parse(data, json_data)); 183 | BOOST_REQUIRE_EQUAL("/get", json_data["url"].asString()); 184 | BOOST_REQUIRE_EQUAL("127.0.0.1", json_data["origin"].asString()); 185 | BOOST_REQUIRE_EQUAL(200, connection->get_status_code()); 186 | BOOST_REQUIRE_EQUAL("OK", connection->get_status()); 187 | } 188 | 189 | struct scoped_thread 190 | { 191 | boost::thread & thread_; 192 | scoped_thread(boost::thread & thread) 193 | : thread_(thread) 194 | { 195 | } 196 | ~scoped_thread() 197 | { 198 | thread_.join(); 199 | } 200 | }; 201 | 202 | BOOST_AUTO_TEST_CASE( test_multiple_get_requests ) 203 | { 204 | // Checks if server cleans up properly internal structures 205 | // after two correct requests were processed. 206 | std::ostringstream data; 207 | data << "GET /get HTTP/1.1\r\n"; 208 | data << "Header1: Value1\r\n"; 209 | data << "Host: " << server.get_acceptor().local_endpoint() << "\r\n"; 210 | data << "Accept: */*\r\n"; 211 | data << "Referer: \r\n\r\n"; 212 | 213 | data << "GET /get HTTP/1.1\r\n"; 214 | data << "Header2: Value2\r\n"; 215 | data << "Host: " << server.get_acceptor().local_endpoint() << "\r\n"; 216 | data << "Accept: */*\r\n"; 217 | data << "Referer: \r\n\r\n"; 218 | 219 | boost::thread th(boost::bind(&boost::asio::io_service::run, &io_service)); 220 | 221 | boost::asio::ip::tcp::socket client(io_service); 222 | client.connect(server.get_acceptor().local_endpoint()); 223 | std::size_t bytes_written = boost::asio::write(client, boost::asio::buffer(data.str())); 224 | std::cout << "sent " << bytes_written << std::endl; 225 | BOOST_REQUIRE_EQUAL(bytes_written, data.str().size()); 226 | std::vector values; 227 | for (int i = 1; i <= 2; ++i) 228 | { 229 | boost::asio::streambuf response_buffer; 230 | std::size_t received_bytes = boost::asio::read_until(client, response_buffer, std::string("}")); 231 | const char * ptr = boost::asio::buffer_cast(response_buffer.data()); 232 | std::string response1(ptr, ptr + received_bytes); 233 | std::string json_str = response1.substr(response1.find_first_of('{')); 234 | json_str += "}"; 235 | Json::Reader reader; 236 | Json::Value value; 237 | BOOST_REQUIRE(reader.parse(json_str, value)); 238 | values.push_back(value); 239 | } 240 | io_service.stop(); 241 | th.join(); 242 | BOOST_REQUIRE(values[0]["headers"].isMember("Header1")); 243 | BOOST_REQUIRE(!values[0]["headers"].isMember("Header2")); 244 | BOOST_REQUIRE_EQUAL("Value1", values[0]["headers"]["Header1"].asString()); 245 | 246 | BOOST_REQUIRE(values[1]["headers"].isMember("Header2")); 247 | BOOST_REQUIRE(!values[1]["headers"].isMember("Header1")); 248 | BOOST_REQUIRE_EQUAL("Value2", values[1]["headers"]["Header2"].asString()); 249 | } 250 | 251 | BOOST_AUTO_TEST_SUITE_END() 252 | 253 | #endif -------------------------------------------------------------------------------- /tests/test_server.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Integration tests for the HTTP server. Each of test case runs 3 | against real server that implements few features that outputs 4 | various request data to JSON. 5 | ''' 6 | import sys 7 | import signal 8 | import unittest 9 | import subprocess 10 | import json 11 | import urllib2 12 | 13 | _, TEST_SERVER_EXE = sys.argv 14 | 15 | 16 | class BetterHTTPErrorProcessor(urllib2.BaseHandler): 17 | 18 | def http_error_405(self, request, response, code, msg, hdrs): 19 | return response 20 | 21 | 22 | class Response(object): 23 | 24 | def __init__(self, request): 25 | self.request = request 26 | self.data = None 27 | 28 | @property 29 | def url(self): 30 | return self.request.geturl() 31 | 32 | @property 33 | def response_code(self): 34 | return self.request.getcode() 35 | 36 | @property 37 | def json(self): 38 | if self.data is None: 39 | self.data = self.request.read() 40 | return json.loads(self.data) 41 | 42 | @property 43 | def content(self): 44 | if self.data is None: 45 | self.data = self.request.read() 46 | return self.data 47 | 48 | 49 | def json_request(url, data=None, headers={}): 50 | request = urllib2.Request(url, data=data) 51 | for k, v in headers.items(): 52 | request.add_header(k, v) 53 | r = urllib2.urlopen(request) 54 | return Response(r) 55 | 56 | 57 | class ServerTestCase(unittest.TestCase): 58 | 59 | @classmethod 60 | def setUpClass(cls): 61 | opener = urllib2.build_opener(BetterHTTPErrorProcessor) 62 | urllib2.install_opener(opener) 63 | 64 | def setUp(self): 65 | '''Start server instance 66 | ''' 67 | self.process = subprocess.Popen([TEST_SERVER_EXE], 68 | stdout=subprocess.PIPE) 69 | b = self.process.stdout.readline() 70 | json_data = json.loads(b) 71 | self.assertEqual(json_data['type'], 'listen') 72 | self.url = 'http://127.0.0.1:{0}'.format(json_data['data']['port']) 73 | 74 | def tearDown(self): 75 | self.process.send_signal(signal.SIGTERM) 76 | self.process.wait() 77 | 78 | def test_get(self): 79 | r = json_request(self.url + '/get') 80 | self.assertEqual(r.url, self.url + '/get') 81 | self.assertEqual(r.response_code, 200) 82 | self.assertEqual(r.json['url'], '/get') 83 | 84 | def test_get_with_post(self): 85 | r = json_request(self.url + '/get', data='Hello world') 86 | self.assertEqual(r.url, self.url + '/get') 87 | self.assertEqual(r.response_code, 405) 88 | self.assertIn('Method Not Allowed', r.content) 89 | 90 | def test_post(self): 91 | r = json_request(self.url + '/post', data='Hello world') 92 | self.assertEqual(r.url, self.url + '/post') 93 | self.assertEqual(r.response_code, 200) 94 | self.assertEqual(r.json['url'], '/post') 95 | self.assertEqual(r.json['data'], 'Hello world') 96 | 97 | if __name__ == '__main__': 98 | unittest.main(argv=sys.argv[:1]) 99 | -------------------------------------------------------------------------------- /tests/test_url_parser.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MODULE url_parser 2 | #include 3 | #include 4 | #include 5 | 6 | BOOST_AUTO_TEST_CASE( test_parse_correct_url ) 7 | { 8 | url_parser url("http://www.github.com"); 9 | } 10 | 11 | BOOST_AUTO_TEST_CASE( test_parse_and_validate_memory ) 12 | { 13 | BOOST_REQUIRE_EQUAL(url_parser("http://www.github.com").get_schema(), "http"); 14 | } 15 | 16 | BOOST_AUTO_TEST_CASE( test_parse_incorrect_url ) 17 | { 18 | BOOST_REQUIRE_THROW(url_parser("http:///"), std::runtime_error); 19 | } 20 | 21 | struct F 22 | { 23 | url_parser url; 24 | F() 25 | : url("http://www.github.com:666/query?key=foo&value=bar") 26 | {} 27 | ~F() 28 | {} 29 | }; 30 | 31 | BOOST_FIXTURE_TEST_SUITE( s, F ) 32 | 33 | BOOST_AUTO_TEST_CASE (test_correct_schema) 34 | { 35 | BOOST_REQUIRE_EQUAL(url.get_schema(), "http"); 36 | } 37 | 38 | BOOST_AUTO_TEST_CASE (test_correct_host) 39 | { 40 | BOOST_REQUIRE_EQUAL(url.get_host(), "www.github.com"); 41 | } 42 | 43 | BOOST_AUTO_TEST_CASE (test_correct_port) 44 | { 45 | BOOST_REQUIRE_EQUAL(url.get_port(), 666); 46 | } 47 | 48 | BOOST_AUTO_TEST_CASE (test_correct_path) 49 | { 50 | BOOST_REQUIRE_EQUAL(url.get_path(), "/query"); 51 | } 52 | 53 | BOOST_AUTO_TEST_CASE (test_correct_query) 54 | { 55 | BOOST_REQUIRE_EQUAL(url.get_query(), "key=foo&value=bar"); 56 | } 57 | 58 | BOOST_AUTO_TEST_CASE (test_correct_fragment) 59 | { 60 | BOOST_REQUIRE_EQUAL(url.get_fragment(), ""); 61 | } 62 | 63 | BOOST_AUTO_TEST_CASE (test_correct_userinfo) 64 | { 65 | BOOST_REQUIRE_EQUAL(url.get_userinfo(), ""); 66 | } 67 | 68 | BOOST_AUTO_TEST_SUITE_END() 69 | --------------------------------------------------------------------------------