9 static const char rcsid[] __attribute__((used)) =
"$Id: HTTPRequest.cpp 1783 2016-11-27 19:17:46Z sella $";
11 #include "HTTPRequest.h"
14 #include "../util/String.h"
16 #include <sys/types.h>
21 using namespace sella::server;
22 using namespace sella::util;
24 const std::string HTTPRequest::ContentTypeDefault =
"application/octet-stream";
26 HTTPRequest::HTTPRequest(
const HTTP &http)
throw () :
53 HTTPRequest::~HTTPRequest() throw () { }
55 void HTTPRequest::parse(
const std::string &buffer)
throw (ServerException) {
56 char buf[buffer.size() + 1];
57 char *line, *nline, *token, *rest, *ptr = buf;
59 strncpy(buf, buffer.c_str(),
sizeof(buf));
61 if ((line = strtok_r(ptr,
"\r\n", &rest)) == NULL) {
62 THROW_CODE(ServerException, Code::BadRequest,
"Request was empty");
65 TRACE(PACKAGE,
"Request-Line: %s", line);
70 if ((token = strtok_r(ptr,
" ", &rest)) != NULL) {
71 if (strncmp(
"GET", token, 3) == 0) {
73 }
else if (strncmp(
"POST", token, 4) == 0) {
75 }
else if (strncmp(
"PUT", token, 3) == 0) {
77 }
else if (strncmp(
"DELETE", token, 6) == 0) {
79 }
else if (strncmp(
"PATCH", token, 5) == 0) {
81 }
else if (strncmp(
"HEAD", token, 4) == 0) {
83 }
else if (strncmp(
"OPTIONS", token, 7) == 0) {
85 }
else if (strncmp(
"TRACE", token, 5) == 0) {
87 }
else if (strncmp(
"CONNECT", token, 7) == 0) {
95 THROW_CODE(ServerException, Code::BadRequest,
"Request syntax is malformed: %s", line);
98 if ((token = strrchr(rest,
' ')) != NULL) {
102 THROW_CODE(ServerException, Code::BadRequest,
"Request syntax is malformed: %s", line);
105 if (strncmp(
"HTTP/", ++token, 5) == 0) {
108 THROW_CODE(ServerException, Code::BadRequest,
"Request syntax is malformed: %s", line);
111 if ((token = strchr(rest,
'?')) != NULL) {
119 if ((token = strrchr(rest,
'.')) != NULL) {
120 extension = token + 1;
126 while ((line = strtok_r(ptr,
"\r\n", &rest)) != NULL) {
128 if (strncasecmp(
"Host: ", line, 6) == 0) {
130 }
else if (strncasecmp(
"User-Agent: ", line, 12) == 0) {
131 userAgent = line + 12;
132 }
else if (strncasecmp(
"Accept: ", line, 8) == 0) {
133 accept = String::split(line + 8,
",",
false,
false);
134 }
else if (strncasecmp(
"Accept-Charset: ", line, 16) == 0) {
135 acceptCharset = String::split(line + 16,
",",
false,
false);
136 }
else if (strncasecmp(
"Accept-Language: ", line, 17) == 0) {
137 acceptLanguage = String::split(line + 17,
",",
false,
false);
138 }
else if (strncasecmp(
"Accept-Encoding: ", line, 17) == 0) {
139 acceptEncoding = String::split(line + 17,
",",
false,
false);
140 }
else if (strncasecmp(
"Authorization: ", line, 15) == 0) {
141 authorization = line + 15;
142 }
else if (strncasecmp(
"Transfer-Encoding: ", line, 19) == 0) {
143 transferEncoding = line + 19;
144 }
else if (strncasecmp(
"Connection: ", line, 12) == 0) {
145 connection = line + 12;
146 }
else if (strncasecmp(
"Cache-Control: ", line, 15) == 0) {
147 cacheControl = line + 15;
148 }
else if (strncasecmp(
"Expect: ", line, 8) == 0) {
150 }
else if (strncasecmp(
"Content-Type: ", line, 14) == 0) {
151 contentType = line + 14;
152 }
else if (strncasecmp(
"Content-Length: ", line, 16) == 0) {
154 contentLength = std::stoull(line + 16);
155 }
catch (std::exception &e) {
156 THROW_CODE(ServerException, Code::BadRequest,
"Content-Length is invalid: %s", line + 15);
158 }
else if (strncasecmp(
"X-HTTP-Method-Override: ", line, 24) == 0) {
159 methodOverrideRaw = line + 24;
160 }
else if (strncasecmp(
"X-HTTP-Method: ", line, 15) == 0) {
161 methodOverrideRaw = line + 15;
162 }
else if (strncasecmp(
"X-METHOD-OVERRIDE: ", line, 19) == 0) {
163 methodOverrideRaw = line + 19;
166 headers.push_back(line);
170 if (rest != NULL && strncmp(rest,
"\n\r\n", 3) == 0) {
177 if (!methodOverrideRaw.empty()) {
178 if (methodOverrideRaw.compare(
"GET")) {
179 methodOverride = Get;
180 }
else if (methodOverrideRaw.compare(
"POST")) {
181 methodOverride = Post;
182 }
else if (methodOverrideRaw.compare(
"PUT")) {
183 methodOverride = Put;
184 }
else if (methodOverrideRaw.compare(
"DELETE")) {
185 methodOverride = Delete;
186 }
else if (methodOverrideRaw.compare(
"PATCH")) {
187 methodOverride = Patch;
188 }
else if (methodOverrideRaw.compare(
"HEAD")) {
189 methodOverride = Head;
190 }
else if (methodOverrideRaw.compare(
"OPTIONS")) {
191 methodOverride = Options;
192 }
else if (methodOverrideRaw.compare(
"TRACE")) {
193 methodOverride = Trace;
194 }
else if (methodOverrideRaw.compare(
"CONNECT")) {
195 methodOverride = Connect;
197 methodOverride = Extension;
202 THROW_CODE(ServerException, Code::BadRequest,
"Host header field is missing");
206 std::string body(rest);
210 if (contentType.empty()) {
211 contentType = getMIMEType(body, body.size(), ContentTypeDefault);
216 void HTTPRequest::appendBody(
const std::string &data)
throw (ServerException) {
217 if (content.size() + data.size() > http.getRequestBodyLimit()) {
218 THROW_CODE(ServerException, Code::RequestEntityTooLarge,
"The requested URI %s is larger than the configured limit of %zd bytes.", uri.c_str(), http.getRequestBodyLimit());
221 content.reserve(content.size() + data.size());
222 content.insert(content.end(), data.begin(), data.end());
225 void HTTPRequest::appendBody(
const std::vector<char> &data)
throw (ServerException) {
226 if (content.size() + data.size() > http.getRequestBodyLimit()) {
227 THROW_CODE(ServerException, Code::RequestEntityTooLarge,
"The requested URI %s is larger than the configured limit of %zd bytes.", uri.c_str(), http.getRequestBodyLimit());
230 content.reserve(content.size() + data.size());
231 content.insert(content.end(), data.begin(), data.end());
234 HTTPRequest::Method HTTPRequest::getMethod(
bool useOverride)
const throw () {
235 if (useOverride && methodOverride != None) {
236 return methodOverride;
242 const std::string& HTTPRequest::getMethodRaw(
bool useOverride)
const throw () {
243 if (useOverride && methodOverride != None) {
244 return methodOverrideRaw;
250 HTTPRequest::Method HTTPRequest::getMethodOverride(
void)
const throw () {
251 return methodOverride;
254 const std::string& HTTPRequest::getMethodOverrideRaw(
void)
const throw () {
255 return methodOverrideRaw;
258 const std::string& HTTPRequest::getURI(
void)
const throw () {
262 const std::string& HTTPRequest::getPath(
void)
const throw () {
266 const std::string& HTTPRequest::getExtension(
void)
const throw () {
270 const std::string& HTTPRequest::getQuery(
void)
const throw () {
274 const std::string& HTTPRequest::getVersion(
void)
const throw () {
278 const std::string& HTTPRequest::getHost(
void)
const throw () {
282 const std::string& HTTPRequest::getUserAgent(
void)
const throw () {
286 const std::string& HTTPRequest::getAuthorization(
void)
const throw () {
287 return authorization;
290 bool HTTPRequest::getAuthorizationCredentials(std::string &username, std::string &password)
const throw () {
291 if (isBasicAuthorization()) {
292 std::string tmp = util::String::base64_decode(authorization);
293 size_t delim = tmp.find_first_of(
':');
295 if (delim != std::string::npos) {
296 username = tmp.substr(0, delim - 1);
297 password = tmp.substr(delim + 1);
299 tmp.assign(tmp.size(),
'\0');
308 const std::vector<std::string>& HTTPRequest::getAccept(
void)
const throw () {
312 HTTPRequest::AcceptType HTTPRequest::getAcceptType(AcceptType fallback)
const throw () {
313 static const boost::regex pattern_json(
"^application/(?:x-)?json");
314 static const boost::regex pattern_jsonp(
"^application/(?:x-)?javascript");
315 static const boost::regex pattern_icon(
"^image/(?:x-icon|vnd.microsoft.icon)");
317 if (!extension.empty()) {
318 if (extension.compare(
"json") == 0) {
319 return AcceptType::JSON;
320 }
else if (extension.compare(
"xml") == 0) {
321 return AcceptType::XML;
322 }
else if (extension.compare(
"jsonp") == 0) {
323 return AcceptType::JSONP;
324 }
else if (extension.compare(
"csv") == 0) {
325 return AcceptType::CSV;
326 }
else if (extension.compare(
"html") == 0) {
327 return AcceptType::HTML;
328 }
else if (extension.compare(
"plain") == 0) {
329 return AcceptType::Plain;
333 if (accept.empty()) {
337 for (
auto a = accept.begin(); a != accept.end(); ++a) {
338 if (a->compare(0, 9,
"text/html") == 0) {
339 return AcceptType::HTML;
340 }
else if (a->compare(0, 10,
"text/plain") == 0) {
341 return AcceptType::Plain;
342 }
else if (String::regex_isearch(*a, pattern_json)) {
343 return AcceptType::JSON;
344 }
else if (a->compare(0, 15,
"application/xml") == 0) {
345 return AcceptType::XML;
346 }
else if (String::regex_isearch(*a, pattern_jsonp)) {
347 return AcceptType::JSONP;
348 }
else if (a->compare(0, 21,
"application/xhtml+xml") == 0) {
349 return AcceptType::XHTML_XML;
350 }
else if (a->compare(0, 9,
"image/png") == 0) {
351 return AcceptType::PNG;
352 }
else if (a->compare(0, 9,
"image/gif") == 0) {
353 return AcceptType::GIF;
354 }
else if (a->compare(0, 10,
"image/jpeg") == 0) {
355 return AcceptType::JPEG;
356 }
else if (a->compare(0, 8,
"text/csv") == 0) {
357 return AcceptType::CSV;
358 }
else if (String::regex_isearch(*a, pattern_icon)) {
359 return AcceptType::Icon;
360 }
else if (a->compare(0, 3,
"*/*") == 0) {
361 return AcceptType::Any;
362 }
else if (a->compare(0, 6,
"text/*") == 0) {
363 return AcceptType::Text_Any;
364 }
else if (a->compare(0, 5,
"text/") == 0) {
365 return AcceptType::Text;
366 }
else if (a->compare(0, 13,
"application/*") == 0) {
367 return AcceptType::Application_Any;
368 }
else if (a->compare(0, 12,
"application/") == 0) {
369 return AcceptType::Application;
370 }
else if (a->compare(0, 7,
"image/*") == 0) {
371 return AcceptType::Image_Any;
372 }
else if (a->compare(0, 6,
"image/") == 0) {
373 return AcceptType::Image;
374 }
else if (a->compare(0, 7,
"audio/*") == 0) {
375 return AcceptType::Audio_Any;
376 }
else if (a->compare(0, 7,
"audio/") == 0) {
377 return AcceptType::Audio;
384 const std::vector<std::string>& HTTPRequest::getHeaders()
const throw () {
388 std::string HTTPRequest::getHeader(
const std::string &header)
const throw (ServerException) {
389 std::vector<std::string> matches;
390 const boost::regex pattern(header + std::string(
": *(\\S+) *"));
392 for (
auto h = headers.begin(); h != headers.end(); ++h) {
394 if (String::regex_imatch(*h, pattern, matches)) {
397 }
catch (RegexException &e) {
398 RETHROW_CODE(ServerException, Code::InternalServerError, e);
402 THROW_CODE(ServerException, Code::InternalServerError,
"The requested header '%s' was not found.", header.c_str());
405 const std::string HTTPRequest::getBody(
void)
const throw () {
406 std::string buf(content.begin(), content.end());
411 const std::vector<char>& HTTPRequest::getContent(
void)
const throw () {
415 bool HTTPRequest::isContinue(
void)
const throw () {
416 return !expect.compare(
"100-continue");
419 bool HTTPRequest::isChunked(
void)
const throw () {
420 return !transferEncoding.compare(
"chunked");
423 bool HTTPRequest::isAcceptGzip(
void)
const throw () {
424 auto end = acceptEncoding.end();
426 return (std::find(acceptEncoding.begin(), end,
"gzip") != end);
429 bool HTTPRequest::isAcceptDeflate(
void)
const throw () {
430 auto end = acceptEncoding.end();
432 return (std::find(acceptEncoding.begin(), end,
"delfate") != end);
435 bool HTTPRequest::isBasicAuthorization(
void)
const throw () {
436 return (authorization.compare(0, 5,
"Basic") == 0);
439 bool HTTPRequest::empty(
void)
const throw () {
440 return (method == None);
443 bool HTTPRequest::hasHeader(
const std::string &header)
const throw (ServerException) {
445 const boost::regex pattern(std::string(
"^") + header + std::string(
":"));
447 for (
auto h = headers.begin(); h != headers.end(); ++h) {
449 if (String::regex_isearch(*h, pattern)) {
452 }
catch (RegexException &e) {
453 RETHROW_CODE(ServerException, Code::InternalServerError, e);
457 std::string pattern = String::lower(header) +
":";
458 size_t len = pattern.size();
460 for (
auto h = headers.begin(), end = headers.end(); h != end; ++h) {
461 if (pattern.compare(0, len, String::lower(*h))) {
470 void HTTPRequest::clear(
void) throw () {
473 methodOverride = None;
474 methodOverrideRaw.clear();
484 acceptCharset.clear();
485 acceptEncoding.clear();
486 acceptLanguage.clear();
487 transferEncoding.clear();
488 authorization.clear();
490 cacheControl.clear();
494 std::vector<char>().swap(content);
497 std::string HTTPRequest::str(
void)
const throw () {
498 return String::sprintf(
"%s %s %s\n%s\n%s", methodRaw.c_str(), uri.c_str(), version.c_str(), String::join(headers,
'\n').c_str(), getBody().c_str());
501 void HTTPRequest::print(
void)
const throw () {
502 printf(
"[request]\n");
503 printf(
"%s %s %s\n", methodRaw.c_str(), uri.c_str(), version.c_str());
505 for (
auto h = headers.begin(); h != headers.end(); ++h) {
506 printf(
"%s\n", h->c_str());
509 printf(
"uri: %s\n", uri.c_str());
510 printf(
"path: %s\n", path.c_str());
511 printf(
"extension: %s\n", extension.c_str());
512 printf(
"query: %s\n", query.c_str());
514 printf(
"%s\n", getBody().c_str());
517 std::string HTTPRequest::getFullpath(
const std::string &file)
throw (ServerException) {
518 std::string fullpath = http.getRoot();
521 fullpath += getPath();
526 if (isDirectory(fullpath)) {
527 const auto &indexes = http.getDirectoryIndexes();
529 for (
auto i = indexes.begin(); i != indexes.end(); ++i) {
530 std::string indexpath = fullpath + *i;
532 if (isFile(indexpath)) {
533 return std::move(indexpath);
536 }
else if (isFile(fullpath)) {
537 return std::move(fullpath);
540 THROW_CODE(ServerException, Code::NotFound,
"The requested URI %s was not found on this server.", getPath().c_str());
543 std::string HTTPRequest::getMIMEType(
const std::string &file,
const std::string &fallback)
const throw (ServerException) {
544 const char *result = NULL;
551 if ((result = ::magic_file(magic, file.c_str())) == NULL) {
552 THROW(ServerException,
"magic_file: failed");
556 }
catch (ServerException &e) {
557 if (fallback.empty()) {
567 std::string HTTPRequest::getMIMEType(
const std::string &buffer,
size_t size,
const std::string &fallback)
const throw (ServerException) {
568 const char *result = NULL;
575 if ((result = ::magic_buffer(magic, buffer.c_str(), (size < 0) ? buffer.size() : size)) == NULL) {
576 THROW(ServerException,
"magic_buffer: failed");
580 }
catch (ServerException &e) {
581 if (fallback.empty()) {
591 bool HTTPRequest::isDirectory(
const std::string &path)
const throw () {
594 return (stat(path.c_str(), &s) == 0 && S_ISDIR(s.st_mode));
597 bool HTTPRequest::isFile(
const std::string &file)
const throw () {
600 return (stat(file.c_str(), &s) == 0 && S_ISREG(s.st_mode));
603 void HTTPRequest::magic_open(magic_t &magic)
const throw (ServerException) {
604 if ((magic = ::magic_open(MAGIC_MIME)) == NULL) {
605 THROW_CODE(ServerException, Code::InternalServerError,
"magic_open: failed to initialize");
608 if (::magic_load(magic, NULL) != 0) {
609 std::string err = String::sprintf(
"magic_open: %s", magic_error(magic));
610 ::magic_close(magic);
612 THROW_CODE(ServerException, Code::InternalServerError, err.c_str());
616 void HTTPRequest::magic_close(magic_t &magic)
const throw () {
617 ::magic_close(magic);