forked from fkie/async_web_server_cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_request_handler.cpp
150 lines (133 loc) · 4.28 KB
/
http_request_handler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include "async_web_server_cpp/http_request_handler.hpp"
#include "async_web_server_cpp/http_connection.hpp"
#include "async_web_server_cpp/http_reply.hpp"
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/regex.hpp>
#include <boost/shared_ptr.hpp>
#if defined(_MSC_VER)
# include <BaseTsd.h>
using ssize_t = SSIZE_T;
#endif
namespace async_web_server_cpp
{
HttpRequestHandlerGroup::HttpRequestHandlerGroup(
HttpServerRequestHandler default_handler)
: default_handler_(default_handler)
{
}
class PathMatcher
{
public:
explicit PathMatcher(const std::string& path_regex_string)
: path_regex_(boost::regex(path_regex_string))
{
}
bool operator()(const HttpRequest& request)
{
return regex_match(request.path, path_regex_);
}
private:
const boost::regex path_regex_;
};
void HttpRequestHandlerGroup::addHandlerForPath(
const std::string& path_regex, HttpServerRequestHandler handler)
{
addHandler(PathMatcher(path_regex), handler);
}
void HttpRequestHandlerGroup::addHandler(HandlerPredicate predicate,
HttpServerRequestHandler handler)
{
handlers_.push_back(std::make_pair(predicate, handler));
}
bool HttpRequestHandlerGroup::operator()(
const HttpRequest& request, boost::shared_ptr<HttpConnection> connection,
const char* begin, const char* end)
{
for (auto& handler : handlers_)
{
if (handler.first(request))
{
if (handler.second(request, connection, begin, end))
return true;
}
}
return default_handler_(request, connection, begin, end);
}
class BodyCollectingConnection;
typedef boost::shared_ptr<BodyCollectingConnection> BodyCollectingConnectionPtr;
typedef boost::weak_ptr<BodyCollectingConnection>
BodyCollectingConnectionWeakPtr;
class BodyCollectingConnection
: public boost::enable_shared_from_this<BodyCollectingConnection>,
private boost::noncopyable
{
public:
BodyCollectingConnection(HttpRequestBodyCollector::Handler handler,
const HttpRequest& request,
boost::shared_ptr<HttpConnection> connection)
: handler_(handler), request_(request), connection_(connection),
received_length_(0)
{
std::string length_str =
request_.get_header_value_or_default("Content-Length", "");
try
{
length_ = boost::lexical_cast<ssize_t>(length_str);
}
catch (const boost::bad_lexical_cast&)
{
length_ = -1; // indicate error
}
}
static void static_handle_read(BodyCollectingConnectionPtr _this,
const char* begin, const char* end)
{
_this->handle_read(begin, end);
}
void handle_read(const char* begin, const char* end)
{
if (length_ < 0)
{
HttpReply::builder(HttpReply::bad_request).write(connection_);
connection_->write("No Content-Length header");
return;
}
std::string chunk(begin, end - begin);
body_stream_ << chunk;
received_length_ += chunk.length();
if (received_length_ >= static_cast<size_t>(length_))
{
handler_(request_, connection_,
body_stream_.str().substr(0, length_));
}
else
{
connection_->async_read(
boost::bind(&BodyCollectingConnection::static_handle_read,
shared_from_this(), _1, _2));
}
}
private:
HttpRequestBodyCollector::Handler handler_;
const HttpRequest request_;
boost::shared_ptr<HttpConnection> connection_;
std::stringstream body_stream_;
ssize_t length_;
size_t received_length_;
};
HttpRequestBodyCollector::HttpRequestBodyCollector(Handler handler)
: handler_(handler)
{
}
bool HttpRequestBodyCollector::operator()(
const HttpRequest& request, boost::shared_ptr<HttpConnection> connection,
const char* begin, const char* end)
{
BodyCollectingConnectionPtr collecting_connection(
new BodyCollectingConnection(handler_, request, connection));
collecting_connection->handle_read(begin, end);
return true;
}
} // namespace async_web_server_cpp