📄 request.cxx
字号:
{ del(fvalue, 0, 6); const char* p = fvalue; char* e; int rmin = strtol(p, &e, 10); if (*e == '-') { p = e + 1; int rmax = strtol(p, &e, 10); if (e == p) rmax = -1; // we don't support multiple ranges, neither negative ranges if (*e == 0 && rmin >= 0) { partial = true; range_min = rmin; range_max = rmax; } } } } // other headers go to request_rec::headers for use in // custom plugins/modules. so called "coalesce" headers // are not supported yet else put(headers, fname, fvalue); } // convert the referer URI to relative if on the same host if (!isempty(referer)) { string s = "http://" + host; if (strncmp(s, referer, length(s)) == 0) del(referer, 0, length(s)); if (isempty(referer)) referer = "/"; } sockin->skipline();}void request_rec::analyze_uri(){ string s = uri; if (!isurl(uri)) { // if request URI is just a path if (*pconst(uri) != '/') rsp_bad_request(); if (version > HTTP_VER_09 && isempty(host)) rsp_bad_request(); s = "http://" + host + uri; } urlcrack(s, url); // split the path into components strlist pathlist; split_path(url.path, pathlist); abs_path = cfg_document_root + '/'; rel_path = '/'; file_type = FT_DIRECTORY; // we respond to "http://localhost/.wstat" by showing the server status report if (length(pathlist) > 0 && getstr(pathlist, 0) == DEF_STAT_PATH) rsp_wstat(); // analyze path components one by one and stop where the // component does not exist, or where we see a file, not a // directory. note, that the request-uri may be longer and // may contain extra components which are ignored. f.ex. // the request uri is /dir/file.html/other/stuff/. the // scan stops at file.html and ignores the rest. this is the // way apache and most other daemons work. // besides, we may pass a non-existent path to a registered // path handler. int i = 0; while (i < length(pathlist)) { string s = getstr(pathlist, i); abs_path += s; rel_path += s; file_type = get_file_type(abs_path); if (file_type == FT_DIRECTORY) { abs_path += '/'; rel_path += '/'; } else if (file_type == FT_FILE) { file_name = s; break; } else if (file_type == FT_OTHER) // pipe or device rsp_forbidden(); else { handler_info* h = find_handler(path_list, rel_path); if (h != 0) { path_callback(h->callback)(*this); // the handler must throw an ehttp exception fatal(253, "Internal error 253"); } else rsp_not_found(); } i++; } if (file_type == FT_DIRECTORY) { // check for the trailing slash and redirect to the canonical // form if necessary if (trail_char(url.path) != '/') { url.path += '/'; rsp_redirect(urlcreate(url)); } // find the default index file if (cfg_def_index_files) { const char** idx = cfg_index_files; while (*idx != nil) { string t = abs_path + *idx; if (is_file(t)) { abs_path = t; file_type = FT_FILE; file_name = *idx; break; } idx++; } } } // other useful info about the object sym_link = is_symlink(abs_path); executable = is_executable(abs_path); if (!isempty(file_name)) { file_ext = get_file_ext(file_name); handler_info* h = find_handler(file_list, file_ext); if (h != 0) { file_callback(h->callback)(*this); // the handler must throw an ehttp exception fatal(251, "Internal error 251"); } }}void request_rec::rsp_dir_index(){ if (!cfg_dir_indexes) rsp_dir_index_forbidden(); filist list(slflags(SL_SORTED | SL_CASESENS | SL_OWNOBJECTS)); get_directory(list, abs_path, true, 500); if (length(list) == 0) rsp_forbidden(); // we don't know the length of the resulting file keep_alive = false; begin_response(200, "OK"); if (method == HTTP_GET) // not HEAD put_content_type("text/html"); end_headers(); // build an index HTML page std_html_header(*sockout, "Index of " + rel_path); sockout->put("<hr noshade size=1>\n"); sockout->put("<pre>\n"); html_show_file_list(*sockout, list); clear(list); get_directory(list, abs_path, false, 500); html_show_file_list(*sockout, list); sockout->put("</pre>\n"); std_html_footer(*sockout); end_response();}static void show_lifetime(outstm* sockout, datetime t){ datetime diff = now() - t; int d = days(diff); if (d > 0) sockout->putf("%dd ", d); sockout->put(dttostring(diff, "%H:%M:%S"));}static ipaddress localhost(127, 0, 0, 1);void request_rec::rsp_wstat(){ // only requests from localhost are allowed if (client_ip != localhost) rsp_forbidden(); keep_alive = false; begin_response(200, "OK"); if (method == HTTP_GET) // not HEAD put_content_type("text/html"); end_headers(); std_html_header(*sockout, "wshare status report"); sockout->put("<pre>\n"); sockout->putf(" Requests: %d\n", thread_seq); sockout->put(" Running: "); show_lifetime(sockout, ::started); sockout->put("\n\n"); sockout->put(" Client Status Lifetime Request\n");// sockout->put("</pre>\n"); sockout->put("<hr noshade size=1>\n");// sockout->put("<pre>\n"); for (int i = 0; i < threads.count; i++) { if (threads.list[i] == 0) continue; // copy all parameters to local vars to free the thread_list mutex earlier ipaddress tclient_ip = 0; req_stat_t tstat = STAT_READ; datetime tstarted = invdatetime; string treq_line; threads.lock.rdlock(); try { client_thread* t = threads.list[i]; if (t != 0) { tclient_ip = t->client_ip; tstat = t->stat; tstarted = t->started; treq_line = t->req_line; } } catch(...) { threads.lock.unlock(); } threads.lock.unlock(); if (tclient_ip == 0) continue; sockout->putf(" %-15s %s", pconst(iptostring(tclient_ip)), stat_str[tstat]); show_lifetime(sockout, tstarted); sockout->put(" "); html_encode(*sockout, treq_line); sockout->put("\n"); } sockout->put("</pre>\n"); std_html_footer(*sockout); end_response();}void request_rec::rsp_file(){ // .ht* files are forbidden, like with Apache if (strncmp(file_name, ".ht", 3) == 0) rsp_forbidden(); large fsize64 = get_file_size(abs_path); // large files are not supported yet if (fsize64 >= (large)INT_MAX) rsp_not_found(); int fsize = (int)fsize64; if (fsize < 0) rsp_not_found(); // test the file for readability infile f(abs_path); try { f.set_bufsize(0); f.open(); } catch(estream* e) { delete e; rsp_forbidden(); } // partial content int txsize = fsize; if (partial) { if (range_max == -1) range_max = fsize - 1; if (range_min >= 0 && range_min < range_max && range_max < fsize) txsize = range_max - range_min + 1; else partial = false; } // send headers datetime fmtime = get_file_mtime(abs_path); if (if_modified != invdatetime && fmtime != invdatetime && fmtime <= if_modified) rsp_not_modified(); if (partial) begin_response(206, "Partial Content"); else begin_response(200, "OK"); put_content_type(get_mimetype(abs_path)); put_content_length(txsize); if (partial) put_header("Content-Range", "bytes " + itostring(range_min) + '-' + itostring(range_max) + '/' + itostring(fsize)); if (cfg_file_mtime) put_header("Last-Modified", http_time_stamp(fmtime)); end_headers(); // send content if (partial) f.seek(range_min); // will implement 64-bit seek in the future char buf[FILE_BUF_SIZE]; while (txsize > 0) { int r = f.read(buf, imin(sizeof(buf), txsize)); if (r <= 0) break; sockout->write(buf, r); txsize -= r; } // if for some reason the number of bytes sent does not correspond to the // promised content length, just close the connection if (txsize != 0) keep_alive = false; end_response();}void request_rec::respond(){ try { if (thread_count > cfg_max_clients) rsp_overloaded(); parse_method(); parse_request_line(); parse_headers(); analyze_uri(); if (file_type == FT_DIRECTORY) rsp_dir_index(); else if (file_type == FT_FILE) rsp_file(); else rsp_not_found(); // all branches must throw an ehttp exception // before reaching this point fatal(254, "Internal error 254"); } catch(ehttp e) { if (keep_alive) { sockout->flush(); stat = STAT_WAIT; } else { sockin->close(); sockout->close(); } htlog_write(client_ip, req_line, e.code, sockout->tell() - hdr_size, referer); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -