spectrum2/3rdparty/cpprestsdk/include/cpprest/details/http_server_asio.h
2015-11-19 15:19:14 +01:00

202 lines
6 KiB
C++

/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif
#include <boost/asio.hpp>
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#include <boost/bind.hpp>
#include <set>
#include "pplx/threadpool.h"
#include "cpprest/details/http_server.h"
namespace web
{
namespace http
{
namespace experimental
{
namespace listener
{
class http_linux_server;
namespace details
{
struct linux_request_context : web::http::details::_http_server_context
{
linux_request_context(){}
pplx::task_completion_event<void> m_response_completed;
private:
linux_request_context(const linux_request_context&);
linux_request_context& operator=(const linux_request_context&);
};
class hostport_listener;
class connection
{
private:
std::unique_ptr<boost::asio::ip::tcp::socket> m_socket;
boost::asio::streambuf m_request_buf;
boost::asio::streambuf m_response_buf;
http_linux_server* m_p_server;
hostport_listener* m_p_parent;
http_request m_request;
size_t m_read, m_write;
size_t m_read_size, m_write_size;
bool m_close;
bool m_chunked;
std::atomic<int> m_refs; // track how many threads are still referring to this
public:
connection(std::unique_ptr<boost::asio::ip::tcp::socket> socket, http_linux_server* server, hostport_listener* parent)
: m_socket(std::move(socket))
, m_request_buf()
, m_response_buf()
, m_p_server(server)
, m_p_parent(parent)
, m_refs(1)
{
start_request_response();
}
connection(const connection&) = delete;
connection& operator=(const connection&) = delete;
void close();
private:
void start_request_response();
void handle_http_line(const boost::system::error_code& ec);
void handle_headers();
void handle_body(const boost::system::error_code& ec);
void handle_chunked_header(const boost::system::error_code& ec);
void handle_chunked_body(const boost::system::error_code& ec, int toWrite);
void dispatch_request_to_listener();
void do_response(bool bad_request);
template <typename ReadHandler>
void async_read_until_buffersize(size_t size, const ReadHandler &handler);
void async_process_response(http_response response);
void cancel_sending_response_with_error(const http_response &response, const std::exception_ptr &);
void handle_headers_written(const http_response &response, const boost::system::error_code& ec);
void handle_write_large_response(const http_response &response, const boost::system::error_code& ec);
void handle_write_chunked_response(const http_response &response, const boost::system::error_code& ec);
void handle_response_written(const http_response &response, const boost::system::error_code& ec);
void finish_request_response();
};
class hostport_listener
{
private:
friend class connection;
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_acceptor;
std::map<std::string, web::http::experimental::listener::details::http_listener_impl* > m_listeners;
pplx::extensibility::reader_writer_lock_t m_listeners_lock;
pplx::extensibility::recursive_lock_t m_connections_lock;
pplx::extensibility::event_t m_all_connections_complete;
std::set<connection*> m_connections;
http_linux_server* m_p_server;
std::string m_host;
std::string m_port;
public:
hostport_listener(http_linux_server* server, const std::string& hostport)
: m_acceptor()
, m_listeners()
, m_listeners_lock()
, m_connections_lock()
, m_connections()
, m_p_server(server)
{
m_all_connections_complete.set();
std::istringstream hostport_in(hostport);
hostport_in.imbue(std::locale::classic());
std::getline(hostport_in, m_host, ':');
std::getline(hostport_in, m_port);
}
~hostport_listener()
{
stop();
}
void start();
void stop();
void add_listener(const std::string& path, web::http::experimental::listener::details::http_listener_impl* listener);
void remove_listener(const std::string& path, web::http::experimental::listener::details::http_listener_impl* listener);
private:
void on_accept(boost::asio::ip::tcp::socket* socket, const boost::system::error_code& ec);
};
}
struct iequal_to
{
bool operator()(const std::string& left, const std::string& right) const
{
return boost::ilexicographical_compare(left, right);
}
};
class http_linux_server : public web::http::experimental::details::http_server
{
private:
friend class http::experimental::listener::details::connection;
pplx::extensibility::reader_writer_lock_t m_listeners_lock;
std::map<std::string, std::unique_ptr<details::hostport_listener>, iequal_to> m_listeners;
std::unordered_map<details::http_listener_impl *, std::unique_ptr<pplx::extensibility::reader_writer_lock_t>> m_registered_listeners;
bool m_started;
public:
http_linux_server()
: m_listeners_lock()
, m_listeners()
, m_started(false)
{}
~http_linux_server()
{
stop();
}
virtual pplx::task<void> start();
virtual pplx::task<void> stop();
virtual pplx::task<void> register_listener(web::http::experimental::listener::details::http_listener_impl* listener);
virtual pplx::task<void> unregister_listener(web::http::experimental::listener::details::http_listener_impl* listener);
pplx::task<void> respond(http::http_response response);
};
}}}}