bool http_connection::process_request(unsigned fd) { char* data = _M_in.data(); data[_M_uri + _M_urilen] = 0; // Parse URL. _M_url.reset(); url_parser::parse_result parse_result = _M_url.parse(data + _M_uri, _M_urilen, _M_path); if (parse_result == url_parser::ERROR_NO_MEMORY) { logger::instance().log(logger::LOG_INFO, "[http_connection::process_request] (fd %d) No memory, URL (%.*s).", fd, _M_urilen, data + _M_uri); _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } else if (parse_result == url_parser::PARSE_ERROR) { if (_M_path.count() > 0) { logger::instance().log(logger::LOG_INFO, "[http_connection::process_request] (fd %d) URL parse error (%.*s).", fd, _M_path.count(), _M_path.data()); } else { logger::instance().log(logger::LOG_INFO, "[http_connection::process_request] (fd %d) URL parse error (%.*s).", fd, _M_urilen, data + _M_uri); } _M_error = http_error::BAD_REQUEST; return true; } else if (parse_result == url_parser::FORBIDDEN) { logger::instance().log(logger::LOG_INFO, "[http_connection::process_request] (fd %d) Forbidden URL (%.*s).", fd, _M_path.count(), _M_path.data()); _M_error = http_error::FORBIDDEN; return true; } // If an absolute URL has been received... unsigned short hostlen; const char* host = _M_url.get_host(hostlen); if (host) { if ((_M_major_number == 1) && (_M_minor_number == 1)) { // Ignore Host header (if present). const char* value; unsigned short valuelen; if (!_M_headers.get_value_known_header(http_headers::HOST_HEADER, value, &valuelen)) { _M_error = http_error::BAD_REQUEST; return true; } } if (!_M_host.append_nul_terminated_string(host, hostlen)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } _M_port = _M_url.get_port(); #if PROXY _M_http_rule.handler = rulelist::HTTP_HANDLER; _M_rule = &_M_http_rule; return process_non_local_handler(fd); #endif // PROXY if (_M_url.get_port() != static_cast<http_server*>(_M_server)->_M_port) { not_found(); return true; } _M_vhost = static_cast<http_server*>(_M_server)->_M_vhosts.get_host(host, hostlen); } else { if (_M_headers.get_value_known_header(http_headers::HOST_HEADER, host, &hostlen)) { // If the host includes port number... unsigned port; const char* semicolon = (const char*) memrchr(host, ':', hostlen); if (semicolon) { if (number::parse_unsigned(semicolon + 1, (host + hostlen) - (semicolon + 1), port, 1, 65535) != number::PARSE_SUCCEEDED) { _M_error = http_error::BAD_REQUEST; return true; } hostlen = semicolon - host; } else { port = url_parser::HTTP_DEFAULT_PORT; } if (port != static_cast<http_server*>(_M_server)->_M_port) { not_found(); return true; } _M_vhost = static_cast<http_server*>(_M_server)->_M_vhosts.get_host(host, hostlen); } else { if ((_M_major_number == 1) && (_M_minor_number == 1)) { _M_error = http_error::BAD_REQUEST; return true; } _M_vhost = static_cast<http_server*>(_M_server)->_M_vhosts.get_default_host(); } } if (!_M_vhost) { not_found(); return true; } unsigned short pathlen; const char* urlpath = _M_url.get_path(pathlen); unsigned short extensionlen; const char* extension = _M_url.get_extension(extensionlen); _M_rule = _M_vhost->rules->find(_M_method, urlpath, pathlen, extension, extensionlen); if (_M_rule->handler != rulelist::LOCAL_HANDLER) { if (_M_rule->handler == rulelist::FCGI_HANDLER) { size_t len = _M_vhost->rootlen + pathlen; if (len > PATH_MAX) { _M_error = http_error::REQUEST_URI_TOO_LONG; return true; } // Save decoded path. if (!_M_decoded_path.append(urlpath, pathlen)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } unsigned short query_string_len; const char* query_string = _M_url.get_query(query_string_len); if (query_string_len > 1) { // Save query string. if (!_M_query_string.append(query_string + 1, query_string_len - 1)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } } return process_non_local_handler(fd); } // Local handler. if ((_M_method != http_method::GET) && (_M_method != http_method::HEAD)) { _M_error = http_error::NOT_IMPLEMENTED; return true; } char path[PATH_MAX + 1]; size_t len = _M_vhost->rootlen + pathlen; if (len >= sizeof(path)) { _M_error = http_error::REQUEST_URI_TOO_LONG; return true; } memcpy(path, _M_vhost->root, _M_vhost->rootlen); memcpy(path + _M_vhost->rootlen, urlpath, pathlen); path[len] = 0; struct stat buf; if (stat(path, &buf) < 0) { not_found(); return true; } bool dirlisting; bool index_file; // If the URI points to a directory... if (S_ISDIR(buf.st_mode)) { // If the directory name doesn't end with '/'... if (path[len - 1] != '/') { moved_permanently(); return true; } // Search index file. if (static_cast<http_server*>(_M_server)->_M_index_file_finder.search(path, len, &buf)) { // File. dirlisting = false; index_file = true; } else { // If we don't have directory listing... if (!_M_vhost->have_dirlisting) { not_found(); return true; } else { // Build directory listing. if (!_M_vhost->dir_listing->build(urlpath, pathlen, _M_body)) { logger::instance().log(logger::LOG_WARNING, "[http_connection::process_request] (fd %d) Couldn't build directory listing for (%s).", fd, path); _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } _M_bodyp = &_M_body; dirlisting = true; index_file = false; } } } else if ((S_ISREG(buf.st_mode)) || (S_ISLNK(buf.st_mode))) { // File. dirlisting = false; index_file = false; } else { not_found(); return true; } if (!dirlisting) { const char* value; unsigned short valuelen; if (_M_headers.get_value_known_header(http_headers::IF_MODIFIED_SINCE_HEADER, value, &valuelen)) { time_t t; if ((t = date_parser::parse(value, valuelen, &_M_last_modified)) != (time_t) -1) { if (t == buf.st_mtime) { not_modified(); return true; } else if (t > buf.st_mtime) { gmtime_r(&buf.st_mtime, &_M_last_modified); not_modified(); return true; } } } if (_M_method == http_method::GET) { if ((_M_headers.get_value_known_header(http_headers::RANGE_HEADER, value, &valuelen)) && (valuelen > 6) && (strncasecmp(value, "bytes=", 6) == 0)) { if (!range_parser::parse(value + 6, valuelen - 6, buf.st_size, _M_ranges)) { requested_range_not_satisfiable(); return true; } } if ((_M_fd = file_wrapper::open(path, O_RDONLY)) < 0) { logger::instance().log(logger::LOG_WARNING, "[http_connection::process_request] (fd %d) Couldn't open file (%s).", fd, path); _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } } http_headers* headers = &(static_cast<http_server*>(_M_server)->_M_headers); headers->reset(); if (!headers->add_known_header(http_headers::DATE_HEADER, &now::_M_tm)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } if (keep_alive()) { if (!headers->add_known_header(http_headers::CONNECTION_HEADER, "Keep-Alive", 10, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } else { if (!headers->add_known_header(http_headers::CONNECTION_HEADER, "close", 5, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } if (!headers->add_known_header(http_headers::SERVER_HEADER, WEBSERVER_NAME, sizeof(WEBSERVER_NAME) - 1, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } unsigned short status_code; // Directory listing? if (dirlisting) { char num[32]; int numlen = snprintf(num, sizeof(num), "%lu", _M_body.count()); if (!headers->add_known_header(http_headers::CONTENT_LENGTH_HEADER, num, numlen, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } if (!headers->add_known_header(http_headers::CONTENT_TYPE_HEADER, "text/html; charset=UTF-8", 24, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } status_code = 200; } else { if (!headers->add_known_header(http_headers::ACCEPT_RANGES_HEADER, "bytes", 5, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } if (index_file) { const char* end = path + len; const char* ptr = end; extension = NULL; while ((ptr > path) && (*(ptr - 1) != '/')) { if (*(ptr - 1) == '.') { extension = ptr; extensionlen = end - extension; break; } ptr--; } } if ((extension) && (extensionlen > 0)) { _M_type = static_cast<http_server*>(_M_server)->_M_mime_types.get_mime_type(extension, extensionlen, _M_typelen); } else { _M_type = mime_types::DEFAULT_MIME_TYPE; _M_typelen = mime_types::DEFAULT_MIME_TYPE_LEN; } _M_filesize = compute_content_length(buf.st_size); char num[32]; int numlen = snprintf(num, sizeof(num), "%lld", _M_filesize); if (!headers->add_known_header(http_headers::CONTENT_LENGTH_HEADER, num, numlen, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } const range_list::range* range; char content_range[128]; switch (_M_ranges.count()) { case 0: if (!headers->add_known_header(http_headers::CONTENT_TYPE_HEADER, _M_type, _M_typelen, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } status_code = 200; break; case 1: if (!headers->add_known_header(http_headers::CONTENT_TYPE_HEADER, _M_type, _M_typelen, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } range = _M_ranges.get(0); len = snprintf(content_range, sizeof(content_range), "bytes %lld-%lld/%lld", range->from, range->to, buf.st_size); if (!headers->add_known_header(http_headers::CONTENT_RANGE_HEADER, content_range, len, false)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } status_code = 206; break; default: _M_boundary = ++(static_cast<http_server*>(_M_server)->_M_boundary); if (!headers->add_content_type_multipart(_M_boundary)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } status_code = 206; } // Add 'Last-Modified' header. struct tm timestamp; gmtime_r(&buf.st_mtime, ×tamp); if (!headers->add_known_header(http_headers::LAST_MODIFIED_HEADER, ×tamp)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } _M_out.reset(); if (status_code == 200) { if (!_M_out.append("HTTP/1.1 200 OK\r\n", 17)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } else { if (!_M_out.append("HTTP/1.1 206 Partial Content\r\n", 30)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } if (!headers->serialize(_M_out)) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } _M_response_header_size = _M_out.count(); // If multipart... if (_M_ranges.count() >= 2) { if (!build_part_header()) { _M_error = http_error::INTERNAL_SERVER_ERROR; return true; } } _M_error = http_error::OK; if (_M_method == http_method::HEAD) { _M_filesize = 0; _M_state = SENDING_HEADERS_STATE; } else { if (dirlisting) { _M_filesize = _M_body.count(); _M_state = SENDING_TWO_BUFFERS_STATE; } else { socket_wrapper::cork(fd); _M_state = SENDING_HEADERS_STATE; } } return true; }
int handle_client_connection() { char buffer[8096]; int buffer_len; // Length of buffer char method[256]; char url[256]; char version[256]; int i = 0, // Used to iterate over the first line to get method, url, version j = 0; // Read first line buffer_len = read_line(client_sockfd, buffer, sizeof(buffer)); // Unable to read from socket, not sure what to do in this case if (buffer_len <= 0) { return -1; } fprintf(stderr, "==== Read Next Request ====\n"); // Get Method (e.g. GET, POST, etc) while ((i < (sizeof(method) - 1)) && (!isspace(buffer[i]))) { method[i] = buffer[i]; i++; } method[i] = '\0'; // fprintf(stderr, "method: %s\n", method); // Skip over spaces while (i < buffer_len && isspace(buffer[i])) { i++; } // Get URL j = 0; while (i < buffer_len && (j < (sizeof(url) - 1)) && !isspace(buffer[i])) { url[j] = buffer[i]; i++; j++; } url[j] = '\0'; // fprintf(stderr, "url: %s\n", url); // Skip over spaces while (i < buffer_len && isspace(buffer[i])) { i++; } j = 0; while (j < sizeof(version) - 1 && !isspace(buffer[i])) { version[j] = buffer[i]; i++; j++; } version[j] = '\0'; // fprintf(stderr, "version: %s\n", version); read_headers(); if (header_err_flag) { keep_alive = FALSE; bad_request(); return -1; } if (content_length > 0) { content = (char*) malloc(content_length + 1); read_socket(client_sockfd, content, content_length); } // fprintf(stderr, "Content-Length: %d\n", content_length); // fprintf(stderr, "Connection (keep_alive): %d\n", keep_alive); // fprintf(stderr, "Cookie: %d\n", cookie); // fprintf(stderr, "If-Modified-Since Valid Time: %d\n", time_is_valid); // fprintf(stderr, "If-Modified-Since Time: %p\n", if_modified_since); if (content != NULL) { // fprintf(stderr, "Content: %s\n", content); } /***********************************************************/ /* Full message has been read, respond to client */ /***********************************************************/ if (strcmp(method, "GET") != 0) { // Inform client we don't support method fprintf(stderr, "Method Not Allowed: %s\n", method); method_not_allowed(); return 0; } if (cookie) { // Inform client we don't support cookies not_implemented(); return 0; } if (not_eng) { // Inform client we only support English not_implemented(); return 0; } if (!acceptable_text) { // Inform client we only support plain text not_implemented(); return 0; } if (!acceptable_charset) { // Inform client we only support ASCII not_implemented(); return 0; } // Fix filename char file_path[512]; sprintf(file_path, "htdocs%s", url); if (file_path[strlen(file_path)-1] == '/') { file_path[strlen(file_path)-1] = '\0'; } // fprintf(stderr, "%s\n", file_path); int fname_valid = is_valid_fname(file_path); struct stat file_info; if (!fname_valid) { // invalid filename fprintf(stderr, "403 Forbidden: Invalid file name\n"); forbidden(); return 0; } if (stat(file_path, &file_info)) { fprintf(stderr, "404 Not Found: Stat failed\n"); // Stat failed not_found(); return 1; } if (!S_ISREG(file_info.st_mode)) { // Not a file forbidden(); fprintf(stderr, "403 Forbidden: Not a regular file\n"); return 0; } if (!(file_info.st_mode & S_IRUSR)) { // No read permissions forbidden(); fprintf(stderr, "403 Forbidden: No read permissions\n"); return 0; } FILE *f = fopen(file_path, "r"); if (f == NULL) { // No file not_found(); fprintf(stderr, "404 Not Found: Unable to open file\n"); return 0; } if (if_modified_since != NULL) { struct tm *last_modified = gmtime(&file_info.st_mtime); time_t last = mktime(last_modified); time_t since = mktime(if_modified_since); double diff = difftime(last, since); if (diff <= 0) { fprintf(stderr, "304 Not Modified\n"); not_modified(); return 0; } } fprintf(stderr, "All looks good, serving up content in %s\n", file_path); char *file_contents = NULL; int contents_length = 0; char line[512]; while (fgets(line, sizeof(line), f) != NULL) { if (file_contents != NULL) { char *new_contents = (char*) malloc(contents_length + strlen(line) + 1); strcpy(new_contents, file_contents); strcpy(new_contents + strlen(new_contents), line); contents_length += strlen(line); free(file_contents); file_contents = new_contents; } else { file_contents = (char*) malloc(strlen(line) + 1); strcpy(file_contents, line); contents_length += strlen(line); } } fclose(f); // fprintf(stderr, "File Contents:\n"); // fprintf(stderr, "%s\n", file_contents); ok(file_contents); return 0; }