diff --git a/playground/tkg/Design1/cgi-bin/script/cgi_ClientRedirWithDoc.cpp b/playground/tkg/Design1/cgi-bin/script/cgi_ClientRedirWithDoc.cpp index cb0b4f0..5dbef4d 100644 --- a/playground/tkg/Design1/cgi-bin/script/cgi_ClientRedirWithDoc.cpp +++ b/playground/tkg/Design1/cgi-bin/script/cgi_ClientRedirWithDoc.cpp @@ -6,8 +6,8 @@ int main() { std::string input; std::cin >> input; - std::cout << "Location:http://example.com" << std::endl; std::cout << "Status:301 CRWD(CGI)" << std::endl; + std::cout << "Location:http://example.com" << std::endl; std::cout << "Content-Type:text/html" << std::endl; std::cout << std::endl; std::stringstream ss; diff --git a/playground/tkg/Design1/cgi-bin/script/cgi_getCWD.cpp b/playground/tkg/Design1/cgi-bin/script/cgi_getCWD.cpp new file mode 100644 index 0000000..25a2d42 --- /dev/null +++ b/playground/tkg/Design1/cgi-bin/script/cgi_getCWD.cpp @@ -0,0 +1,18 @@ +#include + +#include +#include + +int main() { + std::string input; + std::cin >> input; + std::cout << "Content-Type:text/html" << std::endl; + std::cout << std::endl; + + std::stringstream ss; + char cwd[1028]; + getcwd(cwd, 1028); + ss << input << std::endl; + ss << cwd << std::endl; + std::cout << ss.str(); +} \ No newline at end of file diff --git a/playground/tkg/Design1/cgi-bin/script/cgi_meta.cpp b/playground/tkg/Design1/cgi-bin/script/cgi_meta.cpp index 0363c8c..4870558 100644 --- a/playground/tkg/Design1/cgi-bin/script/cgi_meta.cpp +++ b/playground/tkg/Design1/cgi-bin/script/cgi_meta.cpp @@ -6,8 +6,8 @@ int main() { extern char **environ; std::string input; std::cin >> input; - std::cout << "Content-Type:text/plain" << std::endl; std::cout << "Status:200 " << std::endl; + std::cout << "Content-Type:text/plain" << std::endl; std::cout << std::endl; std::cout << input << std::endl; std::cout << "AUTH_TYPE: " << getenv("AUTH_TYPE") << std::endl; diff --git a/playground/tkg/Design1/src/HttpResponse.cpp b/playground/tkg/Design1/src/HttpResponse.cpp index c5b16f6..0e50783 100644 --- a/playground/tkg/Design1/src/HttpResponse.cpp +++ b/playground/tkg/Design1/src/HttpResponse.cpp @@ -24,6 +24,11 @@ void HttpResponse::setStatusAndReason(const int status, const std::string &reaso reason_phrase_ = reason; } +void HttpResponse::setStatusAndReason(const int status) { + status_ = status; + reason_phrase_ = conf_->cache_.statusMsg_[status_]; +} + void HttpResponse::setContentType(const std::string &path) { std::string ext = getExtension(path); if (ext == "") return; diff --git a/playground/tkg/Design1/src/HttpResponse.hpp b/playground/tkg/Design1/src/HttpResponse.hpp index 781d06c..8b11b42 100644 --- a/playground/tkg/Design1/src/HttpResponse.hpp +++ b/playground/tkg/Design1/src/HttpResponse.hpp @@ -33,6 +33,7 @@ class HttpResponse { int getStatus() const; void setStatus(const int status); void setStatusAndReason(const int status, const std::string &reason); + void setStatusAndReason(const int status); void setContentType(const std::string &path); void appendHeader(const std::string &key, const std::string &value); void appendBody(const char *str, size_t size); diff --git a/playground/tkg/Design1/src/Observee/CGI/CGI.cpp b/playground/tkg/Design1/src/Observee/CGI/CGI.cpp index 1399b9b..8bed1ea 100644 --- a/playground/tkg/Design1/src/Observee/CGI/CGI.cpp +++ b/playground/tkg/Design1/src/Observee/CGI/CGI.cpp @@ -31,7 +31,7 @@ void CGI::shutdown() { em_->deleteTimerEvent(id_); kill(pid_, SIGTERM); waitpid(pid_, &status, 0); - if (status != 0) response_->setStatusAndReason(500, ""); + if (status != 0) response_->setStatusAndReason(500); em_->remove(std::pair(id_, FD)); } @@ -44,194 +44,164 @@ void CGI::terminate() { std::pair getHeaderField(std::string &field) { std::stringstream ss(field); std::string name, value; - if (field.find(':') == std::string::npos) throw std::runtime_error("invalid header field"); + if (field.find(':') == std::string::npos) throw InternalServerErrorException("invalid header field"); std::getline(ss, name, ':'); + toLower(name); std::getline(ss, value); - trimOws(value); + value = trimOws(value); return std::pair(name, value); } -void CGI::parseDocRes(std::vector &lines) { - DEBUG_PUTS("parseDocRes"); - std::size_t i = 0; - while (i < lines.size() && lines[i] != "") { - t_field field = getHeaderField(lines[i]); - if (i == 0) { - response_->appendHeader("content-type", field.second); - } else if (i == 1 && field.first == "Status") { - std::istringstream iss(field.second); +void CGI::processDocRes(std::string &body) { + DEBUG_PUTS("processDocRes"); + for (HttpResponse::t_headers::const_iterator itr = headers_.cbegin(); itr != headers_.cend(); itr++) { + if (itr->first == "status") { + std::istringstream iss(itr->second); std::string status; std::string reason; iss >> status; iss.ignore(); - getline(iss, reason, '\n'); + getline(iss, reason); response_->setStatusAndReason(std::atoi(status.c_str()), reason); } else { - response_->appendHeader(toLower(field.first), field.second); + response_->appendHeader(itr->first, itr->second); } - i++; - } - // parse body - i++; - for (; i < lines.size(); i++) { - response_->appendBody(lines[i] + "\n"); } - return; + response_->appendBody(body); + em_->registerWriteEvent(parent_->id_); + shutdown(); } -void CGI::parseClientRedirect(std::vector &lines) { - DEBUG_PUTS("parseClientRedirect"); - t_field field = getHeaderField(lines[0]); - response_->appendHeader("location", field.second); - response_->setStatusAndReason(302, ""); +void CGI::processClientRedirect() { + DEBUG_PUTS("processClientRedirect"); + for (HttpResponse::t_headers::const_iterator itr = headers_.cbegin(); itr != headers_.cend(); itr++) { + response_->appendHeader(itr->first, itr->second); + } + response_->setStatusAndReason(302); + em_->registerWriteEvent(parent_->id_); + shutdown(); } -void CGI::parseClientRedirectWithDoc(std::vector &lines) { - DEBUG_PUTS("parseClientRedirectWithDoc"); - t_field field = getHeaderField(lines[0]); - response_->appendHeader("location", field.second); - field = getHeaderField(lines[1]); - response_->setStatusAndReason(std::atoi(field.second.c_str()), field.second.substr(4)); - field = getHeaderField(lines[2]); - response_->appendHeader("content-type", field.second); - std::size_t i = 3; - while (i < lines.size() && lines[i] != "") { - field = getHeaderField(lines[i]); - response_->appendHeader(toLower(field.first), field.second); - i++; - } - if (lines[i] == "") i++; - while (i < lines.size() && lines[i] != "") { - response_->appendBody(lines[i] + "\n"); - i++; +void CGI::processClientRedirectWithDoc(std::string &body) { + DEBUG_PUTS("processClientRedirectWithDoc"); + for (HttpResponse::t_headers::const_iterator itr = headers_.cbegin(); itr != headers_.cend(); itr++) { + if (itr->first == "status") { + std::istringstream iss(itr->second); + std::string status; + std::string reason; + iss >> status; + iss.ignore(); + getline(iss, reason); + response_->setStatusAndReason(std::atoi(status.c_str()), reason); + } else { + response_->appendHeader(itr->first, itr->second); + } } + response_->appendBody(body); + em_->registerWriteEvent(parent_->id_); + shutdown(); } -void CGI::parseLocalRedirect(std::vector &lines) { - DEBUG_PUTS("parseLocalRedirect"); - t_field field = getHeaderField(lines[0]); +void CGI::processLocalRedirect() { + DEBUG_PUTS("processLocalRedirect"); + std::string loc_value = headers_["location"]; try { - const size_t fragment_pos = field.second.find("#"); + const size_t fragment_pos = loc_value.find("#"); std::string fragment; if (fragment_pos != std::string::npos) { - fragment = field.second.substr(fragment_pos + 1); + fragment = loc_value.substr(fragment_pos + 1); } - URI *uri = URI::parseRequestURI(field.second.substr(0, fragment_pos)); + URI *uri = URI::parseRequestURI(loc_value.substr(0, fragment_pos)); delete request_->request_target_; request_->request_target_ = uri; request_->body_.clear(); dynamic_cast(parent_)->initExtension(); + ConnectionSocket *parent = dynamic_cast(parent_); + shutdown(); + parent->process(); } catch (std::runtime_error &e) { - throw(InternalServerErrorException("CGI response URI is invalid")); + throw InternalServerErrorException("CGI Local Redirect response URI is invalid"); } } -bool CGI::isDocRes(std::vector &lines) { - try { - std::size_t i = 0; - while (i < lines.size() && lines[i] != "") { - t_field field = getHeaderField(lines[i]); - if (i == 0 && field.first != "Content-Type") return false; - if (i == 1 && field.first == "Status" && !CGIValidation::isStatusHeaderValue(field.second)) return false; - i++; - } - } catch (std::runtime_error &e) { - std::cout << e.what() << std::endl; +bool CGI::isDocRes() { + if (headers_.find("content-type") == headers_.end()) return false; + if (!CGIValidation::isMediaType(headers_["content-type"])) return false; + if (headers_.find("status") != headers_.end() && !CGIValidation::isStatusHeaderValue(headers_["status"])) return false; - } + if (headers_.find("location") != headers_.end()) return false; return true; } -bool CGI::isLocalRedirectRes(std::vector &lines) { - if (lines.size() != 2) return false; - try { - t_field field = getHeaderField(lines[0]); - if (field.first != "Location") return false; - if (!CGIValidation::isAbsPath(field.second.c_str())) return false; - if (lines[1] != "") return false; - } catch (std::runtime_error &e) { - std::cout << e.what() << std::endl; - return false; - } +bool CGI::isLocalRedirectRes() { + if (headers_.size() != 1) return false; + if (headers_.find("location") == headers_.end()) return false; + if (!CGIValidation::isAbsPath(headers_["location"].c_str())) return false; + return true; } -bool CGI::isClientRedirectRes(std::vector &lines) { - if (lines.size() != 2) return false; - try { - t_field filed = getHeaderField(lines[0]); - if (filed.first != "Location") return false; - if (!CGIValidation::isAbsURI(filed.second)) return false; - if (lines[1] != "") return false; - } catch (std::runtime_error &e) { - std::cout << e.what() << std::endl; - return false; - } +bool CGI::isClientRedirectRes() { + if (headers_.find("location") == headers_.end()) return false; + if (!CGIValidation::isAbsURI(headers_["location"])) return false; + if (headers_.find("content-type") != headers_.end()) return false; + if (headers_.find("status") != headers_.end()) return false; return true; } -bool CGI::isClientRedirectWithDocRes(std::vector &lines) { - if (lines.size() < 3) return false; - try { - t_field field = getHeaderField(lines[0]); - if (field.first != "Location" || !CGIValidation::isAbsURI(field.second)) return false; - field = getHeaderField(lines[1]); - if (field.first != "Status" || !CGIValidation::isStatusHeaderValue(field.second)) return false; - field = getHeaderField(lines[2]); - if (field.first != "Content-Type" || !CGIValidation::isMediaType(field.second)) return false; - std::size_t i = 3; - while (i < lines.size() && lines[i] != "") { - field = getHeaderField(lines[i]); - i++; - } - if (i == lines.size() && lines[i - 1] != "") return false; - } catch (std::runtime_error &e) { - std::cout << e.what() << std::endl; - return false; +bool CGI::isClientRedirectWithDocRes() { + if (headers_.find("location") == headers_.end()) return false; + if (!CGIValidation::isAbsURI(headers_["location"])) return false; + if (headers_.find("content-type") == headers_.end()) return false; + if (!CGIValidation::isMediaType(headers_["content-type"])) return false; + if (headers_.find("status") != headers_.end()) { + if (!CGIValidation::isStatusHeaderValue(headers_["status"])) return false; } return true; } -CGI::Type CGI::getResponseType(std::vector &lines) { - if (isDocRes(lines)) { +CGI::Type CGI::getResponseType() { + if (isDocRes()) { return CGI::Doc; - } else if (isLocalRedirectRes(lines)) { + } else if (isLocalRedirectRes()) { return CGI::LocalRedir; - } else if (isClientRedirectRes(lines)) { + } else if (isClientRedirectRes()) { return CGI::ClientRedir; - } else if (isClientRedirectWithDocRes(lines)) { + } else if (isClientRedirectWithDocRes()) { return CGI::ClientRedirWithDoc; } return CGI::Error; } -void CGI::parseCGIResponse() { - std::vector lines = CGIValidation::extractLines(recieved_data_); - CGI::Type type = getResponseType(lines); +void CGI::parseHeaders(std::string &headers) { + std::vector fields = CGIValidation::extractLines(headers); + std::vector::size_type i = 0; + while (i < fields.size() && fields[i] != "") { + t_field field = getHeaderField(fields[i]); + headers_[field.first] = field.second; + i++; + } +} + +void CGI::handleCGIResponse() { + DEBUG_PRINTF("CGI LAST RESULT: '%s'\n", escape(recieved_data_).c_str()); + std::size_t nlnl_pos = recieved_data_.find("\n\n"); + if (nlnl_pos == std::string::npos) + throw InternalServerErrorException("CGI response is invalid(no end NL of headers)"); + std::string headers = recieved_data_.substr(0, nlnl_pos); + std::string body = recieved_data_.substr(recieved_data_.find("\n\n") + 2); + parseHeaders(headers); + CGI::Type type = getResponseType(); if (type == CGI::Error) { - response_->setStatusAndReason(500, ""); + throw InternalServerErrorException("CGI response is invalid"); } else if (type == CGI::Doc) { - parseDocRes(lines); + processDocRes(body); } else if (type == CGI::LocalRedir) { - DEBUG_PRINTF("CGI LAST RESULT: '%s'\n", escape(recieved_data_).c_str()); - try { - parseLocalRedirect(lines); - ConnectionSocket *parent = dynamic_cast(parent_); - shutdown(); - parent->process(); - } catch (HttpException &e) { - response_->setStatusAndReason(e.statusCode(), ""); - type = CGI::Error; - } + processLocalRedirect(); } else if (type == CGI::ClientRedir) { - parseClientRedirect(lines); + processClientRedirect(); } else if (type == CGI::ClientRedirWithDoc) { - parseClientRedirectWithDoc(lines); - } - if (type != CGI::LocalRedir) { - DEBUG_PRINTF("CGI LAST RESULT: '%s'\n", escape(recieved_data_).c_str()); - em_->registerWriteEvent(parent_->id_); - shutdown(); + processClientRedirectWithDoc(body); } } @@ -244,13 +214,20 @@ void CGI::notify(struct kevent ev) { int res = read(id_, &buff[0], FILE_READ_SIZE); if (res == -1) { - response_->setStatusAndReason(501, ""); + response_->setStatusAndReason(500); em_->registerWriteEvent(parent_->id_); shutdown(); return; } else if (res == 0) { - response_->setStatusAndReason(200, ""); - parseCGIResponse(); + response_->setStatusAndReason(200); + try { + handleCGIResponse(); + } catch (HttpException &e) { + DEBUG_PUTS(e.what()); + response_->setStatusAndReason(e.statusCode()); + em_->registerWriteEvent(parent_->id_); + shutdown(); + } } else { std::cout << "CGI read res: " << res << std::endl; buff[res] = '\0'; @@ -269,7 +246,7 @@ void CGI::notify(struct kevent ev) { int res = write(id_, response + sending_size_, size); if (res == -1) { perror("sendto"); - response_->setStatusAndReason(501, ""); + response_->setStatusAndReason(500); em_->registerWriteEvent(parent_->id_); shutdown(); return; diff --git a/playground/tkg/Design1/src/Observee/CGI/CGI.hpp b/playground/tkg/Design1/src/Observee/CGI/CGI.hpp index 90e9a28..f32ba0a 100644 --- a/playground/tkg/Design1/src/Observee/CGI/CGI.hpp +++ b/playground/tkg/Design1/src/Observee/CGI/CGI.hpp @@ -20,21 +20,23 @@ class CGI : public Observee { void notify(struct kevent ev); void shutdown(); void terminate(); - void parseCGIResponse(); - Type getResponseType(std::vector &lines); - bool isDocRes(std::vector &line); - bool isLocalRedirectRes(std::vector &line); - bool isClientRedirectRes(std::vector &line); - bool isClientRedirectWithDocRes(std::vector &line); - void parseDocRes(std::vector &lines); - void parseClientRedirect(std::vector &lines); - void parseLocalRedirect(std::vector &lines); - void parseClientRedirectWithDoc(std::vector &lines); + void handleCGIResponse(); + void parseHeaders(std::string &headers); + Type getResponseType(); + bool isDocRes(); + bool isLocalRedirectRes(); + bool isClientRedirectRes(); + bool isClientRedirectWithDocRes(); + void processDocRes(std::string &body); + void processClientRedirect(); + void processLocalRedirect(); + void processClientRedirectWithDoc(std::string &body); private: pid_t pid_; HttpRequest *request_; HttpResponse *response_; + HttpResponse::t_headers headers_; std::string recieved_data_; std::size_t sending_size_; std::size_t recieved_size_; diff --git a/playground/tkg/Design1/src/Observee/CGI/CGIInfo.cpp b/playground/tkg/Design1/src/Observee/CGI/CGIInfo.cpp index 6cb1a3f..bc3c227 100644 --- a/playground/tkg/Design1/src/Observee/CGI/CGIInfo.cpp +++ b/playground/tkg/Design1/src/Observee/CGI/CGIInfo.cpp @@ -22,6 +22,8 @@ std::string getScriptName(const std::string &path, const std::string &ext) { return ""; } +std::string CGIInfo::getCGIWorkingDirectory() { return script_name_.substr(0, script_name_.rfind("/")); } + std::string getPathInfo(const std::string &path, const std::string &ext) { std::size_t pos = path.find(ext); while (pos != std::string::npos) { diff --git a/playground/tkg/Design1/src/Observee/CGI/CGIInfo.hpp b/playground/tkg/Design1/src/Observee/CGI/CGIInfo.hpp index d4353bc..232cdab 100644 --- a/playground/tkg/Design1/src/Observee/CGI/CGIInfo.hpp +++ b/playground/tkg/Design1/src/Observee/CGI/CGIInfo.hpp @@ -26,6 +26,7 @@ struct CGIInfo { std::string cookie_; void setEnv(std::vector &env); + std::string getCGIWorkingDirectory(); }; std::string getPathInfo(const std::string &path, const std::string &ext); diff --git a/playground/tkg/Design1/src/Observee/CGI/helper.cpp b/playground/tkg/Design1/src/Observee/CGI/helper.cpp index 9f7c40a..adb345f 100644 --- a/playground/tkg/Design1/src/Observee/CGI/helper.cpp +++ b/playground/tkg/Design1/src/Observee/CGI/helper.cpp @@ -105,7 +105,7 @@ bool isPrintable(const char c) { bool isMediaType(std::string &raw_media) { std::string media = raw_media.substr(0, raw_media.find(";")); - trimOws(media); + media = trimOws(media); toLower(media); std::size_t pos = media.find("/"); if (pos == std::string::npos) return false; diff --git a/playground/tkg/Design1/src/Observee/ConnectionSocket.cpp b/playground/tkg/Design1/src/Observee/ConnectionSocket.cpp index 759e63f..bc27214 100644 --- a/playground/tkg/Design1/src/Observee/ConnectionSocket.cpp +++ b/playground/tkg/Design1/src/Observee/ConnectionSocket.cpp @@ -78,26 +78,29 @@ void ConnectionSocket::execCGI(const std::string &path) { em_->terminateAll(); std::vector env; info.setEnv(env); + std::string cwd = info.getCGIWorkingDirectory(); + if (chdir(cwd.c_str()) == -1) { + perror("chdir"); + close(fd[1]); + exit(1); + } argv[0] = const_cast(info.script_name_.c_str()); if (dup2(fd[1], STDIN_FILENO) == -1) { perror("dup2"); close(fd[1]); - deleteEnv(env); exit(1); } if (dup2(fd[1], STDOUT_FILENO) == -1) { perror("dup2"); close(fd[1]); - deleteEnv(env); exit(1); } close(fd[1]); if (execve(info.script_name_.c_str(), argv, &env[0]) == -1) { perror("execve"); - deleteEnv(env); exit(1); } - deleteEnv(env); + exit(1); } else { close(fd[1]); std::cout << "pid: " << pid << std::endl; @@ -169,7 +172,7 @@ void ConnectionSocket::processGET() { if (idx_path == "" && loc_conf_->common_.autoindex_) { DEBUG_PUTS("autoindex"); response_.appendBody(GET::listFilesAndDirectories(path, request_)); - response_.setStatusAndReason(200, ""); + response_.setStatusAndReason(200); em_->disableReadEvent(id_); em_->registerWriteEvent(id_); return; // 200 OK @@ -191,7 +194,7 @@ void ConnectionSocket::processRedirect() { if (!isAcceptableMethod(loc_conf_, request_.method_)) { throw MethodNotAllowedException("No Suitable Location"); } - response_.setStatusAndReason(std::atoi(loc_conf_->getRedirectStatus().c_str()), ""); + response_.setStatusAndReason(std::atoi(loc_conf_->getRedirectStatus().c_str())); response_.appendHeader("location", loc_conf_->getRedirectURI()); em_->disableReadEvent(id_); em_->registerWriteEvent(id_); @@ -242,7 +245,7 @@ void ConnectionSocket::notify(struct kevent ev) { } catch (HttpException &e) { // all 4xx 5xx exception(readRequest and process) is caught here std::cerr << e.what() << std::endl; - response_.setStatusAndReason(e.statusCode(), ""); + response_.setStatusAndReason(e.statusCode()); if (loc_conf_) { // error_page directive is ignored when error ocuured reading Request processErrorPage(loc_conf_); diff --git a/playground/tkg/Design1/src/Observee/GET.cpp b/playground/tkg/Design1/src/Observee/GET.cpp index 8e86c0f..01f84e6 100644 --- a/playground/tkg/Design1/src/Observee/GET.cpp +++ b/playground/tkg/Design1/src/Observee/GET.cpp @@ -89,7 +89,7 @@ void GET::notify(struct kevent ev) { response_->appendBody(buff, res); if (res == 0 || res == ev.data) { close(id_); - response_->setStatusAndReason(200, ""); + response_->setStatusAndReason(200); parent_->obliviateChild(this); em_->deleteTimerEvent(id_); em_->registerWriteEvent(parent_->id_); diff --git a/playground/tkg/Design1/test/client.py b/playground/tkg/Design1/test/client.py index ca7e706..b49842a 100755 --- a/playground/tkg/Design1/test/client.py +++ b/playground/tkg/Design1/test/client.py @@ -34,6 +34,7 @@ def send_http_request(host, port, method, path, body, headers): body = argv[5] headers = dict() for h in argv[6:]: - pair = h.split(":") - headers[pair[0]] = pair[1] + name = h[:h.find(":")] + value = h[h.find(":")+1:] + headers[name] = value send_http_request(host, port, method, path, body, headers) \ No newline at end of file diff --git a/playground/tkg/Design1/test/e2e_test.cpp b/playground/tkg/Design1/test/e2e_test.cpp index 4b3ae0a..6d650f4 100644 --- a/playground/tkg/Design1/test/e2e_test.cpp +++ b/playground/tkg/Design1/test/e2e_test.cpp @@ -25,6 +25,19 @@ TEST(E2E, CGIDoc) { ASSERT_TRUE(includes(res, "CGI Response \n")); } +TEST(E2E, CGI_CWD) { + std::string host = "localhost"; + std::string port = "80"; + std::string method = "GET"; + std::string path = "/cgi-bin/cgi_getCWD.cgi?query"; + std::string body = ""; + std::string headers = "Host: localhost;Content-Length:0;Date: Wed, 16 Oct 2019 07:28:00 GMT"; + std::string res = sendRequest(host, port, method, path, body, headers); + + // ASSERT_TRUE(includes(res, "HTTP/1.1 200 OK")); + ASSERT_TRUE(includes(res, "/playground/tkg/Design1/cgi-bin\n")); +} + TEST(E2E, CGILocalRedirect) { std::string host = "localhost"; std::string port = "80";