📄 request.cxx
字号:
/* * * C++ Portable Types Library (PTypes) * Version 1.7.5 Released 9-Mar-2003 * * Copyright (c) 2001, 2002, 2003 Hovik Melikyan * * http://www.melikyan.com/ptypes/ * http://ptypes.sourceforge.net/ * */#include <stdlib.h>#include <limits.h>#include "config.h"#include "log.h"#include "utils.h"#include "sysutils.h"#include "request.h"#include "clients.h"#include "modules.h"USING_PTYPESconst char* http_version_str[HTTP_VER_MAX] = {"", "HTTP/1.0", "HTTP/1.1", "HTTP/1.1"};// const char* http_method_str[HTTP_METHOD_MAX] = {"GET", "HEAD", ""};const char* stat_str[STAT_MAX] = {"READ ", "WRITE ", "WAIT "};request_rec::request_rec(instm& isockin, outstm& isockout, ipaddress iclient_ip) : started(now()), rsp_code(0), stat(STAT_READ), sockin(&isockin), sockout(&isockout), client_ip(iclient_ip), version(HTTP_VER_10), method(HTTP_GET), method_str(), keep_alive(false), if_modified(invdatetime), req_line(), uri(), host(), referer(), partial(false), range_min(0), range_max(0), headers(), url(), file_type(FT_ERROR), sym_link(false), executable(false), abs_path(), rel_path(), file_name(), file_ext(), user(0), location(), hdr_size(0){}request_rec::~request_rec(){ delete user;}void request_rec::put_header(const char* name, const char* value){ if (version > HTTP_VER_09) { sockout->put(name); sockout->put(": "); sockout->put(value); sockout->put("\r\n"); }}void request_rec::put_header(const char* name, const string& value){ if (version > HTTP_VER_09) { sockout->put(name); sockout->put(": "); sockout->put(value); sockout->put("\r\n"); }}void request_rec::put_content_type(const char* mime){ if (method != HTTP_HEAD) put_header("Content-Type", mime);}void request_rec::put_content_length(int length){ if (method != HTTP_HEAD) { char buf[32]; snprintf(buf, sizeof(buf), "%d", length); put_header("Content-Length", buf); }}void request_rec::end_headers(){ if (version > HTTP_VER_09) sockout->put("\r\n"); hdr_size = sockout->tell(); if (method == HTTP_HEAD) end_response();}void request_rec::begin_response(int code, const char* msg){ rsp_code = code; stat = STAT_WRITE; if (version > HTTP_VER_09) { sockout->putf("%s %d %s\r\n", http_version_str[version], code, msg); put_header("Date", http_time_stamp(now(true))); put_header("Server", SERVER_APP_NAME); // put_header("Accept-Ranges", "bytes"); if (!isempty(location)) put_header("Location", location); static const char* sconn[2] = {"close", "keep-alive"}; if (version < HTTP_VER_11) put_header("Connection", sconn[keep_alive]); else if (!keep_alive) // HTTP/1.1 put_header("Connection", "close"); }}void request_rec::std_response(bool conn_close, int code, const char* msg, const char* descr){ if (conn_close) keep_alive = false; // we need a memory stream to temporarily store the response outmemory s(4096); string smsg = msg; // write out the standard response page in HTML format // to the memory stream s.open(); if (strlen(descr) != 0) { std_html_header(s, itostring(code) + ' ' + smsg); s.put("<p>"); html_encode(s, descr); s.put("</p>\n"); std_html_footer(s); } // send the response begin_response(code, msg); if (s.tell() > 0) // some responses do not return any content, e.g. 304 { put_content_type("text/html"); put_content_length(s.tell()); } end_headers(); if (s.tell() > 0) sockout->write(s.get_data(), s.tell()); end_response();}void request_rec::std_response(bool conn_close, int code, const char* msg, const char* descr, const string& dparam){ char buf[1024]; snprintf(buf, sizeof(buf), descr, pconst(dparam)); std_response(conn_close, code, msg, buf);}void request_rec::rsp_not_found(){ std_response(false, 404, "Not found", "The requested object %s was not found on this server.", url.path);}void request_rec::rsp_bad_request(){ std_response(true, 400, "Bad request", "Your browser sent a request that this server could not understand.");}void request_rec::rsp_uri_too_long(){ std_response(true, 414, "Request-URI too long", "The request-URI string sent by your browser is too long.");}void request_rec::rsp_forbidden(){ std_response(false, 403, "Forbidden", "You don't have permission to access %s on this server", url.path);}void request_rec::rsp_dir_index_forbidden(){ std_response(false, 403, "Directory index forbidden", "Directory index forbidden: %s", url.path);}void request_rec::rsp_redirect(const string& newurl){ location = newurl; std_response(false, 301, "Moved permanently", "The document has moved to %s", newurl);}void request_rec::rsp_overloaded(){ std_response(true, 504, "Service unavailable", "The server is overloaded. Please, try again later.");}void request_rec::rsp_not_modified(){ std_response(false, 304, "Not modified", "");}void request_rec::abort_request(){#ifdef DEBUG syslog_write(SYSLOG_WARNING, "Request from %s aborted", pconst(iptostring(client_ip)));#endif keep_alive = false; throw ehttp(0);}void request_rec::end_response(){ throw ehttp(rsp_code);}//// request parsers//const cset method_chars = "A-Z";const cset uri_chars = "~21-~FF";const cset field_chars = uri_chars - cset(":");const cset ws_chars = "~20";string request_rec::get_token(const cset& chars){ char buf[MAX_TOKEN]; int bufsize = sockin->token(chars, buf, sizeof(buf)); if (bufsize == 0 || bufsize >= MAX_TOKEN) rsp_bad_request(); return string(buf, bufsize);}string request_rec::get_uri(){ char buf[MAX_REQUEST_URI]; int bufsize = sockin->token(uri_chars, buf, sizeof(buf)); if (bufsize == 0) rsp_bad_request(); if (bufsize >= MAX_REQUEST_URI) rsp_uri_too_long(); return string(buf, bufsize);}void request_rec::parse_method(){ while (!sockin->get_eof() && sockin->get_eol()) sockin->skipline(); method_str = get_token(method_chars); req_line = method_str; if (method_str == "GET") method = HTTP_GET; else if (method_str == "HEAD") method = HTTP_HEAD; else if (length(method_str) == 0) abort_request(); else { // try to pass this method to a registered method handler. // the rest of the request line can be parsed using // parse_request_line(), if it's HTTP/1.1-like. handler_info* h = find_handler(method_list, method_str); if (h != 0) { method_callback(h->callback)(*this); // the handler must throw an ehttp exception fatal(252, "Internal error 252"); } else rsp_bad_request(); }}void request_rec::parse_request_line(){ if (sockin->skiptoken(ws_chars) == 0) abort_request(); // read the request URI uri = get_uri(); req_line += ' ' + uri; // read the version number, if present if (sockin->get_eol()) version = HTTP_VER_09; else { string s; if (sockin->skiptoken(ws_chars) == 0) abort_request(); s = get_token(uri_chars); req_line += ' ' + s; const char* p = s; if (length(s) < 8 || strncmp(p, "HTTP/1.", 7) != 0) rsp_bad_request(); if (p[7] == '0') version = HTTP_VER_10; else if (p[7] == '1') version = HTTP_VER_11; else version = HTTP_VER_UNKNOWN; // 1.x is ok for us } // HTTP/1.1 requires to keep the connection alive by default; // can be overridden by `Connection:' header keep_alive = version >= HTTP_VER_11; if (!sockin->get_eol()) rsp_bad_request(); if (version > HTTP_VER_09) sockin->skipline();}void request_rec::parse_hdr(string& fname, string& fvalue){ fname = get_token(field_chars); // read the field name sockin->skiptoken(ws_chars); if (sockin->get() != ':') // malformed header (no colon) rsp_bad_request(); do { sockin->skiptoken(ws_chars); // skip leading ws chars do { if (sockin->get_eol()) // the value may be empty (?) break; string t = get_token(uri_chars); // read field value if (!isempty(fvalue)) fvalue += ' '; fvalue += t; if (length(fvalue) > MAX_TOKEN) rsp_bad_request(); // according to RFC2616 all ws chars inside the field value // can become a single space } while (sockin->skiptoken(ws_chars) > 0); if (!sockin->get_eol()) rsp_bad_request(); sockin->skipline(); } while (sockin->preview() & ws_chars); // see if field value continues on the next line}void request_rec::parse_headers(){ while (!sockin->get_eol()) { string fname, fvalue; parse_hdr(fname, fvalue); fname = lowercase(fname); if (fname == "host") host = fvalue; else if (fname == "connection") { fvalue = lowercase(fvalue); if (fvalue == "close") keep_alive = false; else if (fvalue == "keep-alive") keep_alive = true; } else if (fname == "if-modified-since") { if_modified = parse_http_date(fvalue); if (if_modified == invdatetime) rsp_bad_request(); } else if (fname == "referer") referer = fvalue; else if (fname == "range") { if (strncmp(fvalue, "bytes=", 6) == 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -