//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   15 Mar 06  Andy Frank  Creation
//

using inet

**
** WebRes encapsulates a response to a web request.
**
** See [pod doc]`pod-doc#webRes`
**
abstract class WebRes
{

//////////////////////////////////////////////////////////////////////////
// Public
//////////////////////////////////////////////////////////////////////////

  **
  ** Get or set the HTTP status code for this response. Status code
  ** defaults to 200.  Throw an err if the response has already been
  ** committed.
  **
  abstract Int statusCode

  **
  ** Reason phrase to include in HTTP response line.  If null, then
  ** a status phrase is used based on the `statusCode`.
  **
  abstract Str? statusPhrase

  **
  ** Map of HTTP response headers.  You must set all headers before
  ** you access out() for the first time, which commits the response.
  ** Throw an err if response is already committed.
  **
  abstract Str:Str headers()

  **
  ** Get the list of cookies to set via header fields.  Add a
  ** a Cookie to this list to set a cookie.  Throw an err if
  ** response is already committed.
  **
  ** Example:
  **   res.cookies.add(Cookie("foo", "123"))
  **   res.cookies.add(Cookie("persistent", "some val") { maxAge = 3day })
  **
  abstract Cookie[] cookies()

  **
  ** Remove a cookie for this response.  This method is a
  ** convenience for:
  **   res.cookies.add(Cookie(name, "") { maxAge=0day }
  **
  Void removeCookie(Str name)
  {
    cookies.add(Cookie(name, "") { maxAge=0day })
  }

  **
  ** Return true if this response has been commmited.  A committed
  ** response has written its response headers, and can no longer
  ** modify its status code or headers.  A response is committed
  ** the first time that `out` is called.
  **
  abstract Bool isCommitted()

  **
  ** Return the WebOutStream for this response.  The first time this
  ** method is accessed the response is committed: all headers
  ** currently set will be written to the stream, and can no longer
  ** be modified.  If the "Content-Length" header defines a fixed
  ** number of bytes, then attemps to write too many bytes will throw
  ** an IOErr.  If "Content-Length" is not defined, then a chunked
  ** transfer encoding is automatically used.
  **
  abstract WebOutStream out()

  **
  ** Send a redirect response to the client using the specified status
  ** code and url.  If this response has already been committed this
  ** method throws an Err.  This method implicitly calls `done`.
  **
  abstract Void redirect(Uri uri, Int statusCode := 303)

  **
  ** Send an error response to client using the specified status and
  ** HTML formatted message.  If this response has already been committed
  ** this method throws an Err.  This method implicitly calls `done`.
  **
  abstract Void sendErr(Int statusCode, Str? msg := null)

  **
  ** Return if this response is complete - see `done`.
  **
  abstract Bool isDone()

  **
  ** Done is called to indicate that that response is complete
  ** to terminate pipeline processing.
  **
  abstract Void done()

  **
  ** Write response headers to socket, then and return ownership
  ** of socket to upgrade to different protocol.
  **
  @NoDoc abstract TcpSocket upgrade(Int statusCode := 101)

//////////////////////////////////////////////////////////////////////////
// Static
//////////////////////////////////////////////////////////////////////////

  **
  ** Map of HTTP status codes to status messages.
  **
  static const Int:Str statusMsg :=
  [
    // 100
    100: "Continue",
    101: "Switching Protocols",
    // 200
    200: "OK",
    201: "Created",
    202: "Accepted",
    203: "203 Non-Authoritative Information",
    204: "No Content",
    205: "Reset Content",
    206: "Partial Content",
    // 300
    300: "Multiple Choices",
    301: "Moved Permanently",
    302: "Found",
    303: "See Other",
    304: "Not Modified",
    305: "Use Proxy",
    307: "Temporary Redirect",
    // 400
    400: "Bad Request",
    401: "Unauthorized",
    402: "Payment Required",
    403: "Forbidden",
    404: "Not Found",
    405: "Method Not Allowed",
    406: "Not Acceptable",
    407: "Proxy Authentication Required",
    408: "Request Timeout",
    409: "Conflict",
    410: "Gone",
    411: "Length Required",
    412: "Precondition Failed",
    413: "Request Entity Too Large",
    414: "Request-URI Too Long",
    415: "Unsupported Media Type",
    416: "Requested Range Not Satisfiable",
    417: "Expectation Failed",
    429: "Too Many Requests",
    // 500
    500: "Internal Server Error",
    501: "Not Implemented",
    502: "Bad Gateway",
    503: "Service Unavailable",
    504: "Gateway Timeout",
    505: "HTTP Version Not Supported"
  ]

}