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

Generated on Sun Nov 7 19:06:51 2004 for Mango by doxygen 1.3.6