Move files in src/ include/
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "connection.hpp"
|
||||
#include "request_handler.hpp"
|
||||
#include "request_parser.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/iostreams/filtering_stream.hpp>
|
||||
#include <boost/iostreams/filter/gzip.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
Connection::Connection(boost::asio::io_service &io_service, RequestHandler &handler)
|
||||
: strand(io_service), TCP_socket(io_service), request_handler(handler)
|
||||
{
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket &Connection::socket() { return TCP_socket; }
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void Connection::start()
|
||||
{
|
||||
TCP_socket.async_read_some(
|
||||
boost::asio::buffer(incoming_data_buffer),
|
||||
strand.wrap(boost::bind(&Connection::handle_read, this->shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred)));
|
||||
}
|
||||
|
||||
void Connection::handle_read(const boost::system::error_code &error, std::size_t bytes_transferred)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// no error detected, let's parse the request
|
||||
compression_type compression_type(no_compression);
|
||||
osrm::tribool result;
|
||||
std::tie(result, compression_type) =
|
||||
request_parser.parse(current_request, incoming_data_buffer.data(),
|
||||
incoming_data_buffer.data() + bytes_transferred);
|
||||
|
||||
// the request has been parsed
|
||||
if (result == osrm::tribool::yes)
|
||||
{
|
||||
current_request.endpoint = TCP_socket.remote_endpoint().address();
|
||||
request_handler.handle_request(current_request, current_reply);
|
||||
|
||||
// Header compression_header;
|
||||
std::vector<boost::asio::const_buffer> output_buffer;
|
||||
|
||||
// compress the result w/ gzip/deflate if requested
|
||||
switch (compression_type)
|
||||
{
|
||||
case deflate_rfc1951:
|
||||
// use deflate for compression
|
||||
current_reply.headers.insert(current_reply.headers.begin(),
|
||||
{"Content-Encoding", "deflate"});
|
||||
compressed_output = compress_buffers(current_reply.content, compression_type);
|
||||
current_reply.set_size(static_cast<unsigned>(compressed_output.size()));
|
||||
output_buffer = current_reply.headers_to_buffers();
|
||||
output_buffer.push_back(boost::asio::buffer(compressed_output));
|
||||
break;
|
||||
case gzip_rfc1952:
|
||||
// use gzip for compression
|
||||
current_reply.headers.insert(current_reply.headers.begin(),
|
||||
{"Content-Encoding", "gzip"});
|
||||
compressed_output = compress_buffers(current_reply.content, compression_type);
|
||||
current_reply.set_size(static_cast<unsigned>(compressed_output.size()));
|
||||
output_buffer = current_reply.headers_to_buffers();
|
||||
output_buffer.push_back(boost::asio::buffer(compressed_output));
|
||||
break;
|
||||
case no_compression:
|
||||
// don't use any compression
|
||||
current_reply.set_uncompressed_size();
|
||||
output_buffer = current_reply.to_buffers();
|
||||
break;
|
||||
}
|
||||
// write result to stream
|
||||
boost::asio::async_write(
|
||||
TCP_socket, output_buffer,
|
||||
strand.wrap(boost::bind(&Connection::handle_write, this->shared_from_this(),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else if (result == osrm::tribool::no)
|
||||
{ // request is not parseable
|
||||
current_reply = reply::stock_reply(reply::bad_request);
|
||||
|
||||
boost::asio::async_write(
|
||||
TCP_socket, current_reply.to_buffers(),
|
||||
strand.wrap(boost::bind(&Connection::handle_write, this->shared_from_this(),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't have a result yet, so continue reading
|
||||
TCP_socket.async_read_some(
|
||||
boost::asio::buffer(incoming_data_buffer),
|
||||
strand.wrap(boost::bind(&Connection::handle_read, this->shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle completion of a write operation.
|
||||
void Connection::handle_write(const boost::system::error_code &error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// Initiate graceful connection closure.
|
||||
boost::system::error_code ignore_error;
|
||||
TCP_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_error);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char> Connection::compress_buffers(const std::vector<char> &uncompressed_data,
|
||||
const compression_type compression_type)
|
||||
{
|
||||
boost::iostreams::gzip_params compression_parameters;
|
||||
|
||||
// there's a trade-off between speed and size. speed wins
|
||||
compression_parameters.level = boost::iostreams::zlib::best_speed;
|
||||
// check which compression flavor is used
|
||||
if (deflate_rfc1951 == compression_type)
|
||||
{
|
||||
compression_parameters.noheader = true;
|
||||
}
|
||||
|
||||
std::vector<char> compressed_data;
|
||||
// plug data into boost's compression stream
|
||||
boost::iostreams::filtering_ostream gzip_stream;
|
||||
gzip_stream.push(boost::iostreams::gzip_compressor(compression_parameters));
|
||||
gzip_stream.push(boost::iostreams::back_inserter(compressed_data));
|
||||
gzip_stream.write(&uncompressed_data[0], uncompressed_data.size());
|
||||
boost::iostreams::close(gzip_stream);
|
||||
|
||||
return compressed_data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "reply.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
const char ok_html[] = "";
|
||||
const char bad_request_html[] = "{\"status\": 400,\"status_message\":\"Bad Request\"}";
|
||||
const char internal_server_error_html[] =
|
||||
"{\"status\": 500,\"status_message\":\"Internal Server Error\"}";
|
||||
const char seperators[] = {':', ' '};
|
||||
const char crlf[] = {'\r', '\n'};
|
||||
const std::string http_ok_string = "HTTP/1.0 200 OK\r\n";
|
||||
const std::string http_bad_request_string = "HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string http_internal_server_error_string = "HTTP/1.0 500 Internal Server Error\r\n";
|
||||
|
||||
void reply::set_size(const std::size_t size)
|
||||
{
|
||||
for (header &h : headers)
|
||||
{
|
||||
if ("Content-Length" == h.name)
|
||||
{
|
||||
h.value = std::to_string(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reply::set_uncompressed_size() { set_size(content.size()); }
|
||||
|
||||
std::vector<boost::asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
buffers.push_back(status_to_buffer(status));
|
||||
for (const header &h : headers)
|
||||
{
|
||||
buffers.push_back(boost::asio::buffer(h.name));
|
||||
buffers.push_back(boost::asio::buffer(seperators));
|
||||
buffers.push_back(boost::asio::buffer(h.value));
|
||||
buffers.push_back(boost::asio::buffer(crlf));
|
||||
}
|
||||
buffers.push_back(boost::asio::buffer(crlf));
|
||||
buffers.push_back(boost::asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
std::vector<boost::asio::const_buffer> reply::headers_to_buffers()
|
||||
{
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
buffers.push_back(status_to_buffer(status));
|
||||
for (const header ¤t_header : headers)
|
||||
{
|
||||
buffers.push_back(boost::asio::buffer(current_header.name));
|
||||
buffers.push_back(boost::asio::buffer(seperators));
|
||||
buffers.push_back(boost::asio::buffer(current_header.value));
|
||||
buffers.push_back(boost::asio::buffer(crlf));
|
||||
}
|
||||
buffers.push_back(boost::asio::buffer(crlf));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
reply reply::stock_reply(const reply::status_type status)
|
||||
{
|
||||
reply reply;
|
||||
reply.status = status;
|
||||
reply.content.clear();
|
||||
|
||||
const std::string status_string = reply.status_to_string(status);
|
||||
reply.content.insert(reply.content.end(), status_string.begin(), status_string.end());
|
||||
reply.headers.emplace_back("Access-Control-Allow-Origin", "*");
|
||||
reply.headers.emplace_back("Content-Length", std::to_string(reply.content.size()));
|
||||
reply.headers.emplace_back("Content-Type", "text/html");
|
||||
return reply;
|
||||
}
|
||||
|
||||
std::string reply::status_to_string(const reply::status_type status)
|
||||
{
|
||||
if (reply::ok == status)
|
||||
{
|
||||
return ok_html;
|
||||
}
|
||||
if (reply::bad_request == status)
|
||||
{
|
||||
return bad_request_html;
|
||||
}
|
||||
return internal_server_error_html;
|
||||
}
|
||||
|
||||
boost::asio::const_buffer reply::status_to_buffer(const reply::status_type status)
|
||||
{
|
||||
if (reply::ok == status)
|
||||
{
|
||||
return boost::asio::buffer(http_ok_string);
|
||||
}
|
||||
if (reply::internal_server_error == status)
|
||||
{
|
||||
return boost::asio::buffer(http_internal_server_error_string);
|
||||
}
|
||||
return boost::asio::buffer(http_bad_request_string);
|
||||
}
|
||||
|
||||
reply::reply() : status(ok) {}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "request_handler.hpp"
|
||||
|
||||
#include "api_grammar.hpp"
|
||||
#include "http/reply.hpp"
|
||||
#include "http/request.hpp"
|
||||
|
||||
#include "../util/json_renderer.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
#include "../util/string_util.hpp"
|
||||
#include "../util/xml_renderer.hpp"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/route_parameters.hpp>
|
||||
#include <osrm/json_container.hpp>
|
||||
#include <osrm/osrm.hpp>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
RequestHandler::RequestHandler() : routing_machine(nullptr) {}
|
||||
|
||||
void RequestHandler::handle_request(const http::request ¤t_request,
|
||||
http::reply ¤t_reply)
|
||||
{
|
||||
osrm::json::Object json_result;
|
||||
|
||||
// parse command
|
||||
try
|
||||
{
|
||||
std::string request_string;
|
||||
URIDecode(current_request.uri, request_string);
|
||||
|
||||
// deactivated as GCC apparently does not implement that, not even in 4.9
|
||||
// std::time_t t = std::time(nullptr);
|
||||
// SimpleLogger().Write() << std::put_time(std::localtime(&t), "%m-%d-%Y %H:%M:%S") <<
|
||||
// " " << current_request.endpoint.to_string() << " " <<
|
||||
// current_request.referrer << ( 0 == current_request.referrer.length() ? "- " :" ") <<
|
||||
// current_request.agent << ( 0 == current_request.agent.length() ? "- " :" ") <<
|
||||
// request;
|
||||
|
||||
time_t ltime;
|
||||
struct tm *time_stamp;
|
||||
|
||||
ltime = time(nullptr);
|
||||
time_stamp = localtime(<ime);
|
||||
|
||||
// log timestamp
|
||||
SimpleLogger().Write() << (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday
|
||||
<< "-" << (time_stamp->tm_mon + 1 < 10 ? "0" : "")
|
||||
<< (time_stamp->tm_mon + 1) << "-" << 1900 + time_stamp->tm_year
|
||||
<< " " << (time_stamp->tm_hour < 10 ? "0" : "")
|
||||
<< time_stamp->tm_hour << ":" << (time_stamp->tm_min < 10 ? "0" : "")
|
||||
<< time_stamp->tm_min << ":" << (time_stamp->tm_sec < 10 ? "0" : "")
|
||||
<< time_stamp->tm_sec << " " << current_request.endpoint.to_string()
|
||||
<< " " << current_request.referrer
|
||||
<< (0 == current_request.referrer.length() ? "- " : " ")
|
||||
<< current_request.agent
|
||||
<< (0 == current_request.agent.length() ? "- " : " ")
|
||||
<< request_string;
|
||||
|
||||
RouteParameters route_parameters;
|
||||
APIGrammarParser api_parser(&route_parameters);
|
||||
|
||||
auto api_iterator = request_string.begin();
|
||||
const bool result =
|
||||
boost::spirit::qi::parse(api_iterator, request_string.end(), api_parser);
|
||||
|
||||
// check if the was an error with the request
|
||||
if (result && api_iterator == request_string.end())
|
||||
{
|
||||
// parsing done, lets call the right plugin to handle the request
|
||||
BOOST_ASSERT_MSG(routing_machine != nullptr, "pointer not init'ed");
|
||||
|
||||
if (!route_parameters.jsonp_parameter.empty())
|
||||
{ // prepend response with jsonp parameter
|
||||
const std::string json_p = (route_parameters.jsonp_parameter + "(");
|
||||
current_reply.content.insert(current_reply.content.end(), json_p.begin(), json_p.end());
|
||||
}
|
||||
|
||||
const int return_code = routing_machine->RunQuery(route_parameters, json_result);
|
||||
json_result.values["status"] = return_code;
|
||||
// 4xx bad request return code
|
||||
if (return_code / 100 == 4)
|
||||
{
|
||||
current_reply.status = http::reply::bad_request;
|
||||
current_reply.content.clear();
|
||||
route_parameters.output_format.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2xx valid request
|
||||
BOOST_ASSERT(return_code / 100 == 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto position = std::distance(request_string.begin(), api_iterator);
|
||||
|
||||
current_reply.status = http::reply::bad_request;
|
||||
json_result.values["status"] = http::reply::bad_request;
|
||||
json_result.values["status_message"] = "Query string malformed close to position " + std::to_string(position);
|
||||
}
|
||||
|
||||
current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*");
|
||||
current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET");
|
||||
current_reply.headers.emplace_back("Access-Control-Allow-Headers",
|
||||
"X-Requested-With, Content-Type");
|
||||
|
||||
// set headers
|
||||
current_reply.headers.emplace_back("Content-Length",
|
||||
std::to_string(current_reply.content.size()));
|
||||
if ("gpx" == route_parameters.output_format)
|
||||
{ // gpx file
|
||||
osrm::json::gpx_render(current_reply.content, json_result.values["route"]);
|
||||
current_reply.headers.emplace_back("Content-Type",
|
||||
"application/gpx+xml; charset=UTF-8");
|
||||
current_reply.headers.emplace_back("Content-Disposition",
|
||||
"attachment; filename=\"route.gpx\"");
|
||||
}
|
||||
else if (route_parameters.jsonp_parameter.empty())
|
||||
{ // json file
|
||||
osrm::json::render(current_reply.content, json_result);
|
||||
current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8");
|
||||
current_reply.headers.emplace_back("Content-Disposition",
|
||||
"inline; filename=\"response.json\"");
|
||||
}
|
||||
else
|
||||
{ // jsonp
|
||||
osrm::json::render(current_reply.content, json_result);
|
||||
current_reply.headers.emplace_back("Content-Type", "text/javascript; charset=UTF-8");
|
||||
current_reply.headers.emplace_back("Content-Disposition",
|
||||
"inline; filename=\"response.js\"");
|
||||
}
|
||||
if (!route_parameters.jsonp_parameter.empty())
|
||||
{ // append brace to jsonp response
|
||||
current_reply.content.push_back(')');
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
current_reply = http::reply::stock_reply(http::reply::internal_server_error);;
|
||||
SimpleLogger().Write(logWARNING) << "[server error] code: " << e.what()
|
||||
<< ", uri: " << current_request.uri;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestHandler::RegisterRoutingMachine(OSRM *osrm) { routing_machine = osrm; }
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "request_parser.hpp"
|
||||
|
||||
#include "http/compression_type.hpp"
|
||||
#include "http/header.hpp"
|
||||
#include "http/request.hpp"
|
||||
|
||||
#include "../data_structures/tribool.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http
|
||||
{
|
||||
|
||||
RequestParser::RequestParser()
|
||||
: state(internal_state::method_start), current_header({"", ""}),
|
||||
selected_compression(no_compression), is_post_header(false),
|
||||
content_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::tuple<osrm::tribool, compression_type>
|
||||
RequestParser::parse(request ¤t_request, char *begin, char *end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
osrm::tribool result = consume(current_request, *begin++);
|
||||
if (result != osrm::tribool::indeterminate)
|
||||
{
|
||||
return std::make_tuple(result, selected_compression);
|
||||
}
|
||||
}
|
||||
osrm::tribool result = osrm::tribool::indeterminate;
|
||||
|
||||
if(state == internal_state::post_request && content_length <= 0)
|
||||
{
|
||||
result = osrm::tribool::yes;
|
||||
}
|
||||
return std::make_tuple(result, selected_compression);
|
||||
}
|
||||
|
||||
osrm::tribool RequestParser::consume(request ¤t_request, const char input)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case internal_state::method_start:
|
||||
if (!is_char(input) || is_CTL(input) || is_special(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
if(input == 'P')
|
||||
{
|
||||
state = internal_state::post_O;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
state = internal_state::method;
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::post_O:
|
||||
if(input == 'O')
|
||||
{
|
||||
state = internal_state::post_S;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::post_S:
|
||||
if(input == 'S')
|
||||
{
|
||||
state = internal_state::post_T;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::post_T:
|
||||
if(input == 'T')
|
||||
{
|
||||
is_post_header = true;
|
||||
state = internal_state::method;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::post_request:
|
||||
current_request.uri.push_back(input);
|
||||
--content_length;
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::method:
|
||||
if (input == ' ')
|
||||
{
|
||||
state = internal_state::uri;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (!is_char(input) || is_CTL(input) || is_special(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::uri_start:
|
||||
if (is_CTL(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
state = internal_state::uri;
|
||||
current_request.uri.push_back(input);
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::uri:
|
||||
if (input == ' ')
|
||||
{
|
||||
state = internal_state::http_version_h;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (is_CTL(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
current_request.uri.push_back(input);
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::http_version_h:
|
||||
if (input == 'H')
|
||||
{
|
||||
state = internal_state::http_version_t_1;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_t_1:
|
||||
if (input == 'T')
|
||||
{
|
||||
state = internal_state::http_version_t_2;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_t_2:
|
||||
if (input == 'T')
|
||||
{
|
||||
state = internal_state::http_version_p;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_p:
|
||||
if (input == 'P')
|
||||
{
|
||||
state = internal_state::http_version_slash;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_slash:
|
||||
if (input == '/')
|
||||
{
|
||||
state = internal_state::http_version_major_start;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_major_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
state = internal_state::http_version_major;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_major:
|
||||
if (input == '.')
|
||||
{
|
||||
state = internal_state::http_version_minor_start;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (is_digit(input))
|
||||
{
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_minor_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
state = internal_state::http_version_minor;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::http_version_minor:
|
||||
if (input == '\r')
|
||||
{
|
||||
state = internal_state::expecting_newline_1;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (is_digit(input))
|
||||
{
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::expecting_newline_1:
|
||||
if (input == '\n')
|
||||
{
|
||||
state = internal_state::header_line_start;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::header_line_start:
|
||||
if (boost::iequals(current_header.name, "Accept-Encoding"))
|
||||
{
|
||||
/* giving gzip precedence over deflate */
|
||||
if (boost::icontains(current_header.value, "deflate"))
|
||||
{
|
||||
selected_compression = deflate_rfc1951;
|
||||
}
|
||||
if (boost::icontains(current_header.value, "gzip"))
|
||||
{
|
||||
selected_compression = gzip_rfc1952;
|
||||
}
|
||||
}
|
||||
|
||||
if (boost::iequals(current_header.name, "Referer"))
|
||||
{
|
||||
current_request.referrer = current_header.value;
|
||||
}
|
||||
|
||||
if (boost::iequals(current_header.name, "User-Agent"))
|
||||
{
|
||||
current_request.agent = current_header.value;
|
||||
}
|
||||
if (boost::iequals(current_header.name, "Content-Length"))
|
||||
{
|
||||
try
|
||||
{
|
||||
content_length = std::stoi(current_header.value);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// Ignore the header if the parameter isn't an int
|
||||
}
|
||||
}
|
||||
if (boost::iequals(current_header.name, "Content-Type"))
|
||||
{
|
||||
if (!boost::icontains(current_header.value, "application/x-www-form-urlencoded"))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
}
|
||||
|
||||
if (input == '\r')
|
||||
{
|
||||
state = internal_state::expecting_newline_3;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (!is_char(input) || is_CTL(input) || is_special(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
state = internal_state::header_name;
|
||||
current_header.clear();
|
||||
current_header.name.push_back(input);
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::header_lws:
|
||||
if (input == '\r')
|
||||
{
|
||||
state = internal_state::expecting_newline_2;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (input == ' ' || input == '\t')
|
||||
{
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (is_CTL(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
state = internal_state::header_value;
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::header_name:
|
||||
if (input == ':')
|
||||
{
|
||||
state = internal_state::space_before_header_value;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (!is_char(input) || is_CTL(input) || is_special(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
current_header.name.push_back(input);
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::space_before_header_value:
|
||||
if (input == ' ')
|
||||
{
|
||||
state = internal_state::header_value;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::header_value:
|
||||
if (input == '\r')
|
||||
{
|
||||
state = internal_state::expecting_newline_2;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
if (is_CTL(input))
|
||||
{
|
||||
return osrm::tribool::no;
|
||||
}
|
||||
current_header.value.push_back(input);
|
||||
return osrm::tribool::indeterminate;
|
||||
case internal_state::expecting_newline_2:
|
||||
if (input == '\n')
|
||||
{
|
||||
state = internal_state::header_line_start;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
case internal_state::expecting_newline_3:
|
||||
if (input == '\n')
|
||||
{
|
||||
if (is_post_header)
|
||||
{
|
||||
if (content_length > 0)
|
||||
{
|
||||
current_request.uri.push_back('?');
|
||||
}
|
||||
state = internal_state::post_request;
|
||||
return osrm::tribool::indeterminate;
|
||||
}
|
||||
return osrm::tribool::yes;
|
||||
}
|
||||
return osrm::tribool::no;
|
||||
default: // should never be reached
|
||||
return input == '\n' ? osrm::tribool::yes : osrm::tribool::no;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequestParser::is_char(const int character) const
|
||||
{
|
||||
return character >= 0 && character <= 127;
|
||||
}
|
||||
|
||||
bool RequestParser::is_CTL(const int character) const
|
||||
{
|
||||
return (character >= 0 && character <= 31) || (character == 127);
|
||||
}
|
||||
|
||||
bool RequestParser::is_special(const int character) const
|
||||
{
|
||||
switch (character)
|
||||
{
|
||||
case '(':
|
||||
case ')':
|
||||
case '<':
|
||||
case '>':
|
||||
case '@':
|
||||
case ',':
|
||||
case ';':
|
||||
case ':':
|
||||
case '\\':
|
||||
case '"':
|
||||
case '/':
|
||||
case '[':
|
||||
case ']':
|
||||
case '?':
|
||||
case '=':
|
||||
case '{':
|
||||
case '}':
|
||||
case ' ':
|
||||
case '\t':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequestParser::is_digit(const int character) const
|
||||
{
|
||||
return character >= '0' && character <= '9';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user