// $Id$
#include "ace/Process.h"
#include "ace/Mem_Map.h"
#include "HTTP_Response.h"
#include "HTTP_Request.h"
#include "HTTP_Helpers.h"
#include "HTTP_Config.h"
#include "IO.h"
static char * const EMPTY_HEADER = (char *)"";
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 (void)
{
#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 (void)
{
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;
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 (void)
{
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 authroization 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 ("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 (void)
{
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 ("SERVER_SOFTWARE", "%s", "JAWS/1.0");
cgi_options.setenv ("SERVER_NAME", "%s", "localhost");
cgi_options.setenv ("GATEWAY_INTERFACE", "%s", "CGI/1.1");
cgi_options.setenv ("SERVER_PROTOCOL", "%s",
this->request_.version ()
? this->request_.version ()
: "HTTP/0.9");
cgi_options.setenv ("SERVER_PORT", "%d", 5432);
cgi_options.setenv ("REQUEST_METHOD", "%s", this->request_.method ());
if (this->request_.path_info ())
{
cgi_options.setenv ("PATH_INFO", "%s",
this->request_.path_info ());
cgi_options.setenv ("PATH_TRANSLATED",
"%s/%s",
HTTP_Config::instance ()->document_root (),
this->request_.path_info ());
}
cgi_options.setenv ("SCRIPT_NAME",
"%s",
this->request_.uri ());
if (this->request_.query_string ())
cgi_options.setenv ("QUERY_STRING",
"%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 (this->request_.cgi_env ()[i],
"%s",
this->request_.cgi_env ()[i+1]);
char buf[BUFSIZ];
char *p, *q;
ACE_OS::strcpy (buf, "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 (isalpha (c))
*q++ = 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 (void)
{
// 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 = (char *) 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\r\n",
"text/html");
#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;
}