Пример #1
0
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, &timestamp);

		if (!headers->add_known_header(http_headers::LAST_MODIFIED_HEADER, &timestamp)) {
			_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;
}
Пример #2
0
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;
}