00001
00002
00003
00004
00005
00006
00007
00008
00009 static const char rcsid[] __attribute__((used)) = "$Id: HTTPConnection.cpp 1197 2014-10-14 22:26:11Z sella $";
00010
00011 #include "HTTPConnection.h"
00012 #include "HTTP.h"
00013 #include "../util/String.h"
00014 #include "../util/Time.h"
00015
00016 #include <string.h>
00017 #include <sys/types.h>
00018 #include <sys/stat.h>
00019 #include <unistd.h>
00020 #include <magic.h>
00021
00022 #include <iostream>
00023 #include <fstream>
00024 #include <string>
00025
00026 using namespace sella::server;
00027 using namespace sella::net;
00028 using namespace sella::util;
00029
00030 const timeval HTTPConnection::ReceiveTimeoutDefault = { 30, 0 };
00031 const std::string HTTPConnection::MIMEFallback = "text/plain; charset=us-ascii";
00032 const std::string HTTPConnection::ContentTypeDefault = "text/html; charset=iso-8859-1";
00033
00034 HTTPConnection::HTTPConnection(const HTTP &http, TCPSocket::shared &socket, timeval tv) throw () :
00035 http(http),
00036 socket(socket),
00037 request(http)
00038 {
00039 socket->setSoSndTimeo(tv);
00040 socket->setSoRcvTimeo(tv);
00041 }
00042
00043 HTTPConnection::~HTTPConnection() throw () { }
00044
00045 const HTTPConnection::shared HTTPConnection::shared_ptr(const HTTP &http, sella::net::TCPSocket::shared &socket, timeval tv) throw () {
00046 return std::make_shared<HTTPConnection>(http, socket, tv);
00047 }
00048
00049 const HTTPRequest& HTTPConnection::getRequest(void) throw (ServerException) {
00050 if (request.empty()) {
00051 std::string buffer;
00052
00053 read(buffer);
00054 request.parse(buffer);
00055
00056 if (request.contentLength > http.getRequestBodyLimit()) {
00057 THROW_CODE(ServerException, Code::RequestEntityTooLarge, "Content-Length is larger than %zd", http.getRequestBodyLimit());
00058 }
00059
00060 if (request.isContinue()) {
00061 if (request.isChunked()) {
00062 chunker();
00063 } else {
00064 THROW_CODE(ServerException, Code::NotImplemented, "Unsupported transfer-encoding");
00065 }
00066 }
00067 }
00068
00069 return request;
00070 }
00071
00072 bool HTTPConnection::isUsable(void) throw () {
00073 return socket->isUsable();
00074 }
00075
00076 void HTTPConnection::response(const std::string &body, const std::string &contentType, bool close) throw (ServerException) {
00077 response(Code::OK, body, contentType, close);
00078 }
00079
00080 void HTTPConnection::response(const std::vector<char> &content, const std::string &contentType, bool close) throw (ServerException) {
00081 response(Code::OK, content, contentType, close);
00082 }
00083
00084 void HTTPConnection::error(int code, const std::string &detail) throw (ServerException) {
00085 std::string body = getErrorBody(code, detail);
00086
00087 response(code, body);
00088 }
00089
00090 void HTTPConnection::error(int code, const std::vector<char> &content, const std::string &contentType = ContentTypeDefault) throw (ServerException) {
00091 response(code, content, contentType, true);
00092 }
00093
00094 void HTTPConnection::methodOptions(void) throw (ServerException) {
00095 std::string buf;
00096
00097 if (request.getURI().compare("*") == 0) {
00098
00099 } else {
00100
00101 }
00102
00103 try {
00104 response(Code::OK, buf, ContentTypeDefault);
00105 } catch (SocketException &e) {
00106 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00107 }
00108 }
00109
00110 void HTTPConnection::methodGet(void) throw (ServerException) {
00111 std::vector<char> content;
00112 std::string contentType;
00113
00114 getFileContent(request.getPath(), content, contentType);
00115
00116 response(content, contentType);
00117 }
00118
00119 void HTTPConnection::methodHead(void) throw (ServerException) {
00120 int code = Code::OK;
00121 std::vector<char> content;
00122 std::string contentType;
00123
00124 try {
00125 getFileContent(request.getPath(), content, contentType);
00126 } catch (ServerException &e) {
00127 code = e.getCode();
00128 }
00129
00130 try {
00131 sendStatusLine(Code::OK);
00132 sendHeaders(content.capacity(), contentType);
00133 } catch (SocketException &e) {
00134 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00135 }
00136 }
00137
00138 void HTTPConnection::methodPost(void) throw (ServerException) {
00139 THROW_CODE(ServerException, Code::MethodNotAllowed, "The method %s is not allowed for the URL %s.", request.getMethodRaw().c_str(), request.getPath().c_str());
00140 }
00141
00142 void HTTPConnection::methodPut(void) throw (ServerException) {
00143 THROW_CODE(ServerException, Code::MethodNotAllowed, "The method %s is not allowed for the URL %s.", request.getMethodRaw().c_str(), request.getPath().c_str());
00144 }
00145
00146 void HTTPConnection::methodDelete(void) throw (ServerException) {
00147 THROW_CODE(ServerException, Code::MethodNotAllowed, "The method %s is not allowed for the URL %s.", request.getMethodRaw().c_str(), request.getPath().c_str());
00148 }
00149
00150 void HTTPConnection::methodTrace(void) throw (ServerException) {
00151 const std::vector<char> &content = request.getContent();
00152 std::string trace = String::sprintf("%s %s %s\r\n", request.getMethodRaw().c_str(), request.getURI().c_str(), request.getVersion().c_str());
00153
00154 for (auto h = request.getHeaders().begin(); h != request.getHeaders().end(); ++h) {
00155 trace += *h + "\r\n";
00156 }
00157 trace += "\r\n";
00158
00159 try {
00160 sendStatusLine(Code::OK);
00161 sendHeaders(trace.size() + content.capacity(), "message/http");
00162 sendBody(trace);
00163 sendContent(content);
00164 } catch (SocketException &e) {
00165 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00166 }
00167 }
00168
00169 void HTTPConnection::methodConnect(void) throw (ServerException) {
00170 THROW_CODE(ServerException, Code::MethodNotAllowed, "The method %s is not allowed for the URL %s.", request.getMethodRaw().c_str(), request.getPath().c_str());
00171 }
00172
00173 void HTTPConnection::methodPatch(void) throw (ServerException) {
00174 THROW_CODE(ServerException, Code::MethodNotAllowed, "The method %s is not allowed for the URL %s.", request.getMethodRaw().c_str(), request.getPath().c_str());
00175 }
00176
00177 IPAddressPort HTTPConnection::getLocalIPAddressPort(void) const throw (ServerException) {
00178 try {
00179 return socket->getLocalIPAddressPort();
00180 } catch (SocketException &e) {
00181 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00182 }
00183 }
00184
00185 IPAddressPort HTTPConnection::getRemoteIPAddressPort(void) const throw (ServerException) {
00186 try {
00187 return socket->getRemoteIPAddressPort();
00188 } catch (SocketException &e) {
00189 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00190 }
00191 }
00192
00193 void HTTPConnection::clear(void) throw () {
00194 socket.reset();
00195 request.clear();
00196 }
00197
00198 void HTTPConnection::response(int code, const std::string &body, const std::string &contentType, bool close) throw (ServerException) {
00199 try {
00200 sendStatusLine(code);
00201 sendHeaders(body.size(), contentType, close);
00202 sendBody(body);
00203 } catch (SocketException &e) {
00204 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00205 }
00206 }
00207
00208 void HTTPConnection::response(int code, const std::vector<char> &content, const std::string &contentType, bool close) throw (ServerException) {
00209 try {
00210 sendStatusLine(code);
00211 sendHeaders(content.capacity(), contentType, close);
00212 sendContent(content);
00213 } catch (SocketException &e) {
00214 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00215 }
00216 }
00217
00218 void HTTPConnection::sendStatusLine(int code, const std::string &detail) throw (SocketException) {
00219 std::string buf("HTTP/1.1 ");
00220
00221 buf += statusCodeString(code);
00222
00223 if (!detail.empty()) {
00224 buf += " - ";
00225 buf += detail;
00226 }
00227
00228 buf += "\r\n";
00229
00230 socket->send(buf.c_str(), buf.size());
00231 }
00232
00233 void HTTPConnection::sendHeaders(size_t size, const std::string &contentType, bool close) throw (SocketException) {
00234 std::string buf = buildHeaders(size, contentType, close);
00235
00236 socket->send(buf.c_str(), buf.size());
00237 }
00238
00239 void HTTPConnection::sendBody(const std::string &body) throw (SocketException) {
00240 socket->send(body.c_str(), body.size());
00241 }
00242
00243 void HTTPConnection::sendContent(const std::vector<char> &content) throw (SocketException) {
00244 socket->send(&content.front(), content.capacity());
00245 }
00246
00247 void HTTPConnection::read(std::string &buffer) throw (ServerException) {
00248 char buf[262144];
00249 ssize_t pktlen;
00250
00251 try {
00252 do {
00253 if ((pktlen = socket->recv(buf, sizeof(buf))) <= 0) {
00254 THROW_CODE(ServerException, Code::InternalServerError, "socket disconnected");
00255 }
00256
00257 buffer.append(buf, pktlen);
00258 } while (socket->hasData());
00259 } catch (SocketException &e) {
00260 RETHROW_CODE(ServerException, Code::InternalServerError, e);
00261 }
00262 }
00263
00264 void HTTPConnection::chunker(void) throw (ServerException) {
00265 std::string buffer;
00266 size_t size;
00267
00268 sendStatusLine(Code::Continue);
00269
00270 do {
00271
00272 while ((size = buffer.find("\r\n")) == std::string::npos) {
00273
00274 read(buffer);
00275 }
00276
00277 {
00278 std::string buf = buffer.substr(0, size);
00279
00280
00281 try {
00282 size = std::stoull(buf, NULL, 16);
00283 } catch (std::exception &e) {
00284 THROW_CODE(ServerException, Code::InternalServerError, "Invalid chunk size");
00285 }
00286
00287
00288
00289 if (size == 0) {
00290
00291
00292 return;
00293 }
00294
00295 buffer.erase(0, buf.size() + 2);
00296 }
00297
00298
00299 while (buffer.size() < size + 2) {
00300
00301 read(buffer);
00302 }
00303
00304 {
00305 request.appendBody(buffer.substr(0, size));
00306 buffer.erase(0, size + 2);
00307 }
00308
00309 FIXME("Need to imlement support for trailers");
00310
00311
00312
00313
00314 } while (true);
00315 }
00316
00317 bool HTTPConnection::isDirectory(const std::string &path) const throw () {
00318 struct stat s;
00319
00320 return (stat(path.c_str(), &s) == 0 && S_ISDIR(s.st_mode));
00321 }
00322
00323 bool HTTPConnection::isFile(const std::string &file) const throw () {
00324 struct stat s;
00325
00326 return (stat(file.c_str(), &s) == 0 && S_ISREG(s.st_mode));
00327 }
00328
00329 const std::string HTTPConnection::buildHeaders(size_t contentSize, const std::string &contentType, bool close, bool useAllow, const std::string &transferEncoding) throw () {
00330 std::string buf;
00331
00332 buf = "Date: " + Time::getRFC1123Date() + "\r\n";
00333 buf += "Server: " + http.getBanner() + "\r\n";
00334
00335 if (useAllow && !http.getAllow().empty()) {
00336 buf += "Allow: ";
00337
00338 for (auto a = http.getAllow().begin(); a != http.getAllow().end(); ++a) {
00339 switch (*a) {
00340 case HTTPRequest::Options:
00341 buf += "OPTIONS,";
00342 break;
00343
00344 case HTTPRequest::Get:
00345 buf += "GET,";
00346 break;
00347
00348 case HTTPRequest::Head:
00349 buf += "HEAD,";
00350 break;
00351
00352 case HTTPRequest::Post:
00353 buf += "POST,";
00354 break;
00355
00356 case HTTPRequest::Put:
00357 buf += "PUT,";
00358 break;
00359
00360 case HTTPRequest::Delete:
00361 buf += "DELETE,";
00362 break;
00363
00364 case HTTPRequest::Trace:
00365 buf += "TRACE,";
00366 break;
00367
00368 case HTTPRequest::Connect:
00369 buf += "CONNECT,";
00370 break;
00371
00372 case HTTPRequest::Patch:
00373 buf += "PATCH,";
00374 break;
00375
00376 default:
00377 break;
00378 }
00379 }
00380
00381 buf.erase(buf.size() - 1);
00382 buf += "\r\n";
00383 }
00384
00385 if (transferEncoding.empty()) {
00386 if (request.getMethod() != HTTPRequest::Head) {
00387 buf += String::sprintf("Content-Length: %zd\r\n", contentSize);
00388 }
00389 } else {
00390 buf += String::sprintf("Transfer-Encoding: %s\r\n", transferEncoding.c_str());
00391 }
00392
00393 const auto &headers = http.getCustomHeaders();
00394 for (auto h = headers.begin(); h != headers.end(); ++h) {
00395 buf += *h + "\r\n";
00396 }
00397
00398 if (close) {
00399 buf += "Connection: close\r\n";
00400 }
00401
00402 buf += "Content-Type: " + contentType + "\r\n";
00403
00404 buf += "\r\n";
00405
00406 return buf;
00407 }
00408
00409 std::string HTTPConnection::statusCodeString(int code) throw () {
00410 switch (code) {
00411 case 100:
00412 return std::string("100 Continue");
00413
00414 case 101:
00415 return std::string("101 Switching Protocols");
00416
00417 case 200:
00418 return std::string("200 OK");
00419
00420 case 201:
00421 return std::string("201 Created");
00422
00423 case 202:
00424 return std::string("202 Accepted");
00425
00426 case 203:
00427 return std::string("203 Non-Authoritative Information");
00428
00429 case 204:
00430 return std::string("204 No Content");
00431
00432 case 205:
00433 return std::string("205 Reset Content");
00434
00435 case 206:
00436 return std::string("206 Partial Content");
00437
00438 case 300:
00439 return std::string("300 Multiple Choices");
00440
00441 case 301:
00442 return std::string("301 Moved Permanently");
00443
00444 case 302:
00445 return std::string("302 Found");
00446
00447 case 303:
00448 return std::string("303 See Other");
00449
00450 case 304:
00451 return std::string("304 Not Modified");
00452
00453 case 305:
00454 return std::string("305 Use Proxy");
00455
00456 case 307:
00457 return std::string("307 Temporary Redirect");
00458
00459 case 400:
00460 return std::string("400 Bad Request");
00461
00462 case 401:
00463 return std::string("401 Unauthorized");
00464
00465 case 402:
00466 return std::string("402 Payment Required");
00467
00468 case 403:
00469 return std::string("403 Forbidden");
00470
00471 case 404:
00472 return std::string("404 Not Found");
00473
00474 case 405:
00475 return std::string("405 Method Not Allowed");
00476
00477 case 406:
00478 return std::string("406 Not Acceptable");
00479
00480 case 407:
00481 return std::string("407 Proxy Authentication Required");
00482
00483 case 408:
00484 return std::string("408 Request Time-out");
00485
00486 case 409:
00487 return std::string("409 Conflict");
00488
00489 case 410:
00490 return std::string("410 Gone");
00491
00492 case 411:
00493 return std::string("411 Length Required");
00494
00495 case 412:
00496 return std::string("412 Precondition Failed");
00497
00498 case 413:
00499 return std::string("413 Request Entity Too Large");
00500
00501 case 414:
00502 return std::string("414 Request-URI Too Large");
00503
00504 case 415:
00505 return std::string("415 Unsupported Media Type");
00506
00507 case 416:
00508 return std::string("416 Requested range not satisfiable");
00509
00510 case 417:
00511 return std::string("417 Expectation Failed");
00512
00513 case 500:
00514 return std::string("500 Internal Server Error");
00515
00516 case 501:
00517 return std::string("501 Not Implemented");
00518
00519 case 502:
00520 return std::string("502 Bad Gateway");
00521
00522 case 503:
00523 return std::string("503 Service Unavailable");
00524
00525 case 504:
00526 return std::string("504 Gateway Time-out");
00527
00528 case 505:
00529 return std::string("505 HTTP Version not supported");
00530 }
00531
00532 if (code >= 500 && code < 600) {
00533 return String::sprintf("%d Server Error", code);
00534 } else if (code >= 400) {
00535 return String::sprintf("%d Client Error", code);
00536 } else if (code >= 300) {
00537 return String::sprintf("%d Redirection", code);
00538 } else if (code >= 200) {
00539 return String::sprintf("%d Successful", code);
00540 } else if (code >= 100) {
00541 return String::sprintf("%d Informational", code);
00542 }
00543
00544 return String::sprintf("%d Unknown", code);
00545 }
00546
00547 std::string HTTPConnection::getMIMEType(const std::string &file, const std::string &fallback) const throw (ServerException) {
00548 std::string result;
00549 magic_t magic_cookie;
00550
00551 if ((magic_cookie = magic_open(MAGIC_MIME)) == NULL) {
00552 if (fallback.empty()) {
00553 THROW_CODE(ServerException, Code::InternalServerError, "magic_open: failed to initialize");
00554 }
00555
00556 return fallback;
00557 }
00558
00559 if (magic_load(magic_cookie, NULL) != 0) {
00560 std::string err = String::sprintf("magic_open: %s", magic_error(magic_cookie));
00561 magic_close(magic_cookie);
00562
00563 if (fallback.empty()) {
00564 THROW_CODE(ServerException, Code::InternalServerError, err.c_str());
00565 }
00566
00567 return fallback;
00568 }
00569
00570 result = magic_file(magic_cookie, file.c_str());
00571 magic_close(magic_cookie);
00572
00573 return result;
00574 }
00575
00576 std::string HTTPConnection::getFullpath(const std::string &file) throw (ServerException) {
00577 std::string fullpath = http.getRoot();
00578
00579 if (file.empty()) {
00580 fullpath += request.getPath();
00581 } else {
00582 fullpath += file;
00583 }
00584
00585 if (isDirectory(fullpath)) {
00586 const auto &indexes = http.getDirectoryIndexes();
00587
00588 for (auto i = indexes.begin(); i != indexes.end(); ++i) {
00589 std::string indexpath = fullpath + *i;
00590
00591 if (isFile(indexpath)) {
00592 return std::move(indexpath);
00593 }
00594 }
00595 } else if (isFile(fullpath)) {
00596 return std::move(fullpath);
00597 }
00598
00599 THROW_CODE(ServerException, Code::NotFound, "The requested URI %s was not found on this server.", request.getPath().c_str());
00600 }
00601
00602 void HTTPConnection::getFileContent(const std::string &file, std::vector<char> &content, std::string &contentType) throw (ServerException) {
00603 try {
00604 std::string buf;
00605 std::string fullpath = getFullpath(file);
00606 std::ifstream ifile(fullpath, std::ifstream::in);
00607 size_t size = getFileSize(ifile);
00608
00609 content.reserve(size);
00610 ifile.read(&content.front(), size);
00611 ifile.close();
00612
00613 contentType = getMIMEType(fullpath, MIMEFallback);
00614 } catch (std::ifstream::failure &e) {
00615 RETHROW_CODE(ServerException, Code::NotFound, e);
00616 }
00617 }
00618
00619 size_t HTTPConnection::getFileSize(std::ifstream &ifile) throw (ServerException) {
00620 if (!ifile.is_open()) {
00621 THROW_CODE(ServerException, Code::Forbidden, "You don't have permission to access %s on this server.", request.getPath().c_str());
00622 }
00623
00624 ifile.seekg(0, std::ios::end);
00625 size_t size = ifile.tellg();
00626 ifile.seekg(0, std::ios::beg);
00627
00628 if (size > http.getRequestBodyLimit()) {
00629 THROW_CODE(ServerException, Code::RequestEntityTooLarge, "The requested URI %s is larger than the configured limit of %zd bytes.", request.getPath().c_str(), http.getRequestBodyLimit()).c_str();
00630 }
00631
00632 return size;
00633 }
00634
00635 std::string HTTPConnection::getErrorBody(int code, const std::string &detail) throw () {
00636 std::string body, buf(statusCodeString(code));
00637
00638 if (!http.getBanner().empty()) {
00639 std::string banner = http.getBanner();
00640
00641 body = "<html><head><title>" + buf + "</title></head><body><h1>" + buf + "</h1>" + detail + "<p><hr><address>" + banner + "</address></body></html>\r\n";
00642 } else {
00643 body = "<html><head><title>" + buf + "</title></head><body><h1>" + buf + "</h1>" + detail + "</body></html>\r\n";
00644 }
00645
00646 return body;
00647 }
00648
00649
00650
00651