#include "ace/OS_NS_stdio.h" #include "ace/OS_NS_string.h" #include "ace/OS_NS_ctype.h" #include "ace/Process.h" #include "ace/Mem_Map.h" #include "ace/Log_Msg.h" #include "HTTP_Response.h" #include "HTTP_Request.h" #include "HTTP_Helpers.h" #include "HTTP_Config.h" #include "JAWS_IO.h" #if defined (ACE_JAWS_BASELINE) static char * const EMPTY_HEADER = ""; #else static const char * const EMPTY_HEADER = ""; #endif /* ACE_JAWS_BASELINE */ HTTP_Response::HTTP_Response (JAWS_IO &io, HTTP_Request &request) : io_(io), request_(request) { } HTTP_Response::HTTP_Response (HTTP_Request &request, JAWS_IO &io) : io_(io), request_(request) { } HTTP_Response::~HTTP_Response () { #if defined (ACE_JAWS_BASELINE) if (this->HTTP_HEADER != EMPTY_HEADER) delete [] this->HTTP_HEADER; // The [] is important. Without it, there was a huge memory leak! #endif /* ACE_JAWS_BASELINE */ } void HTTP_Response::process_request(HTTP_Response &response) { response.process_request(); } void HTTP_Response::process_request () { ACE_DEBUG ((LM_DEBUG, " (%t) processing request: %s\n", this->request_.status_string ())); switch (this->request_.status ()) { case HTTP_Status_Code::STATUS_OK : if (this->request_.cgi ()) { this->cgi_response (); } else { this->normal_response (); } break; default: this->error_response (this->request_.status (), this->request_.status_string ()); } } void HTTP_Response::error_response (int status_code, const char *log_message) { ACE_DEBUG ((LM_DEBUG, "(%t) [%s %s %s] %s\n", this->request_.method () ? this->request_.method () : "-", this->request_.uri () ? this->request_.uri () : "-", this->request_.version() ? this->request_.version () : "-", log_message ? log_message : "-")); static char const error_header1[] = "%s %d %s\r\n" "Server: JAWS/1.0prebeta\r\n" "Content-type: text/html\r\n" "Content-length: %d\r\n" "\r\n" "%s" ; static char const error_header2[] = "%s %d %s\r\n" "Server: JAWS/1.0prebeta\r\n" "WWW-Authenticate: Basic realm=\"JAWS_authorization\"\r\n" "Content-type: text/html\r\n" "Content-length: %d\r\n" "\r\n" "%s" ; static char const error_message[] = "\n" "Server error message\n" "\n" "

Error %d: %s

\n" "The request could not be completed because:\n %s\n" "\n" "\n" ; char *buf = 0; char buf1[4 * BUFSIZ]; char buf2[BUFSIZ]; int length; const char *error_header = error_header1; if (status_code == HTTP_Status_Code::STATUS_UNAUTHORIZED) error_header = error_header2; length = ACE_OS::sprintf (buf2, error_message, status_code, HTTP_Status_Code::instance ()[status_code], log_message); if (this->request_.version () == 0 || ACE_OS::strcmp ("HTTP/0.9", this->request_.version ()) == 0) buf = buf2; else { length = ACE_OS::sprintf (buf1, error_header, this->request_.version(), status_code, HTTP_Status_Code::instance ()[status_code], length, buf2); buf = buf1; } this->io_.send_error_message (buf, length); } void HTTP_Response::normal_response () { const char *hv = 0;; ACE_DEBUG ((LM_DEBUG, " (%t) %s request for %s [%s], version %s\n", request_.method (), request_.uri (), request_.path (), (request_.version () ? request_.version () : "HTTP/0.9"))); switch (this->request_.type ()) { case HTTP_Request::GET : this->build_headers (); this->io_.transmit_file (this->request_.path (), this->HTTP_HEADER, this->HTTP_HEADER_LENGTH, this->HTTP_TRAILER, this->HTTP_TRAILER_LENGTH); break; case HTTP_Request::HEAD : this->build_headers (); this->io_.send_confirmation_message (this->HTTP_HEADER, this->HTTP_HEADER_LENGTH); break; case HTTP_Request::POST : // What to do here? // Standard says this is implementation dependent. // Examples: annotations, page updates, etc. // This may be a good place to stick CORBA stuff, // and mobile code. this->error_response (HTTP_Status_Code::STATUS_NOT_IMPLEMENTED, "Requested method is not implemented."); break; case HTTP_Request::PUT : // Only commit to this if we can authenticate it // if there is no Authentication: header on the incoming request, // deny it hv = this->request_.headers ()["Authorization"].value (); if (hv == 0 || *hv == '\0') this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED, "Unauthorized to use PUT method"); else if (ACE_OS::strncmp (hv, "Basic ", 6) != 0) // ``6'' is the length of the string "Basic " this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED, "Unknown authorization method"); else { ACE_Mem_Map mmapfile; const char *hvv = hv + 6; // Skip past the string "Basic " char *buf = new char [ACE_OS::strlen (hv)]; char *auth = HTTP_Helper::HTTP_decode_base64 (ACE_OS::strcpy (buf, hvv)); if (mmapfile.map (ACE_TEXT ("jaws.auth")) != -1 && auth != 0 && ACE_OS::strstr((const char *) mmapfile.addr (), auth) != 0) this->io_.receive_file (this->request_.path (), this->request_.data (), this->request_.data_length (), this->request_.content_length ()); else this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED, "Invalid authorization attempt"); delete [] buf; } break; default : this->error_response (HTTP_Status_Code::STATUS_NOT_IMPLEMENTED, "Requested method is not implemented."); } } void HTTP_Response::cgi_response () { ACE_Process_Options cgi_options; if (this->request_.cgi_args ()) cgi_options.command_line ("%s %s", this->request_.path (), this->request_.cgi_args ()); else cgi_options.command_line ("%s", this->request_.path ()); // Build environment variables cgi_options.setenv (ACE_TEXT ("SERVER_SOFTWARE"), ACE_TEXT ("%s"), ACE_TEXT ("JAWS/1.0")); cgi_options.setenv (ACE_TEXT ("SERVER_NAME"), ACE_TEXT ("%s"), ACE_TEXT ("localhost")); cgi_options.setenv (ACE_TEXT ("GATEWAY_INTERFACE"), ACE_TEXT ("%s"), ACE_TEXT ("CGI/1.1")); cgi_options.setenv (ACE_TEXT ("SERVER_PROTOCOL"), ACE_TEXT ("%s"), this->request_.version () ? this->request_.version () : "HTTP/0.9"); cgi_options.setenv (ACE_TEXT ("SERVER_PORT"), ACE_TEXT ("%d"), 5432); cgi_options.setenv (ACE_TEXT ("REQUEST_METHOD"), ACE_TEXT ("%s"), this->request_.method ()); if (this->request_.path_info ()) { cgi_options.setenv (ACE_TEXT ("PATH_INFO"), ACE_TEXT ("%s"), this->request_.path_info ()); cgi_options.setenv (ACE_TEXT ("PATH_TRANSLATED"), ACE_TEXT ("%s/%s"), HTTP_Config::instance ()->document_root (), this->request_.path_info ()); } cgi_options.setenv (ACE_TEXT ("SCRIPT_NAME"), ACE_TEXT ("%s"), this->request_.uri ()); if (this->request_.query_string ()) cgi_options.setenv (ACE_TEXT ("QUERY_STRING"), ACE_TEXT ("%s"), this->request_.query_string ()); if (this->request_.cgi_env ()) for (size_t i = 0; this->request_.cgi_env ()[i]; i += 2) cgi_options.setenv (ACE_TEXT_CHAR_TO_TCHAR (this->request_.cgi_env ()[i]), ACE_TEXT ("%s"), ACE_TEXT_CHAR_TO_TCHAR (this->request_.cgi_env ()[i+1])); ACE_TCHAR buf[BUFSIZ]; ACE_TCHAR *p = 0, *q = 0; ACE_OS::strcpy (buf, ACE_TEXT ("HTTP_")); p = q = buf + ACE_OS::strlen (buf); for (size_t i = 0; i < HTTP_Request::NUM_HEADER_STRINGS; i++) { int j = 0; for (char c; (c = this->request_.header_strings (i)[j++]) != '\0'; ) if (ACE_OS::ace_isalpha (c)) *q++ = ACE_OS::ace_toupper (c); else if (c == '-') *q++ = '_'; else *q++ = c; *q = '\0'; const char *hv = this->request_.header_values (i); if (hv && *hv) cgi_options.setenv (buf, "%s", hv); q = p; } cgi_options.set_handles (this->io_.handle (), this->io_.handle (), this->io_.handle ()); this->build_headers (); this->io_.send_confirmation_message (this->HTTP_HEADER, this->HTTP_HEADER_LENGTH); // ACE::send (this->io_.handle (), // this->HTTP_HEADER, this->HTTP_HEADER_LENGTH); // Exec the CGI program. ACE_Process cgi_process; cgi_process.spawn (cgi_options); // cgi_process.wait (); } void HTTP_Response::build_headers () { // At this point, we should really determine the type of request // this is, and build the appropriate header. // Let's assume this is HTML for now. Unless the request is CGI, // then do not include content-* headers. if (this->request_.version () == 0 || ACE_OS::strcmp ("HTTP/0.9", this->request_.version ()) == 0) { HTTP_HEADER = EMPTY_HEADER; HTTP_HEADER_LENGTH = 0; } else { #if defined (ACE_JAWS_BASELINE) HTTP_HEADER = new char[BUFSIZ * 4]; // We assume that at this point everything is OK HTTP_HEADER_LENGTH = ACE_OS::sprintf (HTTP_HEADER, "%s", "HTTP/1.0 200 OK\r\n"); char date_ptr [40]; // 40 bytes is the maximum length needed to store the date if (HTTP_Helper::HTTP_date (date_ptr) != 0) HTTP_HEADER_LENGTH += ACE_OS::sprintf (HTTP_HEADER+HTTP_HEADER_LENGTH, "Date: %s\r\n", date_ptr); if (! this->request_.cgi ()) { HTTP_HEADER_LENGTH += ACE_OS::sprintf (HTTP_HEADER+HTTP_HEADER_LENGTH, "Content-type: %s\r\n", "text/html"); struct stat file_stat; // If possible, add the Content-length field to the header. // @@ Note that using 'ACE_OS::stat' is a hack. Normally, a // web browser will have a 'virtual' file system. In a VFS, // 'stat' might not reference the correct location. if ((this->request_.type () == HTTP_Request::GET) && (ACE_OS::stat (this->request_.path (), &file_stat) == 0)) { HTTP_HEADER_LENGTH += ACE_OS::sprintf (HTTP_HEADER+HTTP_HEADER_LENGTH, "Content-length: %u\r\n", file_stat.st_size); } // Complete header with empty line and adjust header length. HTTP_HEADER[HTTP_HEADER_LENGTH++] = '\r'; HTTP_HEADER[HTTP_HEADER_LENGTH++] = '\n'; } #else if (! this->request_.cgi ()) HTTP_HEADER = "HTTP/1.0 200 OK\r\n" "Content-type: text/html\r\n\r\n"; else HTTP_HEADER = "HTTP/1.0 200 OK\r\n"; HTTP_HEADER_LENGTH = ACE_OS::strlen (HTTP_HEADER); #endif /* ACE_JAWS_BASELINE */ } HTTP_TRAILER = ""; HTTP_TRAILER_LENGTH = 0; }