-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp_server.cpp
More file actions
230 lines (204 loc) · 8.52 KB
/
Copy pathhttp_server.cpp
File metadata and controls
230 lines (204 loc) · 8.52 KB
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
* Copyright 2011 Iouri Khramtsov.
*
* This software is available under Apache License, Version
* 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/shared_ptr.hpp>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <signal.h>
#include <event2/event.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>
#include "http_server.h"
#include "http_utils.h"
namespace isaword {
using boost::shared_ptr;
/*---------------------------------------------------------
HttpServer class.
----------------------------------------------------------*/
void HttpServer::initialize() {
event_base_ = event_base_new();
server_ = evhttp_new(event_base_);
evhttp_set_gencb(server_, &HttpServer::event_handler, (void*)this);
not_found_handler_ = boost::shared_ptr<UriHandler>(new UriHandler());
}
/**
* Add a url handler. Whenever a server receives a request, it will
* be compared against each URI regular expression in order of their
* addition, and will run the handler corresponding to the first
* matching URI pattern.
*
* @param pattern a regular expression pattern for the URL to match.
* @param callback a function that will handle the requests
* @param data additional data to pass to the callback.
*/
bool HttpServer::add_url_handler(const std::string& pattern,
void(*callback)(struct evhttp_request*, void*),
void* data) {
shared_ptr<UriHandler> handler(new UriHandler());
handler->initialize(pattern, callback, data);
uri_handlers_.push_back(handler);
return false;
}
/**
* Set handler for requests that do not match any URL pattern.
* @param callback a function that will handle the requests
* @param data additional data to pass to the callback.
*/
void HttpServer::set_not_found_handler(void(*callback)(struct evhttp_request*, void*),
void* data) {
not_found_handler_ = shared_ptr<UriHandler>(new UriHandler());
not_found_handler_->set_handler(callback);
not_found_handler_->set_handler_data(data);
}
/**
* Start serving on a specified IP address and port.
* @param address a string with IP address to listen on
* @param port the port number to listen on
* @return true if succeeded, false if not.
*/
bool HttpServer::serve(const std::string& address, u_short port) {
if (!evhttp_bind_socket(server_, address.c_str(), port)) {
// Set the process to ignore SIGPIPE, which can be produced
// if libevent will write to a closed socket. Ideally
// we'd do this per socket, but libevent doesn't
// provide a way to get the socket assosiated with a
// connection.
struct sigaction ingore_sigpipe_action;
ingore_sigpipe_action.sa_handler = SIG_IGN;
sigemptyset(&ingore_sigpipe_action.sa_mask);
ingore_sigpipe_action.sa_flags = 0;
sigaction(SIGPIPE, &ingore_sigpipe_action, NULL);
// Start the main event loop.
event_base_dispatch(event_base_);
return true;
}
return false;
}
/// Handle the request event.
void HttpServer::handle_request(struct evhttp_request* request) {
//Find a callback associated with the URI request.
std::string uri(request_uri_path(request));
bool has_handled_request = false;
// Check the request URI and invoke the correct handler.
for (size_t i = 0; i < uri_handlers_.size(); ++i) {
if (uri_handlers_[i]->handle_if_matched(uri, request)) {
has_handled_request = true;
break;
}
}
// No handler is registered for the current URI; return 404.
if (!has_handled_request) {
//Check whether the user has provided a 404 handler.
if (not_found_handler_->handler() != NULL) {
not_found_handler_->handle(request);
has_handled_request = true;
} else {
//Handle the request on our own.
std::stringstream message;
message << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
<< "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
message << "<html><head><title>Not Found</title></head><body>";
message << "<h1>Error 404: Not Found</h1>";
message << "<p>We could not find the resource you requested at <em>"
<< uri << "</em>.</p>";
message << "<p> You are seeing this message because the 404 error "
<< "handler was not set. You can set it using "
<< "HttpServer::set_not_found_handler().</p>";
message << "<p><em>Since IE and Chrome don't display custom 404 pages "
<< "undper 512 bytes in length, we have to add this text to "
<< "make sure that this page is displayed.</em></p>";
message << "</body></html>";
this->send_response(request, message.str(), HTTP_NOTFOUND);
}
}
}
/**
* Send a string response. This is appropriate for text-type responses.
* For responses containing binary data use send_response/4 function.
*/
void HttpServer::send_response(struct evhttp_request* request,
const std::string& response,
int response_code) {
this->send_response_data(request, response.c_str(), response.length(), response_code);
}
/**
* Send a response containing binary data.
*/
void HttpServer::send_response_data(struct evhttp_request* request,
const char* response,
size_t response_size,
int response_code) {
//Prepare the response body.
struct evbuffer* buffer = evbuffer_new();
evbuffer_add(buffer, response, response_size);
//Prepare the status string.
const char* status_string = this->response_string(response_code);
evhttp_send_reply(request, response_code, status_string, buffer);
evbuffer_free(buffer);
}
/**
* Create a response string from a response code.
*/
const char* HttpServer::response_string(int response_code) const {
const char* status_string = NULL;
switch (response_code) {
case HTTP_BADREQUEST: status_string = "Bad Request"; break;
case HTTP_MOVEPERM: status_string = "Moved Permanently"; break;
case HTTP_MOVETEMP: status_string = "Moved Temporarily"; break;
case HTTP_NOCONTENT: status_string = "No Content"; break;
case HTTP_NOTFOUND: status_string = "Not Found"; break;
case HTTP_NOTMODIFIED: status_string = "Not Modified"; break;
case HTTP_OK: status_string = "OK"; break;
case HTTP_SERVUNAVAIL: status_string = "Service Unavailable"; break;
//default:
//TODO: crash and/or report an error.
//return;
}
return status_string;
}
/*---------------------------------------------------------
UriHandler class.
----------------------------------------------------------*/
///Initialize the URI handler. Throws an exception if the
///URI is not a valid regex expression.
void UriHandler::initialize(const std::string& uri_pattern,
void(*request_handler)(struct evhttp_request*, void*),
void* handler_data) {
//TODO: Implement
pattern_ = boost::regex(uri_pattern);
request_handler_ = request_handler;
handler_data_ = handler_data;
}
/// Handle the request if the URI matches the pattern.
/// Returns true if the pattern matches and the callback was called;
/// false otherwise.
bool UriHandler::handle_if_matched(const std::string& uri, struct evhttp_request* request) {
if (regex_match(uri, pattern_)) {
(*request_handler_)(request, handler_data_);
return true;
}
return false;
}
/// Handle the request.
void UriHandler::handle(struct evhttp_request* request) {
(*request_handler_)(request, handler_data_);
}
} /* namespace isaword */