1 #include "ace/OS_NS_stdio.h"
2 #include "ace/OS_NS_string.h"
3 #include "ace/OS_NS_ctype.h"
4 #include "ace/Process.h"
5 #include "ace/Mem_Map.h"
6 #include "ace/Log_Msg.h"
8 #include "HTTP_Response.h"
9 #include "HTTP_Request.h"
10 #include "HTTP_Helpers.h"
11 #include "HTTP_Config.h"
14 #if defined (ACE_JAWS_BASELINE)
15 static char * const EMPTY_HEADER
= "";
17 static const char * const EMPTY_HEADER
= "";
18 #endif /* ACE_JAWS_BASELINE */
20 HTTP_Response::HTTP_Response (JAWS_IO
&io
, HTTP_Request
&request
)
21 : io_(io
), request_(request
)
25 HTTP_Response::HTTP_Response (HTTP_Request
&request
, JAWS_IO
&io
)
26 : io_(io
), request_(request
)
30 HTTP_Response::~HTTP_Response ()
32 #if defined (ACE_JAWS_BASELINE)
33 if (this->HTTP_HEADER
!= EMPTY_HEADER
)
34 delete [] this->HTTP_HEADER
;
35 // The [] is important. Without it, there was a huge memory leak!
36 #endif /* ACE_JAWS_BASELINE */
40 HTTP_Response::process_request(HTTP_Response
&response
)
42 response
.process_request();
46 HTTP_Response::process_request ()
48 ACE_DEBUG ((LM_DEBUG
, " (%t) processing request: %s\n",
49 this->request_
.status_string ()));
51 switch (this->request_
.status ())
53 case HTTP_Status_Code::STATUS_OK
:
55 if (this->request_
.cgi ())
57 this->cgi_response ();
61 this->normal_response ();
67 this->error_response (this->request_
.status (),
68 this->request_
.status_string ());
73 HTTP_Response::error_response (int status_code
, const char *log_message
)
75 ACE_DEBUG ((LM_DEBUG
, "(%t) [%s %s %s] %s\n",
76 this->request_
.method () ? this->request_
.method () : "-",
77 this->request_
.uri () ? this->request_
.uri () : "-",
78 this->request_
.version() ? this->request_
.version () : "-",
79 log_message
? log_message
: "-"));
81 static char const error_header1
[] =
83 "Server: JAWS/1.0prebeta\r\n"
84 "Content-type: text/html\r\n"
85 "Content-length: %d\r\n"
90 static char const error_header2
[] =
92 "Server: JAWS/1.0prebeta\r\n"
93 "WWW-Authenticate: Basic realm=\"JAWS_authorization\"\r\n"
94 "Content-type: text/html\r\n"
95 "Content-length: %d\r\n"
100 static char const error_message
[] =
102 "<head><title>Server error message</title></head>\n"
104 "<h1>Error %d: %s</h1>\n"
105 "The request could not be completed because:\n %s\n"
112 char buf1
[4 * BUFSIZ
];
116 const char *error_header
= error_header1
;
118 if (status_code
== HTTP_Status_Code::STATUS_UNAUTHORIZED
)
119 error_header
= error_header2
;
122 ACE_OS::sprintf (buf2
, error_message
,
123 status_code
, HTTP_Status_Code::instance ()[status_code
],
126 if (this->request_
.version () == 0
127 || ACE_OS::strcmp ("HTTP/0.9", this->request_
.version ()) == 0)
132 ACE_OS::sprintf (buf1
, error_header
,
133 this->request_
.version(), status_code
,
134 HTTP_Status_Code::instance ()[status_code
],
140 this->io_
.send_error_message (buf
, length
);
144 HTTP_Response::normal_response ()
148 ACE_DEBUG ((LM_DEBUG
, " (%t) %s request for %s [%s], version %s\n",
149 request_
.method (), request_
.uri (), request_
.path (),
150 (request_
.version () ? request_
.version () : "HTTP/0.9")));
152 switch (this->request_
.type ())
154 case HTTP_Request::GET
:
156 this->build_headers ();
157 this->io_
.transmit_file (this->request_
.path (),
159 this->HTTP_HEADER_LENGTH
,
161 this->HTTP_TRAILER_LENGTH
);
164 case HTTP_Request::HEAD
:
165 this->build_headers ();
166 this->io_
.send_confirmation_message (this->HTTP_HEADER
,
167 this->HTTP_HEADER_LENGTH
);
170 case HTTP_Request::POST
:
172 // Standard says this is implementation dependent.
173 // Examples: annotations, page updates, etc.
174 // This may be a good place to stick CORBA stuff,
176 this->error_response (HTTP_Status_Code::STATUS_NOT_IMPLEMENTED
,
177 "Requested method is not implemented.");
180 case HTTP_Request::PUT
:
181 // Only commit to this if we can authenticate it
183 // if there is no Authentication: header on the incoming request,
185 hv
= this->request_
.headers ()["Authorization"].value ();
186 if (hv
== 0 || *hv
== '\0')
187 this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED
,
188 "Unauthorized to use PUT method");
189 else if (ACE_OS::strncmp (hv
, "Basic ", 6) != 0)
190 // ``6'' is the length of the string "Basic "
191 this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED
,
192 "Unknown authorization method");
195 ACE_Mem_Map mmapfile
;
196 const char *hvv
= hv
+ 6;
197 // Skip past the string "Basic "
198 char *buf
= new char [ACE_OS::strlen (hv
)];
200 = HTTP_Helper::HTTP_decode_base64 (ACE_OS::strcpy (buf
, hvv
));
202 if (mmapfile
.map (ACE_TEXT ("jaws.auth")) != -1
204 && ACE_OS::strstr((const char *) mmapfile
.addr (), auth
) != 0)
205 this->io_
.receive_file (this->request_
.path (),
206 this->request_
.data (),
207 this->request_
.data_length (),
208 this->request_
.content_length ());
210 this->error_response (HTTP_Status_Code::STATUS_UNAUTHORIZED
,
211 "Invalid authorization attempt");
217 this->error_response (HTTP_Status_Code::STATUS_NOT_IMPLEMENTED
,
218 "Requested method is not implemented.");
224 HTTP_Response::cgi_response ()
226 ACE_Process_Options cgi_options
;
228 if (this->request_
.cgi_args ())
229 cgi_options
.command_line ("%s %s",
230 this->request_
.path (),
231 this->request_
.cgi_args ());
233 cgi_options
.command_line ("%s", this->request_
.path ());
235 // Build environment variables
236 cgi_options
.setenv (ACE_TEXT ("SERVER_SOFTWARE"), ACE_TEXT ("%s"), ACE_TEXT ("JAWS/1.0"));
237 cgi_options
.setenv (ACE_TEXT ("SERVER_NAME"), ACE_TEXT ("%s"), ACE_TEXT ("localhost"));
238 cgi_options
.setenv (ACE_TEXT ("GATEWAY_INTERFACE"), ACE_TEXT ("%s"), ACE_TEXT ("CGI/1.1"));
240 cgi_options
.setenv (ACE_TEXT ("SERVER_PROTOCOL"), ACE_TEXT ("%s"),
241 this->request_
.version ()
242 ? this->request_
.version ()
244 cgi_options
.setenv (ACE_TEXT ("SERVER_PORT"), ACE_TEXT ("%d"), 5432);
246 cgi_options
.setenv (ACE_TEXT ("REQUEST_METHOD"), ACE_TEXT ("%s"), this->request_
.method ());
248 if (this->request_
.path_info ())
250 cgi_options
.setenv (ACE_TEXT ("PATH_INFO"), ACE_TEXT ("%s"),
251 this->request_
.path_info ());
252 cgi_options
.setenv (ACE_TEXT ("PATH_TRANSLATED"),
254 HTTP_Config::instance ()->document_root (),
255 this->request_
.path_info ());
258 cgi_options
.setenv (ACE_TEXT ("SCRIPT_NAME"),
260 this->request_
.uri ());
262 if (this->request_
.query_string ())
263 cgi_options
.setenv (ACE_TEXT ("QUERY_STRING"),
265 this->request_
.query_string ());
267 if (this->request_
.cgi_env ())
268 for (size_t i
= 0; this->request_
.cgi_env ()[i
]; i
+= 2)
269 cgi_options
.setenv (ACE_TEXT_CHAR_TO_TCHAR (this->request_
.cgi_env ()[i
]),
271 ACE_TEXT_CHAR_TO_TCHAR (this->request_
.cgi_env ()[i
+1]));
273 ACE_TCHAR buf
[BUFSIZ
];
274 ACE_TCHAR
*p
= 0, *q
= 0;
275 ACE_OS::strcpy (buf
, ACE_TEXT ("HTTP_"));
276 p
= q
= buf
+ ACE_OS::strlen (buf
);
278 for (size_t i
= 0; i
< HTTP_Request::NUM_HEADER_STRINGS
; i
++)
282 for (char c
; (c
= this->request_
.header_strings (i
)[j
++]) != '\0'; )
283 if (ACE_OS::ace_isalpha (c
))
284 *q
++ = ACE_OS::ace_toupper (c
);
292 const char *hv
= this->request_
.header_values (i
);
295 cgi_options
.setenv (buf
, "%s", hv
);
299 cgi_options
.set_handles (this->io_
.handle (),
301 this->io_
.handle ());
303 this->build_headers ();
304 this->io_
.send_confirmation_message (this->HTTP_HEADER
,
305 this->HTTP_HEADER_LENGTH
);
306 // ACE::send (this->io_.handle (),
307 // this->HTTP_HEADER, this->HTTP_HEADER_LENGTH);
309 // Exec the CGI program.
310 ACE_Process cgi_process
;
311 cgi_process
.spawn (cgi_options
);
312 // cgi_process.wait ();
316 HTTP_Response::build_headers ()
318 // At this point, we should really determine the type of request
319 // this is, and build the appropriate header.
321 // Let's assume this is HTML for now. Unless the request is CGI,
322 // then do not include content-* headers.
324 if (this->request_
.version () == 0
325 || ACE_OS::strcmp ("HTTP/0.9", this->request_
.version ()) == 0)
327 HTTP_HEADER
= EMPTY_HEADER
;
328 HTTP_HEADER_LENGTH
= 0;
332 #if defined (ACE_JAWS_BASELINE)
333 HTTP_HEADER
= new char[BUFSIZ
* 4];
335 // We assume that at this point everything is OK
337 ACE_OS::sprintf (HTTP_HEADER
, "%s", "HTTP/1.0 200 OK\r\n");
340 // 40 bytes is the maximum length needed to store the date
342 if (HTTP_Helper::HTTP_date (date_ptr
) != 0)
343 HTTP_HEADER_LENGTH
+=
344 ACE_OS::sprintf (HTTP_HEADER
+HTTP_HEADER_LENGTH
,
345 "Date: %s\r\n", date_ptr
);
347 if (! this->request_
.cgi ()) {
348 HTTP_HEADER_LENGTH
+=
349 ACE_OS::sprintf (HTTP_HEADER
+HTTP_HEADER_LENGTH
,
350 "Content-type: %s\r\n",
353 struct stat file_stat
;
354 // If possible, add the Content-length field to the header.
355 // @@ Note that using 'ACE_OS::stat' is a hack. Normally, a
356 // web browser will have a 'virtual' file system. In a VFS,
357 // 'stat' might not reference the correct location.
358 if ((this->request_
.type () == HTTP_Request::GET
) &&
359 (ACE_OS::stat (this->request_
.path (), &file_stat
) == 0))
361 HTTP_HEADER_LENGTH
+=
362 ACE_OS::sprintf (HTTP_HEADER
+HTTP_HEADER_LENGTH
,
363 "Content-length: %u\r\n", file_stat
.st_size
);
366 // Complete header with empty line and adjust header length.
367 HTTP_HEADER
[HTTP_HEADER_LENGTH
++] = '\r';
368 HTTP_HEADER
[HTTP_HEADER_LENGTH
++] = '\n';
371 if (! this->request_
.cgi ())
372 HTTP_HEADER
= "HTTP/1.0 200 OK\r\n"
373 "Content-type: text/html\r\n\r\n";
375 HTTP_HEADER
= "HTTP/1.0 200 OK\r\n";
377 HTTP_HEADER_LENGTH
= ACE_OS::strlen (HTTP_HEADER
);
379 #endif /* ACE_JAWS_BASELINE */
383 HTTP_TRAILER_LENGTH
= 0;