Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

HttpResponse.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file HttpResponse.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032         
00033         @version        Initial version, April 2004      
00034         @author         Kris
00035 
00036 
00037 *******************************************************************************/
00038 
00039 module mango.http.server.HttpResponse;
00040 
00041 private import  mango.io.Buffer,
00042                 mango.io.Writer;
00043 
00044 private import  mango.io.model.IWriter;
00045 
00046 private import  mango.http.HttpWriter;
00047 
00048 private import  mango.http.server.HttpParams,
00049                 mango.http.server.HttpCookies,
00050                 mango.http.server.HttpHeaders,
00051                 mango.http.server.HttpMessage;
00052 
00053 private import  mango.http.server.model.IProviderBridge;
00054 
00055 //version = ShowHeaders;
00056 
00057 /*******************************************************************************
00058 
00059         Some constants for output buffer sizes
00060 
00061 *******************************************************************************/
00062 
00063 private static const int ParamsBufferSize = 1 * 1024;
00064 private static const int HeaderBufferSize = 4 * 1024;
00065 
00066 
00067 /*******************************************************************************
00068 
00069         Status is a compound type, with a name and a code.
00070 
00071 *******************************************************************************/
00072 
00073 struct HttpStatus
00074 {
00075         int     code; 
00076         char[]  name;  
00077 }
00078 
00079 /*******************************************************************************
00080 
00081         Declare the traditional set of HTTP response codes
00082 
00083 *******************************************************************************/
00084 
00085 enum HttpResponseCode
00086 {       
00087         Continue                     = 100,
00088         SwitchingProtocols           = 101,
00089         OK                           = 200,
00090         Created                      = 201,
00091         Accepted                     = 202,
00092         NonAuthoritativeInformation  = 203,
00093         NoContent                    = 204,
00094         ResetContent                 = 205,
00095         PartialContent               = 206,
00096         MultipleChoices              = 300,
00097         MovedPermanently             = 301,
00098         MovedTemporarily             = 302,
00099         SeeOther                     = 303,
00100         NotModified                  = 304,
00101         UseProxy                     = 305,
00102         BadRequest                   = 400,
00103         Unauthorized                 = 401,
00104         PaymentRequired              = 402,
00105         Forbidden                    = 403,
00106         NotFound                     = 404,
00107         MethodNotAllowed             = 405,
00108         NotAcceptable                = 406,
00109         ProxyAuthenticationRequired  = 407,
00110         RequestTimeout               = 408,
00111         Conflict                     = 409,
00112         Gone                         = 410,
00113         LengthRequired               = 411,
00114         PreconditionFailed           = 412,
00115         RequestEntityTooLarge        = 413,
00116         RequestURITooLarge           = 414,
00117         UnsupportedMediaType         = 415,
00118         RequestedRangeNotSatisfiable = 416,
00119         ExpectationFailed            = 417,
00120         InternalServerError          = 500,
00121         NotImplemented               = 501,
00122         BadGateway                   = 502,
00123         ServiceUnavailable           = 503,
00124         GatewayTimeout               = 504,
00125         VersionNotSupported          = 505,
00126 };
00127 
00128 /*******************************************************************************
00129 
00130         Declare the traditional set of HTTP responses
00131 
00132 *******************************************************************************/
00133 
00134 struct HttpResponses
00135 {       
00136         static final HttpStatus Continue                     = {HttpResponseCode.Continue, "Continue"};
00137         static final HttpStatus SwitchingProtocols           = {HttpResponseCode.SwitchingProtocols, "SwitchingProtocols"};
00138         static final HttpStatus OK                           = {HttpResponseCode.OK, "OK"};
00139         static final HttpStatus Created                      = {HttpResponseCode.Created, "Created"};
00140         static final HttpStatus Accepted                     = {HttpResponseCode.Accepted, "Accepted"};
00141         static final HttpStatus NonAuthoritativeInformation  = {HttpResponseCode.NonAuthoritativeInformation, "NonAuthoritativeInformation"};
00142         static final HttpStatus NoContent                    = {HttpResponseCode.NoContent, "NoContent"};
00143         static final HttpStatus ResetContent                 = {HttpResponseCode.ResetContent, "ResetContent"};
00144         static final HttpStatus PartialContent               = {HttpResponseCode.PartialContent, "PartialContent"};
00145         static final HttpStatus MultipleChoices              = {HttpResponseCode.MultipleChoices, "MultipleChoices"};
00146         static final HttpStatus MovedPermanently             = {HttpResponseCode.MovedPermanently, "MovedPermanently"};
00147         static final HttpStatus MovedTemporarily             = {HttpResponseCode.MovedTemporarily, "MovedTemporarily"};
00148         static final HttpStatus SeeOther                     = {HttpResponseCode.SeeOther, "SeeOther"};
00149         static final HttpStatus NotModified                  = {HttpResponseCode.NotModified, "NotModified"};
00150         static final HttpStatus UseProxy                     = {HttpResponseCode.UseProxy, "UseProxy"};
00151         static final HttpStatus BadRequest                   = {HttpResponseCode.BadRequest, "BadRequest"};
00152         static final HttpStatus Unauthorized                 = {HttpResponseCode.Unauthorized, "Unauthorized"};
00153         static final HttpStatus PaymentRequired              = {HttpResponseCode.PaymentRequired, "PaymentRequired"};
00154         static final HttpStatus Forbidden                    = {HttpResponseCode.Forbidden, "Forbidden"};
00155         static final HttpStatus NotFound                     = {HttpResponseCode.NotFound, "NotFound"};
00156         static final HttpStatus MethodNotAllowed             = {HttpResponseCode.MethodNotAllowed, "MethodNotAllowed"};
00157         static final HttpStatus NotAcceptable                = {HttpResponseCode.NotAcceptable, "NotAcceptable"};
00158         static final HttpStatus ProxyAuthenticationRequired  = {HttpResponseCode.ProxyAuthenticationRequired, "ProxyAuthenticationRequired"};
00159         static final HttpStatus RequestTimeout               = {HttpResponseCode.RequestTimeout, "RequestTimeout"};
00160         static final HttpStatus Conflict                     = {HttpResponseCode.Conflict, "Conflict"};
00161         static final HttpStatus Gone                         = {HttpResponseCode.Gone, "Gone"};
00162         static final HttpStatus LengthRequired               = {HttpResponseCode.LengthRequired, "LengthRequired"};
00163         static final HttpStatus PreconditionFailed           = {HttpResponseCode.PreconditionFailed, "PreconditionFailed"};
00164         static final HttpStatus RequestEntityTooLarge        = {HttpResponseCode.RequestEntityTooLarge, "RequestEntityTooLarge"};
00165         static final HttpStatus RequestURITooLarge           = {HttpResponseCode.RequestURITooLarge, "RequestURITooLarge"};
00166         static final HttpStatus UnsupportedMediaType         = {HttpResponseCode.UnsupportedMediaType, "UnsupportedMediaType"};
00167         static final HttpStatus RequestedRangeNotSatisfiable = {HttpResponseCode.RequestedRangeNotSatisfiable, "RequestedRangeNotSatisfiable"};
00168         static final HttpStatus ExpectationFailed            = {HttpResponseCode.ExpectationFailed, "ExpectationFailed"};
00169         static final HttpStatus InternalServerError          = {HttpResponseCode.InternalServerError, "InternalServerError"};
00170         static final HttpStatus NotImplemented               = {HttpResponseCode.NotImplemented, "NotImplemented"};
00171         static final HttpStatus BadGateway                   = {HttpResponseCode.BadGateway, "BadGateway"};
00172         static final HttpStatus ServiceUnavailable           = {HttpResponseCode.ServiceUnavailable, "ServiceUnavailable"};
00173         static final HttpStatus GatewayTimeout               = {HttpResponseCode.GatewayTimeout, "GatewayTimeout"};
00174         static final HttpStatus VersionNotSupported          = {HttpResponseCode.VersionNotSupported, "VersionNotSupported"};
00175 }
00176 
00177 
00178 /******************************************************************************
00179 
00180         Define an http response to a user-agent (client). Note that all
00181         data is managed on a thread-by-thread basis.
00182 
00183 ******************************************************************************/
00184 
00185 class HttpResponse : HttpMessage
00186 {
00187         private HttpMutableParams       params;
00188         private HttpMutableCookies      cookies;
00189         private HttpStatus              status;
00190         private HttpWriter              writer;
00191         private bool                    commited;
00192 
00193         static private InvalidStateException InvalidState;
00194 
00195         /**********************************************************************
00196 
00197                 Construct static instances of exceptions etc. 
00198 
00199         **********************************************************************/
00200 
00201         static this()
00202         {
00203                 InvalidState = new InvalidStateException("Invalid response state");
00204         }
00205 
00206         /**********************************************************************
00207 
00208                 Create a Response instance. Note that we create a bunch of
00209                 internal support objects on a per-thread basis. This is so
00210                 we don't have to create them on demand; however, we should
00211                 be careful about resetting them all before each new usage.
00212 
00213         **********************************************************************/
00214 
00215         this (IProviderBridge bridge)
00216         {
00217                 // create a seperate output buffer for headers to reside
00218                 super (bridge, new Buffer(HeaderBufferSize));
00219 
00220                 // create a default output writer
00221                 writer = new HttpWriter (super.getBuffer());
00222         
00223                 // create a cached query-parameter processor. We
00224                 // support a maximum output parameter list of 1K bytes
00225                 params = new HttpMutableParams (new Buffer(ParamsBufferSize));
00226         
00227                 // create a wrapper for output cookies. This is more akin 
00228                 // to a specialized writer, since it just adds additional
00229                 // content to the output headers.
00230                 cookies = new HttpMutableCookies (super.getHeader());
00231         }
00232 
00233         /**********************************************************************
00234 
00235                 Reset this response, ready for the next connection
00236 
00237         **********************************************************************/
00238 
00239         void reset()
00240         {
00241                 // response is "OK" by default
00242                 commited = false;
00243                 setStatus (HttpResponses.OK);
00244 
00245                 // reset the headers
00246                 super.reset();
00247 
00248                 // reset output parameters
00249                 params.reset();
00250         }
00251 
00252         /**********************************************************************
00253 
00254                 Send an error status to the user-agent
00255 
00256         **********************************************************************/
00257 
00258         void sendError (inout HttpStatus status)
00259         {
00260                 sendError (status, "");
00261         }
00262 
00263         /**********************************************************************
00264 
00265                 Send an error status to the user-agent, along with the
00266                 provided message
00267 
00268         **********************************************************************/
00269 
00270         void sendError (inout HttpStatus status, char[] msg)
00271         {       
00272                 sendError (status, status.name, msg);
00273         }
00274 
00275         /**********************************************************************
00276 
00277                 Send an error status to the user-agent, along with the
00278                 provided exception text
00279 
00280         **********************************************************************/
00281 
00282         void sendError (inout HttpStatus status, Exception ex)
00283         {
00284                 sendError (status, status.name, ex.toString());
00285         }
00286 
00287         /**********************************************************************
00288 
00289                 Set the current response status.
00290 
00291         **********************************************************************/
00292 
00293         void setStatus (inout HttpStatus status)
00294         {
00295                 this.status = status;
00296         }
00297 
00298         /**********************************************************************
00299 
00300                 Return the current response status
00301 
00302         **********************************************************************/
00303 
00304         HttpStatus getStatus ()
00305         {
00306                 return status;
00307         }
00308 
00309         /**********************************************************************
00310 
00311                 Return the output writer. This set a sentinel indicating
00312                 that we cannot add any more headers (since they have to
00313                 be flushed before any additional output is sent).
00314 
00315         **********************************************************************/
00316 
00317         HttpWriter getWriter()
00318         {
00319                 // write headers, and cause InvalidState on next call
00320                 // to getOutputHeaders()
00321                 commit (writer);               
00322                 return writer;
00323         }
00324 
00325         /**********************************************************************
00326 
00327                 Return the wrapper for adding output parameters
00328 
00329         **********************************************************************/
00330 
00331         HttpMutableParams getOutputParams()
00332         {
00333                 return params;
00334         }
00335 
00336         /**********************************************************************
00337 
00338                 Return the wrapper for output cookies
00339 
00340         **********************************************************************/
00341 
00342         HttpMutableCookies getOutputCookies()
00343         {
00344                 return cookies;
00345         }
00346 
00347         /**********************************************************************
00348 
00349                 Return the wrapper for output headers.
00350 
00351         **********************************************************************/
00352 
00353         HttpMutableHeaders getOutputHeaders()
00354         {
00355                 // can't access headers after commiting
00356                 if (commited)
00357                     throw InvalidState;
00358                 return super.getHeader();
00359         }
00360 
00361         /**********************************************************************
00362 
00363                 Return the buffer attached to the output conduit. Note that
00364                 further additions to the output headers is disabled from
00365                 this point forward. 
00366 
00367         **********************************************************************/
00368 
00369         IBuffer getOutputBuffer()
00370         {
00371                 // write headers, and cause InvalidState on next call
00372                 // to getOutputHeaders()
00373                 commit (writer);
00374                 return super.getBuffer();
00375         }
00376 
00377         /**********************************************************************
00378 
00379                 Send a redirect response to the user-agent
00380 
00381         **********************************************************************/
00382 
00383         void sendRedirect (char[] location)
00384         {
00385                 setStatus (HttpResponses.MovedTemporarily);
00386                 getHeader().add (HttpHeader.Location, location);
00387                 flush (writer);
00388         }
00389 
00390         /**********************************************************************
00391 
00392                 Write the response and the output headers 
00393 
00394         **********************************************************************/
00395 
00396         void write (IWriter writer)
00397         {
00398                 commit (writer);
00399         }
00400 
00401         /**********************************************************************
00402 
00403                 Ensure the output is flushed
00404 
00405         **********************************************************************/
00406 
00407         void flush (IWriter writer)
00408         {
00409                 commit (writer);
00410 
00411                 version (ShowHeaders)
00412                         {
00413                         Stdout.put ("###############").cr();
00414                         Stdout.put (super.getBuffer.toString).cr();
00415                         Stdout.put ("###############").cr();
00416                         }
00417                 writer.flush();
00418         }
00419 
00420         /**********************************************************************
00421 
00422                 Private method to send the response status, and the
00423                 output headers, back to the user-agent
00424 
00425         **********************************************************************/
00426 
00427         private void commit (IWriter writer)
00428         {
00429                 if (! commited)
00430                    {
00431                    // say we've send headers on this response
00432                    commited = true;
00433 
00434                    char[]               header;
00435                    HttpMutableHeaders   headers = getHeader();
00436 
00437                    // write the response header
00438                    writer.put (HttpHeader.Version.value)
00439                          .put (' ')
00440                          .put (status.code)
00441                          .put (' ')
00442                          .put (status.name)
00443                          .cr  ();
00444 
00445                    // tell client we don't support keep-alive
00446                    if (! headers.get (HttpHeader.Connection))
00447                          headers.add (HttpHeader.Connection, "close");
00448                   
00449                    // write the header tokens, followed by a blank line
00450                    super.write (writer);
00451                    writer.cr ();
00452 
00453                    // send it back to the UA (and empty the buffer)
00454                    writer.flush();
00455                         
00456                    version (ShowHeaders)
00457                            {
00458                            Stdout.put (">>>> output headers").cr();
00459                            Stdout.put (HttpHeader.Version.value)
00460                                  .put (' ')
00461                                  .put (status.code)
00462                                  .put (' ')
00463                                  .put (status.name)
00464                                  .cr  ();
00465                            super.write (Stdout);
00466                            }
00467                    }
00468         }
00469 
00470         /**********************************************************************
00471 
00472                 Send an error back to the user-agent. We have to be careful
00473                 about which errors actually have content returned and those
00474                 that don't.
00475 
00476         **********************************************************************/
00477 
00478         private void sendError (inout HttpStatus status, char[] reason, char[] message)
00479         {
00480                 setStatus (status);
00481 
00482                 if (status.code != HttpResponses.NoContent.code && 
00483                     status.code != HttpResponses.NotModified.code && 
00484                     status.code != HttpResponses.PartialContent.code && 
00485                     status.code >= HttpResponses.OK.code)
00486                    {
00487                    // error-page is html
00488                    setContentType (HttpHeader.TextHtml.value);
00489 
00490                    // output the headers
00491                    commit (writer);
00492 
00493                    // output an error-page
00494                    writer.put ("<HTML>\n<HEAD>\n<TITLE>Error ")
00495                          .put (status.code)
00496                          .put (' ')
00497                          .put (reason)
00498                          .put ("</TITLE>\n<BODY>\n<H2>HTTP Error: ")
00499                          .put (status.code)
00500                          .put (' ')
00501                          .put (reason)                       
00502                          .put ("</H2>\n")
00503                          .put (message ? message : "")
00504                          .put ("\n</BODY>\n</HTML>\n");
00505 
00506                    flush (writer);
00507                    }
00508         }
00509 }
00510 
00511 
00512 

Generated on Fri Nov 11 18:44:20 2005 for Mango by  doxygen 1.4.0