content: // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
  // for details. All rights reserved. Use of this source code is governed by a
  // BSD-style license that can be found in the LICENSE file.
  
  part of dart.io;
  
  /**
   * HTTP status codes.
   */
  abstract class HttpStatus {
    static const int CONTINUE = 100;
    static const int SWITCHING_PROTOCOLS = 101;
    static const int OK = 200;
    static const int CREATED = 201;
    static const int ACCEPTED = 202;
    static const int NON_AUTHORITATIVE_INFORMATION = 203;
    static const int NO_CONTENT = 204;
    static const int RESET_CONTENT = 205;
    static const int PARTIAL_CONTENT = 206;
    static const int MULTIPLE_CHOICES = 300;
    static const int MOVED_PERMANENTLY = 301;
    static const int FOUND = 302;
    static const int MOVED_TEMPORARILY = 302; // Common alias for FOUND.
    static const int SEE_OTHER = 303;
    static const int NOT_MODIFIED = 304;
    static const int USE_PROXY = 305;
    static const int TEMPORARY_REDIRECT = 307;
    static const int BAD_REQUEST = 400;
    static const int UNAUTHORIZED = 401;
    static const int PAYMENT_REQUIRED = 402;
    static const int FORBIDDEN = 403;
    static const int NOT_FOUND = 404;
    static const int METHOD_NOT_ALLOWED = 405;
    static const int NOT_ACCEPTABLE = 406;
    static const int PROXY_AUTHENTICATION_REQUIRED = 407;
    static const int REQUEST_TIMEOUT = 408;
    static const int CONFLICT = 409;
    static const int GONE = 410;
    static const int LENGTH_REQUIRED = 411;
    static const int PRECONDITION_FAILED = 412;
    static const int REQUEST_ENTITY_TOO_LARGE = 413;
    static const int REQUEST_URI_TOO_LONG = 414;
    static const int UNSUPPORTED_MEDIA_TYPE = 415;
    static const int REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    static const int EXPECTATION_FAILED = 417;
    static const int INTERNAL_SERVER_ERROR = 500;
    static const int NOT_IMPLEMENTED = 501;
    static const int BAD_GATEWAY = 502;
    static const int SERVICE_UNAVAILABLE = 503;
    static const int GATEWAY_TIMEOUT = 504;
    static const int HTTP_VERSION_NOT_SUPPORTED = 505;
    // Client generated status code.
    static const int NETWORK_CONNECT_TIMEOUT_ERROR = 599;
  }
  
  
  /**
   * A server that delivers content, such as web pages, using
   * the HTTP protocol.
   *
   * The [HttpServer] is a [Stream] of [HttpRequest]s. Each
   * [HttpRequest] has an associated [HttpResponse] object as its
   * [HttpRequest.response] member, and the server responds to a request by
   * writing to that [HttpResponse] object.
   *
   * Incomplete requests where all or parts of the header is missing, are
   * ignored and no exceptions or [HttpRequest] objects are generated for them.
   * Likewise, when writing to a [HttpResponse], any [Socket] exceptions are
   * ignored and any future writes are ignored.
   *
   * The [HttpRequest] exposes the request headers, and provides the request body,
   * if it exists, as a stream of data. If the body is unread, it'll be drained
   * when the [HttpResponse] is being written to or closed.
   *
   * The following example shows how to bind a [HttpServer] to a IPv6
   * [InternetAddress] on port 80, and listening to requests.
   *
   *   HttpServer.bind(InternetAddress.ANY_IP_V6, 80).then((server) {
   *     server.listen((HttpRequest request) {
   *       // Handle requests.
   *     });
   *   });
   */
  abstract class HttpServer implements Stream<HttpRequest> {
    /**
     * Starts listening for HTTP requests on the specified [address] and
     * [port].
     *
     * The [address] can either be a [String] or an
     * [InternetAddress]. If [address] is a [String], [bind] will
     * perform a [InternetAddress.lookup] and use the first value in the
     * list. To listen on the loopback adapter, which will allow only
     * incoming connections from the local host, use the value
     * [InternetAddress.LOOPBACK_IP_V4] or
     * [InternetAddress.LOOPBACK_IP_V6]. To allow for incoming
     * connection from the network use either one of the values
     * [InternetAddress.ANY_IP_V4] or [InternetAddress.ANY_IP_V6] to
     * bind to all interfaces or the IP address of a specific interface.
     *
     * If an IP version 6 (IPv6) address is used, both IP version 6
     * (IPv6) and version 4 (IPv4) connections will be accepted. To
     * restrict this to version 6 (IPv6) only, use [HttpServer.listenOn]
     * with a [ServerSocket] configured for IP version 6 connections
     * only.
     *
     * If [port] has the value [:0:] an ephemeral port will be chosen by
     * the system. The actual port used can be retrieved using the
     * [port] getter.
     *
     * The optional argument [backlog] can be used to specify the listen
     * backlog for the underlying OS listen setup. If [backlog] has the
     * value of [:0:] (the default) a reasonable value will be chosen by
     * the system.
     */
    static Future<HttpServer> bind(address,
                                   int port,
                                   {int backlog: 0})
        => _HttpServer.bind(address, port, backlog);
  
    /**
     * The [address] can either be a [String] or an
     * [InternetAddress]. If [address] is a [String], [bind] will
     * perform a [InternetAddress.lookup] and use the first value in the
     * list. To listen on the loopback adapter, which will allow only
     * incoming connections from the local host, use the value
     * [InternetAddress.LOOPBACK_IP_V4] or
     * [InternetAddress.LOOPBACK_IP_V6]. To allow for incoming
     * connection from the network use either one of the values
     * [InternetAddress.ANY_IP_V4] or [InternetAddress.ANY_IP_V6] to
     * bind to all interfaces or the IP address of a specific interface.
     *
     * If an IP version 6 (IPv6) address is used, both IP version 6
     * (IPv6) and version 4 (IPv4) connections will be accepted. To
     * restrict this to version 6 (IPv6) only, use [HttpServer.listenOn]
     * with a [ServerSocket] configured for IP version 6 connections
     * only.
     *
     * If [port] has the value [:0:] an ephemeral port will be chosen by
     * the system. The actual port used can be retrieved using the
     * [port] getter.
     *
     * The optional argument [backlog] can be used to specify the listen
     * backlog for the underlying OS listen setup. If [backlog] has the
     * value of [:0:] (the default) a reasonable value will be chosen by
     * the system.
     *
     * The certificate with nickname or distinguished name (DN) [certificateName]
     * is looked up in the certificate database, and is used as the server
     * certificate. If [requestClientCertificate] is true, the server will
     * request clients to authenticate with a client certificate.
     */
  
    static Future<HttpServer> bindSecure(address,
                                         int port,
                                         {int backlog: 0,
                                          String certificateName,
                                          bool requestClientCertificate: false})
        => _HttpServer.bindSecure(address,
                                  port,
                                  backlog,
                                  certificateName,
                                  requestClientCertificate);
  
    /**
     * Attaches the HTTP server to an existing [ServerSocket]. When the
     * [HttpServer] is closed, the [HttpServer] will just detach itself,
     * closing current connections but not closing [serverSocket].
     */
    factory HttpServer.listenOn(ServerSocket serverSocket)
        => new _HttpServer.listenOn(serverSocket);
  
    /**
     * Permanently stops this [HttpServer] from listening for new
     * connections.  This closes the [Stream] of [HttpRequest]s with a
     * done event. The returned future completes when the server is
     * stopped. For a server started using [bind] or [bindSecure] this
     * means that the port listened on no longer in use.
     *
     * If [force] is `true`, active connections will be closed immediately.
     */
    Future close({bool force: false});
  
    /**
     * Returns the port that the server is listening on. This can be
     * used to get the actual port used when a value of 0 for [:port:] is
     * specified in the [bind] or [bindSecure] call.
     */
    int get port;
  
    /**
     * Returns the address that the server is listening on. This can be
     * used to get the actual address used, when the address is fetched by
     * a lookup from a hostname.
     */
    InternetAddress get address;
  
    /**
     * Sets the timeout, in seconds, for sessions of this [HttpServer].
     * The default timeout is 20 minutes.
     */
    set sessionTimeout(int timeout);
  
    /**
     * Returns an [HttpConnectionsInfo] object summarizing the number of
     * current connections handled by the server.
     */
    HttpConnectionsInfo connectionsInfo();
  
    /**
     * Set and get the default value of the `Server` header for all responses
     * generated by this [HttpServer]. The default value is
     * `Dart/<version> (dart:io)`.
     *
     * If the serverHeader is set to `null`, no default `Server` header will be
     * added to each response.
     */
    String serverHeader;
  
    /**
     * Get or set the timeout used for idle keep-alive connections. If no further
     * request is seen within [idleTimeout] after the previous request was
     * completed, the connection is droped.
     *
     * Default is 120 seconds.
     *
     * To disable, set [idleTimeout] to `null`.
     */
    Duration idleTimeout;
  }
  
  
  /**
   * Summary statistics about an [HttpServer]s current socket connections.
   */
  class HttpConnectionsInfo {
    /**
     * Total number of socket connections.
     */
    int total = 0;
  
    /**
     * Number of active connections where actual request/response
     * processing is active.
     */
    int active = 0;
  
    /**
     * Number of idle connections held by clients as persistent connections.
     */
    int idle = 0;
  
    /**
     * Number of connections which are preparing to close. Note: These
     * connections are also part of the [:active:] count as they might
     * still be sending data to the client before finally closing.
     */
    int closing = 0;
  }
  
  
  /**
   * Access to the HTTP headers for requests and responses. In some
   * situations the headers will be immutable and the mutating methods
   * will then throw exceptions.
   *
   * For all operations on HTTP headers the header name is
   * case-insensitive.
   */
  abstract class HttpHeaders {
    static const ACCEPT = "accept";
    static const ACCEPT_CHARSET = "accept-charset";
    static const ACCEPT_ENCODING = "accept-encoding";
    static const ACCEPT_LANGUAGE = "accept-language";
    static const ACCEPT_RANGES = "accept-ranges";
    static const AGE = "age";
    static const ALLOW = "allow";
    static const AUTHORIZATION = "authorization";
    static const CACHE_CONTROL = "cache-control";
    static const CONNECTION = "connection";
    static const CONTENT_ENCODING = "content-encoding";
    static const CONTENT_LANGUAGE = "content-language";
    static const CONTENT_LENGTH = "content-length";
    static const CONTENT_LOCATION = "content-location";
    static const CONTENT_MD5 = "content-md5";
    static const CONTENT_RANGE = "content-range";
    static const CONTENT_TYPE = "content-type";
    static const DATE = "date";
    static const ETAG = "etag";
    static const EXPECT = "expect";
    static const EXPIRES = "expires";
    static const FROM = "from";
    static const HOST = "host";
    static const IF_MATCH = "if-match";
    static const IF_MODIFIED_SINCE = "if-modified-since";
    static const IF_NONE_MATCH = "if-none-match";
    static const IF_RANGE = "if-range";
    static const IF_UNMODIFIED_SINCE = "if-unmodified-since";
    static const LAST_MODIFIED = "last-modified";
    static const LOCATION = "location";
    static const MAX_FORWARDS = "max-forwards";
    static const PRAGMA = "pragma";
    static const PROXY_AUTHENTICATE = "proxy-authenticate";
    static const PROXY_AUTHORIZATION = "proxy-authorization";
    static const RANGE = "range";
    static const REFERER = "referer";
    static const RETRY_AFTER = "retry-after";
    static const SERVER = "server";
    static const TE = "te";
    static const TRAILER = "trailer";
    static const TRANSFER_ENCODING = "transfer-encoding";
    static const UPGRADE = "upgrade";
    static const USER_AGENT = "user-agent";
    static const VARY = "vary";
    static const VIA = "via";
    static const WARNING = "warning";
    static const WWW_AUTHENTICATE = "www-authenticate";
  
    // Cookie headers from RFC 6265.
    static const COOKIE = "cookie";
    static const SET_COOKIE = "set-cookie";
  
    static const GENERAL_HEADERS = const [CACHE_CONTROL,
                                          CONNECTION,
                                          DATE,
                                          PRAGMA,
                                          TRAILER,
                                          TRANSFER_ENCODING,
                                          UPGRADE,
                                          VIA,
                                          WARNING];
  
    static const ENTITY_HEADERS = const [ALLOW,
                                         CONTENT_ENCODING,
                                         CONTENT_LANGUAGE,
                                         CONTENT_LENGTH,
                                         CONTENT_LOCATION,
                                         CONTENT_MD5,
                                         CONTENT_RANGE,
                                         CONTENT_TYPE,
                                         EXPIRES,
                                         LAST_MODIFIED];
  
  
    static const RESPONSE_HEADERS = const [ACCEPT_RANGES,
                                           AGE,
                                           ETAG,
                                           LOCATION,
                                           PROXY_AUTHENTICATE,
                                           RETRY_AFTER,
                                           SERVER,
                                           VARY,
                                           WWW_AUTHENTICATE];
  
    static const REQUEST_HEADERS = const [ACCEPT,
                                          ACCEPT_CHARSET,
                                          ACCEPT_ENCODING,
                                          ACCEPT_LANGUAGE,
                                          AUTHORIZATION,
                                          EXPECT,
                                          FROM,
                                          HOST,
                                          IF_MATCH,
                                          IF_MODIFIED_SINCE,
                                          IF_NONE_MATCH,
                                          IF_RANGE,
                                          IF_UNMODIFIED_SINCE,
                                          MAX_FORWARDS,
                                          PROXY_AUTHORIZATION,
                                          RANGE,
                                          REFERER,
                                          TE,
                                          USER_AGENT];
  
    /**
     * Returns the list of values for the header named [name]. If there
     * is no header with the provided name, [:null:] will be returned.
     */
    List<String> operator[](String name);
  
    /**
     * Convenience method for the value for a single valued header. If
     * there is no header with the provided name, [:null:] will be
     * returned. If the header has more than one value an exception is
     * thrown.
     */
    String value(String name);
  
    /**
     * Adds a header value. The header named [name] will have the value
     * [value] added to its list of values. Some headers are single
     * valued, and for these adding a value will replace the previous
     * value. If the value is of type DateTime a HTTP date format will be
     * applied. If the value is a [:List:] each element of the list will
     * be added separately. For all other types the default [:toString:]
     * method will be used.
     */
    void add(String name, Object value);
  
    /**
     * Sets a header. The header named [name] will have all its values
     * cleared before the value [value] is added as its value.
     */
    void set(String name, Object value);
  
    /**
     * Removes a specific value for a header name. Some headers have
     * system supplied values and for these the system supplied values
     * will still be added to the collection of values for the header.
     */
    void remove(String name, Object value);
  
    /**
     * Removes all values for the specified header name. Some headers
     * have system supplied values and for these the system supplied
     * values will still be added to the collection of values for the
     * header.
     */
    void removeAll(String name);
  
    /**
     * Enumerates the headers, applying the function [f] to each
     * header. The header name passed in [:name:] will be all lower
     * case.
     */
    void forEach(void f(String name, List<String> values));
  
    /**
     * Disables folding for the header named [name] when sending the HTTP
     * header. By default, multiple header values are folded into a
     * single header line by separating the values with commas. The
     * 'set-cookie' header has folding disabled by default.
     */
    void noFolding(String name);
  
    /**
     * Gets and sets the date. The value of this property will
     * reflect the 'date' header.
     */
    DateTime date;
  
    /**
     * Gets and sets the expiry date. The value of this property will
     * reflect the 'expires' header.
     */
    DateTime expires;
  
    /**
     * Gets and sets the "if-modified-since" date. The value of this property will
     * reflect the "if-modified-since" header.
     */
    DateTime ifModifiedSince;
  
    /**
     * Gets and sets the host part of the 'host' header for the
     * connection.
     */
    String host;
  
    /**
     * Gets and sets the port part of the 'host' header for the
     * connection.
     */
    int port;
  
    /**
     * Gets and sets the content type. Note that the content type in the
     * header will only be updated if this field is set
     * directly. Mutating the returned current value will have no
     * effect.
     */
    ContentType contentType;
  
    /**
     * Gets and sets the content length header value.
     */
    int contentLength;
  
    /**
     * Gets and sets the persistent connection header value.
     */
    bool persistentConnection;
  
    /**
     * Gets and sets the chunked transfer encoding header value.
     */
    bool chunkedTransferEncoding;
  }
  
  
  /**
   * Representation of a header value in the form:
   *
   *   [:value; parameter1=value1; parameter2=value2:]
   *
   * [HeaderValue] can be used to conveniently build and parse header
   * values on this form.
   *
   * To build an [:accepts:] header with the value
   *
   *     text/plain; q=0.3, text/html
   *
   * use code like this:
   *
   *     HttpClientRequest request = ...;
   *     var v = new HeaderValue("text/plain", {"q": "0.3"});
   *     request.headers.add(HttpHeaders.ACCEPT, v);
   *     request.headers.add(HttpHeaders.ACCEPT, "text/html");
   *
   * To parse the header values use the [:parse:] static method.
   *
   *     HttpRequest request = ...;
   *     List<String> values = request.headers[HttpHeaders.ACCEPT];
   *     values.forEach((value) {
   *       HeaderValue v = HeaderValue.parse(value);
   *       // Use v.value and v.parameters
   *     });
   *
   * An instance of [HeaderValue] is immutable.
   */
  abstract class HeaderValue {
    /**
     * Creates a new header value object setting the value and parameters.
     */
    factory HeaderValue([String value = "", Map<String, String> parameters]) {
      return new _HeaderValue(value, parameters);
    }
  
    /**
     * Creates a new header value object from parsing a header value
     * string with both value and optional parameters.
     */
    static HeaderValue parse(String value,
                             {String parameterSeparator: ";",
                              bool preserveBackslash: false}) {
      return _HeaderValue.parse(value,
                                parameterSeparator: parameterSeparator,
                                preserveBackslash: preserveBackslash);
    }
  
    /**
     * Gets the header value.
     */
    String get value;
  
    /**
     * Gets the map of parameters.
     *
     * This map cannot be modified. invoking any operation which would
     * modify the map will throw [UnsupportedError].
     */
    Map<String, String> get parameters;
  
    /**
     * Returns the formatted string representation in the form:
     *
     *     value; parameter1=value1; parameter2=value2
     */
    String toString();
  }
  
  abstract class HttpSession implements Map {
    /**
     * Gets the id for the current session.
     */
    String get id;
  
    /**
     * Destroys the session. This will terminate the session and any further
     * connections with this id will be given a new id and session.
     */
    void destroy();
  
    /**
     * Sets a callback that will be called when the session is timed out.
     */
    void set onTimeout(void callback());
  
    /**
     * Is true if the session has not been sent to the client yet.
     */
    bool get isNew;
  }
  
  
  /**
   * Representation of a content type. An instance of [ContentType] is
   * immutable.
   */
  abstract class ContentType implements HeaderValue {
    /**
     * Creates a new content type object setting the primary type and
     * sub type. The charset and additional parameters can also be set
     * using [charset] and [parameters]. If charset is passed and
     * [parameters] contains charset as well the passed [charset] will
     * override the value in parameters. Keys and values passed in
     * parameters will be converted to lower case.
     */
    factory ContentType(String primaryType,
                        String subType,
                        {String charset, Map<String, String> parameters}) {
      return new _ContentType(primaryType, subType, charset, parameters);
    }
  
    /**
     * Creates a new content type object from parsing a Content-Type
     * header value. As primary type, sub type and parameter names and
     * values are not case sensitive all these values will be converted
     * to lower case. Parsing this string
     *
     *     text/html; charset=utf-8
     *
     * will create a content type object with primary type [:text:], sub
     * type [:html:] and parameter [:charset:] with value [:utf-8:].
     */
    static ContentType parse(String value) {
      return _ContentType.parse(value);
    }
  
    /**
     * Gets the mime-type, without any parameters.
     */
    String get mimeType;
  
    /**
     * Gets the primary type.
     */
    String get primaryType;
  
    /**
     * Gets the sub type.
     */
    String get subType;
  
    /**
     * Gets the character set.
     */
    String get charset;
  }
  
  
  /**
   * Representation of a cookie. For cookies received by the server as
   * Cookie header values only [:name:] and [:value:] fields will be
   * set. When building a cookie for the 'set-cookie' header in the server
   * and when receiving cookies in the client as 'set-cookie' headers all
   * fields can be used.
   */
  abstract class Cookie {
    /**
     * Creates a new cookie optionally setting the name and value.
     */
    factory Cookie([String name, String value]) => new _Cookie(name, value);
  
    /**
     * Creates a new cookie by parsing a header value from a 'set-cookie'
     * header.
     */
    factory Cookie.fromSetCookieValue(String value) {
      return new _Cookie.fromSetCookieValue(value);
    }
  
    /**
     * Gets and sets the name.
     */
    String name;
  
    /**
     * Gets and sets the value.
     */
    String value;
  
    /**
     * Gets and sets the expiry date.
     */
    DateTime expires;
  
    /**
     * Gets and sets the max age. A value of [:0:] means delete cookie
     * now.
     */
    int maxAge;
  
    /**
     * Gets and sets the domain.
     */
    String domain;
  
    /**
     * Gets and sets the path.
     */
    String path;
  
    /**
     * Gets and sets whether this cookie is secure.
     */
    bool secure;
  
    /**
     * Gets and sets whether this cookie is HTTP only.
     */
    bool httpOnly;
  
    /**
     * Returns the formatted string representation of the cookie. The
     * string representation can be used for for setting the Cookie or
     * 'set-cookie' headers
     */
    String toString();
  }
  
  
  /**
   * A server-side object
   * that contains the content of and information about an HTTP request.
   *
   * __Note__: Check out the
   * [http_server](http://pub.dartlang.org/packages/http_server)
   * package, which makes working with the low-level
   * dart:io HTTP server subsystem easier.
   *
   * HttpRequest objects are generated by an [HttpServer],
   * which listens for HTTP requests on a specific host and port.
   * For each request received, the HttpServer, which is a [Stream],
   * generates an HttpRequest object and adds it to the stream.
   *
   * An HttpRequest object delivers the body content of the request
   * as a stream of bytes.
   * The object also contains information about the request,
   * such as the method, URI, and headers.
   *
   * In the following code, an HttpServer listens
   * for HTTP requests and, within the callback function,
   * uses the HttpRequest object's `method` property to dispatch requests.
   *
   *     final HOST = InternetAddress.LOOPBACK_IP_V4;
   *     final PORT = 4040;
   *
   *     HttpServer.bind(HOST, PORT).then((_server) {
   *       _server.listen((HttpRequest request) {
   *         switch (request.method) {
   *           case 'GET': 
   *             handleGetRequest(request);
   *             break;
   *           case 'POST': 
   *             ...
   *         }
   *       },
   *       onError: handleError);    // listen() failed.
   *     }).catchError(handleError);
   *   
   * Listen to the HttpRequest stream to handle the
   * data and be notified once the entire body is received.
   * An HttpRequest object contains an [HttpResponse] object,
   * to which the server can write its response.
   * For example, here's a skeletal callback function
   * that responds to a request:
   *
   *     void handleGetRequest(HttpRequest req) {
   *       HttpResponse res = req.response;
   *       var body = [];
   *       req.listen((List<int> buffer) => body.add(buffer),
   *         onDone: () {
   *           res.write('Received ${body.length} for request ');
   *           res.write(' ${req.method}: ${req.uri.path}');
   *           res.close();
   *         },
   *         onError: handleError);
   *     }
   */
  abstract class HttpRequest implements Stream<List<int>> {
    /**
     * The content length of the request body (read-only).
     *
     * If the size of the request body is not known in advance,
     * this value is -1.
     */
    int get contentLength;
  
    /**
     * The method, such as 'GET' or 'POST', for the request (read-only).
     */
    String get method;
  
    /**
     * The URI for the request (read-only).
     *
     * This provides access to the
     * path, query string, and fragment identifier for the request.
     */
    Uri get uri;
  
    /**
     * The request headers (read-only).
     */
    HttpHeaders get headers;
  
    /**
     * The cookies in the request, from the Cookie headers (read-only).
     */
    List<Cookie> get cookies;
  
    /**
     * The persistent connection state signaled by the client (read-only).
     */
    bool get persistentConnection;
  
    /**
     * The client certificate of the client making the request (read-only).
     *
     * This value is null if the connection is not a secure TLS or SSL connection,
     * or if the server does not request a client certificate, or if the client
     * does not provide one.
     */
    X509Certificate get certificate;
  
    /**
     * The session for the given request (read-only).
     *
     * If the session is
     * being initialized by this call, [:isNew:] is true for the returned
     * session.
     * See [HttpServer.sessionTimeout] on how to change default timeout.
     */
    HttpSession get session;
  
    /**
     * The HTTP protocol version used in the request, 
     * either "1.0" or "1.1" (read-only).
     */
    String get protocolVersion;
  
    /**
     * Information about the client connection (read-only).
     *
     * Returns [null] if the socket is not available.
     */
    HttpConnectionInfo get connectionInfo;
  
    /**
     * The [HttpResponse] object, used for sending back the response to the
     * client (read-only).
     *
     * If the [contentLength] of the body isn't 0, and the body isn't being read,
     * any write calls on the [HttpResponse] automatically drain the request
     * body.
     */
    HttpResponse get response;
  }
  
  
  /**
   * An [HttpResponse] represents the headers and data to be returned to
   * a client in response to an HTTP request.
   *
   * This object has a number of properties for setting up the HTTP
   * header of the response. When the header has been set up the methods
   * from the [IOSink] can be used to write the actual body of the HTTP
   * response. When one of the [IOSink] methods is used for the
   * first time the request header is send. Calling any methods that
   * will change the header after it is sent will throw an exception.
   *
   * When writing string data through the [IOSink] the encoding used
   * will be determined from the "charset" parameter of the
   * "Content-Type" header.
   *
   *     HttpResponse response = ...
   *     response.headers.contentType
   *         = new ContentType("application", "json", charset: "utf-8");
   *     response.write(...);  // Strings written will be UTF-8 encoded.
   *
   * If no charset is provided the default of ISO-8859-1 (Latin 1) will
   * be used.
   *
   *     HttpResponse response = ...
   *     response.headers.add(HttpHeaders.CONTENT_TYPE, "text/plain");
   *     response.write(...);  // Strings written will be ISO-8859-1 encoded.
   *
   * If an unsupported encoding is used an exception will be thrown if
   * using one of the write methods taking a string.
   */
  abstract class HttpResponse implements IOSink {
    // TODO(ajohnsen): Add documentation of how to pipe a file to the response.
    /**
     * Gets and sets the content length of the response. If the size of
     * the response is not known in advance set the content length to
     * -1 - which is also the default if not set.
     */
    int contentLength;
  
    /**
     * Gets and sets the status code. Any integer value is accepted. For
     * the official HTTP status codes use the fields from
     * [HttpStatus]. If no status code is explicitly set the default
     * value [HttpStatus.OK] is used.
     */
    int statusCode;
  
    /**
     * Gets and sets the reason phrase. If no reason phrase is explicitly
     * set a default reason phrase is provided.
     */
    String reasonPhrase;
  
    /**
     * Gets and sets the persistent connection state. The initial value
     * of this property is the persistent connection state from the
     * request.
     */
    bool persistentConnection;
  
    /**
     * Set and get the [deadline] for the response. The deadline is timed from the
     * time it's set. Setting a new deadline will override any previous deadline.
     * When a deadline is exceeded, the response will be closed and any further
     * data ignored.
     *
     * To disable a deadline, set the [deadline] to `null`.
     *
     * The [deadline] is `null` by default.
     */
    Duration deadline;
  
    /**
     * Returns the response headers.
     */
    HttpHeaders get headers;
  
    /**
     * Cookies to set in the client (in the 'set-cookie' header).
     */
    List<Cookie> get cookies;
  
    /**
     * Respond with a redirect to [location].
     *
     * The URI in [location] should be absolute, but there are no checks
     * to enforce that.
     *
     * By default the HTTP status code `HttpStatus.MOVED_TEMPORARILY`
     * (`302`) is used for the redirect, but an alternative one can be
     * specified using the [status] argument.
     *
     * This method will also call `close`, and the returned future is
     * the furure returned by `close`.
     */
    Future redirect(Uri location, {int status: HttpStatus.MOVED_TEMPORARILY});
  
    /**
     * Detaches the underlying socket from the HTTP server. When the
     * socket is detached the HTTP server will no longer perform any
     * operations on it.
     *
     * This is normally used when a HTTP upgrade request is received
     * and the communication should continue with a different protocol.
     */
    Future<Socket> detachSocket();
  
    /**
     * Gets information about the client connection. Returns [null] if the socket
     * is not available.
     */
    HttpConnectionInfo get connectionInfo;
  }
  
  
  /**
   * A client that receives content, such as web pages, from
   * a server using the HTTP protocol.
   *
   * HttpClient contains a number of methods to send a HTTP request
   * to a HTTP server and receive a HTTP response back.
   *
   * This is a two-step process, triggered by two futures. When the
   * first future completes with a [HttpClientRequest] the underlying
   * network connection has been established, but no data has yet been
   * sent. The HTTP headers and body can be set on the request, and
   * [:close:] is called to sent it to the server.
   *
   * The second future, which is returned by [:close:], completes with
   * an [HttpClientResponse] object when the response is received from
   * the server. This object contains the headers and body of the
   * response.
  
   * The future for [HttpClientRequest] is created by methods such as
   * [getUrl] and [open].
   *
   * When the HTTP response is ready a [HttpClientResponse] object is
   * provided which provides access to the headers and body of the response. The
   * body is available as a stream implemented by [HttpClientResponse].
   * If a body is present, it must be read. Otherwise, it'll lead to a resource
   * leaks. Consider using [HttpClientResponse.drain] if the body is unused.
   *
   *     HttpClient client = new HttpClient();
   *     client.getUrl(Uri.parse("http://www.example.com/"))
   *         .then((HttpClientRequest request) {
   *           // Prepare the request then call close on it to send it.
   *           return request.close();
   *         })
   *         .then((HttpClientResponse response) {
   *          // Process the response.
   *         });
   *
   * All [HttpClient] requests set the following header by default:
   *
   *     Accept-Encoding: gzip
   *
   * This allows the HTTP server to use gzip compression for the body if
   * possible. If this behavior is not desired set the
   * "Accept-Encoding" header to something else.
   *
   * The [HttpClient] supports persistent connections and caches network
   * connections to reuse then for multiple requests whenever
   * possible. This means that network connections can be kept open for
   * some time after a request have completed. Use [:HttpClient.close:]
   * to force shut down the [HttpClient] object and close the idle
   * network connections.
   *
   * By default the HttpClient uses the proxy configuration available
   * from the environment, see [findProxyFromEnvironment]. To turn off
   * the use of proxies all together set the [findProxy] property to
   * [:null:].
   *
   *     HttpClient client = new HttpClient();
   *     client.findProxy = null;
   */
  abstract class HttpClient {
    static const int DEFAULT_HTTP_PORT = 80;
    static const int DEFAULT_HTTPS_PORT = 443;
  
    /**
     * Get and set the idle timeout of non-active persistent (keep-alive)
     * connections. The default value is 15 seconds.
     */
    Duration idleTimeout;
  
    /**
     * Set and get the default value of the `User-Agent` header for all requests
     * generated by this [HttpClient]. The default value is
     * `Dart/<version> (dart:io)`.
     *
     * If the userAgent is set to `null`, no default `User-Agent` header will be
     * added to each request.
     */
    String userAgent;
  
    factory HttpClient() => new _HttpClient();
  
    /**
     * Opens a HTTP connection.
     *
     * The HTTP method to use is specified in [method], the server is
     * specified using [host] and [port], and the path (including
     * possible fragment and query) is specified using [path].
     *
     * The `Host` header for the request will be set to the value
     * [host]:[port]. This can be overridden through the
     * [HttpClientRequest] interface before the request is sent.  NOTE
     * if [host] is an IP address this will still be set in the `Host`
     * header.
     *
     * For additional information on the sequence of events during an
     * HTTP transaction, and the objects returned by the futures, see
     * the overall documentation for the class [HttpClient].
     */
    Future<HttpClientRequest> open(String method,
                                   String host,
                                   int port,
                                   String path);
  
    /**
     * Opens a HTTP connection.
     *
     * The HTTP method is specified in [method] and the URL to use in
     * [url].
     *
     * The `Host` header for the request will be set to the value
     * [host]:[port]. This can be overridden through the
     * [HttpClientRequest] interface before the request is sent.  NOTE
     * if [host] is an IP address this will still be set in the `Host`
     * header.
     *
     * For additional information on the sequence of events during an
     * HTTP transaction, and the objects returned by the futures, see
     * the overall documentation for the class [HttpClient].
     */
    Future<HttpClientRequest> openUrl(String method, Uri url);
  
    /**
     * Opens a HTTP connection using the GET method.
     *
     * The server is specified using [host] and [port], and the path
     * (including possible fragment and query) is specified using
     * [path].
     *
     * See [open] for details.
     */
    Future<HttpClientRequest> get(String host, int port, String path);
  
    /**
     * Opens a HTTP connection using the GET method.
     *
     * The URL to use is specified in [url].
     *
     * See [openUrl] for details.
     */
    Future<HttpClientRequest> getUrl(Uri url);
  
    /**
     * Opens a HTTP connection using the POST method.
     *
     * The server is specified using [host] and [port], and the path
     * (including possible fragment and query) is specified using
     * [path].
     *
     * See [open] for details.
     */
    Future<HttpClientRequest> post(String host, int port, String path);
  
    /**
     * Opens a HTTP connection using the POST method.
     *
     * The URL to use is specified in [url].
     *
     * See [openUrl] for details.
     */
    Future<HttpClientRequest> postUrl(Uri url);
  
    /**
     * Sets the function to be called when a site is requesting
     * authentication. The URL requested and the security realm from the
     * server are passed in the arguments [url] and [realm].
     *
     * The function returns a [Future] which should complete when the
     * authentication has been resolved. If credentials cannot be
     * provided the [Future] should complete with [false]. If
     * credentials are available the function should add these using
     * [addCredentials] before completing the [Future] with the value
     * [true].
     *
     * If the [Future] completes with true the request will be retried
     * using the updated credentials. Otherwise response processing will
     * continue normally.
     */
    set authenticate(Future<bool> f(Uri url, String scheme, String realm));
  
    /**
     * Add credentials to be used for authorizing HTTP requests.
     */
    void addCredentials(Uri url, String realm, HttpClientCredentials credentials);
  
    /**
     * Sets the function used to resolve the proxy server to be used for
     * opening a HTTP connection to the specified [url]. If this
     * function is not set, direct connections will always be used.
     *
     * The string returned by [f] must be in the format used by browser
     * PAC (proxy auto-config) scripts. That is either
     *
     *   "DIRECT"
     *
     * for using a direct connection or
     *
     *   "PROXY host:port"
     *
     * for using the proxy server [:host:] on port [:port:].
     *
     * A configuration can contain several configuration elements
     * separated by semicolons, e.g.
     *
     *   "PROXY host:port; PROXY host2:port2; DIRECT"
     *
     * The static function [findProxyFromEnvironment] on this class can
     * be used to implement proxy server resolving based on environment
     * variables.
     */
    set findProxy(String f(Uri url));
  
    /**
     * Function for resolving the proxy server to be used for a HTTP
     * connection from the proxy configuration specified through
     * environment variables.
     *
     * The following environment variables are taken into account:
     *
     *     http_proxy
     *     https_proxy
     *     no_proxy
     *     HTTP_PROXY
     *     HTTPS_PROXY
     *     NO_PROXY
     *
     * [:http_proxy:] and [:HTTP_PROXY:] specify the proxy server to use for
     * http:// urls. Use the format [:hostname:port:]. If no port is used a
     * default of 1080 will be used. If both are set the lower case one takes
     * precedence.
     *
     * [:https_proxy:] and [:HTTPS_PROXY:] specify the proxy server to use for
     * https:// urls. Use the format [:hostname:port:]. If no port is used a
     * default of 1080 will be used. If both are set the lower case one takes
     * precedence.
     *
     * [:no_proxy:] and [:NO_PROXY:] specify a comma separated list of
     * postfixes of hostnames for which not to use the proxy
     * server. E.g. the value "localhost,127.0.0.1" will make requests
     * to both "localhost" and "127.0.0.1" not use a proxy. If both are set
     * the lower case one takes precedence.
     *
     * To activate this way of resolving proxies assign this function to
     * the [findProxy] property on the [HttpClient].
     *
     *     HttpClient client = new HttpClient();
     *     client.findProxy = HttpClient.findProxyFromEnvironment;
     *
     * If you don't want to use the system environment you can use a
     * different one by wrapping the function.
     *
     *     HttpClient client = new HttpClient();
     *     client.findProxy = (url) {
     *       return HttpClient.findProxyFromEnvironment(
     *           url, {"http_proxy": ..., "no_proxy": ...});
     *     }
     *
     * If a proxy requires authentication it is possible to configure
     * the username and password as well. Use the format
     * [:username:password@hostname:port:] to include the username and
     * password. Alternatively the API [addProxyCredentials] can be used
     * to set credentials for proxies which require authentication.
     */
    static String findProxyFromEnvironment(Uri url,
                                           {Map<String, String> environment}) {
      return _HttpClient._findProxyFromEnvironment(url, environment);
    }
  
    /**
     * Sets the function to be called when a proxy is requesting
     * authentication. Information on the proxy in use and the security
     * realm for the authentication are passed in the arguments [host],
     * [port] and [realm].
     *
     * The function returns a [Future] which should complete when the
     * authentication has been resolved. If credentials cannot be
     * provided the [Future] should complete with [false]. If
     * credentials are available the function should add these using
     * [addProxyCredentials] before completing the [Future] with the value
     * [true].
     *
     * If the [Future] completes with [true] the request will be retried
     * using the updated credentials. Otherwise response processing will
     * continue normally.
     */
    set authenticateProxy(
        Future<bool> f(String host, int port, String scheme, String realm));
  
    /**
     * Add credentials to be used for authorizing HTTP proxies.
     */
    void addProxyCredentials(String host,
                             int port,
                             String realm,
                             HttpClientCredentials credentials);
  
    /**
     * Sets a callback that will decide whether to accept a secure connection
     * with a server certificate that cannot be authenticated by any of our
     * trusted root certificates.
     *
     * When an secure HTTP request if made, using this HttpClient, and the
     * server returns a server certificate that cannot be authenticated, the
     * callback is called asynchronously with the [X509Certificate] object and
     * the server's hostname and port.  If the value of [badCertificateCallback]
     * is [null], the bad certificate is rejected, as if the callback
     * returned [false]
     *
     * If the callback returns true, the secure connection is accepted and the
     * [:Future<HttpClientRequest>:] that was returned from the call making the
     * request completes with a valid HttpRequest object. If the callback returns
     * false, the [:Future<HttpClientRequest>:] completes with an exception.
     *
     * If a bad certificate is received on a connection attempt, the library calls
     * the function that was the value of badCertificateCallback at the time
     * the the request is made, even if the value of badCertificateCallback
     * has changed since then.
     */
    set badCertificateCallback(bool callback(X509Certificate cert,
                                             String host,
                                             int port));
  
    /**
     * Shutdown the HTTP client. If [force] is [:false:] (the default)
     * the [:HttpClient:] will be kept alive until all active
     * connections are done. If [force] is [:true:] any active
     * connections will be closed to immediately release all
     * resources. These closed connections will receive an [:onError:]
     * callback to indicate that the client was shutdown. In both cases
     * trying to establish a new connection after calling [shutdown]
     * will throw an exception.
     */
    void close({bool force: false});
  }
  
  
  /**
   * HTTP request for a client connection.
   *
   * This object has a number of properties for setting up the HTTP
   * header of the request. When the header has been set up the methods
   * from the [IOSink] can be used to write the actual body of the HTTP
   * request. When one of the [IOSink] methods is used for the first
   * time the request header is send. Calling any methods that will
   * change the header after it is sent will throw an exception.
   *
   * When writing string data through the [IOSink] the
   * encoding used will be determined from the "charset" parameter of
   * the "Content-Type" header.
   *
   *     HttpClientRequest request = ...
   *     request.headers.contentType
   *         = new ContentType("application", "json", charset: "utf-8");
   *     request.write(...);  // Strings written will be UTF-8 encoded.
   *
   * If no charset is provided the default of ISO-8859-1 (Latin 1) will
   * be used.
   *
   *     HttpClientRequest request = ...
   *     request.headers.add(HttpHeaders.CONTENT_TYPE, "text/plain");
   *     request.write(...);  // Strings written will be ISO-8859-1 encoded.
   *
   * If an unsupported encoding is used an exception will be thrown if
   * using one of the write methods taking a string.
   */
  abstract class HttpClientRequest implements IOSink {
    /**
     * The method of the request.
     */
    String get method;
  
    /**
     * The uri of the request.
     */
    Uri get uri;
  
    /**
     * Gets and sets the content length of the request. If the size of
     * the request is not known in advance set content length to -1,
     * which is also the default.
     */
    int contentLength;
  
    /**
     * Returns the request headers.
     */
    HttpHeaders get headers;
  
    /**
     * Cookies to present to the server (in the 'cookie' header).
     */
    List<Cookie> get cookies;
  
    /**
     * Gets and sets the requested persistent connection state.
     * The default value is [:true:].
     */
    bool persistentConnection;
  
    /**
     * A [HttpClientResponse] future that will complete once the response is
     * available. If an error occurs before the response is available, this
     * future will complete with an error.
     */
    Future<HttpClientResponse> get done;
  
    /**
     * Close the request for input. Returns the value of [done].
     */
    Future<HttpClientResponse> close();
  
    /**
     * Set this property to [:true:] if this request should
     * automatically follow redirects. The default is [:true:].
     *
     * Automatic redirect will only happen for "GET" and "HEAD" requests
     * and only for the status codes [:HttpHeaders.MOVED_PERMANENTLY:]
     * (301), [:HttpStatus.FOUND:] (302),
     * [:HttpStatus.MOVED_TEMPORARILY:] (302, alias for
     * [:HttpStatus.FOUND:]), [:HttpStatus.SEE_OTHER:] (303) and
     * [:HttpStatus.TEMPORARY_REDIRECT:] (307). For
     * [:HttpStatus.SEE_OTHER:] (303) autmatic redirect will also happen
     * for "POST" requests with the method changed to "GET" when
     * following the redirect.
     *
     * All headers added to the request will be added to the redirection
     * request(s). However, any body send with the request will not be
     * part of the redirection request(s).
     */
    bool followRedirects;
  
    /**
     * Set this property to the maximum number of redirects to follow
     * when [followRedirects] is [:true:]. If this number is exceeded the
     * [onError] callback will be called with a [RedirectException].
     *
     * The default value is 5.
     */
    int maxRedirects;
  
    /**
     * Get information about the client connection. Returns [null] if the socket
     * is not available.
     */
    HttpConnectionInfo get connectionInfo;
  }
  
  
  /**
   * HTTP response for a client connection. The [HttpClientResponse] is a
   * [Stream] of the body content of the response. Listen to the body to handle
   * the data and be notified once the entire body is received.
   */
  abstract class HttpClientResponse implements Stream<List<int>> {
    /**
     * Returns the status code.
     */
    int get statusCode;
  
    /**
     * Returns the reason phrase associated with the status code.
     */
    String get reasonPhrase;
  
    /**
     * Returns the content length of the response body. Returns -1 if the size of
     * the response body is not known in advance.
     */
    int get contentLength;
  
    /**
     * Gets the persistent connection state returned by the server.
     */
    bool get persistentConnection;
  
    /**
     * Returns whether the status code is one of the normal redirect
     * codes [HttpStatus.MOVED_PERMANENTLY], [HttpStatus.FOUND],
     * [HttpStatus.MOVED_TEMPORARILY], [HttpStatus.SEE_OTHER] and
     * [HttpStatus.TEMPORARY_REDIRECT].
     */
    bool get isRedirect;
  
    /**
     * Returns the series of redirects this connection has been through. The
     * list will be empty if no redirects were followed. [redirects] will be
     * updated both in the case of an automatic and a manual redirect.
     */
    List<RedirectInfo> get redirects;
  
    /**
     * Redirects this connection to a new URL. The default value for
     * [method] is the method for the current request. The default value
     * for [url] is the value of the [HttpHeaders.LOCATION] header of
     * the current response. All body data must have been read from the
     * current response before calling [redirect].
     *
     * All headers added to the request will be added to the redirection
     * request. However, any body sent with the request will not be
     * part of the redirection request.
     *
     * If [followLoops] is set to [true], redirect will follow the redirect,
     * even if the URL was already visited. The default value is [false].
     *
     * [redirect] will ignore [maxRedirects] and will always perform the redirect.
     */
    Future<HttpClientResponse> redirect([String method,
                                         Uri url,
                                         bool followLoops]);
  
  
    /**
     * Returns the response headers.
     */
    HttpHeaders get headers;
  
    /**
     * Detach the underlying socket from the HTTP client. When the
     * socket is detached the HTTP client will no longer perform any
     * operations on it.
     *
     * This is normally used when a HTTP upgrade is negotiated and the
     * communication should continue with a different protocol.
     */
    Future<Socket> detachSocket();
  
    /**
     * Cookies set by the server (from the 'set-cookie' header).
     */
    List<Cookie> get cookies;
  
    /**
     * Returns the certificate of the HTTPS server providing the response.
     * Returns null if the connection is not a secure TLS or SSL connection.
     */
    X509Certificate get certificate;
  
    /**
     * Gets information about the client connection. Returns [null] if the socket
     * is not available.
     */
    HttpConnectionInfo get connectionInfo;
  }
  
  
  abstract class HttpClientCredentials { }
  
  
  /**
   * Represents credentials for basic authentication.
   */
  abstract class HttpClientBasicCredentials extends HttpClientCredentials {
    factory HttpClientBasicCredentials(String username, String password) =>
        new _HttpClientBasicCredentials(username, password);
  }
  
  
  /**
   * Represents credentials for digest authentication. Digest
   * authentication is only supported for servers using the MD5
   * algorithm and quality of protection (qop) of either "none" or
   * "auth".
   */
  abstract class HttpClientDigestCredentials extends HttpClientCredentials {
    factory HttpClientDigestCredentials(String username, String password) =>
        new _HttpClientDigestCredentials(username, password);
  }
  
  
  /**
   * Information about an [HttpRequest], [HttpResponse], [HttpClientRequest], or
   * [HttpClientResponse] connection.
   */
  abstract class HttpConnectionInfo {
    InternetAddress get remoteAddress;
    int get remotePort;
    int get localPort;
  }
  
  
  /**
   * Redirect information.
   */
  abstract class RedirectInfo {
    /**
     * Returns the status code used for the redirect.
     */
    int get statusCode;
  
    /**
     * Returns the method used for the redirect.
     */
    String get method;
  
    /**
     * Returns the location for the redirect.
     */
    Uri get location;
  }
  
  
  /**
   * When detaching a socket from either the [:HttpServer:] or the
   * [:HttpClient:] due to a HTTP connection upgrade there might be
   * unparsed data already read from the socket. This unparsed data
   * together with the detached socket is returned in an instance of
   * this class.
   */
  abstract class DetachedSocket {
    Socket get socket;
    List<int> get unparsedData;
  }
  
  
  class HttpException implements IOException {
    final String message;
    final Uri uri;
  
    const HttpException(String this.message, {Uri this.uri});
  
    String toString() {
      var b = new StringBuffer();
      b.write('HttpException: ');
      b.write(message);
      if (uri != null) {
        b.write(', uri = $uri');
      }
      return b.toString();
    }
  }
  
  
  class RedirectException implements HttpException {
    final String message;
    final List<RedirectInfo> redirects;
  
    const RedirectException(String this.message,
                            List<RedirectInfo> this.redirects);
  
    String toString() => "RedirectException: $message";
  
    Uri get uri => redirects.last.location;
  }
  