Lesson 35 - Get Compute Auth Token Working
This commit is contained in:
251
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/Makefile.am
vendored
Normal file
251
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/Makefile.am
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
if SEPARATE_COMPILATION
|
||||
noinst_LIBRARIES = libasio.a
|
||||
libasio_a_SOURCES = ../../asio.cpp
|
||||
if HAVE_OPENSSL
|
||||
libasio_a_SOURCES += ../../asio_ssl.cpp
|
||||
endif
|
||||
LDADD = libasio.a
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
allocation/server \
|
||||
buffers/reference_counted \
|
||||
chat/chat_client \
|
||||
chat/chat_server \
|
||||
echo/async_tcp_echo_server \
|
||||
echo/async_udp_echo_server \
|
||||
echo/blocking_tcp_echo_client \
|
||||
echo/blocking_tcp_echo_server \
|
||||
echo/blocking_udp_echo_client \
|
||||
echo/blocking_udp_echo_server \
|
||||
http/client/async_client \
|
||||
http/client/sync_client \
|
||||
http/server/http_server \
|
||||
http/server2/http_server \
|
||||
http/server3/http_server \
|
||||
http/server4/http_server \
|
||||
icmp/ping \
|
||||
invocation/prioritised_handlers \
|
||||
iostreams/daytime_client \
|
||||
iostreams/daytime_server \
|
||||
iostreams/http_client \
|
||||
multicast/receiver \
|
||||
multicast/sender \
|
||||
nonblocking/third_party_lib \
|
||||
porthopper/client \
|
||||
porthopper/server \
|
||||
services/daytime_client \
|
||||
socks4/sync_client \
|
||||
timeouts/async_tcp_client \
|
||||
timeouts/blocking_tcp_client \
|
||||
timeouts/blocking_token_tcp_client \
|
||||
timeouts/blocking_udp_client \
|
||||
timeouts/server \
|
||||
timers/time_t_timer \
|
||||
tutorial/timer1/timer \
|
||||
tutorial/timer2/timer \
|
||||
tutorial/timer3/timer \
|
||||
tutorial/timer4/timer \
|
||||
tutorial/timer5/timer \
|
||||
tutorial/daytime1/client \
|
||||
tutorial/daytime2/server \
|
||||
tutorial/daytime3/server \
|
||||
tutorial/daytime4/client \
|
||||
tutorial/daytime5/server \
|
||||
tutorial/daytime6/server \
|
||||
tutorial/daytime7/server
|
||||
|
||||
if !WINDOWS_TARGET
|
||||
noinst_PROGRAMS += \
|
||||
chat/posix_chat_client \
|
||||
fork/daemon \
|
||||
fork/process_per_connection \
|
||||
local/connect_pair \
|
||||
local/iostream_client \
|
||||
local/stream_server \
|
||||
local/stream_client
|
||||
endif
|
||||
|
||||
if WINDOWS_TARGET
|
||||
noinst_PROGRAMS += \
|
||||
windows/transmit_file
|
||||
endif
|
||||
|
||||
if HAVE_OPENSSL
|
||||
noinst_PROGRAMS += \
|
||||
ssl/client \
|
||||
ssl/server
|
||||
endif
|
||||
|
||||
if HAVE_BOOST_COROUTINE
|
||||
noinst_PROGRAMS += \
|
||||
spawn/echo_server \
|
||||
spawn/parallel_grep
|
||||
endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
chat/chat_message.hpp \
|
||||
services/basic_logger.hpp \
|
||||
services/logger.hpp \
|
||||
services/logger_service.hpp
|
||||
|
||||
AM_CXXFLAGS = -I$(srcdir)/../../../include
|
||||
|
||||
allocation_server_SOURCES = allocation/server.cpp
|
||||
buffers_reference_counted_SOURCES = buffers/reference_counted.cpp
|
||||
chat_chat_client_SOURCES = chat/chat_client.cpp
|
||||
chat_chat_server_SOURCES = chat/chat_server.cpp
|
||||
echo_async_tcp_echo_server_SOURCES = echo/async_tcp_echo_server.cpp
|
||||
echo_async_udp_echo_server_SOURCES = echo/async_udp_echo_server.cpp
|
||||
echo_blocking_tcp_echo_client_SOURCES = echo/blocking_tcp_echo_client.cpp
|
||||
echo_blocking_tcp_echo_server_SOURCES = echo/blocking_tcp_echo_server.cpp
|
||||
echo_blocking_udp_echo_client_SOURCES = echo/blocking_udp_echo_client.cpp
|
||||
echo_blocking_udp_echo_server_SOURCES = echo/blocking_udp_echo_server.cpp
|
||||
http_client_async_client_SOURCES = http/client/async_client.cpp
|
||||
http_client_sync_client_SOURCES = http/client/sync_client.cpp
|
||||
http_server_http_server_SOURCES = \
|
||||
http/server/connection.cpp \
|
||||
http/server/connection_manager.cpp \
|
||||
http/server/main.cpp \
|
||||
http/server/mime_types.cpp \
|
||||
http/server/reply.cpp \
|
||||
http/server/request_handler.cpp \
|
||||
http/server/request_parser.cpp \
|
||||
http/server/server.cpp
|
||||
http_server2_http_server_SOURCES = \
|
||||
http/server2/connection.cpp \
|
||||
http/server2/io_context_pool.cpp \
|
||||
http/server2/main.cpp \
|
||||
http/server2/mime_types.cpp \
|
||||
http/server2/reply.cpp \
|
||||
http/server2/request_handler.cpp \
|
||||
http/server2/request_parser.cpp \
|
||||
http/server2/server.cpp
|
||||
http_server3_http_server_SOURCES = \
|
||||
http/server3/connection.cpp \
|
||||
http/server3/main.cpp \
|
||||
http/server3/mime_types.cpp \
|
||||
http/server3/reply.cpp \
|
||||
http/server3/request_handler.cpp \
|
||||
http/server3/request_parser.cpp \
|
||||
http/server3/server.cpp
|
||||
http_server4_http_server_SOURCES = \
|
||||
http/server4/file_handler.cpp \
|
||||
http/server4/main.cpp \
|
||||
http/server4/mime_types.cpp \
|
||||
http/server4/reply.cpp \
|
||||
http/server4/request_parser.cpp \
|
||||
http/server4/server.cpp
|
||||
icmp_ping_SOURCES = icmp/ping.cpp
|
||||
invocation_prioritised_handlers_SOURCES = invocation/prioritised_handlers.cpp
|
||||
iostreams_daytime_client_SOURCES = iostreams/daytime_client.cpp
|
||||
iostreams_daytime_server_SOURCES = iostreams/daytime_server.cpp
|
||||
iostreams_http_client_SOURCES = iostreams/http_client.cpp
|
||||
multicast_receiver_SOURCES = multicast/receiver.cpp
|
||||
multicast_sender_SOURCES = multicast/sender.cpp
|
||||
nonblocking_third_party_lib_SOURCES = nonblocking/third_party_lib.cpp
|
||||
porthopper_client_SOURCES = porthopper/client.cpp
|
||||
porthopper_server_SOURCES = porthopper/server.cpp
|
||||
services_daytime_client_SOURCES = \
|
||||
services/daytime_client.cpp \
|
||||
services/logger_service.cpp
|
||||
socks4_sync_client_SOURCES = socks4/sync_client.cpp
|
||||
timeouts_async_tcp_client_SOURCES = timeouts/async_tcp_client.cpp
|
||||
timeouts_blocking_tcp_client_SOURCES = timeouts/blocking_tcp_client.cpp
|
||||
timeouts_blocking_token_tcp_client_SOURCES = timeouts/blocking_token_tcp_client.cpp
|
||||
timeouts_blocking_udp_client_SOURCES = timeouts/blocking_udp_client.cpp
|
||||
timeouts_server_SOURCES = timeouts/server.cpp
|
||||
timers_time_t_timer_SOURCES = timers/time_t_timer.cpp
|
||||
tutorial_timer1_timer_SOURCES = tutorial/timer1/timer.cpp
|
||||
tutorial_timer2_timer_SOURCES = tutorial/timer2/timer.cpp
|
||||
tutorial_timer3_timer_SOURCES = tutorial/timer3/timer.cpp
|
||||
tutorial_timer4_timer_SOURCES = tutorial/timer4/timer.cpp
|
||||
tutorial_timer5_timer_SOURCES = tutorial/timer5/timer.cpp
|
||||
tutorial_daytime1_client_SOURCES = tutorial/daytime1/client.cpp
|
||||
tutorial_daytime2_server_SOURCES = tutorial/daytime2/server.cpp
|
||||
tutorial_daytime3_server_SOURCES = tutorial/daytime3/server.cpp
|
||||
tutorial_daytime4_client_SOURCES = tutorial/daytime4/client.cpp
|
||||
tutorial_daytime5_server_SOURCES = tutorial/daytime5/server.cpp
|
||||
tutorial_daytime6_server_SOURCES = tutorial/daytime6/server.cpp
|
||||
tutorial_daytime7_server_SOURCES = tutorial/daytime7/server.cpp
|
||||
|
||||
if !WINDOWS_TARGET
|
||||
chat_posix_chat_client_SOURCES = chat/posix_chat_client.cpp
|
||||
fork_daemon_SOURCES = fork/daemon.cpp
|
||||
fork_process_per_connection_SOURCES = fork/process_per_connection.cpp
|
||||
local_connect_pair_SOURCES = local/connect_pair.cpp
|
||||
local_iostream_client_SOURCES = local/iostream_client.cpp
|
||||
local_stream_server_SOURCES = local/stream_server.cpp
|
||||
local_stream_client_SOURCES = local/stream_client.cpp
|
||||
endif
|
||||
|
||||
if WINDOWS_TARGET
|
||||
windows_transmit_file_SOURCES = windows/transmit_file.cpp
|
||||
endif
|
||||
|
||||
if HAVE_OPENSSL
|
||||
ssl_client_SOURCES = ssl/client.cpp
|
||||
ssl_server_SOURCES = ssl/server.cpp
|
||||
endif
|
||||
|
||||
if HAVE_BOOST_COROUTINE
|
||||
spawn_echo_server_SOURCES = spawn/echo_server.cpp
|
||||
spawn_echo_server_LDADD = $(LDADD) -lboost_coroutine -lboost_context -lboost_thread -lboost_chrono -lboost_system
|
||||
spawn_parallel_grep_SOURCES = spawn/parallel_grep.cpp
|
||||
spawn_parallel_grep_LDADD = $(LDADD) -lboost_coroutine -lboost_context -lboost_thread -lboost_chrono -lboost_system
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
http/server/connection.hpp \
|
||||
http/server/connection_manager.hpp \
|
||||
http/server/header.hpp \
|
||||
http/server/mime_types.hpp \
|
||||
http/server/reply.hpp \
|
||||
http/server/request.hpp \
|
||||
http/server/request_handler.hpp \
|
||||
http/server/request_parser.hpp \
|
||||
http/server/server.hpp \
|
||||
http/server2/connection.hpp \
|
||||
http/server2/io_context_pool.hpp \
|
||||
http/server2/header.hpp \
|
||||
http/server2/mime_types.hpp \
|
||||
http/server2/reply.hpp \
|
||||
http/server2/request.hpp \
|
||||
http/server2/request_handler.hpp \
|
||||
http/server2/request_parser.hpp \
|
||||
http/server2/server.hpp \
|
||||
http/server3/connection.hpp \
|
||||
http/server3/header.hpp \
|
||||
http/server3/mime_types.hpp \
|
||||
http/server3/reply.hpp \
|
||||
http/server3/request.hpp \
|
||||
http/server3/request_handler.hpp \
|
||||
http/server3/request_parser.hpp \
|
||||
http/server3/server.hpp \
|
||||
http/server4/file_handler.hpp \
|
||||
http/server4/header.hpp \
|
||||
http/server4/mime_types.hpp \
|
||||
http/server4/reply.hpp \
|
||||
http/server4/request.hpp \
|
||||
http/server4/request_parser.hpp \
|
||||
http/server4/server.hpp \
|
||||
icmp/icmp_header.hpp \
|
||||
icmp/ipv4_header.hpp \
|
||||
porthopper/protocol.hpp \
|
||||
serialization/client.cpp \
|
||||
serialization/server.cpp \
|
||||
serialization/connection.hpp \
|
||||
serialization/stock.hpp \
|
||||
services/basic_logger.hpp \
|
||||
services/logger.hpp \
|
||||
services/logger_service.hpp \
|
||||
socks4/socks4.hpp \
|
||||
ssl/README \
|
||||
ssl/ca.pem \
|
||||
ssl/server.pem \
|
||||
ssl/dh2048.pem
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
$(srcdir)/Makefile.in
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
server
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,285 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/aligned_storage.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// Class to manage the memory to be used for handler-based custom allocation.
|
||||
// It contains a single block of memory which may be returned for allocation
|
||||
// requests. If the memory is in use when an allocation request is made, the
|
||||
// allocator delegates allocation to the global heap.
|
||||
class handler_memory
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
handler_memory()
|
||||
: in_use_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void* allocate(std::size_t size)
|
||||
{
|
||||
if (!in_use_ && size < storage_.size)
|
||||
{
|
||||
in_use_ = true;
|
||||
return storage_.address();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ::operator new(size);
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(void* pointer)
|
||||
{
|
||||
if (pointer == storage_.address())
|
||||
{
|
||||
in_use_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Storage space used for handler-based custom memory allocation.
|
||||
boost::aligned_storage<1024> storage_;
|
||||
|
||||
// Whether the handler-based custom allocation storage has been used.
|
||||
bool in_use_;
|
||||
};
|
||||
|
||||
// The allocator to be associated with the handler objects. This allocator only
|
||||
// needs to satisfy the C++11 minimal allocator requirements, plus rebind when
|
||||
// targeting C++03.
|
||||
template <typename T>
|
||||
class handler_allocator
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
explicit handler_allocator(handler_memory& mem)
|
||||
: memory_(mem)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
handler_allocator(const handler_allocator<U>& other)
|
||||
: memory_(other.memory_)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
typedef handler_allocator<U> other;
|
||||
};
|
||||
|
||||
bool operator==(const handler_allocator& other) const
|
||||
{
|
||||
return &memory_ == &other.memory_;
|
||||
}
|
||||
|
||||
bool operator!=(const handler_allocator& other) const
|
||||
{
|
||||
return &memory_ != &other.memory_;
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n) const
|
||||
{
|
||||
return static_cast<T*>(memory_.allocate(sizeof(T) * n));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t /*n*/) const
|
||||
{
|
||||
return memory_.deallocate(p);
|
||||
}
|
||||
|
||||
//private:
|
||||
// The underlying memory.
|
||||
handler_memory& memory_;
|
||||
};
|
||||
|
||||
// Wrapper class template for handler objects to allow handler memory
|
||||
// allocation to be customised. The allocator_type typedef and get_allocator()
|
||||
// member function are used by the asynchronous operations to obtain the
|
||||
// allocator. Calls to operator() are forwarded to the encapsulated handler.
|
||||
template <typename Handler>
|
||||
class custom_alloc_handler
|
||||
{
|
||||
public:
|
||||
typedef handler_allocator<Handler> allocator_type;
|
||||
|
||||
custom_alloc_handler(handler_memory& m, Handler h)
|
||||
: memory_(m),
|
||||
handler_(h)
|
||||
{
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const
|
||||
{
|
||||
return allocator_type(memory_);
|
||||
}
|
||||
|
||||
template <typename Arg1>
|
||||
void operator()(Arg1 arg1)
|
||||
{
|
||||
handler_(arg1);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2>
|
||||
void operator()(Arg1 arg1, Arg2 arg2)
|
||||
{
|
||||
handler_(arg1, arg2);
|
||||
}
|
||||
|
||||
private:
|
||||
handler_memory& memory_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
// Helper function to wrap a handler object to add custom allocation.
|
||||
template <typename Handler>
|
||||
inline custom_alloc_handler<Handler> make_custom_alloc_handler(
|
||||
handler_memory& m, Handler h)
|
||||
{
|
||||
return custom_alloc_handler<Handler>(m, h);
|
||||
}
|
||||
|
||||
class session
|
||||
: public boost::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(asio::io_context& io_context)
|
||||
: socket_(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
tcp::socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
make_custom_alloc_handler(handler_memory_,
|
||||
boost::bind(&session::handle_read,
|
||||
shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred)));
|
||||
}
|
||||
|
||||
void handle_read(const asio::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(data_, bytes_transferred),
|
||||
make_custom_alloc_handler(handler_memory_,
|
||||
boost::bind(&session::handle_write,
|
||||
shared_from_this(),
|
||||
asio::placeholders::error)));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
make_custom_alloc_handler(handler_memory_,
|
||||
boost::bind(&session::handle_read,
|
||||
shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred)));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// The socket used to communicate with the client.
|
||||
tcp::socket socket_;
|
||||
|
||||
// Buffer used to store data received from the client.
|
||||
boost::array<char, 1024> data_;
|
||||
|
||||
// The memory to use for handler-based custom memory allocation.
|
||||
handler_memory handler_memory_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<session> session_ptr;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: io_context_(io_context),
|
||||
acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
session_ptr new_session(new session(io_context_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void handle_accept(session_ptr new_session,
|
||||
const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
new_session->start();
|
||||
}
|
||||
|
||||
new_session.reset(new session(io_context_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
reference_counted
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// reference_counted.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// A reference-counted non-modifiable buffer class.
|
||||
class shared_const_buffer
|
||||
{
|
||||
public:
|
||||
// Construct from a std::string.
|
||||
explicit shared_const_buffer(const std::string& data)
|
||||
: data_(new std::vector<char>(data.begin(), data.end())),
|
||||
buffer_(asio::buffer(*data_))
|
||||
{
|
||||
}
|
||||
|
||||
// Implement the ConstBufferSequence requirements.
|
||||
typedef asio::const_buffer value_type;
|
||||
typedef const asio::const_buffer* const_iterator;
|
||||
const asio::const_buffer* begin() const { return &buffer_; }
|
||||
const asio::const_buffer* end() const { return &buffer_ + 1; }
|
||||
|
||||
private:
|
||||
boost::shared_ptr<std::vector<char> > data_;
|
||||
asio::const_buffer buffer_;
|
||||
};
|
||||
|
||||
class session
|
||||
: public boost::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(asio::io_context& io_context)
|
||||
: socket_(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
tcp::socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
using namespace std; // For time_t, time and ctime.
|
||||
time_t now = time(0);
|
||||
shared_const_buffer buffer(ctime(&now));
|
||||
asio::async_write(socket_, buffer,
|
||||
boost::bind(&session::handle_write, shared_from_this()));
|
||||
}
|
||||
|
||||
void handle_write()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// The socket used to communicate with the client.
|
||||
tcp::socket socket_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<session> session_ptr;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: io_context_(io_context),
|
||||
acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
session_ptr new_session(new session(io_context_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void handle_accept(session_ptr new_session,
|
||||
const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
new_session->start();
|
||||
}
|
||||
|
||||
new_session.reset(new session(io_context_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: reference_counted <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/chat/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/chat/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
177
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/chat/chat_client.cpp
vendored
Normal file
177
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/chat/chat_client.cpp
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// chat_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::io_context& io_context,
|
||||
const tcp::resolver::results_type& endpoints)
|
||||
: io_context_(io_context),
|
||||
socket_(io_context)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
boost::bind(&chat_client::handle_connect, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void write(const chat_message& msg)
|
||||
{
|
||||
asio::post(io_context_,
|
||||
boost::bind(&chat_client::do_write, this, msg));
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
asio::post(io_context_,
|
||||
boost::bind(&chat_client::do_close, this));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void handle_connect(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
boost::bind(&chat_client::handle_read_header, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_header(const asio::error_code& error)
|
||||
{
|
||||
if (!error && read_msg_.decode_header())
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
boost::bind(&chat_client::handle_read_body, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
do_close();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_body(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout.write(read_msg_.body(), read_msg_.body_length());
|
||||
std::cout << "\n";
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
boost::bind(&chat_client::handle_read_header, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
do_close();
|
||||
}
|
||||
}
|
||||
|
||||
void do_write(chat_message msg)
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
boost::bind(&chat_client::handle_write, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
boost::bind(&chat_client::handle_write, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do_close();
|
||||
}
|
||||
}
|
||||
|
||||
void do_close()
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
tcp::socket socket_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: chat_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]);
|
||||
|
||||
chat_client c(io_context, endpoints);
|
||||
|
||||
asio::thread t(boost::bind(&asio::io_context::run, &io_context));
|
||||
|
||||
char line[chat_message::max_body_length + 1];
|
||||
while (std::cin.getline(line, chat_message::max_body_length + 1))
|
||||
{
|
||||
using namespace std; // For strlen and memcpy.
|
||||
chat_message msg;
|
||||
msg.body_length(strlen(line));
|
||||
memcpy(msg.body(), line, msg.body_length());
|
||||
msg.encode_header();
|
||||
c.write(msg);
|
||||
}
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// chat_message.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef CHAT_MESSAGE_HPP
|
||||
#define CHAT_MESSAGE_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
class chat_message
|
||||
{
|
||||
public:
|
||||
enum { header_length = 4 };
|
||||
enum { max_body_length = 512 };
|
||||
|
||||
chat_message()
|
||||
: body_length_(0)
|
||||
{
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
size_t length() const
|
||||
{
|
||||
return header_length + body_length_;
|
||||
}
|
||||
|
||||
const char* body() const
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
char* body()
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
size_t body_length() const
|
||||
{
|
||||
return body_length_;
|
||||
}
|
||||
|
||||
void body_length(size_t new_length)
|
||||
{
|
||||
body_length_ = new_length;
|
||||
if (body_length_ > max_body_length)
|
||||
body_length_ = max_body_length;
|
||||
}
|
||||
|
||||
bool decode_header()
|
||||
{
|
||||
using namespace std; // For strncat and atoi.
|
||||
char header[header_length + 1] = "";
|
||||
strncat(header, data_, header_length);
|
||||
body_length_ = atoi(header);
|
||||
if (body_length_ > max_body_length)
|
||||
{
|
||||
body_length_ = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encode_header()
|
||||
{
|
||||
using namespace std; // For sprintf and memcpy.
|
||||
char header[header_length + 1] = "";
|
||||
sprintf(header, "%4d", static_cast<int>(body_length_));
|
||||
memcpy(data_, header, header_length);
|
||||
}
|
||||
|
||||
private:
|
||||
char data_[header_length + max_body_length];
|
||||
size_t body_length_;
|
||||
};
|
||||
|
||||
#endif // CHAT_MESSAGE_HPP
|
||||
249
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/chat/chat_server.cpp
vendored
Normal file
249
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/chat/chat_server.cpp
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
//
|
||||
// chat_server.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_participant
|
||||
{
|
||||
public:
|
||||
virtual ~chat_participant() {}
|
||||
virtual void deliver(const chat_message& msg) = 0;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_participant> chat_participant_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_room
|
||||
{
|
||||
public:
|
||||
void join(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.insert(participant);
|
||||
std::for_each(recent_msgs_.begin(), recent_msgs_.end(),
|
||||
boost::bind(&chat_participant::deliver,
|
||||
participant, boost::placeholders::_1));
|
||||
}
|
||||
|
||||
void leave(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.erase(participant);
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
recent_msgs_.push_back(msg);
|
||||
while (recent_msgs_.size() > max_recent_msgs)
|
||||
recent_msgs_.pop_front();
|
||||
|
||||
std::for_each(participants_.begin(), participants_.end(),
|
||||
boost::bind(&chat_participant::deliver,
|
||||
boost::placeholders::_1, boost::ref(msg)));
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<chat_participant_ptr> participants_;
|
||||
enum { max_recent_msgs = 100 };
|
||||
chat_message_queue recent_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_session
|
||||
: public chat_participant,
|
||||
public boost::enable_shared_from_this<chat_session>
|
||||
{
|
||||
public:
|
||||
chat_session(asio::io_context& io_context, chat_room& room)
|
||||
: socket_(io_context),
|
||||
room_(room)
|
||||
{
|
||||
}
|
||||
|
||||
tcp::socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
room_.join(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
boost::bind(
|
||||
&chat_session::handle_read_header, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
boost::bind(&chat_session::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_header(const asio::error_code& error)
|
||||
{
|
||||
if (!error && read_msg_.decode_header())
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
boost::bind(&chat_session::handle_read_body, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_body(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
room_.deliver(read_msg_);
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
boost::bind(&chat_session::handle_read_header, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
boost::bind(&chat_session::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_session> chat_session_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_server
|
||||
{
|
||||
public:
|
||||
chat_server(asio::io_context& io_context,
|
||||
const tcp::endpoint& endpoint)
|
||||
: io_context_(io_context),
|
||||
acceptor_(io_context, endpoint)
|
||||
{
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void start_accept()
|
||||
{
|
||||
chat_session_ptr new_session(new chat_session(io_context_, room_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&chat_server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void handle_accept(chat_session_ptr session,
|
||||
const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
session->start();
|
||||
}
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
tcp::acceptor acceptor_;
|
||||
chat_room room_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_server> chat_server_ptr;
|
||||
typedef std::list<chat_server_ptr> chat_server_list;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cerr << "Usage: chat_server <port> [<port> ...]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
chat_server_list servers;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
using namespace std; // For atoi.
|
||||
tcp::endpoint endpoint(tcp::v4(), atoi(argv[i]));
|
||||
chat_server_ptr server(new chat_server(io_context, endpoint));
|
||||
servers.push_back(server);
|
||||
}
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
//
|
||||
// posix_chat_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
|
||||
|
||||
using asio::ip::tcp;
|
||||
namespace posix = asio::posix;
|
||||
|
||||
class posix_chat_client
|
||||
{
|
||||
public:
|
||||
posix_chat_client(asio::io_context& io_context,
|
||||
const tcp::resolver::results_type& endpoints)
|
||||
: socket_(io_context),
|
||||
input_(io_context, ::dup(STDIN_FILENO)),
|
||||
output_(io_context, ::dup(STDOUT_FILENO)),
|
||||
input_buffer_(chat_message::max_body_length)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
boost::bind(&posix_chat_client::handle_connect, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void handle_connect(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// Read the fixed-length header of the next message from the server.
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
boost::bind(&posix_chat_client::handle_read_header, this,
|
||||
asio::placeholders::error));
|
||||
|
||||
// Read a line of input entered by the user.
|
||||
asio::async_read_until(input_, input_buffer_, '\n',
|
||||
boost::bind(&posix_chat_client::handle_read_input, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_header(const asio::error_code& error)
|
||||
{
|
||||
if (!error && read_msg_.decode_header())
|
||||
{
|
||||
// Read the variable-length body of the message from the server.
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
boost::bind(&posix_chat_client::handle_read_body, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_body(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// Write out the message we just received, terminated by a newline.
|
||||
static char eol[] = { '\n' };
|
||||
boost::array<asio::const_buffer, 2> buffers = {{
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
asio::buffer(eol) }};
|
||||
asio::async_write(output_, buffers,
|
||||
boost::bind(&posix_chat_client::handle_write_output, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write_output(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// Read the fixed-length header of the next message from the server.
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
boost::bind(&posix_chat_client::handle_read_header, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_input(const asio::error_code& error,
|
||||
std::size_t length)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// Write the message (minus the newline) to the server.
|
||||
write_msg_.body_length(length - 1);
|
||||
input_buffer_.sgetn(write_msg_.body(), length - 1);
|
||||
input_buffer_.consume(1); // Remove newline from input.
|
||||
write_msg_.encode_header();
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msg_.data(), write_msg_.length()),
|
||||
boost::bind(&posix_chat_client::handle_write, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else if (error == asio::error::not_found)
|
||||
{
|
||||
// Didn't get a newline. Send whatever we have.
|
||||
write_msg_.body_length(input_buffer_.size());
|
||||
input_buffer_.sgetn(write_msg_.body(), input_buffer_.size());
|
||||
write_msg_.encode_header();
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msg_.data(), write_msg_.length()),
|
||||
boost::bind(&posix_chat_client::handle_write, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
// Read a line of input entered by the user.
|
||||
asio::async_read_until(input_, input_buffer_, '\n',
|
||||
boost::bind(&posix_chat_client::handle_read_input, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
else
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
// Cancel all outstanding asynchronous operations.
|
||||
socket_.close();
|
||||
input_.close();
|
||||
output_.close();
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
posix::stream_descriptor input_;
|
||||
posix::stream_descriptor output_;
|
||||
chat_message read_msg_;
|
||||
chat_message write_msg_;
|
||||
asio::streambuf input_buffer_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: posix_chat_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]);
|
||||
|
||||
posix_chat_client c(io_context, endpoints);
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
|
||||
int main() {}
|
||||
#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/echo/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/echo/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// async_tcp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class session
|
||||
{
|
||||
public:
|
||||
session(asio::io_context& io_context)
|
||||
: socket_(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
tcp::socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_, max_length),
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
private:
|
||||
void handle_read(const asio::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(data_, bytes_transferred),
|
||||
boost::bind(&session::handle_write, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_, max_length),
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
else
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
tcp::socket socket_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: io_context_(io_context),
|
||||
acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
start_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void start_accept()
|
||||
{
|
||||
session* new_session = new session(io_context_);
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void handle_accept(session* new_session,
|
||||
const asio::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
new_session->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete new_session;
|
||||
}
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
asio::io_context& io_context_;
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: async_tcp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// async_udp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: socket_(io_context, udp::endpoint(udp::v4(), port))
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(data_, max_length), sender_endpoint_,
|
||||
boost::bind(&server::handle_receive_from, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void handle_receive_from(const asio::error_code& error,
|
||||
size_t bytes_recvd)
|
||||
{
|
||||
if (!error && bytes_recvd > 0)
|
||||
{
|
||||
socket_.async_send_to(
|
||||
asio::buffer(data_, bytes_recvd), sender_endpoint_,
|
||||
boost::bind(&server::handle_send_to, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(data_, max_length), sender_endpoint_,
|
||||
boost::bind(&server::handle_receive_from, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_send_to(const asio::error_code& /*error*/,
|
||||
size_t /*bytes_sent*/)
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(data_, max_length), sender_endpoint_,
|
||||
boost::bind(&server::handle_receive_from, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
private:
|
||||
udp::socket socket_;
|
||||
udp::endpoint sender_endpoint_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: async_udp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// blocking_tcp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
tcp::resolver::results_type endpoints =
|
||||
resolver.resolve(tcp::v4(), argv[1], argv[2]);
|
||||
|
||||
tcp::socket s(io_context);
|
||||
asio::connect(s, endpoints);
|
||||
|
||||
using namespace std; // For strlen.
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = strlen(request);
|
||||
asio::write(s, asio::buffer(request, request_length));
|
||||
|
||||
char reply[max_length];
|
||||
size_t reply_length = asio::read(s,
|
||||
asio::buffer(reply, request_length));
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// blocking_tcp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/smart_ptr.hpp>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
const int max_length = 1024;
|
||||
|
||||
typedef boost::shared_ptr<tcp::socket> socket_ptr;
|
||||
|
||||
void session(socket_ptr sock)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
char data[max_length];
|
||||
|
||||
asio::error_code error;
|
||||
size_t length = sock->read_some(asio::buffer(data), error);
|
||||
if (error == asio::error::eof)
|
||||
break; // Connection closed cleanly by peer.
|
||||
else if (error)
|
||||
throw asio::system_error(error); // Some other error.
|
||||
|
||||
asio::write(*sock, asio::buffer(data, length));
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception in thread: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void server(asio::io_context& io_context, unsigned short port)
|
||||
{
|
||||
tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));
|
||||
for (;;)
|
||||
{
|
||||
socket_ptr sock(new tcp::socket(io_context));
|
||||
a.accept(*sock);
|
||||
asio::thread t(boost::bind(session, sock));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server(io_context, atoi(argv[1]));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// blocking_udp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: blocking_udp_echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
udp::socket s(io_context, udp::endpoint(udp::v4(), 0));
|
||||
|
||||
udp::resolver resolver(io_context);
|
||||
udp::resolver::results_type endpoints =
|
||||
resolver.resolve(udp::v4(), argv[1], argv[2]);
|
||||
|
||||
using namespace std; // For strlen.
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = strlen(request);
|
||||
s.send_to(asio::buffer(request, request_length), *endpoints.begin());
|
||||
|
||||
char reply[max_length];
|
||||
udp::endpoint sender_endpoint;
|
||||
size_t reply_length = s.receive_from(
|
||||
asio::buffer(reply, max_length), sender_endpoint);
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// blocking_udp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
void server(asio::io_context& io_context, unsigned short port)
|
||||
{
|
||||
udp::socket sock(io_context, udp::endpoint(udp::v4(), port));
|
||||
for (;;)
|
||||
{
|
||||
char data[max_length];
|
||||
udp::endpoint sender_endpoint;
|
||||
size_t length = sock.receive_from(
|
||||
asio::buffer(data, max_length), sender_endpoint);
|
||||
sock.send_to(asio::buffer(data, length), sender_endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: blocking_udp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server(io_context, atoi(argv[1]));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/fork/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/fork/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
daemon
|
||||
process_per_connection
|
||||
190
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/fork/daemon.cpp
vendored
Normal file
190
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/fork/daemon.cpp
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// daemon.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/udp.hpp>
|
||||
#include <asio/signal_set.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
class udp_daytime_server
|
||||
{
|
||||
public:
|
||||
udp_daytime_server(asio::io_context& io_context)
|
||||
: socket_(io_context, udp::endpoint(udp::v4(), 13))
|
||||
{
|
||||
start_receive();
|
||||
}
|
||||
|
||||
private:
|
||||
void start_receive()
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(recv_buffer_), remote_endpoint_,
|
||||
boost::bind(&udp_daytime_server::handle_receive,
|
||||
this, boost::placeholders::_1));
|
||||
}
|
||||
|
||||
void handle_receive(const asio::error_code& ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
using namespace std; // For time_t, time and ctime;
|
||||
time_t now = time(0);
|
||||
std::string message = ctime(&now);
|
||||
|
||||
asio::error_code ignored_ec;
|
||||
socket_.send_to(asio::buffer(message),
|
||||
remote_endpoint_, 0, ignored_ec);
|
||||
}
|
||||
|
||||
start_receive();
|
||||
}
|
||||
|
||||
udp::socket socket_;
|
||||
udp::endpoint remote_endpoint_;
|
||||
boost::array<char, 1> recv_buffer_;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
// Initialise the server before becoming a daemon. If the process is
|
||||
// started from a shell, this means any errors will be reported back to the
|
||||
// user.
|
||||
udp_daytime_server server(io_context);
|
||||
|
||||
// Register signal handlers so that the daemon may be shut down. You may
|
||||
// also want to register for other signals, such as SIGHUP to trigger a
|
||||
// re-read of a configuration file.
|
||||
asio::signal_set signals(io_context, SIGINT, SIGTERM);
|
||||
signals.async_wait(
|
||||
boost::bind(&asio::io_context::stop, &io_context));
|
||||
|
||||
// Inform the io_context that we are about to become a daemon. The
|
||||
// io_context cleans up any internal resources, such as threads, that may
|
||||
// interfere with forking.
|
||||
io_context.notify_fork(asio::io_context::fork_prepare);
|
||||
|
||||
// Fork the process and have the parent exit. If the process was started
|
||||
// from a shell, this returns control to the user. Forking a new process is
|
||||
// also a prerequisite for the subsequent call to setsid().
|
||||
if (pid_t pid = fork())
|
||||
{
|
||||
if (pid > 0)
|
||||
{
|
||||
// We're in the parent process and need to exit.
|
||||
//
|
||||
// When the exit() function is used, the program terminates without
|
||||
// invoking local variables' destructors. Only global variables are
|
||||
// destroyed. As the io_context object is a local variable, this means
|
||||
// we do not have to call:
|
||||
//
|
||||
// io_context.notify_fork(asio::io_context::fork_parent);
|
||||
//
|
||||
// However, this line should be added before each call to exit() if
|
||||
// using a global io_context object. An additional call:
|
||||
//
|
||||
// io_context.notify_fork(asio::io_context::fork_prepare);
|
||||
//
|
||||
// should also precede the second fork().
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "First fork failed: %m");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the process a new session leader. This detaches it from the
|
||||
// terminal.
|
||||
setsid();
|
||||
|
||||
// A process inherits its working directory from its parent. This could be
|
||||
// on a mounted filesystem, which means that the running daemon would
|
||||
// prevent this filesystem from being unmounted. Changing to the root
|
||||
// directory avoids this problem.
|
||||
chdir("/");
|
||||
|
||||
// The file mode creation mask is also inherited from the parent process.
|
||||
// We don't want to restrict the permissions on files created by the
|
||||
// daemon, so the mask is cleared.
|
||||
umask(0);
|
||||
|
||||
// A second fork ensures the process cannot acquire a controlling terminal.
|
||||
if (pid_t pid = fork())
|
||||
{
|
||||
if (pid > 0)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Second fork failed: %m");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the standard streams. This decouples the daemon from the terminal
|
||||
// that started it.
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
|
||||
// We don't want the daemon to have any standard input.
|
||||
if (open("/dev/null", O_RDONLY) < 0)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Send standard output to a log file.
|
||||
const char* output = "/tmp/asio.daemon.out";
|
||||
const int flags = O_WRONLY | O_CREAT | O_APPEND;
|
||||
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
if (open(output, flags, mode) < 0)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Also send standard error to the same log file.
|
||||
if (dup(1) < 0)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Inform the io_context that we have finished becoming a daemon. The
|
||||
// io_context uses this opportunity to create any internal file descriptors
|
||||
// that need to be private to the new process.
|
||||
io_context.notify_fork(asio::io_context::fork_child);
|
||||
|
||||
// The io_context can now be used normally.
|
||||
syslog(LOG_INFO | LOG_USER, "Daemon started");
|
||||
io_context.run();
|
||||
syslog(LOG_INFO | LOG_USER, "Daemon stopped");
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Exception: %s", e.what());
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// process_per_connection.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/signal_set.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, unsigned short port)
|
||||
: io_context_(io_context),
|
||||
signal_(io_context, SIGCHLD),
|
||||
acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
|
||||
socket_(io_context)
|
||||
{
|
||||
start_signal_wait();
|
||||
start_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void start_signal_wait()
|
||||
{
|
||||
signal_.async_wait(boost::bind(&server::handle_signal_wait, this));
|
||||
}
|
||||
|
||||
void handle_signal_wait()
|
||||
{
|
||||
// Only the parent process should check for this signal. We can determine
|
||||
// whether we are in the parent by checking if the acceptor is still open.
|
||||
if (acceptor_.is_open())
|
||||
{
|
||||
// Reap completed child processes so that we don't end up with zombies.
|
||||
int status = 0;
|
||||
while (waitpid(-1, &status, WNOHANG) > 0) {}
|
||||
|
||||
start_signal_wait();
|
||||
}
|
||||
}
|
||||
|
||||
void start_accept()
|
||||
{
|
||||
acceptor_.async_accept(socket_,
|
||||
boost::bind(&server::handle_accept, this, boost::placeholders::_1));
|
||||
}
|
||||
|
||||
void handle_accept(const asio::error_code& ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Inform the io_context that we are about to fork. The io_context cleans
|
||||
// up any internal resources, such as threads, that may interfere with
|
||||
// forking.
|
||||
io_context_.notify_fork(asio::io_context::fork_prepare);
|
||||
|
||||
if (fork() == 0)
|
||||
{
|
||||
// Inform the io_context that the fork is finished and that this is the
|
||||
// child process. The io_context uses this opportunity to create any
|
||||
// internal file descriptors that must be private to the new process.
|
||||
io_context_.notify_fork(asio::io_context::fork_child);
|
||||
|
||||
// The child won't be accepting new connections, so we can close the
|
||||
// acceptor. It remains open in the parent.
|
||||
acceptor_.close();
|
||||
|
||||
// The child process is not interested in processing the SIGCHLD signal.
|
||||
signal_.cancel();
|
||||
|
||||
start_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inform the io_context that the fork is finished (or failed) and that
|
||||
// this is the parent process. The io_context uses this opportunity to
|
||||
// recreate any internal resources that were cleaned up during
|
||||
// preparation for the fork.
|
||||
io_context_.notify_fork(asio::io_context::fork_parent);
|
||||
|
||||
socket_.close();
|
||||
start_accept();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Accept error: " << ec.message() << std::endl;
|
||||
start_accept();
|
||||
}
|
||||
}
|
||||
|
||||
void start_read()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
boost::bind(&server::handle_read, this,
|
||||
boost::placeholders::_1, boost::placeholders::_2));
|
||||
}
|
||||
|
||||
void handle_read(const asio::error_code& ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
start_write(length);
|
||||
}
|
||||
|
||||
void start_write(std::size_t length)
|
||||
{
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
boost::bind(&server::handle_write, this, boost::placeholders::_1));
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& ec)
|
||||
{
|
||||
if (!ec)
|
||||
start_read();
|
||||
}
|
||||
|
||||
asio::io_context& io_context_;
|
||||
asio::signal_set signal_;
|
||||
tcp::acceptor acceptor_;
|
||||
tcp::socket socket_;
|
||||
boost::array<char, 1024> data_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: process_per_connection <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,204 @@
|
||||
//
|
||||
// async_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class client
|
||||
{
|
||||
public:
|
||||
client(asio::io_context& io_context,
|
||||
const std::string& server, const std::string& path)
|
||||
: resolver_(io_context),
|
||||
socket_(io_context)
|
||||
{
|
||||
// Form the request. We specify the "Connection: close" header so that the
|
||||
// server will close the socket after transmitting the response. This will
|
||||
// allow us to treat all data up until the EOF as the content.
|
||||
std::ostream request_stream(&request_);
|
||||
request_stream << "GET " << path << " HTTP/1.0\r\n";
|
||||
request_stream << "Host: " << server << "\r\n";
|
||||
request_stream << "Accept: */*\r\n";
|
||||
request_stream << "Connection: close\r\n\r\n";
|
||||
|
||||
// Start an asynchronous resolve to translate the server and service names
|
||||
// into a list of endpoints.
|
||||
resolver_.async_resolve(server, "http",
|
||||
boost::bind(&client::handle_resolve, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::results));
|
||||
}
|
||||
|
||||
private:
|
||||
void handle_resolve(const asio::error_code& err,
|
||||
const tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
// Attempt a connection to each endpoint in the list until we
|
||||
// successfully establish a connection.
|
||||
asio::async_connect(socket_, endpoints,
|
||||
boost::bind(&client::handle_connect, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << err.message() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void handle_connect(const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
// The connection was successful. Send the request.
|
||||
asio::async_write(socket_, request_,
|
||||
boost::bind(&client::handle_write_request, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << err.message() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write_request(const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
// Read the response status line. The response_ streambuf will
|
||||
// automatically grow to accommodate the entire line. The growth may be
|
||||
// limited by passing a maximum size to the streambuf constructor.
|
||||
asio::async_read_until(socket_, response_, "\r\n",
|
||||
boost::bind(&client::handle_read_status_line, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << err.message() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_status_line(const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
// Check that response is OK.
|
||||
std::istream response_stream(&response_);
|
||||
std::string http_version;
|
||||
response_stream >> http_version;
|
||||
unsigned int status_code;
|
||||
response_stream >> status_code;
|
||||
std::string status_message;
|
||||
std::getline(response_stream, status_message);
|
||||
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
|
||||
{
|
||||
std::cout << "Invalid response\n";
|
||||
return;
|
||||
}
|
||||
if (status_code != 200)
|
||||
{
|
||||
std::cout << "Response returned with status code ";
|
||||
std::cout << status_code << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the response headers, which are terminated by a blank line.
|
||||
asio::async_read_until(socket_, response_, "\r\n\r\n",
|
||||
boost::bind(&client::handle_read_headers, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << err << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_headers(const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
// Process the response headers.
|
||||
std::istream response_stream(&response_);
|
||||
std::string header;
|
||||
while (std::getline(response_stream, header) && header != "\r")
|
||||
std::cout << header << "\n";
|
||||
std::cout << "\n";
|
||||
|
||||
// Write whatever content we already have to output.
|
||||
if (response_.size() > 0)
|
||||
std::cout << &response_;
|
||||
|
||||
// Start reading remaining data until EOF.
|
||||
asio::async_read(socket_, response_,
|
||||
asio::transfer_at_least(1),
|
||||
boost::bind(&client::handle_read_content, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << err << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read_content(const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
// Write all of the data that has been read so far.
|
||||
std::cout << &response_;
|
||||
|
||||
// Continue reading remaining data until EOF.
|
||||
asio::async_read(socket_, response_,
|
||||
asio::transfer_at_least(1),
|
||||
boost::bind(&client::handle_read_content, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else if (err != asio::error::eof)
|
||||
{
|
||||
std::cout << "Error: " << err << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
tcp::resolver resolver_;
|
||||
tcp::socket socket_;
|
||||
asio::streambuf request_;
|
||||
asio::streambuf response_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cout << "Usage: async_client <server> <path>\n";
|
||||
std::cout << "Example:\n";
|
||||
std::cout << " async_client www.boost.org /LICENSE_1_0.txt\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
client c(io_context, argv[1], argv[2]);
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// sync_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cout << "Usage: sync_client <server> <path>\n";
|
||||
std::cout << "Example:\n";
|
||||
std::cout << " sync_client www.boost.org /LICENSE_1_0.txt\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
// Get a list of endpoints corresponding to the server name.
|
||||
tcp::resolver resolver(io_context);
|
||||
tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "http");
|
||||
|
||||
// Try each endpoint until we successfully establish a connection.
|
||||
tcp::socket socket(io_context);
|
||||
asio::connect(socket, endpoints);
|
||||
|
||||
// Form the request. We specify the "Connection: close" header so that the
|
||||
// server will close the socket after transmitting the response. This will
|
||||
// allow us to treat all data up until the EOF as the content.
|
||||
asio::streambuf request;
|
||||
std::ostream request_stream(&request);
|
||||
request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";
|
||||
request_stream << "Host: " << argv[1] << "\r\n";
|
||||
request_stream << "Accept: */*\r\n";
|
||||
request_stream << "Connection: close\r\n\r\n";
|
||||
|
||||
// Send the request.
|
||||
asio::write(socket, request);
|
||||
|
||||
// Read the response status line. The response streambuf will automatically
|
||||
// grow to accommodate the entire line. The growth may be limited by passing
|
||||
// a maximum size to the streambuf constructor.
|
||||
asio::streambuf response;
|
||||
asio::read_until(socket, response, "\r\n");
|
||||
|
||||
// Check that response is OK.
|
||||
std::istream response_stream(&response);
|
||||
std::string http_version;
|
||||
response_stream >> http_version;
|
||||
unsigned int status_code;
|
||||
response_stream >> status_code;
|
||||
std::string status_message;
|
||||
std::getline(response_stream, status_message);
|
||||
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
|
||||
{
|
||||
std::cout << "Invalid response\n";
|
||||
return 1;
|
||||
}
|
||||
if (status_code != 200)
|
||||
{
|
||||
std::cout << "Response returned with status code " << status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read the response headers, which are terminated by a blank line.
|
||||
asio::read_until(socket, response, "\r\n\r\n");
|
||||
|
||||
// Process the response headers.
|
||||
std::string header;
|
||||
while (std::getline(response_stream, header) && header != "\r")
|
||||
std::cout << header << "\n";
|
||||
std::cout << "\n";
|
||||
|
||||
// Write whatever content we already have to output.
|
||||
if (response.size() > 0)
|
||||
std::cout << &response;
|
||||
|
||||
// Read until EOF, writing data to output as we go.
|
||||
asio::error_code error;
|
||||
while (asio::read(socket, response,
|
||||
asio::transfer_at_least(1), error))
|
||||
std::cout << &response;
|
||||
if (error != asio::error::eof)
|
||||
throw asio::system_error(error);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<!--
|
||||
Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
-->
|
||||
|
||||
<html>
|
||||
<body>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the<br/>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- boostinspect:nounlinked -->
|
||||
@@ -0,0 +1,49 @@
|
||||
<!--
|
||||
Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
-->
|
||||
|
||||
<html>
|
||||
<body>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps ove<br/>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- boostinspect:nounlinked -->
|
||||
@@ -0,0 +1,91 @@
|
||||
<!--
|
||||
Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
-->
|
||||
|
||||
<html>
|
||||
<body>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox<br/>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- boostinspect:nounlinked -->
|
||||
@@ -0,0 +1,175 @@
|
||||
<!--
|
||||
Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
-->
|
||||
|
||||
<html>
|
||||
<body>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
The quick brown fox jumps over the lazy dog<br/>
|
||||
....
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- boostinspect:nounlinked -->
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// connection.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "connection.hpp"
|
||||
#include <vector>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "connection_manager.hpp"
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
connection::connection(asio::io_context& io_context,
|
||||
connection_manager& manager, request_handler& handler)
|
||||
: socket_(io_context),
|
||||
connection_manager_(manager),
|
||||
request_handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket& connection::socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void connection::start()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void connection::stop()
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
void connection::handle_read(const asio::error_code& e,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
boost::tribool result;
|
||||
boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
|
||||
request_, buffer_.data(), buffer_.data() + bytes_transferred);
|
||||
|
||||
if (result)
|
||||
{
|
||||
request_handler_.handle_request(request_, reply_);
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else if (!result)
|
||||
{
|
||||
reply_ = reply::stock_reply(reply::bad_request);
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
else if (e != asio::error::operation_aborted)
|
||||
{
|
||||
connection_manager_.stop(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
void connection::handle_write(const asio::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
// Initiate graceful connection closure.
|
||||
asio::error_code ignored_ec;
|
||||
socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
|
||||
if (e != asio::error::operation_aborted)
|
||||
{
|
||||
connection_manager_.stop(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// connection.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_CONNECTION_HPP
|
||||
#define HTTP_CONNECTION_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
#include "request_handler.hpp"
|
||||
#include "request_parser.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
class connection_manager;
|
||||
|
||||
/// Represents a single connection from a client.
|
||||
class connection
|
||||
: public boost::enable_shared_from_this<connection>,
|
||||
private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct a connection with the given io_context.
|
||||
explicit connection(asio::io_context& io_context,
|
||||
connection_manager& manager, request_handler& handler);
|
||||
|
||||
/// Get the socket associated with the connection.
|
||||
asio::ip::tcp::socket& socket();
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start();
|
||||
|
||||
/// Stop all asynchronous operations associated with the connection.
|
||||
void stop();
|
||||
|
||||
private:
|
||||
/// Handle completion of a read operation.
|
||||
void handle_read(const asio::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
/// Handle completion of a write operation.
|
||||
void handle_write(const asio::error_code& e);
|
||||
|
||||
/// Socket for the connection.
|
||||
asio::ip::tcp::socket socket_;
|
||||
|
||||
/// The manager for this connection.
|
||||
connection_manager& connection_manager_;
|
||||
|
||||
/// The handler used to process the incoming request.
|
||||
request_handler& request_handler_;
|
||||
|
||||
/// Buffer for incoming data.
|
||||
boost::array<char, 8192> buffer_;
|
||||
|
||||
/// The incoming request.
|
||||
request request_;
|
||||
|
||||
/// The parser for the incoming request.
|
||||
request_parser request_parser_;
|
||||
|
||||
/// The reply to be sent back to the client.
|
||||
reply reply_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<connection> connection_ptr;
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_CONNECTION_HPP
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// connection_manager.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "connection_manager.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/bind/bind.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
void connection_manager::start(connection_ptr c)
|
||||
{
|
||||
connections_.insert(c);
|
||||
c->start();
|
||||
}
|
||||
|
||||
void connection_manager::stop(connection_ptr c)
|
||||
{
|
||||
connections_.erase(c);
|
||||
c->stop();
|
||||
}
|
||||
|
||||
void connection_manager::stop_all()
|
||||
{
|
||||
std::for_each(connections_.begin(), connections_.end(),
|
||||
boost::bind(&connection::stop, boost::placeholders::_1));
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// connection_manager.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_CONNECTION_MANAGER_HPP
|
||||
#define HTTP_CONNECTION_MANAGER_HPP
|
||||
|
||||
#include <set>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include "connection.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// Manages open connections so that they may be cleanly stopped when the server
|
||||
/// needs to shut down.
|
||||
class connection_manager
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Add the specified connection to the manager and start it.
|
||||
void start(connection_ptr c);
|
||||
|
||||
/// Stop the specified connection.
|
||||
void stop(connection_ptr c);
|
||||
|
||||
/// Stop all connections.
|
||||
void stop_all();
|
||||
|
||||
private:
|
||||
/// The managed connections.
|
||||
std::set<connection_ptr> connections_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_CONNECTION_MANAGER_HPP
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// header.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_HEADER_HPP
|
||||
#define HTTP_HEADER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_HEADER_HPP
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "server.hpp"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: http_server <address> <port> <doc_root>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 80 .\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 80 .\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialise the server.
|
||||
http::server::server s(argv[1], argv[2], argv[3]);
|
||||
|
||||
// Run the server until stopped.
|
||||
s.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// mime_types.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "mime_types.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
namespace mime_types {
|
||||
|
||||
struct mapping
|
||||
{
|
||||
const char* extension;
|
||||
const char* mime_type;
|
||||
} mappings[] =
|
||||
{
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "png", "image/png" },
|
||||
{ 0, 0 } // Marks end of list.
|
||||
};
|
||||
|
||||
std::string extension_to_type(const std::string& extension)
|
||||
{
|
||||
for (mapping* m = mappings; m->extension; ++m)
|
||||
{
|
||||
if (m->extension == extension)
|
||||
{
|
||||
return m->mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// mime_types.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_MIME_TYPES_HPP
|
||||
#define HTTP_MIME_TYPES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
namespace mime_types {
|
||||
|
||||
/// Convert a file extension into a MIME type.
|
||||
std::string extension_to_type(const std::string& extension);
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_MIME_TYPES_HPP
|
||||
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// reply.cpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "reply.hpp"
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string ok =
|
||||
"HTTP/1.0 200 OK\r\n";
|
||||
const std::string created =
|
||||
"HTTP/1.0 201 Created\r\n";
|
||||
const std::string accepted =
|
||||
"HTTP/1.0 202 Accepted\r\n";
|
||||
const std::string no_content =
|
||||
"HTTP/1.0 204 No Content\r\n";
|
||||
const std::string multiple_choices =
|
||||
"HTTP/1.0 300 Multiple Choices\r\n";
|
||||
const std::string moved_permanently =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n";
|
||||
const std::string moved_temporarily =
|
||||
"HTTP/1.0 302 Moved Temporarily\r\n";
|
||||
const std::string not_modified =
|
||||
"HTTP/1.0 304 Not Modified\r\n";
|
||||
const std::string bad_request =
|
||||
"HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string unauthorized =
|
||||
"HTTP/1.0 401 Unauthorized\r\n";
|
||||
const std::string forbidden =
|
||||
"HTTP/1.0 403 Forbidden\r\n";
|
||||
const std::string not_found =
|
||||
"HTTP/1.0 404 Not Found\r\n";
|
||||
const std::string internal_server_error =
|
||||
"HTTP/1.0 500 Internal Server Error\r\n";
|
||||
const std::string not_implemented =
|
||||
"HTTP/1.0 501 Not Implemented\r\n";
|
||||
const std::string bad_gateway =
|
||||
"HTTP/1.0 502 Bad Gateway\r\n";
|
||||
const std::string service_unavailable =
|
||||
"HTTP/1.0 503 Service Unavailable\r\n";
|
||||
|
||||
asio::const_buffer to_buffer(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return asio::buffer(ok);
|
||||
case reply::created:
|
||||
return asio::buffer(created);
|
||||
case reply::accepted:
|
||||
return asio::buffer(accepted);
|
||||
case reply::no_content:
|
||||
return asio::buffer(no_content);
|
||||
case reply::multiple_choices:
|
||||
return asio::buffer(multiple_choices);
|
||||
case reply::moved_permanently:
|
||||
return asio::buffer(moved_permanently);
|
||||
case reply::moved_temporarily:
|
||||
return asio::buffer(moved_temporarily);
|
||||
case reply::not_modified:
|
||||
return asio::buffer(not_modified);
|
||||
case reply::bad_request:
|
||||
return asio::buffer(bad_request);
|
||||
case reply::unauthorized:
|
||||
return asio::buffer(unauthorized);
|
||||
case reply::forbidden:
|
||||
return asio::buffer(forbidden);
|
||||
case reply::not_found:
|
||||
return asio::buffer(not_found);
|
||||
case reply::internal_server_error:
|
||||
return asio::buffer(internal_server_error);
|
||||
case reply::not_implemented:
|
||||
return asio::buffer(not_implemented);
|
||||
case reply::bad_gateway:
|
||||
return asio::buffer(bad_gateway);
|
||||
case reply::service_unavailable:
|
||||
return asio::buffer(service_unavailable);
|
||||
default:
|
||||
return asio::buffer(internal_server_error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
buffers.push_back(status_strings::to_buffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(asio::buffer(h.name));
|
||||
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(asio::buffer(h.value));
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
buffers.push_back(asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
namespace stock_replies {
|
||||
|
||||
const char ok[] = "";
|
||||
const char created[] =
|
||||
"<html>"
|
||||
"<head><title>Created</title></head>"
|
||||
"<body><h1>201 Created</h1></body>"
|
||||
"</html>";
|
||||
const char accepted[] =
|
||||
"<html>"
|
||||
"<head><title>Accepted</title></head>"
|
||||
"<body><h1>202 Accepted</h1></body>"
|
||||
"</html>";
|
||||
const char no_content[] =
|
||||
"<html>"
|
||||
"<head><title>No Content</title></head>"
|
||||
"<body><h1>204 Content</h1></body>"
|
||||
"</html>";
|
||||
const char multiple_choices[] =
|
||||
"<html>"
|
||||
"<head><title>Multiple Choices</title></head>"
|
||||
"<body><h1>300 Multiple Choices</h1></body>"
|
||||
"</html>";
|
||||
const char moved_permanently[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Permanently</title></head>"
|
||||
"<body><h1>301 Moved Permanently</h1></body>"
|
||||
"</html>";
|
||||
const char moved_temporarily[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Temporarily</title></head>"
|
||||
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||
"</html>";
|
||||
const char not_modified[] =
|
||||
"<html>"
|
||||
"<head><title>Not Modified</title></head>"
|
||||
"<body><h1>304 Not Modified</h1></body>"
|
||||
"</html>";
|
||||
const char bad_request[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Request</title></head>"
|
||||
"<body><h1>400 Bad Request</h1></body>"
|
||||
"</html>";
|
||||
const char unauthorized[] =
|
||||
"<html>"
|
||||
"<head><title>Unauthorized</title></head>"
|
||||
"<body><h1>401 Unauthorized</h1></body>"
|
||||
"</html>";
|
||||
const char forbidden[] =
|
||||
"<html>"
|
||||
"<head><title>Forbidden</title></head>"
|
||||
"<body><h1>403 Forbidden</h1></body>"
|
||||
"</html>";
|
||||
const char not_found[] =
|
||||
"<html>"
|
||||
"<head><title>Not Found</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body>"
|
||||
"</html>";
|
||||
const char internal_server_error[] =
|
||||
"<html>"
|
||||
"<head><title>Internal Server Error</title></head>"
|
||||
"<body><h1>500 Internal Server Error</h1></body>"
|
||||
"</html>";
|
||||
const char not_implemented[] =
|
||||
"<html>"
|
||||
"<head><title>Not Implemented</title></head>"
|
||||
"<body><h1>501 Not Implemented</h1></body>"
|
||||
"</html>";
|
||||
const char bad_gateway[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Gateway</title></head>"
|
||||
"<body><h1>502 Bad Gateway</h1></body>"
|
||||
"</html>";
|
||||
const char service_unavailable[] =
|
||||
"<html>"
|
||||
"<head><title>Service Unavailable</title></head>"
|
||||
"<body><h1>503 Service Unavailable</h1></body>"
|
||||
"</html>";
|
||||
|
||||
std::string to_string(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return ok;
|
||||
case reply::created:
|
||||
return created;
|
||||
case reply::accepted:
|
||||
return accepted;
|
||||
case reply::no_content:
|
||||
return no_content;
|
||||
case reply::multiple_choices:
|
||||
return multiple_choices;
|
||||
case reply::moved_permanently:
|
||||
return moved_permanently;
|
||||
case reply::moved_temporarily:
|
||||
return moved_temporarily;
|
||||
case reply::not_modified:
|
||||
return not_modified;
|
||||
case reply::bad_request:
|
||||
return bad_request;
|
||||
case reply::unauthorized:
|
||||
return unauthorized;
|
||||
case reply::forbidden:
|
||||
return forbidden;
|
||||
case reply::not_found:
|
||||
return not_found;
|
||||
case reply::internal_server_error:
|
||||
return internal_server_error;
|
||||
case reply::not_implemented:
|
||||
return not_implemented;
|
||||
case reply::bad_gateway:
|
||||
return bad_gateway;
|
||||
case reply::service_unavailable:
|
||||
return service_unavailable;
|
||||
default:
|
||||
return internal_server_error;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stock_replies
|
||||
|
||||
reply reply::stock_reply(reply::status_type status)
|
||||
{
|
||||
reply rep;
|
||||
rep.status = status;
|
||||
rep.content = stock_replies::to_string(status);
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "text/html";
|
||||
return rep;
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// reply.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_REPLY_HPP
|
||||
#define HTTP_REPLY_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <asio.hpp>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// A reply to be sent to a client.
|
||||
struct reply
|
||||
{
|
||||
/// The status of the reply.
|
||||
enum status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
} status;
|
||||
|
||||
/// The headers to be included in the reply.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The content to be sent in the reply.
|
||||
std::string content;
|
||||
|
||||
/// Convert the reply into a vector of buffers. The buffers do not own the
|
||||
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||
/// not be changed until the write operation has completed.
|
||||
std::vector<asio::const_buffer> to_buffers();
|
||||
|
||||
/// Get a stock reply.
|
||||
static reply stock_reply(status_type status);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REPLY_HPP
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// request.hpp
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_REQUEST_HPP
|
||||
#define HTTP_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// A request received from a client.
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REQUEST_HPP
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// request_handler.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_handler.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "mime_types.hpp"
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
request_handler::request_handler(const std::string& doc_root)
|
||||
: doc_root_(doc_root)
|
||||
{
|
||||
}
|
||||
|
||||
void request_handler::handle_request(const request& req, reply& rep)
|
||||
{
|
||||
// Decode url to path.
|
||||
std::string request_path;
|
||||
if (!url_decode(req.uri, request_path))
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (request_path.empty() || request_path[0] != '/'
|
||||
|| request_path.find("..") != std::string::npos)
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// If path ends in slash (i.e. is a directory) then add "index.html".
|
||||
if (request_path[request_path.size() - 1] == '/')
|
||||
{
|
||||
request_path += "index.html";
|
||||
}
|
||||
|
||||
// Determine the file extension.
|
||||
std::size_t last_slash_pos = request_path.find_last_of("/");
|
||||
std::size_t last_dot_pos = request_path.find_last_of(".");
|
||||
std::string extension;
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
|
||||
{
|
||||
extension = request_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
|
||||
// Open the file to send back.
|
||||
std::string full_path = doc_root_ + request_path;
|
||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!is)
|
||||
{
|
||||
rep = reply::stock_reply(reply::not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill out the reply to be sent to the client.
|
||||
rep.status = reply::ok;
|
||||
char buf[512];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
rep.content.append(buf, is.gcount());
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = mime_types::extension_to_type(extension);
|
||||
}
|
||||
|
||||
bool request_handler::url_decode(const std::string& in, std::string& out)
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(in.size());
|
||||
for (std::size_t i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if (in[i] == '%')
|
||||
{
|
||||
if (i + 3 <= in.size())
|
||||
{
|
||||
int value = 0;
|
||||
std::istringstream is(in.substr(i + 1, 2));
|
||||
if (is >> std::hex >> value)
|
||||
{
|
||||
out += static_cast<char>(value);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (in[i] == '+')
|
||||
{
|
||||
out += ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// request_handler.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_REQUEST_HANDLER_HPP
|
||||
#define HTTP_REQUEST_HANDLER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
struct reply;
|
||||
struct request;
|
||||
|
||||
/// The common handler for all incoming requests.
|
||||
class request_handler
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct with a directory containing files to be served.
|
||||
explicit request_handler(const std::string& doc_root);
|
||||
|
||||
/// Handle a request and produce a reply.
|
||||
void handle_request(const request& req, reply& rep);
|
||||
|
||||
private:
|
||||
/// The directory containing the files to be served.
|
||||
std::string doc_root_;
|
||||
|
||||
/// Perform URL-decoding on a string. Returns false if the encoding was
|
||||
/// invalid.
|
||||
static bool url_decode(const std::string& in, std::string& out);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REQUEST_HANDLER_HPP
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// request_parser.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_parser.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
request_parser::request_parser()
|
||||
: state_(method_start)
|
||||
{
|
||||
}
|
||||
|
||||
void request_parser::reset()
|
||||
{
|
||||
state_ = method_start;
|
||||
}
|
||||
|
||||
boost::tribool request_parser::consume(request& req, char input)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case method_start:
|
||||
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = method;
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case method:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = uri;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case uri:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = http_version_h;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.uri.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case http_version_h:
|
||||
if (input == 'H')
|
||||
{
|
||||
state_ = http_version_t_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_1:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_t_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_2:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_p;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_p:
|
||||
if (input == 'P')
|
||||
{
|
||||
state_ = http_version_slash;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_slash:
|
||||
if (input == '/')
|
||||
{
|
||||
req.http_version_major = 0;
|
||||
req.http_version_minor = 0;
|
||||
state_ = http_version_major_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
state_ = http_version_major;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major:
|
||||
if (input == '.')
|
||||
{
|
||||
state_ = http_version_minor_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
state_ = http_version_minor;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_1:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_line_start:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_3;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
|
||||
{
|
||||
state_ = header_lws;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.push_back(header());
|
||||
req.headers.back().name.push_back(input);
|
||||
state_ = header_name;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_lws:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (input == ' ' || input == '\t')
|
||||
{
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = header_value;
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_name:
|
||||
if (input == ':')
|
||||
{
|
||||
state_ = space_before_header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().name.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case space_before_header_value:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_value:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case expecting_newline_2:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_3:
|
||||
return (input == '\n');
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_char(int c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
bool request_parser::is_ctl(int c)
|
||||
{
|
||||
return (c >= 0 && c <= 31) || (c == 127);
|
||||
}
|
||||
|
||||
bool request_parser::is_tspecial(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
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 request_parser::is_digit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// request_parser.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_REQUEST_PARSER_HPP
|
||||
#define HTTP_REQUEST_PARSER_HPP
|
||||
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
struct request;
|
||||
|
||||
/// Parser for incoming requests.
|
||||
class request_parser
|
||||
{
|
||||
public:
|
||||
/// Construct ready to parse the request method.
|
||||
request_parser();
|
||||
|
||||
/// Reset to initial parser state.
|
||||
void reset();
|
||||
|
||||
/// Parse some data. The tribool return value is true when a complete request
|
||||
/// has been parsed, false if the data is invalid, indeterminate when more
|
||||
/// data is required. The InputIterator return value indicates how much of the
|
||||
/// input has been consumed.
|
||||
template <typename InputIterator>
|
||||
boost::tuple<boost::tribool, InputIterator> parse(request& req,
|
||||
InputIterator begin, InputIterator end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
boost::tribool result = consume(req, *begin++);
|
||||
if (result || !result)
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
boost::tribool result = boost::indeterminate;
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Handle the next character of input.
|
||||
boost::tribool consume(request& req, char input);
|
||||
|
||||
/// Check if a byte is an HTTP character.
|
||||
static bool is_char(int c);
|
||||
|
||||
/// Check if a byte is an HTTP control character.
|
||||
static bool is_ctl(int c);
|
||||
|
||||
/// Check if a byte is defined as an HTTP tspecial character.
|
||||
static bool is_tspecial(int c);
|
||||
|
||||
/// Check if a byte is a digit.
|
||||
static bool is_digit(int c);
|
||||
|
||||
/// The current state of the parser.
|
||||
enum state
|
||||
{
|
||||
method_start,
|
||||
method,
|
||||
uri,
|
||||
http_version_h,
|
||||
http_version_t_1,
|
||||
http_version_t_2,
|
||||
http_version_p,
|
||||
http_version_slash,
|
||||
http_version_major_start,
|
||||
http_version_major,
|
||||
http_version_minor_start,
|
||||
http_version_minor,
|
||||
expecting_newline_1,
|
||||
header_line_start,
|
||||
header_lws,
|
||||
header_name,
|
||||
space_before_header_value,
|
||||
header_value,
|
||||
expecting_newline_2,
|
||||
expecting_newline_3
|
||||
} state_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REQUEST_PARSER_HPP
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "server.hpp"
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <signal.h>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
server::server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root)
|
||||
: io_context_(),
|
||||
signals_(io_context_),
|
||||
acceptor_(io_context_),
|
||||
connection_manager_(),
|
||||
new_connection_(),
|
||||
request_handler_(doc_root)
|
||||
{
|
||||
// Register to handle the signals that indicate when the server should exit.
|
||||
// It is safe to register for the same signal multiple times in a program,
|
||||
// provided all registration for the specified signal is made through Asio.
|
||||
signals_.add(SIGINT);
|
||||
signals_.add(SIGTERM);
|
||||
#if defined(SIGQUIT)
|
||||
signals_.add(SIGQUIT);
|
||||
#endif // defined(SIGQUIT)
|
||||
signals_.async_wait(boost::bind(&server::handle_stop, this));
|
||||
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
asio::ip::tcp::resolver resolver(io_context_);
|
||||
asio::ip::tcp::endpoint endpoint =
|
||||
*resolver.resolve(address, port).begin();
|
||||
acceptor_.open(endpoint.protocol());
|
||||
acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true));
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.listen();
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void server::run()
|
||||
{
|
||||
// The io_context::run() call will block until all asynchronous operations
|
||||
// have finished. While the server is running, there is always at least one
|
||||
// asynchronous operation outstanding: the asynchronous accept call waiting
|
||||
// for new incoming connections.
|
||||
io_context_.run();
|
||||
}
|
||||
|
||||
void server::start_accept()
|
||||
{
|
||||
new_connection_.reset(new connection(io_context_,
|
||||
connection_manager_, request_handler_));
|
||||
acceptor_.async_accept(new_connection_->socket(),
|
||||
boost::bind(&server::handle_accept, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void server::handle_accept(const asio::error_code& e)
|
||||
{
|
||||
// Check whether the server was stopped by a signal before this completion
|
||||
// handler had a chance to run.
|
||||
if (!acceptor_.is_open())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e)
|
||||
{
|
||||
connection_manager_.start(new_connection_);
|
||||
}
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void server::handle_stop()
|
||||
{
|
||||
// The server is stopped by cancelling all outstanding asynchronous
|
||||
// operations. Once all operations have finished the io_context::run() call
|
||||
// will exit.
|
||||
acceptor_.close();
|
||||
connection_manager_.stop_all();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER_HPP
|
||||
#define HTTP_SERVER_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <string>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include "connection.hpp"
|
||||
#include "connection_manager.hpp"
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// The top-level class of the HTTP server.
|
||||
class server
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root);
|
||||
|
||||
/// Run the server's io_context loop.
|
||||
void run();
|
||||
|
||||
private:
|
||||
/// Initiate an asynchronous accept operation.
|
||||
void start_accept();
|
||||
|
||||
/// Handle completion of an asynchronous accept operation.
|
||||
void handle_accept(const asio::error_code& e);
|
||||
|
||||
/// Handle a request to stop the server.
|
||||
void handle_stop();
|
||||
|
||||
/// The io_context used to perform asynchronous operations.
|
||||
asio::io_context io_context_;
|
||||
|
||||
/// The signal_set is used to register for process termination notifications.
|
||||
asio::signal_set signals_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
/// The connection manager which owns all live connections.
|
||||
connection_manager connection_manager_;
|
||||
|
||||
/// The next connection to be accepted.
|
||||
connection_ptr new_connection_;
|
||||
|
||||
/// The handler for all incoming requests.
|
||||
request_handler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER_HPP
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// connection.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "connection.hpp"
|
||||
#include <vector>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
connection::connection(asio::io_context& io_context,
|
||||
request_handler& handler)
|
||||
: socket_(io_context),
|
||||
request_handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket& connection::socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void connection::start()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void connection::handle_read(const asio::error_code& e,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
boost::tribool result;
|
||||
boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
|
||||
request_, buffer_.data(), buffer_.data() + bytes_transferred);
|
||||
|
||||
if (result)
|
||||
{
|
||||
request_handler_.handle_request(request_, reply_);
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else if (!result)
|
||||
{
|
||||
reply_ = reply::stock_reply(reply::bad_request);
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurs then no new asynchronous operations are started. This
|
||||
// means that all shared_ptr references to the connection object will
|
||||
// disappear and the object will be destroyed automatically after this
|
||||
// handler returns. The connection class's destructor closes the socket.
|
||||
}
|
||||
|
||||
void connection::handle_write(const asio::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
// Initiate graceful connection closure.
|
||||
asio::error_code ignored_ec;
|
||||
socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
|
||||
// No new asynchronous operations are started. This means that all shared_ptr
|
||||
// references to the connection object will disappear and the object will be
|
||||
// destroyed automatically after this handler returns. The connection class's
|
||||
// destructor closes the socket.
|
||||
}
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// connection.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_CONNECTION_HPP
|
||||
#define HTTP_SERVER2_CONNECTION_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
#include "request_handler.hpp"
|
||||
#include "request_parser.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
/// Represents a single connection from a client.
|
||||
class connection
|
||||
: public boost::enable_shared_from_this<connection>,
|
||||
private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct a connection with the given io_context.
|
||||
explicit connection(asio::io_context& io_context,
|
||||
request_handler& handler);
|
||||
|
||||
/// Get the socket associated with the connection.
|
||||
asio::ip::tcp::socket& socket();
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start();
|
||||
|
||||
private:
|
||||
/// Handle completion of a read operation.
|
||||
void handle_read(const asio::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
/// Handle completion of a write operation.
|
||||
void handle_write(const asio::error_code& e);
|
||||
|
||||
/// Socket for the connection.
|
||||
asio::ip::tcp::socket socket_;
|
||||
|
||||
/// The handler used to process the incoming request.
|
||||
request_handler& request_handler_;
|
||||
|
||||
/// Buffer for incoming data.
|
||||
boost::array<char, 8192> buffer_;
|
||||
|
||||
/// The incoming request.
|
||||
request request_;
|
||||
|
||||
/// The parser for the incoming request.
|
||||
request_parser request_parser_;
|
||||
|
||||
/// The reply to be sent back to the client.
|
||||
reply reply_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<connection> connection_ptr;
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_CONNECTION_HPP
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// header.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_HEADER_HPP
|
||||
#define HTTP_SERVER2_HEADER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_HEADER_HPP
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// io_context_pool.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "server.hpp"
|
||||
#include <stdexcept>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
io_context_pool::io_context_pool(std::size_t pool_size)
|
||||
: next_io_context_(0)
|
||||
{
|
||||
if (pool_size == 0)
|
||||
throw std::runtime_error("io_context_pool size is 0");
|
||||
|
||||
// Give all the io_contexts work to do so that their run() functions will not
|
||||
// exit until they are explicitly stopped.
|
||||
for (std::size_t i = 0; i < pool_size; ++i)
|
||||
{
|
||||
io_context_ptr io_context(new asio::io_context);
|
||||
io_contexts_.push_back(io_context);
|
||||
work_.push_back(asio::require(io_context->get_executor(),
|
||||
asio::execution::outstanding_work.tracked));
|
||||
}
|
||||
}
|
||||
|
||||
void io_context_pool::run()
|
||||
{
|
||||
// Create a pool of threads to run all of the io_contexts.
|
||||
std::vector<boost::shared_ptr<asio::thread> > threads;
|
||||
for (std::size_t i = 0; i < io_contexts_.size(); ++i)
|
||||
{
|
||||
boost::shared_ptr<asio::thread> thread(new asio::thread(
|
||||
boost::bind(&asio::io_context::run, io_contexts_[i])));
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
// Wait for all threads in the pool to exit.
|
||||
for (std::size_t i = 0; i < threads.size(); ++i)
|
||||
threads[i]->join();
|
||||
}
|
||||
|
||||
void io_context_pool::stop()
|
||||
{
|
||||
// Explicitly stop all io_contexts.
|
||||
for (std::size_t i = 0; i < io_contexts_.size(); ++i)
|
||||
io_contexts_[i]->stop();
|
||||
}
|
||||
|
||||
asio::io_context& io_context_pool::get_io_context()
|
||||
{
|
||||
// Use a round-robin scheme to choose the next io_context to use.
|
||||
asio::io_context& io_context = *io_contexts_[next_io_context_];
|
||||
++next_io_context_;
|
||||
if (next_io_context_ == io_contexts_.size())
|
||||
next_io_context_ = 0;
|
||||
return io_context;
|
||||
}
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// io_context_pool.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
||||
#define HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
/// A pool of io_context objects.
|
||||
class io_context_pool
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct the io_context pool.
|
||||
explicit io_context_pool(std::size_t pool_size);
|
||||
|
||||
/// Run all io_context objects in the pool.
|
||||
void run();
|
||||
|
||||
/// Stop all io_context objects in the pool.
|
||||
void stop();
|
||||
|
||||
/// Get an io_context to use.
|
||||
asio::io_context& get_io_context();
|
||||
|
||||
private:
|
||||
typedef boost::shared_ptr<asio::io_context> io_context_ptr;
|
||||
|
||||
/// The pool of io_contexts.
|
||||
std::vector<io_context_ptr> io_contexts_;
|
||||
|
||||
/// The work-tracking executors that keep the io_contexts running.
|
||||
std::list<asio::any_io_executor> work_;
|
||||
|
||||
/// The next io_context to use for a connection.
|
||||
std::size_t next_io_context_;
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "server.hpp"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: http_server <address> <port> <threads> <doc_root>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 80 1 .\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 80 1 .\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialise the server.
|
||||
std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]);
|
||||
http::server2::server s(argv[1], argv[2], argv[4], num_threads);
|
||||
|
||||
// Run the server until stopped.
|
||||
s.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// mime_types.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "mime_types.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
namespace mime_types {
|
||||
|
||||
struct mapping
|
||||
{
|
||||
const char* extension;
|
||||
const char* mime_type;
|
||||
} mappings[] =
|
||||
{
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "png", "image/png" },
|
||||
{ 0, 0 } // Marks end of list.
|
||||
};
|
||||
|
||||
std::string extension_to_type(const std::string& extension)
|
||||
{
|
||||
for (mapping* m = mappings; m->extension; ++m)
|
||||
{
|
||||
if (m->extension == extension)
|
||||
{
|
||||
return m->mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// mime_types.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_MIME_TYPES_HPP
|
||||
#define HTTP_SERVER2_MIME_TYPES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
namespace mime_types {
|
||||
|
||||
/// Convert a file extension into a MIME type.
|
||||
std::string extension_to_type(const std::string& extension);
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_MIME_TYPES_HPP
|
||||
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// reply.cpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "reply.hpp"
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string ok =
|
||||
"HTTP/1.0 200 OK\r\n";
|
||||
const std::string created =
|
||||
"HTTP/1.0 201 Created\r\n";
|
||||
const std::string accepted =
|
||||
"HTTP/1.0 202 Accepted\r\n";
|
||||
const std::string no_content =
|
||||
"HTTP/1.0 204 No Content\r\n";
|
||||
const std::string multiple_choices =
|
||||
"HTTP/1.0 300 Multiple Choices\r\n";
|
||||
const std::string moved_permanently =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n";
|
||||
const std::string moved_temporarily =
|
||||
"HTTP/1.0 302 Moved Temporarily\r\n";
|
||||
const std::string not_modified =
|
||||
"HTTP/1.0 304 Not Modified\r\n";
|
||||
const std::string bad_request =
|
||||
"HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string unauthorized =
|
||||
"HTTP/1.0 401 Unauthorized\r\n";
|
||||
const std::string forbidden =
|
||||
"HTTP/1.0 403 Forbidden\r\n";
|
||||
const std::string not_found =
|
||||
"HTTP/1.0 404 Not Found\r\n";
|
||||
const std::string internal_server_error =
|
||||
"HTTP/1.0 500 Internal Server Error\r\n";
|
||||
const std::string not_implemented =
|
||||
"HTTP/1.0 501 Not Implemented\r\n";
|
||||
const std::string bad_gateway =
|
||||
"HTTP/1.0 502 Bad Gateway\r\n";
|
||||
const std::string service_unavailable =
|
||||
"HTTP/1.0 503 Service Unavailable\r\n";
|
||||
|
||||
asio::const_buffer to_buffer(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return asio::buffer(ok);
|
||||
case reply::created:
|
||||
return asio::buffer(created);
|
||||
case reply::accepted:
|
||||
return asio::buffer(accepted);
|
||||
case reply::no_content:
|
||||
return asio::buffer(no_content);
|
||||
case reply::multiple_choices:
|
||||
return asio::buffer(multiple_choices);
|
||||
case reply::moved_permanently:
|
||||
return asio::buffer(moved_permanently);
|
||||
case reply::moved_temporarily:
|
||||
return asio::buffer(moved_temporarily);
|
||||
case reply::not_modified:
|
||||
return asio::buffer(not_modified);
|
||||
case reply::bad_request:
|
||||
return asio::buffer(bad_request);
|
||||
case reply::unauthorized:
|
||||
return asio::buffer(unauthorized);
|
||||
case reply::forbidden:
|
||||
return asio::buffer(forbidden);
|
||||
case reply::not_found:
|
||||
return asio::buffer(not_found);
|
||||
case reply::internal_server_error:
|
||||
return asio::buffer(internal_server_error);
|
||||
case reply::not_implemented:
|
||||
return asio::buffer(not_implemented);
|
||||
case reply::bad_gateway:
|
||||
return asio::buffer(bad_gateway);
|
||||
case reply::service_unavailable:
|
||||
return asio::buffer(service_unavailable);
|
||||
default:
|
||||
return asio::buffer(internal_server_error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
buffers.push_back(status_strings::to_buffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(asio::buffer(h.name));
|
||||
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(asio::buffer(h.value));
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
buffers.push_back(asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
namespace stock_replies {
|
||||
|
||||
const char ok[] = "";
|
||||
const char created[] =
|
||||
"<html>"
|
||||
"<head><title>Created</title></head>"
|
||||
"<body><h1>201 Created</h1></body>"
|
||||
"</html>";
|
||||
const char accepted[] =
|
||||
"<html>"
|
||||
"<head><title>Accepted</title></head>"
|
||||
"<body><h1>202 Accepted</h1></body>"
|
||||
"</html>";
|
||||
const char no_content[] =
|
||||
"<html>"
|
||||
"<head><title>No Content</title></head>"
|
||||
"<body><h1>204 Content</h1></body>"
|
||||
"</html>";
|
||||
const char multiple_choices[] =
|
||||
"<html>"
|
||||
"<head><title>Multiple Choices</title></head>"
|
||||
"<body><h1>300 Multiple Choices</h1></body>"
|
||||
"</html>";
|
||||
const char moved_permanently[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Permanently</title></head>"
|
||||
"<body><h1>301 Moved Permanently</h1></body>"
|
||||
"</html>";
|
||||
const char moved_temporarily[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Temporarily</title></head>"
|
||||
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||
"</html>";
|
||||
const char not_modified[] =
|
||||
"<html>"
|
||||
"<head><title>Not Modified</title></head>"
|
||||
"<body><h1>304 Not Modified</h1></body>"
|
||||
"</html>";
|
||||
const char bad_request[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Request</title></head>"
|
||||
"<body><h1>400 Bad Request</h1></body>"
|
||||
"</html>";
|
||||
const char unauthorized[] =
|
||||
"<html>"
|
||||
"<head><title>Unauthorized</title></head>"
|
||||
"<body><h1>401 Unauthorized</h1></body>"
|
||||
"</html>";
|
||||
const char forbidden[] =
|
||||
"<html>"
|
||||
"<head><title>Forbidden</title></head>"
|
||||
"<body><h1>403 Forbidden</h1></body>"
|
||||
"</html>";
|
||||
const char not_found[] =
|
||||
"<html>"
|
||||
"<head><title>Not Found</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body>"
|
||||
"</html>";
|
||||
const char internal_server_error[] =
|
||||
"<html>"
|
||||
"<head><title>Internal Server Error</title></head>"
|
||||
"<body><h1>500 Internal Server Error</h1></body>"
|
||||
"</html>";
|
||||
const char not_implemented[] =
|
||||
"<html>"
|
||||
"<head><title>Not Implemented</title></head>"
|
||||
"<body><h1>501 Not Implemented</h1></body>"
|
||||
"</html>";
|
||||
const char bad_gateway[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Gateway</title></head>"
|
||||
"<body><h1>502 Bad Gateway</h1></body>"
|
||||
"</html>";
|
||||
const char service_unavailable[] =
|
||||
"<html>"
|
||||
"<head><title>Service Unavailable</title></head>"
|
||||
"<body><h1>503 Service Unavailable</h1></body>"
|
||||
"</html>";
|
||||
|
||||
std::string to_string(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return ok;
|
||||
case reply::created:
|
||||
return created;
|
||||
case reply::accepted:
|
||||
return accepted;
|
||||
case reply::no_content:
|
||||
return no_content;
|
||||
case reply::multiple_choices:
|
||||
return multiple_choices;
|
||||
case reply::moved_permanently:
|
||||
return moved_permanently;
|
||||
case reply::moved_temporarily:
|
||||
return moved_temporarily;
|
||||
case reply::not_modified:
|
||||
return not_modified;
|
||||
case reply::bad_request:
|
||||
return bad_request;
|
||||
case reply::unauthorized:
|
||||
return unauthorized;
|
||||
case reply::forbidden:
|
||||
return forbidden;
|
||||
case reply::not_found:
|
||||
return not_found;
|
||||
case reply::internal_server_error:
|
||||
return internal_server_error;
|
||||
case reply::not_implemented:
|
||||
return not_implemented;
|
||||
case reply::bad_gateway:
|
||||
return bad_gateway;
|
||||
case reply::service_unavailable:
|
||||
return service_unavailable;
|
||||
default:
|
||||
return internal_server_error;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stock_replies
|
||||
|
||||
reply reply::stock_reply(reply::status_type status)
|
||||
{
|
||||
reply rep;
|
||||
rep.status = status;
|
||||
rep.content = stock_replies::to_string(status);
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "text/html";
|
||||
return rep;
|
||||
}
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// reply.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_REPLY_HPP
|
||||
#define HTTP_SERVER2_REPLY_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <asio.hpp>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
/// A reply to be sent to a client.
|
||||
struct reply
|
||||
{
|
||||
/// The status of the reply.
|
||||
enum status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
} status;
|
||||
|
||||
/// The headers to be included in the reply.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The content to be sent in the reply.
|
||||
std::string content;
|
||||
|
||||
/// Convert the reply into a vector of buffers. The buffers do not own the
|
||||
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||
/// not be changed until the write operation has completed.
|
||||
std::vector<asio::const_buffer> to_buffers();
|
||||
|
||||
/// Get a stock reply.
|
||||
static reply stock_reply(status_type status);
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_REPLY_HPP
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// request.hpp
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_REQUEST_HPP
|
||||
#define HTTP_SERVER2_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
/// A request received from a client.
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_REQUEST_HPP
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// request_handler.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_handler.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "mime_types.hpp"
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
request_handler::request_handler(const std::string& doc_root)
|
||||
: doc_root_(doc_root)
|
||||
{
|
||||
}
|
||||
|
||||
void request_handler::handle_request(const request& req, reply& rep)
|
||||
{
|
||||
// Decode url to path.
|
||||
std::string request_path;
|
||||
if (!url_decode(req.uri, request_path))
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (request_path.empty() || request_path[0] != '/'
|
||||
|| request_path.find("..") != std::string::npos)
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// If path ends in slash (i.e. is a directory) then add "index.html".
|
||||
if (request_path[request_path.size() - 1] == '/')
|
||||
{
|
||||
request_path += "index.html";
|
||||
}
|
||||
|
||||
// Determine the file extension.
|
||||
std::size_t last_slash_pos = request_path.find_last_of("/");
|
||||
std::size_t last_dot_pos = request_path.find_last_of(".");
|
||||
std::string extension;
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
|
||||
{
|
||||
extension = request_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
|
||||
// Open the file to send back.
|
||||
std::string full_path = doc_root_ + request_path;
|
||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!is)
|
||||
{
|
||||
rep = reply::stock_reply(reply::not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill out the reply to be sent to the client.
|
||||
rep.status = reply::ok;
|
||||
char buf[512];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
rep.content.append(buf, is.gcount());
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = mime_types::extension_to_type(extension);
|
||||
}
|
||||
|
||||
bool request_handler::url_decode(const std::string& in, std::string& out)
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(in.size());
|
||||
for (std::size_t i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if (in[i] == '%')
|
||||
{
|
||||
if (i + 3 <= in.size())
|
||||
{
|
||||
int value = 0;
|
||||
std::istringstream is(in.substr(i + 1, 2));
|
||||
if (is >> std::hex >> value)
|
||||
{
|
||||
out += static_cast<char>(value);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (in[i] == '+')
|
||||
{
|
||||
out += ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// request_handler.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_REQUEST_HANDLER_HPP
|
||||
#define HTTP_SERVER2_REQUEST_HANDLER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
struct reply;
|
||||
struct request;
|
||||
|
||||
/// The common handler for all incoming requests.
|
||||
class request_handler
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct with a directory containing files to be served.
|
||||
explicit request_handler(const std::string& doc_root);
|
||||
|
||||
/// Handle a request and produce a reply.
|
||||
void handle_request(const request& req, reply& rep);
|
||||
|
||||
private:
|
||||
/// The directory containing the files to be served.
|
||||
std::string doc_root_;
|
||||
|
||||
/// Perform URL-decoding on a string. Returns false if the encoding was
|
||||
/// invalid.
|
||||
static bool url_decode(const std::string& in, std::string& out);
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_REQUEST_HANDLER_HPP
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// request_parser.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_parser.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
request_parser::request_parser()
|
||||
: state_(method_start)
|
||||
{
|
||||
}
|
||||
|
||||
void request_parser::reset()
|
||||
{
|
||||
state_ = method_start;
|
||||
}
|
||||
|
||||
boost::tribool request_parser::consume(request& req, char input)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case method_start:
|
||||
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = method;
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case method:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = uri;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case uri:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = http_version_h;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.uri.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case http_version_h:
|
||||
if (input == 'H')
|
||||
{
|
||||
state_ = http_version_t_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_1:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_t_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_2:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_p;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_p:
|
||||
if (input == 'P')
|
||||
{
|
||||
state_ = http_version_slash;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_slash:
|
||||
if (input == '/')
|
||||
{
|
||||
req.http_version_major = 0;
|
||||
req.http_version_minor = 0;
|
||||
state_ = http_version_major_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
state_ = http_version_major;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major:
|
||||
if (input == '.')
|
||||
{
|
||||
state_ = http_version_minor_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
state_ = http_version_minor;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_1:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_line_start:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_3;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
|
||||
{
|
||||
state_ = header_lws;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.push_back(header());
|
||||
req.headers.back().name.push_back(input);
|
||||
state_ = header_name;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_lws:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (input == ' ' || input == '\t')
|
||||
{
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = header_value;
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_name:
|
||||
if (input == ':')
|
||||
{
|
||||
state_ = space_before_header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().name.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case space_before_header_value:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_value:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case expecting_newline_2:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_3:
|
||||
return (input == '\n');
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_char(int c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
bool request_parser::is_ctl(int c)
|
||||
{
|
||||
return (c >= 0 && c <= 31) || (c == 127);
|
||||
}
|
||||
|
||||
bool request_parser::is_tspecial(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
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 request_parser::is_digit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// request_parser.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_REQUEST_PARSER_HPP
|
||||
#define HTTP_SERVER2_REQUEST_PARSER_HPP
|
||||
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
struct request;
|
||||
|
||||
/// Parser for incoming requests.
|
||||
class request_parser
|
||||
{
|
||||
public:
|
||||
/// Construct ready to parse the request method.
|
||||
request_parser();
|
||||
|
||||
/// Reset to initial parser state.
|
||||
void reset();
|
||||
|
||||
/// Parse some data. The tribool return value is true when a complete request
|
||||
/// has been parsed, false if the data is invalid, indeterminate when more
|
||||
/// data is required. The InputIterator return value indicates how much of the
|
||||
/// input has been consumed.
|
||||
template <typename InputIterator>
|
||||
boost::tuple<boost::tribool, InputIterator> parse(request& req,
|
||||
InputIterator begin, InputIterator end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
boost::tribool result = consume(req, *begin++);
|
||||
if (result || !result)
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
boost::tribool result = boost::indeterminate;
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Handle the next character of input.
|
||||
boost::tribool consume(request& req, char input);
|
||||
|
||||
/// Check if a byte is an HTTP character.
|
||||
static bool is_char(int c);
|
||||
|
||||
/// Check if a byte is an HTTP control character.
|
||||
static bool is_ctl(int c);
|
||||
|
||||
/// Check if a byte is defined as an HTTP tspecial character.
|
||||
static bool is_tspecial(int c);
|
||||
|
||||
/// Check if a byte is a digit.
|
||||
static bool is_digit(int c);
|
||||
|
||||
/// The current state of the parser.
|
||||
enum state
|
||||
{
|
||||
method_start,
|
||||
method,
|
||||
uri,
|
||||
http_version_h,
|
||||
http_version_t_1,
|
||||
http_version_t_2,
|
||||
http_version_p,
|
||||
http_version_slash,
|
||||
http_version_major_start,
|
||||
http_version_major,
|
||||
http_version_minor_start,
|
||||
http_version_minor,
|
||||
expecting_newline_1,
|
||||
header_line_start,
|
||||
header_lws,
|
||||
header_name,
|
||||
space_before_header_value,
|
||||
header_value,
|
||||
expecting_newline_2,
|
||||
expecting_newline_3
|
||||
} state_;
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_REQUEST_PARSER_HPP
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "server.hpp"
|
||||
#include <boost/bind/bind.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
server::server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root, std::size_t io_context_pool_size)
|
||||
: io_context_pool_(io_context_pool_size),
|
||||
signals_(io_context_pool_.get_io_context()),
|
||||
acceptor_(io_context_pool_.get_io_context()),
|
||||
new_connection_(),
|
||||
request_handler_(doc_root)
|
||||
{
|
||||
// Register to handle the signals that indicate when the server should exit.
|
||||
// It is safe to register for the same signal multiple times in a program,
|
||||
// provided all registration for the specified signal is made through Asio.
|
||||
signals_.add(SIGINT);
|
||||
signals_.add(SIGTERM);
|
||||
#if defined(SIGQUIT)
|
||||
signals_.add(SIGQUIT);
|
||||
#endif // defined(SIGQUIT)
|
||||
signals_.async_wait(boost::bind(&server::handle_stop, this));
|
||||
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
asio::ip::tcp::resolver resolver(acceptor_.get_executor());
|
||||
asio::ip::tcp::endpoint endpoint =
|
||||
*resolver.resolve(address, port).begin();
|
||||
acceptor_.open(endpoint.protocol());
|
||||
acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true));
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.listen();
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void server::run()
|
||||
{
|
||||
io_context_pool_.run();
|
||||
}
|
||||
|
||||
void server::start_accept()
|
||||
{
|
||||
new_connection_.reset(new connection(
|
||||
io_context_pool_.get_io_context(), request_handler_));
|
||||
acceptor_.async_accept(new_connection_->socket(),
|
||||
boost::bind(&server::handle_accept, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void server::handle_accept(const asio::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
new_connection_->start();
|
||||
}
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void server::handle_stop()
|
||||
{
|
||||
io_context_pool_.stop();
|
||||
}
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_SERVER_HPP
|
||||
#define HTTP_SERVER2_SERVER_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "connection.hpp"
|
||||
#include "io_context_pool.hpp"
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server2 {
|
||||
|
||||
/// The top-level class of the HTTP server.
|
||||
class server
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root, std::size_t io_context_pool_size);
|
||||
|
||||
/// Run the server's io_context loop.
|
||||
void run();
|
||||
|
||||
private:
|
||||
/// Initiate an asynchronous accept operation.
|
||||
void start_accept();
|
||||
|
||||
/// Handle completion of an asynchronous accept operation.
|
||||
void handle_accept(const asio::error_code& e);
|
||||
|
||||
/// Handle a request to stop the server.
|
||||
void handle_stop();
|
||||
|
||||
/// The pool of io_context objects used to perform asynchronous operations.
|
||||
io_context_pool io_context_pool_;
|
||||
|
||||
/// The signal_set is used to register for process termination notifications.
|
||||
asio::signal_set signals_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
/// The next connection to be accepted.
|
||||
connection_ptr new_connection_;
|
||||
|
||||
/// The handler for all incoming requests.
|
||||
request_handler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace server2
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER2_SERVER_HPP
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// connection.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "connection.hpp"
|
||||
#include <vector>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
connection::connection(asio::io_context& io_context,
|
||||
request_handler& handler)
|
||||
: strand_(asio::make_strand(io_context)),
|
||||
socket_(strand_),
|
||||
request_handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket& connection::socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void connection::start()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void connection::handle_read(const asio::error_code& e,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
boost::tribool result;
|
||||
boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
|
||||
request_, buffer_.data(), buffer_.data() + bytes_transferred);
|
||||
|
||||
if (result)
|
||||
{
|
||||
request_handler_.handle_request(request_, reply_);
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else if (!result)
|
||||
{
|
||||
reply_ = reply::stock_reply(reply::bad_request);
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurs then no new asynchronous operations are started. This
|
||||
// means that all shared_ptr references to the connection object will
|
||||
// disappear and the object will be destroyed automatically after this
|
||||
// handler returns. The connection class's destructor closes the socket.
|
||||
}
|
||||
|
||||
void connection::handle_write(const asio::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
// Initiate graceful connection closure.
|
||||
asio::error_code ignored_ec;
|
||||
socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
|
||||
// No new asynchronous operations are started. This means that all shared_ptr
|
||||
// references to the connection object will disappear and the object will be
|
||||
// destroyed automatically after this handler returns. The connection class's
|
||||
// destructor closes the socket.
|
||||
}
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// connection.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_CONNECTION_HPP
|
||||
#define HTTP_SERVER3_CONNECTION_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
#include "request_handler.hpp"
|
||||
#include "request_parser.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
/// Represents a single connection from a client.
|
||||
class connection
|
||||
: public boost::enable_shared_from_this<connection>,
|
||||
private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct a connection with the given io_context.
|
||||
explicit connection(asio::io_context& io_context,
|
||||
request_handler& handler);
|
||||
|
||||
/// Get the socket associated with the connection.
|
||||
asio::ip::tcp::socket& socket();
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start();
|
||||
|
||||
private:
|
||||
/// Handle completion of a read operation.
|
||||
void handle_read(const asio::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
/// Handle completion of a write operation.
|
||||
void handle_write(const asio::error_code& e);
|
||||
|
||||
/// Strand to ensure the connection's handlers are not called concurrently.
|
||||
asio::strand<asio::io_context::executor_type> strand_;
|
||||
|
||||
/// Socket for the connection.
|
||||
asio::ip::tcp::socket socket_;
|
||||
|
||||
/// The handler used to process the incoming request.
|
||||
request_handler& request_handler_;
|
||||
|
||||
/// Buffer for incoming data.
|
||||
boost::array<char, 8192> buffer_;
|
||||
|
||||
/// The incoming request.
|
||||
request request_;
|
||||
|
||||
/// The parser for the incoming request.
|
||||
request_parser request_parser_;
|
||||
|
||||
/// The reply to be sent back to the client.
|
||||
reply reply_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<connection> connection_ptr;
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_CONNECTION_HPP
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// header.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_HEADER_HPP
|
||||
#define HTTP_SERVER3_HEADER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_HEADER_HPP
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "server.hpp"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: http_server <address> <port> <threads> <doc_root>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 80 1 .\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 80 1 .\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialise the server.
|
||||
std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]);
|
||||
http::server3::server s(argv[1], argv[2], argv[4], num_threads);
|
||||
|
||||
// Run the server until stopped.
|
||||
s.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// mime_types.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "mime_types.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
namespace mime_types {
|
||||
|
||||
struct mapping
|
||||
{
|
||||
const char* extension;
|
||||
const char* mime_type;
|
||||
} mappings[] =
|
||||
{
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "png", "image/png" },
|
||||
{ 0, 0 } // Marks end of list.
|
||||
};
|
||||
|
||||
std::string extension_to_type(const std::string& extension)
|
||||
{
|
||||
for (mapping* m = mappings; m->extension; ++m)
|
||||
{
|
||||
if (m->extension == extension)
|
||||
{
|
||||
return m->mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// mime_types.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_MIME_TYPES_HPP
|
||||
#define HTTP_SERVER3_MIME_TYPES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
namespace mime_types {
|
||||
|
||||
/// Convert a file extension into a MIME type.
|
||||
std::string extension_to_type(const std::string& extension);
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_MIME_TYPES_HPP
|
||||
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// reply.cpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "reply.hpp"
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string ok =
|
||||
"HTTP/1.0 200 OK\r\n";
|
||||
const std::string created =
|
||||
"HTTP/1.0 201 Created\r\n";
|
||||
const std::string accepted =
|
||||
"HTTP/1.0 202 Accepted\r\n";
|
||||
const std::string no_content =
|
||||
"HTTP/1.0 204 No Content\r\n";
|
||||
const std::string multiple_choices =
|
||||
"HTTP/1.0 300 Multiple Choices\r\n";
|
||||
const std::string moved_permanently =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n";
|
||||
const std::string moved_temporarily =
|
||||
"HTTP/1.0 302 Moved Temporarily\r\n";
|
||||
const std::string not_modified =
|
||||
"HTTP/1.0 304 Not Modified\r\n";
|
||||
const std::string bad_request =
|
||||
"HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string unauthorized =
|
||||
"HTTP/1.0 401 Unauthorized\r\n";
|
||||
const std::string forbidden =
|
||||
"HTTP/1.0 403 Forbidden\r\n";
|
||||
const std::string not_found =
|
||||
"HTTP/1.0 404 Not Found\r\n";
|
||||
const std::string internal_server_error =
|
||||
"HTTP/1.0 500 Internal Server Error\r\n";
|
||||
const std::string not_implemented =
|
||||
"HTTP/1.0 501 Not Implemented\r\n";
|
||||
const std::string bad_gateway =
|
||||
"HTTP/1.0 502 Bad Gateway\r\n";
|
||||
const std::string service_unavailable =
|
||||
"HTTP/1.0 503 Service Unavailable\r\n";
|
||||
|
||||
asio::const_buffer to_buffer(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return asio::buffer(ok);
|
||||
case reply::created:
|
||||
return asio::buffer(created);
|
||||
case reply::accepted:
|
||||
return asio::buffer(accepted);
|
||||
case reply::no_content:
|
||||
return asio::buffer(no_content);
|
||||
case reply::multiple_choices:
|
||||
return asio::buffer(multiple_choices);
|
||||
case reply::moved_permanently:
|
||||
return asio::buffer(moved_permanently);
|
||||
case reply::moved_temporarily:
|
||||
return asio::buffer(moved_temporarily);
|
||||
case reply::not_modified:
|
||||
return asio::buffer(not_modified);
|
||||
case reply::bad_request:
|
||||
return asio::buffer(bad_request);
|
||||
case reply::unauthorized:
|
||||
return asio::buffer(unauthorized);
|
||||
case reply::forbidden:
|
||||
return asio::buffer(forbidden);
|
||||
case reply::not_found:
|
||||
return asio::buffer(not_found);
|
||||
case reply::internal_server_error:
|
||||
return asio::buffer(internal_server_error);
|
||||
case reply::not_implemented:
|
||||
return asio::buffer(not_implemented);
|
||||
case reply::bad_gateway:
|
||||
return asio::buffer(bad_gateway);
|
||||
case reply::service_unavailable:
|
||||
return asio::buffer(service_unavailable);
|
||||
default:
|
||||
return asio::buffer(internal_server_error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
buffers.push_back(status_strings::to_buffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(asio::buffer(h.name));
|
||||
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(asio::buffer(h.value));
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
buffers.push_back(asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
namespace stock_replies {
|
||||
|
||||
const char ok[] = "";
|
||||
const char created[] =
|
||||
"<html>"
|
||||
"<head><title>Created</title></head>"
|
||||
"<body><h1>201 Created</h1></body>"
|
||||
"</html>";
|
||||
const char accepted[] =
|
||||
"<html>"
|
||||
"<head><title>Accepted</title></head>"
|
||||
"<body><h1>202 Accepted</h1></body>"
|
||||
"</html>";
|
||||
const char no_content[] =
|
||||
"<html>"
|
||||
"<head><title>No Content</title></head>"
|
||||
"<body><h1>204 Content</h1></body>"
|
||||
"</html>";
|
||||
const char multiple_choices[] =
|
||||
"<html>"
|
||||
"<head><title>Multiple Choices</title></head>"
|
||||
"<body><h1>300 Multiple Choices</h1></body>"
|
||||
"</html>";
|
||||
const char moved_permanently[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Permanently</title></head>"
|
||||
"<body><h1>301 Moved Permanently</h1></body>"
|
||||
"</html>";
|
||||
const char moved_temporarily[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Temporarily</title></head>"
|
||||
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||
"</html>";
|
||||
const char not_modified[] =
|
||||
"<html>"
|
||||
"<head><title>Not Modified</title></head>"
|
||||
"<body><h1>304 Not Modified</h1></body>"
|
||||
"</html>";
|
||||
const char bad_request[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Request</title></head>"
|
||||
"<body><h1>400 Bad Request</h1></body>"
|
||||
"</html>";
|
||||
const char unauthorized[] =
|
||||
"<html>"
|
||||
"<head><title>Unauthorized</title></head>"
|
||||
"<body><h1>401 Unauthorized</h1></body>"
|
||||
"</html>";
|
||||
const char forbidden[] =
|
||||
"<html>"
|
||||
"<head><title>Forbidden</title></head>"
|
||||
"<body><h1>403 Forbidden</h1></body>"
|
||||
"</html>";
|
||||
const char not_found[] =
|
||||
"<html>"
|
||||
"<head><title>Not Found</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body>"
|
||||
"</html>";
|
||||
const char internal_server_error[] =
|
||||
"<html>"
|
||||
"<head><title>Internal Server Error</title></head>"
|
||||
"<body><h1>500 Internal Server Error</h1></body>"
|
||||
"</html>";
|
||||
const char not_implemented[] =
|
||||
"<html>"
|
||||
"<head><title>Not Implemented</title></head>"
|
||||
"<body><h1>501 Not Implemented</h1></body>"
|
||||
"</html>";
|
||||
const char bad_gateway[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Gateway</title></head>"
|
||||
"<body><h1>502 Bad Gateway</h1></body>"
|
||||
"</html>";
|
||||
const char service_unavailable[] =
|
||||
"<html>"
|
||||
"<head><title>Service Unavailable</title></head>"
|
||||
"<body><h1>503 Service Unavailable</h1></body>"
|
||||
"</html>";
|
||||
|
||||
std::string to_string(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return ok;
|
||||
case reply::created:
|
||||
return created;
|
||||
case reply::accepted:
|
||||
return accepted;
|
||||
case reply::no_content:
|
||||
return no_content;
|
||||
case reply::multiple_choices:
|
||||
return multiple_choices;
|
||||
case reply::moved_permanently:
|
||||
return moved_permanently;
|
||||
case reply::moved_temporarily:
|
||||
return moved_temporarily;
|
||||
case reply::not_modified:
|
||||
return not_modified;
|
||||
case reply::bad_request:
|
||||
return bad_request;
|
||||
case reply::unauthorized:
|
||||
return unauthorized;
|
||||
case reply::forbidden:
|
||||
return forbidden;
|
||||
case reply::not_found:
|
||||
return not_found;
|
||||
case reply::internal_server_error:
|
||||
return internal_server_error;
|
||||
case reply::not_implemented:
|
||||
return not_implemented;
|
||||
case reply::bad_gateway:
|
||||
return bad_gateway;
|
||||
case reply::service_unavailable:
|
||||
return service_unavailable;
|
||||
default:
|
||||
return internal_server_error;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stock_replies
|
||||
|
||||
reply reply::stock_reply(reply::status_type status)
|
||||
{
|
||||
reply rep;
|
||||
rep.status = status;
|
||||
rep.content = stock_replies::to_string(status);
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "text/html";
|
||||
return rep;
|
||||
}
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// reply.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_REPLY_HPP
|
||||
#define HTTP_SERVER3_REPLY_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <asio.hpp>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
/// A reply to be sent to a client.
|
||||
struct reply
|
||||
{
|
||||
/// The status of the reply.
|
||||
enum status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
} status;
|
||||
|
||||
/// The headers to be included in the reply.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The content to be sent in the reply.
|
||||
std::string content;
|
||||
|
||||
/// Convert the reply into a vector of buffers. The buffers do not own the
|
||||
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||
/// not be changed until the write operation has completed.
|
||||
std::vector<asio::const_buffer> to_buffers();
|
||||
|
||||
/// Get a stock reply.
|
||||
static reply stock_reply(status_type status);
|
||||
};
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_REPLY_HPP
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// request.hpp
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_REQUEST_HPP
|
||||
#define HTTP_SERVER3_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
/// A request received from a client.
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_REQUEST_HPP
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// request_handler.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_handler.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "mime_types.hpp"
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
request_handler::request_handler(const std::string& doc_root)
|
||||
: doc_root_(doc_root)
|
||||
{
|
||||
}
|
||||
|
||||
void request_handler::handle_request(const request& req, reply& rep)
|
||||
{
|
||||
// Decode url to path.
|
||||
std::string request_path;
|
||||
if (!url_decode(req.uri, request_path))
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (request_path.empty() || request_path[0] != '/'
|
||||
|| request_path.find("..") != std::string::npos)
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// If path ends in slash (i.e. is a directory) then add "index.html".
|
||||
if (request_path[request_path.size() - 1] == '/')
|
||||
{
|
||||
request_path += "index.html";
|
||||
}
|
||||
|
||||
// Determine the file extension.
|
||||
std::size_t last_slash_pos = request_path.find_last_of("/");
|
||||
std::size_t last_dot_pos = request_path.find_last_of(".");
|
||||
std::string extension;
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
|
||||
{
|
||||
extension = request_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
|
||||
// Open the file to send back.
|
||||
std::string full_path = doc_root_ + request_path;
|
||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!is)
|
||||
{
|
||||
rep = reply::stock_reply(reply::not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill out the reply to be sent to the client.
|
||||
rep.status = reply::ok;
|
||||
char buf[512];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
rep.content.append(buf, is.gcount());
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = mime_types::extension_to_type(extension);
|
||||
}
|
||||
|
||||
bool request_handler::url_decode(const std::string& in, std::string& out)
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(in.size());
|
||||
for (std::size_t i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if (in[i] == '%')
|
||||
{
|
||||
if (i + 3 <= in.size())
|
||||
{
|
||||
int value = 0;
|
||||
std::istringstream is(in.substr(i + 1, 2));
|
||||
if (is >> std::hex >> value)
|
||||
{
|
||||
out += static_cast<char>(value);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (in[i] == '+')
|
||||
{
|
||||
out += ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// request_handler.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_REQUEST_HANDLER_HPP
|
||||
#define HTTP_SERVER3_REQUEST_HANDLER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
struct reply;
|
||||
struct request;
|
||||
|
||||
/// The common handler for all incoming requests.
|
||||
class request_handler
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct with a directory containing files to be served.
|
||||
explicit request_handler(const std::string& doc_root);
|
||||
|
||||
/// Handle a request and produce a reply.
|
||||
void handle_request(const request& req, reply& rep);
|
||||
|
||||
private:
|
||||
/// The directory containing the files to be served.
|
||||
std::string doc_root_;
|
||||
|
||||
/// Perform URL-decoding on a string. Returns false if the encoding was
|
||||
/// invalid.
|
||||
static bool url_decode(const std::string& in, std::string& out);
|
||||
};
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_REQUEST_HANDLER_HPP
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// request_parser.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_parser.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
request_parser::request_parser()
|
||||
: state_(method_start)
|
||||
{
|
||||
}
|
||||
|
||||
void request_parser::reset()
|
||||
{
|
||||
state_ = method_start;
|
||||
}
|
||||
|
||||
boost::tribool request_parser::consume(request& req, char input)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case method_start:
|
||||
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = method;
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case method:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = uri;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case uri:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = http_version_h;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.uri.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case http_version_h:
|
||||
if (input == 'H')
|
||||
{
|
||||
state_ = http_version_t_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_1:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_t_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_2:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_p;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_p:
|
||||
if (input == 'P')
|
||||
{
|
||||
state_ = http_version_slash;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_slash:
|
||||
if (input == '/')
|
||||
{
|
||||
req.http_version_major = 0;
|
||||
req.http_version_minor = 0;
|
||||
state_ = http_version_major_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
state_ = http_version_major;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major:
|
||||
if (input == '.')
|
||||
{
|
||||
state_ = http_version_minor_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
state_ = http_version_minor;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_1:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_line_start:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_3;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
|
||||
{
|
||||
state_ = header_lws;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.push_back(header());
|
||||
req.headers.back().name.push_back(input);
|
||||
state_ = header_name;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_lws:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (input == ' ' || input == '\t')
|
||||
{
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = header_value;
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_name:
|
||||
if (input == ':')
|
||||
{
|
||||
state_ = space_before_header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().name.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case space_before_header_value:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_value:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case expecting_newline_2:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_3:
|
||||
return (input == '\n');
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_char(int c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
bool request_parser::is_ctl(int c)
|
||||
{
|
||||
return (c >= 0 && c <= 31) || (c == 127);
|
||||
}
|
||||
|
||||
bool request_parser::is_tspecial(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
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 request_parser::is_digit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// request_parser.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_REQUEST_PARSER_HPP
|
||||
#define HTTP_SERVER3_REQUEST_PARSER_HPP
|
||||
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
struct request;
|
||||
|
||||
/// Parser for incoming requests.
|
||||
class request_parser
|
||||
{
|
||||
public:
|
||||
/// Construct ready to parse the request method.
|
||||
request_parser();
|
||||
|
||||
/// Reset to initial parser state.
|
||||
void reset();
|
||||
|
||||
/// Parse some data. The tribool return value is true when a complete request
|
||||
/// has been parsed, false if the data is invalid, indeterminate when more
|
||||
/// data is required. The InputIterator return value indicates how much of the
|
||||
/// input has been consumed.
|
||||
template <typename InputIterator>
|
||||
boost::tuple<boost::tribool, InputIterator> parse(request& req,
|
||||
InputIterator begin, InputIterator end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
boost::tribool result = consume(req, *begin++);
|
||||
if (result || !result)
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
boost::tribool result = boost::indeterminate;
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Handle the next character of input.
|
||||
boost::tribool consume(request& req, char input);
|
||||
|
||||
/// Check if a byte is an HTTP character.
|
||||
static bool is_char(int c);
|
||||
|
||||
/// Check if a byte is an HTTP control character.
|
||||
static bool is_ctl(int c);
|
||||
|
||||
/// Check if a byte is defined as an HTTP tspecial character.
|
||||
static bool is_tspecial(int c);
|
||||
|
||||
/// Check if a byte is a digit.
|
||||
static bool is_digit(int c);
|
||||
|
||||
/// The current state of the parser.
|
||||
enum state
|
||||
{
|
||||
method_start,
|
||||
method,
|
||||
uri,
|
||||
http_version_h,
|
||||
http_version_t_1,
|
||||
http_version_t_2,
|
||||
http_version_p,
|
||||
http_version_slash,
|
||||
http_version_major_start,
|
||||
http_version_major,
|
||||
http_version_minor_start,
|
||||
http_version_minor,
|
||||
expecting_newline_1,
|
||||
header_line_start,
|
||||
header_lws,
|
||||
header_name,
|
||||
space_before_header_value,
|
||||
header_value,
|
||||
expecting_newline_2,
|
||||
expecting_newline_3
|
||||
} state_;
|
||||
};
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_REQUEST_PARSER_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "server.hpp"
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
server::server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root, std::size_t thread_pool_size)
|
||||
: thread_pool_size_(thread_pool_size),
|
||||
signals_(io_context_),
|
||||
acceptor_(io_context_),
|
||||
new_connection_(),
|
||||
request_handler_(doc_root)
|
||||
{
|
||||
// Register to handle the signals that indicate when the server should exit.
|
||||
// It is safe to register for the same signal multiple times in a program,
|
||||
// provided all registration for the specified signal is made through Asio.
|
||||
signals_.add(SIGINT);
|
||||
signals_.add(SIGTERM);
|
||||
#if defined(SIGQUIT)
|
||||
signals_.add(SIGQUIT);
|
||||
#endif // defined(SIGQUIT)
|
||||
signals_.async_wait(boost::bind(&server::handle_stop, this));
|
||||
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
asio::ip::tcp::resolver resolver(io_context_);
|
||||
asio::ip::tcp::endpoint endpoint =
|
||||
*resolver.resolve(address, port).begin();
|
||||
acceptor_.open(endpoint.protocol());
|
||||
acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true));
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.listen();
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void server::run()
|
||||
{
|
||||
// Create a pool of threads to run all of the io_contexts.
|
||||
std::vector<boost::shared_ptr<asio::thread> > threads;
|
||||
for (std::size_t i = 0; i < thread_pool_size_; ++i)
|
||||
{
|
||||
boost::shared_ptr<asio::thread> thread(new asio::thread(
|
||||
boost::bind(&asio::io_context::run, &io_context_)));
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
// Wait for all threads in the pool to exit.
|
||||
for (std::size_t i = 0; i < threads.size(); ++i)
|
||||
threads[i]->join();
|
||||
}
|
||||
|
||||
void server::start_accept()
|
||||
{
|
||||
new_connection_.reset(new connection(io_context_, request_handler_));
|
||||
acceptor_.async_accept(new_connection_->socket(),
|
||||
boost::bind(&server::handle_accept, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void server::handle_accept(const asio::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
new_connection_->start();
|
||||
}
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void server::handle_stop()
|
||||
{
|
||||
io_context_.stop();
|
||||
}
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER3_SERVER_HPP
|
||||
#define HTTP_SERVER3_SERVER_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "connection.hpp"
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server3 {
|
||||
|
||||
/// The top-level class of the HTTP server.
|
||||
class server
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root, std::size_t thread_pool_size);
|
||||
|
||||
/// Run the server's io_context loop.
|
||||
void run();
|
||||
|
||||
private:
|
||||
/// Initiate an asynchronous accept operation.
|
||||
void start_accept();
|
||||
|
||||
/// Handle completion of an asynchronous accept operation.
|
||||
void handle_accept(const asio::error_code& e);
|
||||
|
||||
/// Handle a request to stop the server.
|
||||
void handle_stop();
|
||||
|
||||
/// The number of threads that will call io_context::run().
|
||||
std::size_t thread_pool_size_;
|
||||
|
||||
/// The io_context used to perform asynchronous operations.
|
||||
asio::io_context io_context_;
|
||||
|
||||
/// The signal_set is used to register for process termination notifications.
|
||||
asio::signal_set signals_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
/// The next connection to be accepted.
|
||||
connection_ptr new_connection_;
|
||||
|
||||
/// The handler for all incoming requests.
|
||||
request_handler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace server3
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_SERVER_HPP
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// file_handler.cpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "file_handler.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "mime_types.hpp"
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
file_handler::file_handler(const std::string& doc_root)
|
||||
: doc_root_(doc_root)
|
||||
{
|
||||
}
|
||||
|
||||
void file_handler::operator()(const request& req, reply& rep)
|
||||
{
|
||||
// Decode url to path.
|
||||
std::string request_path;
|
||||
if (!url_decode(req.uri, request_path))
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (request_path.empty() || request_path[0] != '/'
|
||||
|| request_path.find("..") != std::string::npos)
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// If path ends in slash (i.e. is a directory) then add "index.html".
|
||||
if (request_path[request_path.size() - 1] == '/')
|
||||
{
|
||||
request_path += "index.html";
|
||||
}
|
||||
|
||||
// Determine the file extension.
|
||||
std::size_t last_slash_pos = request_path.find_last_of("/");
|
||||
std::size_t last_dot_pos = request_path.find_last_of(".");
|
||||
std::string extension;
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
|
||||
{
|
||||
extension = request_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
|
||||
// Open the file to send back.
|
||||
std::string full_path = doc_root_ + request_path;
|
||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!is)
|
||||
{
|
||||
rep = reply::stock_reply(reply::not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill out the reply to be sent to the client.
|
||||
rep.status = reply::ok;
|
||||
char buf[512];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
rep.content.append(buf, is.gcount());
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = mime_types::extension_to_type(extension);
|
||||
}
|
||||
|
||||
bool file_handler::url_decode(const std::string& in, std::string& out)
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(in.size());
|
||||
for (std::size_t i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if (in[i] == '%')
|
||||
{
|
||||
if (i + 3 <= in.size())
|
||||
{
|
||||
int value = 0;
|
||||
std::istringstream is(in.substr(i + 1, 2));
|
||||
if (is >> std::hex >> value)
|
||||
{
|
||||
out += static_cast<char>(value);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (in[i] == '+')
|
||||
{
|
||||
out += ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// file_handler.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_FILE_HANDLER_HPP
|
||||
#define HTTP_SERVER4_FILE_HANDLER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
struct reply;
|
||||
struct request;
|
||||
|
||||
/// The common handler for all incoming requests.
|
||||
class file_handler
|
||||
{
|
||||
public:
|
||||
/// Construct with a directory containing files to be served.
|
||||
explicit file_handler(const std::string& doc_root);
|
||||
|
||||
/// Handle a request and produce a reply.
|
||||
void operator()(const request& req, reply& rep);
|
||||
|
||||
private:
|
||||
/// The directory containing the files to be served.
|
||||
std::string doc_root_;
|
||||
|
||||
/// Perform URL-decoding on a string. Returns false if the encoding was
|
||||
/// invalid.
|
||||
static bool url_decode(const std::string& in, std::string& out);
|
||||
};
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_FILE_HANDLER_HPP
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// header.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_HEADER_HPP
|
||||
#define HTTP_SERVER4_HEADER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_HEADER_HPP
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <signal.h>
|
||||
#include "server.hpp"
|
||||
#include "file_handler.hpp"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: http_server <address> <port> <doc_root>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 80 .\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 80 .\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
// Launch the initial server coroutine.
|
||||
http::server4::server(io_context, argv[1], argv[2],
|
||||
http::server4::file_handler(argv[3]))();
|
||||
|
||||
// Wait for signals indicating time to shut down.
|
||||
asio::signal_set signals(io_context);
|
||||
signals.add(SIGINT);
|
||||
signals.add(SIGTERM);
|
||||
#if defined(SIGQUIT)
|
||||
signals.add(SIGQUIT);
|
||||
#endif // defined(SIGQUIT)
|
||||
signals.async_wait(boost::bind(
|
||||
&asio::io_context::stop, &io_context));
|
||||
|
||||
// Run the server.
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// mime_types.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "mime_types.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
namespace mime_types {
|
||||
|
||||
struct mapping
|
||||
{
|
||||
const char* extension;
|
||||
const char* mime_type;
|
||||
} mappings[] =
|
||||
{
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "png", "image/png" },
|
||||
{ 0, 0 } // Marks end of list.
|
||||
};
|
||||
|
||||
std::string extension_to_type(const std::string& extension)
|
||||
{
|
||||
for (mapping* m = mappings; m->extension; ++m)
|
||||
{
|
||||
if (m->extension == extension)
|
||||
{
|
||||
return m->mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// mime_types.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_MIME_TYPES_HPP
|
||||
#define HTTP_SERVER4_MIME_TYPES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
namespace mime_types {
|
||||
|
||||
/// Convert a file extension into a MIME type.
|
||||
std::string extension_to_type(const std::string& extension);
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_MIME_TYPES_HPP
|
||||
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// reply.cpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "reply.hpp"
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string ok =
|
||||
"HTTP/1.0 200 OK\r\n";
|
||||
const std::string created =
|
||||
"HTTP/1.0 201 Created\r\n";
|
||||
const std::string accepted =
|
||||
"HTTP/1.0 202 Accepted\r\n";
|
||||
const std::string no_content =
|
||||
"HTTP/1.0 204 No Content\r\n";
|
||||
const std::string multiple_choices =
|
||||
"HTTP/1.0 300 Multiple Choices\r\n";
|
||||
const std::string moved_permanently =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n";
|
||||
const std::string moved_temporarily =
|
||||
"HTTP/1.0 302 Moved Temporarily\r\n";
|
||||
const std::string not_modified =
|
||||
"HTTP/1.0 304 Not Modified\r\n";
|
||||
const std::string bad_request =
|
||||
"HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string unauthorized =
|
||||
"HTTP/1.0 401 Unauthorized\r\n";
|
||||
const std::string forbidden =
|
||||
"HTTP/1.0 403 Forbidden\r\n";
|
||||
const std::string not_found =
|
||||
"HTTP/1.0 404 Not Found\r\n";
|
||||
const std::string internal_server_error =
|
||||
"HTTP/1.0 500 Internal Server Error\r\n";
|
||||
const std::string not_implemented =
|
||||
"HTTP/1.0 501 Not Implemented\r\n";
|
||||
const std::string bad_gateway =
|
||||
"HTTP/1.0 502 Bad Gateway\r\n";
|
||||
const std::string service_unavailable =
|
||||
"HTTP/1.0 503 Service Unavailable\r\n";
|
||||
|
||||
asio::const_buffer to_buffer(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return asio::buffer(ok);
|
||||
case reply::created:
|
||||
return asio::buffer(created);
|
||||
case reply::accepted:
|
||||
return asio::buffer(accepted);
|
||||
case reply::no_content:
|
||||
return asio::buffer(no_content);
|
||||
case reply::multiple_choices:
|
||||
return asio::buffer(multiple_choices);
|
||||
case reply::moved_permanently:
|
||||
return asio::buffer(moved_permanently);
|
||||
case reply::moved_temporarily:
|
||||
return asio::buffer(moved_temporarily);
|
||||
case reply::not_modified:
|
||||
return asio::buffer(not_modified);
|
||||
case reply::bad_request:
|
||||
return asio::buffer(bad_request);
|
||||
case reply::unauthorized:
|
||||
return asio::buffer(unauthorized);
|
||||
case reply::forbidden:
|
||||
return asio::buffer(forbidden);
|
||||
case reply::not_found:
|
||||
return asio::buffer(not_found);
|
||||
case reply::internal_server_error:
|
||||
return asio::buffer(internal_server_error);
|
||||
case reply::not_implemented:
|
||||
return asio::buffer(not_implemented);
|
||||
case reply::bad_gateway:
|
||||
return asio::buffer(bad_gateway);
|
||||
case reply::service_unavailable:
|
||||
return asio::buffer(service_unavailable);
|
||||
default:
|
||||
return asio::buffer(internal_server_error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
buffers.push_back(status_strings::to_buffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(asio::buffer(h.name));
|
||||
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(asio::buffer(h.value));
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
buffers.push_back(asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
namespace stock_replies {
|
||||
|
||||
const char ok[] = "";
|
||||
const char created[] =
|
||||
"<html>"
|
||||
"<head><title>Created</title></head>"
|
||||
"<body><h1>201 Created</h1></body>"
|
||||
"</html>";
|
||||
const char accepted[] =
|
||||
"<html>"
|
||||
"<head><title>Accepted</title></head>"
|
||||
"<body><h1>202 Accepted</h1></body>"
|
||||
"</html>";
|
||||
const char no_content[] =
|
||||
"<html>"
|
||||
"<head><title>No Content</title></head>"
|
||||
"<body><h1>204 Content</h1></body>"
|
||||
"</html>";
|
||||
const char multiple_choices[] =
|
||||
"<html>"
|
||||
"<head><title>Multiple Choices</title></head>"
|
||||
"<body><h1>300 Multiple Choices</h1></body>"
|
||||
"</html>";
|
||||
const char moved_permanently[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Permanently</title></head>"
|
||||
"<body><h1>301 Moved Permanently</h1></body>"
|
||||
"</html>";
|
||||
const char moved_temporarily[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Temporarily</title></head>"
|
||||
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||
"</html>";
|
||||
const char not_modified[] =
|
||||
"<html>"
|
||||
"<head><title>Not Modified</title></head>"
|
||||
"<body><h1>304 Not Modified</h1></body>"
|
||||
"</html>";
|
||||
const char bad_request[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Request</title></head>"
|
||||
"<body><h1>400 Bad Request</h1></body>"
|
||||
"</html>";
|
||||
const char unauthorized[] =
|
||||
"<html>"
|
||||
"<head><title>Unauthorized</title></head>"
|
||||
"<body><h1>401 Unauthorized</h1></body>"
|
||||
"</html>";
|
||||
const char forbidden[] =
|
||||
"<html>"
|
||||
"<head><title>Forbidden</title></head>"
|
||||
"<body><h1>403 Forbidden</h1></body>"
|
||||
"</html>";
|
||||
const char not_found[] =
|
||||
"<html>"
|
||||
"<head><title>Not Found</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body>"
|
||||
"</html>";
|
||||
const char internal_server_error[] =
|
||||
"<html>"
|
||||
"<head><title>Internal Server Error</title></head>"
|
||||
"<body><h1>500 Internal Server Error</h1></body>"
|
||||
"</html>";
|
||||
const char not_implemented[] =
|
||||
"<html>"
|
||||
"<head><title>Not Implemented</title></head>"
|
||||
"<body><h1>501 Not Implemented</h1></body>"
|
||||
"</html>";
|
||||
const char bad_gateway[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Gateway</title></head>"
|
||||
"<body><h1>502 Bad Gateway</h1></body>"
|
||||
"</html>";
|
||||
const char service_unavailable[] =
|
||||
"<html>"
|
||||
"<head><title>Service Unavailable</title></head>"
|
||||
"<body><h1>503 Service Unavailable</h1></body>"
|
||||
"</html>";
|
||||
|
||||
std::string to_string(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return ok;
|
||||
case reply::created:
|
||||
return created;
|
||||
case reply::accepted:
|
||||
return accepted;
|
||||
case reply::no_content:
|
||||
return no_content;
|
||||
case reply::multiple_choices:
|
||||
return multiple_choices;
|
||||
case reply::moved_permanently:
|
||||
return moved_permanently;
|
||||
case reply::moved_temporarily:
|
||||
return moved_temporarily;
|
||||
case reply::not_modified:
|
||||
return not_modified;
|
||||
case reply::bad_request:
|
||||
return bad_request;
|
||||
case reply::unauthorized:
|
||||
return unauthorized;
|
||||
case reply::forbidden:
|
||||
return forbidden;
|
||||
case reply::not_found:
|
||||
return not_found;
|
||||
case reply::internal_server_error:
|
||||
return internal_server_error;
|
||||
case reply::not_implemented:
|
||||
return not_implemented;
|
||||
case reply::bad_gateway:
|
||||
return bad_gateway;
|
||||
case reply::service_unavailable:
|
||||
return service_unavailable;
|
||||
default:
|
||||
return internal_server_error;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stock_replies
|
||||
|
||||
reply reply::stock_reply(reply::status_type status)
|
||||
{
|
||||
reply rep;
|
||||
rep.status = status;
|
||||
rep.content = stock_replies::to_string(status);
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "text/html";
|
||||
return rep;
|
||||
}
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// reply.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_REPLY_HPP
|
||||
#define HTTP_SERVER4_REPLY_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <asio.hpp>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
/// A reply to be sent to a client.
|
||||
struct reply
|
||||
{
|
||||
/// The status of the reply.
|
||||
enum status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
} status;
|
||||
|
||||
/// The headers to be included in the reply.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The content to be sent in the reply.
|
||||
std::string content;
|
||||
|
||||
/// Convert the reply into a vector of buffers. The buffers do not own the
|
||||
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||
/// not be changed until the write operation has completed.
|
||||
std::vector<asio::const_buffer> to_buffers();
|
||||
|
||||
/// Get a stock reply.
|
||||
static reply stock_reply(status_type status);
|
||||
};
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_REPLY_HPP
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// request.hpp
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_REQUEST_HPP
|
||||
#define HTTP_SERVER4_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
/// A request received from a client.
|
||||
struct request
|
||||
{
|
||||
/// The request method, e.g. "GET", "POST".
|
||||
std::string method;
|
||||
|
||||
/// The requested URI, such as a path to a file.
|
||||
std::string uri;
|
||||
|
||||
/// Major version number, usually 1.
|
||||
int http_version_major;
|
||||
|
||||
/// Minor version number, usually 0 or 1.
|
||||
int http_version_minor;
|
||||
|
||||
/// The headers included with the request.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The optional content sent with the request.
|
||||
std::string content;
|
||||
};
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_REQUEST_HPP
|
||||
@@ -0,0 +1,226 @@
|
||||
//
|
||||
// request_parser.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "request_parser.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
// Enable the pseudo-keywords reenter, yield and fork.
|
||||
#include <asio/yield.hpp>
|
||||
|
||||
std::string request_parser::content_length_name_ = "Content-Length";
|
||||
|
||||
boost::tribool request_parser::consume(request& req, char c)
|
||||
{
|
||||
reenter (this)
|
||||
{
|
||||
req.method.clear();
|
||||
req.uri.clear();
|
||||
req.http_version_major = 0;
|
||||
req.http_version_minor = 0;
|
||||
req.headers.clear();
|
||||
req.content.clear();
|
||||
content_length_ = 0;
|
||||
|
||||
// Request method.
|
||||
while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ' ')
|
||||
{
|
||||
req.method.push_back(c);
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
if (req.method.empty())
|
||||
return false;
|
||||
|
||||
// Space.
|
||||
if (c != ' ') return false;
|
||||
yield return boost::indeterminate;
|
||||
|
||||
// URI.
|
||||
while (!is_ctl(c) && c != ' ')
|
||||
{
|
||||
req.uri.push_back(c);
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
if (req.uri.empty()) return false;
|
||||
|
||||
// Space.
|
||||
if (c != ' ') return false;
|
||||
yield return boost::indeterminate;
|
||||
|
||||
// HTTP protocol identifier.
|
||||
if (c != 'H') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != 'T') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != 'T') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != 'P') return false;
|
||||
yield return boost::indeterminate;
|
||||
|
||||
// Slash.
|
||||
if (c != '/') return false;
|
||||
yield return boost::indeterminate;
|
||||
|
||||
// Major version number.
|
||||
if (!is_digit(c)) return false;
|
||||
while (is_digit(c))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + c - '0';
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
|
||||
// Dot.
|
||||
if (c != '.') return false;
|
||||
yield return boost::indeterminate;
|
||||
|
||||
// Minor version number.
|
||||
if (!is_digit(c)) return false;
|
||||
while (is_digit(c))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + c - '0';
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
|
||||
// CRLF.
|
||||
if (c != '\r') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != '\n') return false;
|
||||
yield return boost::indeterminate;
|
||||
|
||||
// Headers.
|
||||
while ((is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != '\r')
|
||||
|| (c == ' ' || c == '\t'))
|
||||
{
|
||||
if (c == ' ' || c == '\t')
|
||||
{
|
||||
// Leading whitespace. Must be continuation of previous header's value.
|
||||
if (req.headers.empty()) return false;
|
||||
while (c == ' ' || c == '\t')
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start the next header.
|
||||
req.headers.push_back(header());
|
||||
|
||||
// Header name.
|
||||
while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ':')
|
||||
{
|
||||
req.headers.back().name.push_back(c);
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
|
||||
// Colon and space separates the header name from the header value.
|
||||
if (c != ':') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != ' ') return false;
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
|
||||
// Header value.
|
||||
while (is_char(c) && !is_ctl(c) && c != '\r')
|
||||
{
|
||||
req.headers.back().value.push_back(c);
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
|
||||
// CRLF.
|
||||
if (c != '\r') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != '\n') return false;
|
||||
yield return boost::indeterminate;
|
||||
}
|
||||
|
||||
// CRLF.
|
||||
if (c != '\r') return false;
|
||||
yield return boost::indeterminate;
|
||||
if (c != '\n') return false;
|
||||
|
||||
// Check for optional Content-Length header.
|
||||
for (std::size_t i = 0; i < req.headers.size(); ++i)
|
||||
{
|
||||
if (headers_equal(req.headers[i].name, content_length_name_))
|
||||
{
|
||||
try
|
||||
{
|
||||
content_length_ =
|
||||
boost::lexical_cast<std::size_t>(req.headers[i].value);
|
||||
}
|
||||
catch (boost::bad_lexical_cast&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content.
|
||||
while (req.content.size() < content_length_)
|
||||
{
|
||||
yield return boost::indeterminate;
|
||||
req.content.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disable the pseudo-keywords reenter, yield and fork.
|
||||
#include <asio/unyield.hpp>
|
||||
|
||||
bool request_parser::is_char(int c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
bool request_parser::is_ctl(int c)
|
||||
{
|
||||
return (c >= 0 && c <= 31) || (c == 127);
|
||||
}
|
||||
|
||||
bool request_parser::is_tspecial(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
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 request_parser::is_digit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
bool request_parser::tolower_compare(char a, char b)
|
||||
{
|
||||
return std::tolower(a) == std::tolower(b);
|
||||
}
|
||||
|
||||
bool request_parser::headers_equal(const std::string& a, const std::string& b)
|
||||
{
|
||||
if (a.length() != b.length())
|
||||
return false;
|
||||
|
||||
return std::equal(a.begin(), a.end(), b.begin(),
|
||||
&request_parser::tolower_compare);
|
||||
}
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// request_parser.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_REQUEST_PARSER_HPP
|
||||
#define HTTP_SERVER4_REQUEST_PARSER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <asio/coroutine.hpp>
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
struct request;
|
||||
|
||||
/// Parser for incoming requests.
|
||||
class request_parser : asio::coroutine
|
||||
{
|
||||
public:
|
||||
/// Parse some data. The tribool return value is true when a complete request
|
||||
/// has been parsed, false if the data is invalid, indeterminate when more
|
||||
/// data is required. The InputIterator return value indicates how much of the
|
||||
/// input has been consumed.
|
||||
template <typename InputIterator>
|
||||
boost::tuple<boost::tribool, InputIterator> parse(request& req,
|
||||
InputIterator begin, InputIterator end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
boost::tribool result = consume(req, *begin++);
|
||||
if (result || !result)
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
boost::tribool result = boost::indeterminate;
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
|
||||
private:
|
||||
/// The name of the content length header.
|
||||
static std::string content_length_name_;
|
||||
|
||||
/// Content length as decoded from headers. Defaults to 0.
|
||||
std::size_t content_length_;
|
||||
|
||||
/// Handle the next character of input.
|
||||
boost::tribool consume(request& req, char input);
|
||||
|
||||
/// Check if a byte is an HTTP character.
|
||||
static bool is_char(int c);
|
||||
|
||||
/// Check if a byte is an HTTP control character.
|
||||
static bool is_ctl(int c);
|
||||
|
||||
/// Check if a byte is defined as an HTTP tspecial character.
|
||||
static bool is_tspecial(int c);
|
||||
|
||||
/// Check if a byte is a digit.
|
||||
static bool is_digit(int c);
|
||||
|
||||
/// Check if two characters are equal, without regard to case.
|
||||
static bool tolower_compare(char a, char b);
|
||||
|
||||
/// Check whether the two request header names match.
|
||||
bool headers_equal(const std::string& a, const std::string& b);
|
||||
};
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_REQUEST_PARSER_HPP
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "server.hpp"
|
||||
#include "request.hpp"
|
||||
#include "reply.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
server::server(asio::io_context& io_context,
|
||||
const std::string& address, const std::string& port,
|
||||
boost::function<void(const request&, reply&)> request_handler)
|
||||
: request_handler_(request_handler)
|
||||
{
|
||||
tcp::resolver resolver(io_context);
|
||||
asio::ip::tcp::endpoint endpoint =
|
||||
*resolver.resolve(address, port).begin();
|
||||
acceptor_.reset(new tcp::acceptor(io_context, endpoint));
|
||||
}
|
||||
|
||||
// Enable the pseudo-keywords reenter, yield and fork.
|
||||
#include <asio/yield.hpp>
|
||||
|
||||
void server::operator()(asio::error_code ec, std::size_t length)
|
||||
{
|
||||
// In this example we keep the error handling code in one place by
|
||||
// hoisting it outside the coroutine. An alternative approach would be to
|
||||
// check the value of ec after each yield for an asynchronous operation.
|
||||
if (!ec)
|
||||
{
|
||||
// On reentering a coroutine, control jumps to the location of the last
|
||||
// yield or fork. The argument to the "reenter" pseudo-keyword can be a
|
||||
// pointer or reference to an object of type coroutine.
|
||||
reenter (this)
|
||||
{
|
||||
// Loop to accept incoming connections.
|
||||
do
|
||||
{
|
||||
// Create a new socket for the next incoming connection.
|
||||
socket_.reset(new tcp::socket(acceptor_->get_executor()));
|
||||
|
||||
// Accept a new connection. The "yield" pseudo-keyword saves the current
|
||||
// line number and exits the coroutine's "reenter" block. We use the
|
||||
// server coroutine as the completion handler for the async_accept
|
||||
// operation. When the asynchronous operation completes, the io_context
|
||||
// invokes the function call operator, we "reenter" the coroutine, and
|
||||
// then control resumes at the following line.
|
||||
yield acceptor_->async_accept(*socket_, *this);
|
||||
|
||||
// We "fork" by cloning a new server coroutine to handle the connection.
|
||||
// After forking we have a parent coroutine and a child coroutine. Both
|
||||
// parent and child continue execution at the following line. They can
|
||||
// be distinguished using the functions coroutine::is_parent() and
|
||||
// coroutine::is_child().
|
||||
fork server(*this)();
|
||||
|
||||
// The parent continues looping to accept the next incoming connection.
|
||||
// The child exits the loop and processes the connection.
|
||||
} while (is_parent());
|
||||
|
||||
// Create the objects needed to receive a request on the connection.
|
||||
buffer_.reset(new boost::array<char, 8192>);
|
||||
request_.reset(new request);
|
||||
|
||||
// Loop until a complete request (or an invalid one) has been received.
|
||||
do
|
||||
{
|
||||
// Receive some more data. When control resumes at the following line,
|
||||
// the ec and length parameters reflect the result of the asynchronous
|
||||
// operation.
|
||||
yield socket_->async_read_some(asio::buffer(*buffer_), *this);
|
||||
|
||||
// Parse the data we just received.
|
||||
boost::tie(valid_request_, boost::tuples::ignore)
|
||||
= request_parser_.parse(*request_,
|
||||
buffer_->data(), buffer_->data() + length);
|
||||
|
||||
// An indeterminate result means we need more data, so keep looping.
|
||||
} while (boost::indeterminate(valid_request_));
|
||||
|
||||
// Create the reply object that will be sent back to the client.
|
||||
reply_.reset(new reply);
|
||||
|
||||
if (valid_request_)
|
||||
{
|
||||
// A valid request was received. Call the user-supplied function object
|
||||
// to process the request and compose a reply.
|
||||
request_handler_(*request_, *reply_);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The request was invalid.
|
||||
*reply_ = reply::stock_reply(reply::bad_request);
|
||||
}
|
||||
|
||||
// Send the reply back to the client.
|
||||
yield asio::async_write(*socket_, reply_->to_buffers(), *this);
|
||||
|
||||
// Initiate graceful connection closure.
|
||||
socket_->shutdown(tcp::socket::shutdown_both, ec);
|
||||
}
|
||||
}
|
||||
|
||||
// If an error occurs then the coroutine is not reentered. Consequently, no
|
||||
// new asynchronous operations are started. This means that all shared_ptr
|
||||
// references will disappear and the resources associated with the coroutine
|
||||
// will be destroyed automatically after this function call returns.
|
||||
}
|
||||
|
||||
// Disable the pseudo-keywords reenter, yield and fork.
|
||||
#include <asio/unyield.hpp>
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER4_SERVER_HPP
|
||||
#define HTTP_SERVER4_SERVER_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <string>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "request_parser.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server4 {
|
||||
|
||||
struct request;
|
||||
struct reply;
|
||||
|
||||
/// The top-level coroutine of the HTTP server.
|
||||
class server : asio::coroutine
|
||||
{
|
||||
public:
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(asio::io_context& io_context,
|
||||
const std::string& address, const std::string& port,
|
||||
boost::function<void(const request&, reply&)> request_handler);
|
||||
|
||||
/// Perform work associated with the server.
|
||||
void operator()(
|
||||
asio::error_code ec = asio::error_code(),
|
||||
std::size_t length = 0);
|
||||
|
||||
private:
|
||||
typedef asio::ip::tcp tcp;
|
||||
|
||||
/// The user-supplied handler for all incoming requests.
|
||||
boost::function<void(const request&, reply&)> request_handler_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
boost::shared_ptr<tcp::acceptor> acceptor_;
|
||||
|
||||
/// The current connection from a client.
|
||||
boost::shared_ptr<tcp::socket> socket_;
|
||||
|
||||
/// Buffer for incoming data.
|
||||
boost::shared_ptr<boost::array<char, 8192> > buffer_;
|
||||
|
||||
/// The incoming request.
|
||||
boost::shared_ptr<request> request_;
|
||||
|
||||
/// Whether the request is valid or not.
|
||||
boost::tribool valid_request_;
|
||||
|
||||
/// The parser for the incoming request.
|
||||
request_parser request_parser_;
|
||||
|
||||
/// The reply to be sent back to the client.
|
||||
boost::shared_ptr<reply> reply_;
|
||||
};
|
||||
|
||||
} // namespace server4
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER4_SERVER_HPP
|
||||
10
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/icmp/.gitignore
vendored
Normal file
10
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/icmp/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
ping
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// icmp_header.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef ICMP_HEADER_HPP
|
||||
#define ICMP_HEADER_HPP
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <algorithm>
|
||||
|
||||
// ICMP header for both IPv4 and IPv6.
|
||||
//
|
||||
// The wire format of an ICMP header is:
|
||||
//
|
||||
// 0 8 16 31
|
||||
// +---------------+---------------+------------------------------+ ---
|
||||
// | | | | ^
|
||||
// | type | code | checksum | |
|
||||
// | | | | |
|
||||
// +---------------+---------------+------------------------------+ 8 bytes
|
||||
// | | | |
|
||||
// | identifier | sequence number | |
|
||||
// | | | v
|
||||
// +-------------------------------+------------------------------+ ---
|
||||
|
||||
class icmp_header
|
||||
{
|
||||
public:
|
||||
enum { echo_reply = 0, destination_unreachable = 3, source_quench = 4,
|
||||
redirect = 5, echo_request = 8, time_exceeded = 11, parameter_problem = 12,
|
||||
timestamp_request = 13, timestamp_reply = 14, info_request = 15,
|
||||
info_reply = 16, address_request = 17, address_reply = 18 };
|
||||
|
||||
icmp_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
|
||||
|
||||
unsigned char type() const { return rep_[0]; }
|
||||
unsigned char code() const { return rep_[1]; }
|
||||
unsigned short checksum() const { return decode(2, 3); }
|
||||
unsigned short identifier() const { return decode(4, 5); }
|
||||
unsigned short sequence_number() const { return decode(6, 7); }
|
||||
|
||||
void type(unsigned char n) { rep_[0] = n; }
|
||||
void code(unsigned char n) { rep_[1] = n; }
|
||||
void checksum(unsigned short n) { encode(2, 3, n); }
|
||||
void identifier(unsigned short n) { encode(4, 5, n); }
|
||||
void sequence_number(unsigned short n) { encode(6, 7, n); }
|
||||
|
||||
friend std::istream& operator>>(std::istream& is, icmp_header& header)
|
||||
{ return is.read(reinterpret_cast<char*>(header.rep_), 8); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const icmp_header& header)
|
||||
{ return os.write(reinterpret_cast<const char*>(header.rep_), 8); }
|
||||
|
||||
private:
|
||||
unsigned short decode(int a, int b) const
|
||||
{ return (rep_[a] << 8) + rep_[b]; }
|
||||
|
||||
void encode(int a, int b, unsigned short n)
|
||||
{
|
||||
rep_[a] = static_cast<unsigned char>(n >> 8);
|
||||
rep_[b] = static_cast<unsigned char>(n & 0xFF);
|
||||
}
|
||||
|
||||
unsigned char rep_[8];
|
||||
};
|
||||
|
||||
template <typename Iterator>
|
||||
void compute_checksum(icmp_header& header,
|
||||
Iterator body_begin, Iterator body_end)
|
||||
{
|
||||
unsigned int sum = (header.type() << 8) + header.code()
|
||||
+ header.identifier() + header.sequence_number();
|
||||
|
||||
Iterator body_iter = body_begin;
|
||||
while (body_iter != body_end)
|
||||
{
|
||||
sum += (static_cast<unsigned char>(*body_iter++) << 8);
|
||||
if (body_iter != body_end)
|
||||
sum += static_cast<unsigned char>(*body_iter++);
|
||||
}
|
||||
|
||||
sum = (sum >> 16) + (sum & 0xFFFF);
|
||||
sum += (sum >> 16);
|
||||
header.checksum(static_cast<unsigned short>(~sum));
|
||||
}
|
||||
|
||||
#endif // ICMP_HEADER_HPP
|
||||
102
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/icmp/ipv4_header.hpp
vendored
Normal file
102
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/icmp/ipv4_header.hpp
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// ipv4_header.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef IPV4_HEADER_HPP
|
||||
#define IPV4_HEADER_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <asio/ip/address_v4.hpp>
|
||||
|
||||
// Packet header for IPv4.
|
||||
//
|
||||
// The wire format of an IPv4 header is:
|
||||
//
|
||||
// 0 8 16 31
|
||||
// +-------+-------+---------------+------------------------------+ ---
|
||||
// | | | | | ^
|
||||
// |version|header | type of | total length in bytes | |
|
||||
// | (4) | length| service | | |
|
||||
// +-------+-------+---------------+-+-+-+------------------------+ |
|
||||
// | | | | | | |
|
||||
// | identification |0|D|M| fragment offset | |
|
||||
// | | |F|F| | |
|
||||
// +---------------+---------------+-+-+-+------------------------+ |
|
||||
// | | | | |
|
||||
// | time to live | protocol | header checksum | 20 bytes
|
||||
// | | | | |
|
||||
// +---------------+---------------+------------------------------+ |
|
||||
// | | |
|
||||
// | source IPv4 address | |
|
||||
// | | |
|
||||
// +--------------------------------------------------------------+ |
|
||||
// | | |
|
||||
// | destination IPv4 address | |
|
||||
// | | v
|
||||
// +--------------------------------------------------------------+ ---
|
||||
// | | ^
|
||||
// | | |
|
||||
// / options (if any) / 0 - 40
|
||||
// / / bytes
|
||||
// | | |
|
||||
// | | v
|
||||
// +--------------------------------------------------------------+ ---
|
||||
|
||||
class ipv4_header
|
||||
{
|
||||
public:
|
||||
ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
|
||||
|
||||
unsigned char version() const { return (rep_[0] >> 4) & 0xF; }
|
||||
unsigned short header_length() const { return (rep_[0] & 0xF) * 4; }
|
||||
unsigned char type_of_service() const { return rep_[1]; }
|
||||
unsigned short total_length() const { return decode(2, 3); }
|
||||
unsigned short identification() const { return decode(4, 5); }
|
||||
bool dont_fragment() const { return (rep_[6] & 0x40) != 0; }
|
||||
bool more_fragments() const { return (rep_[6] & 0x20) != 0; }
|
||||
unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; }
|
||||
unsigned int time_to_live() const { return rep_[8]; }
|
||||
unsigned char protocol() const { return rep_[9]; }
|
||||
unsigned short header_checksum() const { return decode(10, 11); }
|
||||
|
||||
asio::ip::address_v4 source_address() const
|
||||
{
|
||||
asio::ip::address_v4::bytes_type bytes
|
||||
= { { rep_[12], rep_[13], rep_[14], rep_[15] } };
|
||||
return asio::ip::address_v4(bytes);
|
||||
}
|
||||
|
||||
asio::ip::address_v4 destination_address() const
|
||||
{
|
||||
asio::ip::address_v4::bytes_type bytes
|
||||
= { { rep_[16], rep_[17], rep_[18], rep_[19] } };
|
||||
return asio::ip::address_v4(bytes);
|
||||
}
|
||||
|
||||
friend std::istream& operator>>(std::istream& is, ipv4_header& header)
|
||||
{
|
||||
is.read(reinterpret_cast<char*>(header.rep_), 20);
|
||||
if (header.version() != 4)
|
||||
is.setstate(std::ios::failbit);
|
||||
std::streamsize options_length = header.header_length() - 20;
|
||||
if (options_length < 0 || options_length > 40)
|
||||
is.setstate(std::ios::failbit);
|
||||
else
|
||||
is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length);
|
||||
return is;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned short decode(int a, int b) const
|
||||
{ return (rep_[a] << 8) + rep_[b]; }
|
||||
|
||||
unsigned char rep_[60];
|
||||
};
|
||||
|
||||
#endif // IPV4_HEADER_HPP
|
||||
163
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/icmp/ping.cpp
vendored
Normal file
163
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp03/icmp/ping.cpp
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// ping.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <istream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
#include "icmp_header.hpp"
|
||||
#include "ipv4_header.hpp"
|
||||
|
||||
using asio::ip::icmp;
|
||||
using asio::steady_timer;
|
||||
namespace chrono = asio::chrono;
|
||||
|
||||
class pinger
|
||||
{
|
||||
public:
|
||||
pinger(asio::io_context& io_context, const char* destination)
|
||||
: resolver_(io_context), socket_(io_context, icmp::v4()),
|
||||
timer_(io_context), sequence_number_(0), num_replies_(0)
|
||||
{
|
||||
destination_ = *resolver_.resolve(icmp::v4(), destination, "").begin();
|
||||
|
||||
start_send();
|
||||
start_receive();
|
||||
}
|
||||
|
||||
private:
|
||||
void start_send()
|
||||
{
|
||||
std::string body("\"Hello!\" from Asio ping.");
|
||||
|
||||
// Create an ICMP header for an echo request.
|
||||
icmp_header echo_request;
|
||||
echo_request.type(icmp_header::echo_request);
|
||||
echo_request.code(0);
|
||||
echo_request.identifier(get_identifier());
|
||||
echo_request.sequence_number(++sequence_number_);
|
||||
compute_checksum(echo_request, body.begin(), body.end());
|
||||
|
||||
// Encode the request packet.
|
||||
asio::streambuf request_buffer;
|
||||
std::ostream os(&request_buffer);
|
||||
os << echo_request << body;
|
||||
|
||||
// Send the request.
|
||||
time_sent_ = steady_timer::clock_type::now();
|
||||
socket_.send_to(request_buffer.data(), destination_);
|
||||
|
||||
// Wait up to five seconds for a reply.
|
||||
num_replies_ = 0;
|
||||
timer_.expires_at(time_sent_ + chrono::seconds(5));
|
||||
timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
|
||||
}
|
||||
|
||||
void handle_timeout()
|
||||
{
|
||||
if (num_replies_ == 0)
|
||||
std::cout << "Request timed out" << std::endl;
|
||||
|
||||
// Requests must be sent no less than one second apart.
|
||||
timer_.expires_at(time_sent_ + chrono::seconds(1));
|
||||
timer_.async_wait(boost::bind(&pinger::start_send, this));
|
||||
}
|
||||
|
||||
void start_receive()
|
||||
{
|
||||
// Discard any data already in the buffer.
|
||||
reply_buffer_.consume(reply_buffer_.size());
|
||||
|
||||
// Wait for a reply. We prepare the buffer to receive up to 64KB.
|
||||
socket_.async_receive(reply_buffer_.prepare(65536),
|
||||
boost::bind(&pinger::handle_receive, this, boost::placeholders::_2));
|
||||
}
|
||||
|
||||
void handle_receive(std::size_t length)
|
||||
{
|
||||
// The actual number of bytes received is committed to the buffer so that we
|
||||
// can extract it using a std::istream object.
|
||||
reply_buffer_.commit(length);
|
||||
|
||||
// Decode the reply packet.
|
||||
std::istream is(&reply_buffer_);
|
||||
ipv4_header ipv4_hdr;
|
||||
icmp_header icmp_hdr;
|
||||
is >> ipv4_hdr >> icmp_hdr;
|
||||
|
||||
// We can receive all ICMP packets received by the host, so we need to
|
||||
// filter out only the echo replies that match the our identifier and
|
||||
// expected sequence number.
|
||||
if (is && icmp_hdr.type() == icmp_header::echo_reply
|
||||
&& icmp_hdr.identifier() == get_identifier()
|
||||
&& icmp_hdr.sequence_number() == sequence_number_)
|
||||
{
|
||||
// If this is the first reply, interrupt the five second timeout.
|
||||
if (num_replies_++ == 0)
|
||||
timer_.cancel();
|
||||
|
||||
// Print out some information about the reply packet.
|
||||
chrono::steady_clock::time_point now = chrono::steady_clock::now();
|
||||
chrono::steady_clock::duration elapsed = now - time_sent_;
|
||||
std::cout << length - ipv4_hdr.header_length()
|
||||
<< " bytes from " << ipv4_hdr.source_address()
|
||||
<< ": icmp_seq=" << icmp_hdr.sequence_number()
|
||||
<< ", ttl=" << ipv4_hdr.time_to_live()
|
||||
<< ", time="
|
||||
<< chrono::duration_cast<chrono::milliseconds>(elapsed).count()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
start_receive();
|
||||
}
|
||||
|
||||
static unsigned short get_identifier()
|
||||
{
|
||||
#if defined(ASIO_WINDOWS)
|
||||
return static_cast<unsigned short>(::GetCurrentProcessId());
|
||||
#else
|
||||
return static_cast<unsigned short>(::getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
icmp::resolver resolver_;
|
||||
icmp::endpoint destination_;
|
||||
icmp::socket socket_;
|
||||
steady_timer timer_;
|
||||
unsigned short sequence_number_;
|
||||
chrono::steady_clock::time_point time_sent_;
|
||||
asio::streambuf reply_buffer_;
|
||||
std::size_t num_replies_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: ping <host>" << std::endl;
|
||||
#if !defined(ASIO_WINDOWS)
|
||||
std::cerr << "(You may need to run this program as root.)" << std::endl;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
pinger p(io_context, argv[1]);
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
prioritised_handlers
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// prioritised_handlers.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "asio.hpp"
|
||||
#include <boost/function.hpp>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class handler_priority_queue : public asio::execution_context
|
||||
{
|
||||
public:
|
||||
void add(int priority, boost::function<void()> function)
|
||||
{
|
||||
handlers_.push(queued_handler(priority, function));
|
||||
}
|
||||
|
||||
void execute_all()
|
||||
{
|
||||
while (!handlers_.empty())
|
||||
{
|
||||
queued_handler handler = handlers_.top();
|
||||
handler.execute();
|
||||
handlers_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class executor
|
||||
{
|
||||
public:
|
||||
executor(handler_priority_queue& q, int p)
|
||||
: context_(q), priority_(p)
|
||||
{
|
||||
}
|
||||
|
||||
handler_priority_queue& context() const
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
template <typename Function, typename Allocator>
|
||||
void dispatch(const Function& f, const Allocator&) const
|
||||
{
|
||||
context_.add(priority_, f);
|
||||
}
|
||||
|
||||
template <typename Function, typename Allocator>
|
||||
void post(const Function& f, const Allocator&) const
|
||||
{
|
||||
context_.add(priority_, f);
|
||||
}
|
||||
|
||||
template <typename Function, typename Allocator>
|
||||
void defer(const Function& f, const Allocator&) const
|
||||
{
|
||||
context_.add(priority_, f);
|
||||
}
|
||||
|
||||
void on_work_started() const {}
|
||||
void on_work_finished() const {}
|
||||
|
||||
bool operator==(const executor& other) const
|
||||
{
|
||||
return &context_ == &other.context_ && priority_ == other.priority_;
|
||||
}
|
||||
|
||||
bool operator!=(const executor& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
handler_priority_queue& context_;
|
||||
int priority_;
|
||||
};
|
||||
|
||||
template <typename Handler>
|
||||
asio::executor_binder<Handler, executor>
|
||||
wrap(int priority, Handler handler)
|
||||
{
|
||||
return asio::bind_executor(executor(*this, priority), handler);
|
||||
}
|
||||
|
||||
private:
|
||||
class queued_handler
|
||||
{
|
||||
public:
|
||||
queued_handler(int p, boost::function<void()> f)
|
||||
: priority_(p), function_(f)
|
||||
{
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
function_();
|
||||
}
|
||||
|
||||
friend bool operator<(const queued_handler& a,
|
||||
const queued_handler& b)
|
||||
{
|
||||
return a.priority_ < b.priority_;
|
||||
}
|
||||
|
||||
private:
|
||||
int priority_;
|
||||
boost::function<void()> function_;
|
||||
};
|
||||
|
||||
std::priority_queue<queued_handler> handlers_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void high_priority_handler(const asio::error_code& /*ec*/)
|
||||
{
|
||||
std::cout << "High priority handler\n";
|
||||
}
|
||||
|
||||
void middle_priority_handler(const asio::error_code& /*ec*/)
|
||||
{
|
||||
std::cout << "Middle priority handler\n";
|
||||
}
|
||||
|
||||
void low_priority_handler()
|
||||
{
|
||||
std::cout << "Low priority handler\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
handler_priority_queue pri_queue;
|
||||
|
||||
// Post a completion handler to be run immediately.
|
||||
asio::post(io_context, pri_queue.wrap(0, low_priority_handler));
|
||||
|
||||
// Start an asynchronous accept that will complete immediately.
|
||||
tcp::endpoint endpoint(asio::ip::address_v4::loopback(), 0);
|
||||
tcp::acceptor acceptor(io_context, endpoint);
|
||||
tcp::socket server_socket(io_context);
|
||||
acceptor.async_accept(server_socket,
|
||||
pri_queue.wrap(100, high_priority_handler));
|
||||
tcp::socket client_socket(io_context);
|
||||
client_socket.connect(acceptor.local_endpoint());
|
||||
|
||||
// Set a deadline timer to expire immediately.
|
||||
asio::steady_timer timer(io_context);
|
||||
timer.expires_at(asio::steady_timer::time_point::min());
|
||||
timer.async_wait(pri_queue.wrap(42, middle_priority_handler));
|
||||
|
||||
while (io_context.run_one())
|
||||
{
|
||||
// The custom invocation hook adds the handlers to the priority queue
|
||||
// rather than executing them from within the poll_one() call.
|
||||
while (io_context.poll_one())
|
||||
;
|
||||
|
||||
pri_queue.execute_all();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_client
|
||||
*_server
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user