/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple Inc.  All Rights Reserved.
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 */
#include "HTTPProtocol.h"

StrPtrLen HTTPProtocol::sMethods[] =
{
    StrPtrLen("GET"),
    StrPtrLen("HEAD"),
    StrPtrLen("POST"),
    StrPtrLen("OPTIONS"),
    StrPtrLen("PUT"),
    StrPtrLen("DELETE"),
    StrPtrLen("TRACE"),
    StrPtrLen("CONNECT"),
};

HTTPMethod HTTPProtocol::GetMethod(const StrPtrLen* inMethodStr)
{
    HTTPMethod theMethod = httpIllegalMethod;

    if (inMethodStr->Len == 0)
            return httpIllegalMethod;

    switch((inMethodStr->Ptr)[0])
        {
        case 'G':       theMethod = httpGetMethod; break;
        case 'H':       theMethod = httpHeadMethod; break;
        case 'P':       theMethod = httpPostMethod; break;  // Most likely POST and not PUT
        case 'O':       theMethod = httpOptionsMethod; break;
        case 'D':       theMethod = httpDeleteMethod; break;
        case 'T':       theMethod = httpTraceMethod; break;
        case 'C':       theMethod = httpConnectMethod; break;
        }

    if ( (theMethod != httpIllegalMethod) && (inMethodStr->Equal(sMethods[theMethod])) )
            return theMethod;

    // Check for remaining methods (Only PUT method is left)
    if ( inMethodStr->Equal(sMethods[httpPutMethod]) )
            return httpPutMethod;

    return httpIllegalMethod;
}

StrPtrLen HTTPProtocol::sHeaders[] =
{
    StrPtrLen("Connection"),
    StrPtrLen("Date"),
    StrPtrLen("Authorization"),
    StrPtrLen("If-Modified-Since"),
    StrPtrLen("Server"),
    StrPtrLen("WWW-Authenticate"),
    StrPtrLen("Expires"),
    StrPtrLen("Last-Modified"),

    StrPtrLen("Cache-Control"),
    StrPtrLen("Pragma"),
    StrPtrLen("Trailer"),
    StrPtrLen("Transfer-Encoding"),
    StrPtrLen("Upgrade"),
    StrPtrLen("Via"),
    StrPtrLen("Warning"),

    StrPtrLen("Accept"),
    StrPtrLen("Accept-Charset"),
    StrPtrLen("Accept-Encoding"),
    StrPtrLen("Accept-Language"),
    StrPtrLen("Expect"),
    StrPtrLen("From"),
    StrPtrLen("Host"),
    StrPtrLen("If-Match"),
    StrPtrLen("If-None-Match"),
    StrPtrLen("If-Range"),
    StrPtrLen("If-Unmodified-Since"),
    StrPtrLen("Max-Forwards"),
    StrPtrLen("Proxy-Authorization"),
    StrPtrLen("Range"),
    StrPtrLen("Referer"),
    StrPtrLen("TE"),
    StrPtrLen("User-Agent"),

    StrPtrLen("Accept-Ranges"),
    StrPtrLen("Age"),
    StrPtrLen("ETag"),
    StrPtrLen("Location"),
    StrPtrLen("Proxy-Authenticate"),
    StrPtrLen("Retry-After"),
    StrPtrLen("Vary"),

    StrPtrLen("Allow"),
    StrPtrLen("Content-Encoding"),
    StrPtrLen("Content-Language"),
    StrPtrLen("Content-Length"),
    StrPtrLen("Content-Location"),
    StrPtrLen("Content-MD5"),
    StrPtrLen("Content-Range"),
    StrPtrLen("Content-Type"),
    
    StrPtrLen("X-SessionCookie"),
    StrPtrLen("X-Server-IP-Address"),

    StrPtrLen(" ,")
};

HTTPHeader HTTPProtocol::GetHeader(const StrPtrLen* inHeaderStr)
{
    if (inHeaderStr->Len == 0)
            return httpIllegalHeader;

    HTTPHeader theHeader = httpIllegalHeader;

    //chances are this is one of our selected "VIP" headers. so check for this.
    switch((inHeaderStr->Ptr)[0])
        {
        case 'C':       case 'c':       theHeader = httpConnectionHeader; break;
        case 'S':       case 's':       theHeader = httpServerHeader; break;
        case 'D':       case 'd':       theHeader = httpDateHeader; break;
        case 'A':       case 'a':       theHeader = httpAuthorizationHeader; break;
        case 'W':       case 'w':       theHeader = httpWWWAuthenticateHeader; break;
        case 'I':       case 'i':       theHeader = httpIfModifiedSinceHeader; break;
        case 'E':       case 'e':       theHeader = httpExpiresHeader; break;
        case 'L':       case 'l':       theHeader = httpLastModifiedHeader; break;
        // Added this to optimize for HTTP tunnelling in the server (Not really a VIP header)
        case 'X':       case 'x':       theHeader = httpSessionCookieHeader; break;
        }

    if ((theHeader != httpIllegalHeader) && 
            (inHeaderStr->EqualIgnoreCase(sHeaders[theHeader].Ptr, sHeaders[theHeader].Len)))
            return theHeader;

    //If this isn't one of our VIP headers, go through the remaining request headers, trying
    //to find the right one.
    for (SInt32 x = httpNumVIPHeaders; x < httpNumHeaders; x++)
            if (inHeaderStr->EqualIgnoreCase(sHeaders[x].Ptr, sHeaders[x].Len))
                return x;
    return httpIllegalHeader;
}

StrPtrLen HTTPProtocol::sStatusCodeStrings[] =
{
    StrPtrLen("Continue"),              //kContinue
    StrPtrLen("Switching Protocols"),       //kSwitchingProtocols
    StrPtrLen("OK"),                //kOK
    StrPtrLen("Created"),               //kCreated
    StrPtrLen("Accepted"),              //kAccepted
    StrPtrLen("Non Authoritative Information"), //kNonAuthoritativeInformation
    StrPtrLen("No Content"),            //kNoContent
    StrPtrLen("Reset Content"),         //kResetContent
    StrPtrLen("Partial Content"),           //kPartialContent
    StrPtrLen("Multiple Choices"),          //kMultipleChoices
    StrPtrLen("Moved Permanently"),         //kMovedPermanently
    StrPtrLen("Found"),             //kFound
    StrPtrLen("See Other"),             //kSeeOther
    StrPtrLen("Not Modified"),          //kNotModified
    StrPtrLen("Use Proxy"),             //kUseProxy
    StrPtrLen("Temporary Redirect"),        //kTemporaryRedirect
    StrPtrLen("Bad Request"),           //kBadRequest
    StrPtrLen("Unauthorized"),          //kUnAuthorized
    StrPtrLen("Payment Required"),          //kPaymentRequired
    StrPtrLen("Forbidden"),             //kForbidden
    StrPtrLen("Not Found"),             //kNotFound
    StrPtrLen("Method Not Allowed"),        //kMethodNotAllowed
    StrPtrLen("Not Acceptable"),            //kNotAcceptable
    StrPtrLen("Proxy Authentication Required"), //kProxyAuthenticationRequired
    StrPtrLen("Request Time-out"),          //kRequestTimeout
    StrPtrLen("Conflict"),              //kConflict
    StrPtrLen("Gone"),              //kGone
    StrPtrLen("Length Required"),           //kLengthRequired
    StrPtrLen("Precondition Failed"),       //kPreconditionFailed
    StrPtrLen("Request Entity Too Large"),      //kRequestEntityTooLarge
    StrPtrLen("Request-URI Too Large"),     //kRequestURITooLarge
    StrPtrLen("Unsupported Media Type"),        //kUnsupportedMediaType
    StrPtrLen("Request Range Not Satisfiable"), //kRequestRangeNotSatisfiable
    StrPtrLen("Expectation Failed"),        //kExpectationFailed
    StrPtrLen("Internal Server Error"),     //kInternalServerError
    StrPtrLen("Not Implemented"),           //kNotImplemented
    StrPtrLen("Bad Gateway"),           //kBadGateway
    StrPtrLen("Service Unavailable"),       //kServiceUnavailable
    StrPtrLen("Gateway Timeout"),           //kGatewayTimeout
    StrPtrLen("HTTP Version not supported")     //kHTTPVersionNotSupported
};

SInt32 HTTPProtocol::sStatusCodes[] =
{
    100,            //kContinue
    101,            //kSwitchingProtocols
    200,            //kOK
    201,            //kCreated
    202,            //kAccepted
    203,            //kNonAuthoritativeInformation
    204,            //kNoContent
    205,            //kResetContent
    206,            //kPartialContent
    300,            //kMultipleChoices
    301,            //kMovedPermanently
    302,            //kFound
    303,            //kSeeOther
    304,            //kNotModified
    305,            //kUseProxy
    307,            //kTemporaryRedirect
    400,            //kBadRequest
    401,            //kUnAuthorized
    402,            //kPaymentRequired
    403,            //kForbidden
    404,            //kNotFound
    405,            //kMethodNotAllowed
    406,            //kNotAcceptable
    407,            //kProxyAuthenticationRequired
    408,            //kRequestTimeout
    409,            //kConflict
    410,            //kGone
    411,            //kLengthRequired
    412,            //kPreconditionFailed
    413,            //kRequestEntityTooLarge
    414,            //kRequestURITooLarge
    415,            //kUnsupportedMediaType
    416,            //kRequestRangeNotSatisfiable
    417,            //kExpectationFailed
    500,            //kInternalServerError
    501,            //kNotImplemented
    502,            //kBadGateway
    503,            //kServiceUnavailable
    504,            //kGatewayTimeout
    505     //kHTTPVersionNotSupported
};

StrPtrLen HTTPProtocol::sStatusCodeAsStrings[] =
{
  StrPtrLen("100"),               //kContinue
  StrPtrLen("101"),               //kSwitchingProtocols
  StrPtrLen("200"),               //kOK
  StrPtrLen("201"),               //kCreated
  StrPtrLen("202"),               //kAccepted
  StrPtrLen("203"),               //kNonAuthoritativeInformation
  StrPtrLen("204"),               //kNoContent
  StrPtrLen("205"),               //kResetContent
  StrPtrLen("206"),               //kPartialContent
  StrPtrLen("300"),               //kMultipleChoices
  StrPtrLen("301"),               //kMovedPermanently
  StrPtrLen("302"),               //kFound
  StrPtrLen("303"),               //kSeeOther
  StrPtrLen("304"),               //kNotModified
  StrPtrLen("305"),               //kUseProxy
  StrPtrLen("307"),               //kTemporaryRedirect
  StrPtrLen("400"),               //kBadRequest
  StrPtrLen("401"),               //kUnAuthorized
  StrPtrLen("402"),               //kPaymentRequired
  StrPtrLen("403"),               //kForbidden
  StrPtrLen("404"),               //kNotFound
  StrPtrLen("405"),               //kMethodNotAllowed
  StrPtrLen("406"),               //kNotAcceptable
  StrPtrLen("407"),               //kProxyAuthenticationRequired
  StrPtrLen("408"),               //kRequestTimeout
  StrPtrLen("409"),               //kConflict
  StrPtrLen("410"),               //kGone
  StrPtrLen("411"),               //kLengthRequired
  StrPtrLen("412"),               //kPreconditionFailed
  StrPtrLen("413"),               //kRequestEntityTooLarge
  StrPtrLen("414"),               //kRequestURITooLarge
  StrPtrLen("415"),               //kUnsupportedMediaType
  StrPtrLen("416"),               //kRequestRangeNotSatisfiable
  StrPtrLen("417"),               //kExpectationFailed
  StrPtrLen("500"),               //kInternalServerError
  StrPtrLen("501"),               //kNotImplemented
  StrPtrLen("502"),               //kBadGateway
  StrPtrLen("503"),               //kServiceUnavailable
  StrPtrLen("504"),               //kGatewayTimeout
  StrPtrLen("505")                //kHTTPVersionNotSupported
};

StrPtrLen HTTPProtocol::sVersionStrings[] =
{
    StrPtrLen("HTTP/0.9"),
    StrPtrLen("HTTP/1.0"),
    StrPtrLen("HTTP/1.1")
};

HTTPVersion HTTPProtocol::GetVersion(StrPtrLen* versionStr)
{
    if (versionStr->Len != 8)
            return httpIllegalVersion;
    SInt32 limit = httpNumVersions;
    for (SInt32 x = 0; x < limit; x++)
    {
        if (versionStr->EqualIgnoreCase(sVersionStrings[x].Ptr, sVersionStrings[x].Len))
            return x;
    }
  
    return httpIllegalVersion;
}

