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

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0