6326 lines
		
	
	
		
			166 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			6326 lines
		
	
	
		
			166 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*++
 | 
						||
 | 
						||
Copyright (c) 1995  Microsoft Corporation
 | 
						||
 | 
						||
Module Name:
 | 
						||
 | 
						||
    headers.cxx
 | 
						||
 | 
						||
Abstract:
 | 
						||
 | 
						||
    Methods for HTTP_HEADERS (..\inc\http.hxx) classes
 | 
						||
 | 
						||
    Contents:
 | 
						||
 | 
						||
        HEADER_STRING::CreateHash
 | 
						||
        HTTP_HEADERS::HeaderMatch
 | 
						||
        HTTP_HEADERS::AllocateHeaders
 | 
						||
        HTTP_HEADERS::FreeHeaders
 | 
						||
        HTTP_HEADERS::CopyHeaders
 | 
						||
        HTTP_HEADERS::FindFreeSlot
 | 
						||
        HTTP_HEADERS::AddHeader
 | 
						||
        HTTP_HEADERS::ReplaceHeader
 | 
						||
        HTTP_HEADERS::FindHeader
 | 
						||
        HTTP_HEADERS::QueryRawHeaders
 | 
						||
        HTTP_HEADERS::AddRequest
 | 
						||
        HTTP_HEADERS::ModifyRequest
 | 
						||
        HTTP_HEADERS::SetRequestVersion
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::AddInternalResponseHeader
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::QueryResponseVersion
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::QueryStatusCode
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::QueryStatusText
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::QueryRawResponseHeaders
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::RemoveAllRequestHeadersByName
 | 
						||
        HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders
 | 
						||
        MapHttpMethodType
 | 
						||
        CreateEscapedUrlPath
 | 
						||
        (CalculateHashNoCase)
 | 
						||
Author:
 | 
						||
 | 
						||
    Richard L Firth (rfirth) 20-Dec-1995
 | 
						||
 | 
						||
Revision History:
 | 
						||
 | 
						||
    20-Dec-1995 rfirth
 | 
						||
        Created
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
#include <wininetp.h>
 | 
						||
#include <perfdiag.hxx>
 | 
						||
#include "httpp.h"
 | 
						||
 | 
						||
//
 | 
						||
// private manifests
 | 
						||
//
 | 
						||
 | 
						||
#define DATE_AND_TIME_STRING_BUFFER_LENGTH  128
 | 
						||
 | 
						||
#ifdef COMPRESSED_HEADERS
 | 
						||
 | 
						||
typedef struct tagHEADER_MAP {
 | 
						||
 | 
						||
    LPSTR   lpszLongHeader;
 | 
						||
    DWORD   dwLenLongHeader;
 | 
						||
    LPSTR   lpszShortHeader;
 | 
						||
    DWORD   dwLenShortHeader;
 | 
						||
 | 
						||
}
 | 
						||
HEADER_MAP;
 | 
						||
 | 
						||
 | 
						||
// Sorted Compression map
 | 
						||
 | 
						||
HEADER_MAP rgsHeaderMap[] = {
 | 
						||
{"",                    0,                                 "",    0              },
 | 
						||
{"Accept",             sizeof("Accept")-1,               "#A", sizeof("#A")-1},
 | 
						||
{"Accept-Charset",     sizeof("Accept-Charset")-1,       "#B", sizeof("#B")-1},
 | 
						||
{"Accept-Encoding",    sizeof("Accept-Encoding")-1,      "#C", sizeof("#C")-1},
 | 
						||
{"Accept-Language",    sizeof("Accept-Language")-1,      "#D", sizeof("#D")-1},
 | 
						||
{"Accept-Ranges",      sizeof("Accept-Ranges")-1,        "#E", sizeof("#E")-1},
 | 
						||
{"Age",                sizeof("Age")-1,                  "#F", sizeof("#F")-1},
 | 
						||
{"Allow",              sizeof("Allow")-1,                "#G", sizeof("#G")-1},
 | 
						||
{"Authorization",      sizeof("Authorization")-1,        "#H", sizeof("#H")-1},
 | 
						||
{"Cache-Control",      sizeof("Cache-Control")-1,        "#I", sizeof("#I")-1},
 | 
						||
{"Connection",         sizeof("Connection")-1,           "#J", sizeof("#J")-1},
 | 
						||
{"Content-Base",       sizeof("Content-Base")-1,         "#K", sizeof("#K")-1},
 | 
						||
{"Content-Encoding",   sizeof("Content-Encoding")-1,     "#L", sizeof("#L")-1},
 | 
						||
{"Content-Language",   sizeof("Content-Language")-1,     "#M", sizeof("#M")-1},
 | 
						||
{"Content-Length",     sizeof("Content-Length")-1,       "#N", sizeof("#N")-1},
 | 
						||
{"Content-Location",   sizeof("Content-Location")-1,     "#O", sizeof("#O")-1},
 | 
						||
{"Content-MD5",        sizeof("Content-MD5")-1,          "#P", sizeof("#P")-1},
 | 
						||
{"Content-Range",      sizeof("Content-Range")-1,        "#Q", sizeof("#Q")-1},
 | 
						||
{"Content-Type",       sizeof("Content-Type")-1,         "#R", sizeof("#R")-1},
 | 
						||
 | 
						||
{"Cookie",             sizeof("Cookie")-1,               "#5", sizeof("#5")-1},
 | 
						||
 | 
						||
{"Date",               sizeof("Date")-1,                 "#S", sizeof("#S")-1},
 | 
						||
{"ETag",               sizeof("ETag")-1,                 "#T", sizeof("#T")-1},
 | 
						||
{"Expires",            sizeof("Expires")-1,              "#U", sizeof("#U")-1},
 | 
						||
{"From",               sizeof("From")-1,                 "#V", sizeof("#V")-1},
 | 
						||
{"Host",               sizeof("Host")-1,                 "#W", sizeof("#W")-1},
 | 
						||
{"If-Modified-Since",  sizeof("If-Modified-Since")-1,    "#X", sizeof("#X")-1},
 | 
						||
{"If-Match",           sizeof("If-Match")-1,             "#Y", sizeof("#Y")-1},
 | 
						||
{"If-None-Match",      sizeof("If-None-Match")-1,        "#Z", sizeof("#Z")-1},
 | 
						||
{"If-Range",           sizeof("If-Range")-1,             "#a", sizeof("#a")-1},
 | 
						||
{"If-Unmodified-Since",sizeof("If-Unmodified-Since")-1,  "#b", sizeof("#b")-1},
 | 
						||
{"Last-Modified",      sizeof("Last-Modified")-1,        "#c", sizeof("#c")-1},
 | 
						||
{"Location",           sizeof("Location")-1,             "#d", sizeof("#d")-1},
 | 
						||
{"Max-Forwards",       sizeof("Max-Forwards")-1,         "#e", sizeof("#e")-1},
 | 
						||
{"Pragma",             sizeof("Pragma")-1,               "#f", sizeof("#f")-1},
 | 
						||
{"Proxy-Authenticate", sizeof("Proxy-Authenticate")-1,   "#g", sizeof("#g")-1},
 | 
						||
{"Proxy-Authorization",sizeof("Proxy-Authorization")-1,  "#h", sizeof("#h")-1},
 | 
						||
{"Public",             sizeof("Public")-1,               "#I", sizeof("#I")-1},
 | 
						||
{"Range",              sizeof("Range")-1,                "#j", sizeof("#j")-1},
 | 
						||
{"Referer",            sizeof("Referer")-1,              "#k", sizeof("#k")-1},
 | 
						||
{"Retry-After",        sizeof("Retry-After")-1,          "#l", sizeof("#l")-1},
 | 
						||
{"Server",             sizeof("Server")-1,               "#m", sizeof("#m")-1},
 | 
						||
{"Transfer-Encoding",  sizeof("Transfer-Encoding")-1,    "#n", sizeof("#n")-1},
 | 
						||
 | 
						||
{"UA-color",           sizeof("UA-color")-1,             "#1", sizeof("#1")-1},
 | 
						||
{"UA-cpu",             sizeof("UA-cpu")-1,               "#2", sizeof("#2")-1},
 | 
						||
{"UA-OS",              sizeof("UA-OS")-1,                "#3", sizeof("#3")-1},
 | 
						||
{"UA-pixels",          sizeof("UA-pixels")-1,            "#4", sizeof("#4")-1},
 | 
						||
 | 
						||
{"Upgrade",            sizeof("Upgrade")-1,              "#o", sizeof("#o")-1},
 | 
						||
{"User-Agent",         sizeof("User-Agent")-1,           "#p", sizeof("#p")-1},
 | 
						||
{"Vary",               sizeof("Vary")-1,                 "#q", sizeof("#q")-1},
 | 
						||
{"Via",                sizeof("Via")-1,                  "#r", sizeof("#r")-1},
 | 
						||
{"Warning",            sizeof("Warning")-1,              "#s", sizeof("#s")-1},
 | 
						||
{"WWW-Authenticate",   sizeof("WWW-Authenticate")-1,     "#t", sizeof("#t")-1}
 | 
						||
};
 | 
						||
 | 
						||
 | 
						||
#endif //COMPRESSED_HEADERS
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
//
 | 
						||
// Private functions
 | 
						||
//
 | 
						||
 | 
						||
PRIVATE
 | 
						||
BOOL
 | 
						||
FMatchList(
 | 
						||
    LPSTR *lplpList,
 | 
						||
    DWORD cListLen,
 | 
						||
    HEADER_STRING *lpHeader,
 | 
						||
    LPSTR    lpBase
 | 
						||
    );
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
//
 | 
						||
// external functions
 | 
						||
//
 | 
						||
 | 
						||
extern
 | 
						||
BOOL
 | 
						||
HttpDateToSystemTime(
 | 
						||
    IN LPSTR lpszHttpDate,
 | 
						||
    OUT LPSYSTEMTIME lpSystemTime
 | 
						||
    );
 | 
						||
 | 
						||
#ifdef COMPRESSED_HEADERS
 | 
						||
DWORD
 | 
						||
LookupHeadermap(
 | 
						||
    LPSTR   lpszHeader
 | 
						||
);
 | 
						||
 | 
						||
extern BOOL vfCompressedHeaders;
 | 
						||
#endif //COMPRESSED_HEADERS
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
FASTCALL
 | 
						||
CalculateHashNoCase(
 | 
						||
    IN LPSTR lpszString,
 | 
						||
    IN DWORD dwStringLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Calculate a case-insensitive hash number given a string. Assumes input is
 | 
						||
    7-bit ASCII
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszString      - string to hash
 | 
						||
 | 
						||
    dwStringLength  - length of lpszString, or -1 if we need to calculate
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD - a generated hash value
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DWORD dwHash = HEADER_HASH_SEED;
 | 
						||
 | 
						||
    while (dwStringLength != 0) {
 | 
						||
        CHAR ch = *lpszString;
 | 
						||
 | 
						||
        if ((ch >= 'A') && (ch <= 'Z')) {
 | 
						||
            ch = MAKE_LOWER(ch);
 | 
						||
        }
 | 
						||
        dwHash += (DWORD)(dwHash << 5) + ch; /*+ *pszName++;*/
 | 
						||
 | 
						||
        ++lpszString;
 | 
						||
        --dwStringLength;
 | 
						||
    }
 | 
						||
    return dwHash;
 | 
						||
}
 | 
						||
 | 
						||
//
 | 
						||
// methods
 | 
						||
//
 | 
						||
 | 
						||
VOID
 | 
						||
inline
 | 
						||
HEADER_STRING::CreateHash(
 | 
						||
    LPSTR lpszBase
 | 
						||
    )
 | 
						||
{
 | 
						||
    DWORD i = 0;
 | 
						||
    LPSTR string = StringAddress(lpszBase);
 | 
						||
 | 
						||
    while ((i < (DWORD)StringLength())
 | 
						||
           && !((string[i] == ':')
 | 
						||
                || (string[i] == ' ')
 | 
						||
                || (string[i] == '\r')
 | 
						||
                || (string[i] == '\n'))) {
 | 
						||
        ++i;
 | 
						||
    }
 | 
						||
    m_Hash = CalculateHashNoCase(string, i);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::AllocateHeaders(
 | 
						||
    IN DWORD dwNumberOfHeaders
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Allocates or grows the array of header pointers (HEADER_STRING objects)
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    dwNumberOfHeaders   - number of additional header slots to create
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "AllocateHeaders",
 | 
						||
                 "%d",
 | 
						||
                 dwNumberOfHeaders
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(AllocateHeaders);
 | 
						||
 | 
						||
    //
 | 
						||
    // we really need to be able to realloc an array of HEADER_STRING objects
 | 
						||
    // (see below)
 | 
						||
    //
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
    DWORD slots = _TotalSlots;
 | 
						||
 | 
						||
 | 
						||
    if ( (_TotalSlots + dwNumberOfHeaders) >  (INVALID_HEADER_INDEX-1))
 | 
						||
    {
 | 
						||
        INET_ASSERT(FALSE);
 | 
						||
        _NextOpenSlot = 0;
 | 
						||
        _TotalSlots = 0;
 | 
						||
        _FreeSlots = 0;
 | 
						||
        error = ERROR_NOT_ENOUGH_MEMORY;
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
 | 
						||
    _lpHeaders = (HEADER_STRING *)ResizeBuffer((HLOCAL)_lpHeaders,
 | 
						||
                                               (_TotalSlots + dwNumberOfHeaders)
 | 
						||
                                                    * sizeof(HEADER_STRING),
 | 
						||
                                               FALSE // not moveable
 | 
						||
                                               );
 | 
						||
    if (_lpHeaders != NULL) {
 | 
						||
        _NextOpenSlot = _TotalSlots;
 | 
						||
        _TotalSlots += dwNumberOfHeaders;
 | 
						||
        _FreeSlots += dwNumberOfHeaders;
 | 
						||
 | 
						||
        //
 | 
						||
        // this is slightly ugly, but it seems there's no easy C++ way to
 | 
						||
        // do this - we need to be able to realloc() an array of objects
 | 
						||
        // created by new(), but so far, it can't be done
 | 
						||
        //
 | 
						||
 | 
						||
        for (; slots < _TotalSlots; ++slots) {
 | 
						||
            _lpHeaders[slots].Clear();
 | 
						||
        }
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    } else {
 | 
						||
 | 
						||
        INET_ASSERT(FALSE);
 | 
						||
        _NextOpenSlot = 0;
 | 
						||
        _TotalSlots = 0;
 | 
						||
        _FreeSlots = 0;
 | 
						||
        error = ERROR_NOT_ENOUGH_MEMORY;
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    INET_ASSERT(_FreeSlots <= _TotalSlots);
 | 
						||
 | 
						||
    PERF_LEAVE(AllocateHeaders);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_HEADERS::FreeHeaders(
 | 
						||
    VOID
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Free the headers strings and the headers array
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "FreeHeaders",
 | 
						||
                 NULL
 | 
						||
                 ));
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    //
 | 
						||
    // free up each individual entry (free string buffers)
 | 
						||
    //
 | 
						||
 | 
						||
    for (DWORD i = 0; i < _TotalSlots; ++i) {
 | 
						||
        _lpHeaders[i] = (LPSTR)NULL;
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // followed by the array itself
 | 
						||
    //
 | 
						||
 | 
						||
    if (_lpHeaders) {
 | 
						||
        _lpHeaders = (HEADER_STRING *)FREE_MEMORY((HLOCAL)_lpHeaders);
 | 
						||
 | 
						||
        INET_ASSERT(_lpHeaders == NULL);
 | 
						||
    }
 | 
						||
 | 
						||
    _TotalSlots = 0;
 | 
						||
    _FreeSlots = 0;
 | 
						||
    _HeadersLength = 0;
 | 
						||
    _lpszVerb = NULL;
 | 
						||
    _dwVerbLength = 0;
 | 
						||
    _lpszObjectName = NULL;
 | 
						||
    _dwObjectNameLength = 0;
 | 
						||
    _lpszVersion = NULL;
 | 
						||
    _dwVersionLength = 0;
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_HEADERS::CopyHeaders(
 | 
						||
    IN OUT LPSTR * lpBuffer,
 | 
						||
    IN LPSTR lpszObjectName,
 | 
						||
    IN DWORD dwObjectNameLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Copy the headers to the caller's buffer. Each header is terminated by CR-LF.
 | 
						||
    This method is called to convert the request headers list to a buffer that
 | 
						||
    we can send to the server
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBuffer            - pointer to pointer to buffer where headers are
 | 
						||
                          written. We update the pointer
 | 
						||
 | 
						||
    lpszObjectName      - optional object name
 | 
						||
 | 
						||
    dwObjectNameLength  - optional object name length
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "CopyHeaders",
 | 
						||
                 "%#x, %#x [%q], %d",
 | 
						||
                 lpBuffer,
 | 
						||
                 lpszObjectName,
 | 
						||
                 lpszObjectName,
 | 
						||
                 dwObjectNameLength
 | 
						||
                 ));
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    DWORD i = 0;
 | 
						||
 | 
						||
    if (lpszObjectName != NULL) {
 | 
						||
        memcpy(*lpBuffer, _lpszVerb, _dwVerbLength);
 | 
						||
        *lpBuffer += _dwVerbLength;
 | 
						||
        *(*lpBuffer)++ = ' ';
 | 
						||
        memcpy(*lpBuffer, lpszObjectName, dwObjectNameLength);
 | 
						||
        *lpBuffer += dwObjectNameLength;
 | 
						||
        *(*lpBuffer)++ = ' ';
 | 
						||
        memcpy(*lpBuffer, _lpszVersion, _dwVersionLength);
 | 
						||
        *lpBuffer += _dwVersionLength;
 | 
						||
        *(*lpBuffer)++ = '\r';
 | 
						||
        *(*lpBuffer)++ = '\n';
 | 
						||
        i = 1;
 | 
						||
    }
 | 
						||
    for (; i < _TotalSlots; ++i) {
 | 
						||
        if (_lpHeaders[i].HaveString()) {
 | 
						||
            _lpHeaders[i].CopyTo(*lpBuffer);
 | 
						||
            *lpBuffer += _lpHeaders[i].StringLength();
 | 
						||
            *(*lpBuffer)++ = '\r';
 | 
						||
            *(*lpBuffer)++ = '\n';
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef COMPRESSED_HEADERS
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_HEADERS::CopyCompressedHeaders(
 | 
						||
    IN OUT LPSTR * lpBuffer
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Copy the headers to the caller's buffer. Each header is terminated by CR-LF.
 | 
						||
    This method is called to convert the request headers list to a buffer that
 | 
						||
    we can send to the server
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBuffer    - pointer to pointer to buffer where headers are written. We
 | 
						||
                  update the pointer
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "CopyCompressedHeaders",
 | 
						||
                 "%#x",
 | 
						||
                 lpBuffer
 | 
						||
                 ));
 | 
						||
    LPSTR   lpszHeaderString;
 | 
						||
    DWORD j;
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    for (DWORD i = 0; i < _TotalSlots; ++i) {
 | 
						||
        if (_lpHeaders[i].HaveString()) {
 | 
						||
 | 
						||
            lpszHeaderString = _lpHeaders[i].StringAddress(NULL);
 | 
						||
 | 
						||
            j = LookupHeadermap(lpszHeaderString);
 | 
						||
 | 
						||
            if (j) {
 | 
						||
                // copy the corresponding short header and bump the pointer
 | 
						||
                // ahead
 | 
						||
 | 
						||
 | 
						||
                DEBUG_PRINT(
 | 
						||
                    HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("Compressed:%s using %s\n",
 | 
						||
                        lpszHeaderString,
 | 
						||
                        rgsHeaderMap[j].lpszShortHeader
 | 
						||
                    )
 | 
						||
                );
 | 
						||
 | 
						||
                strcpy(*lpBuffer, rgsHeaderMap[j].lpszShortHeader);
 | 
						||
 | 
						||
                *lpBuffer += rgsHeaderMap[j].dwLenShortHeader;
 | 
						||
 | 
						||
                // then copy the value of the header
 | 
						||
                // and bump the destination further by the right amount
 | 
						||
                strncpy(*lpBuffer,
 | 
						||
                        lpszHeaderString + rgsHeaderMap[j].dwLenLongHeader,
 | 
						||
                        _lpHeaders[i].StringLength() - rgsHeaderMap[j].dwLenLongHeader
 | 
						||
                );
 | 
						||
 | 
						||
                *lpBuffer += (_lpHeaders[i].StringLength() - rgsHeaderMap[j].dwLenLongHeader);
 | 
						||
 | 
						||
            }
 | 
						||
            else {
 | 
						||
 | 
						||
                // No match found, must be a header we don't know about
 | 
						||
                DEBUG_PRINT(
 | 
						||
                    HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("Couldn't compress header for %s\n",
 | 
						||
                        lpszHeaderString
 | 
						||
                    )
 | 
						||
                );
 | 
						||
 | 
						||
                _lpHeaders[i].CopyTo(*lpBuffer);
 | 
						||
                *lpBuffer += _lpHeaders[i].StringLength();
 | 
						||
 | 
						||
            }
 | 
						||
 | 
						||
            *(*lpBuffer)++ = '\r';
 | 
						||
            *(*lpBuffer)++ = '\n';
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
}
 | 
						||
#endif //COMPRESSED_HEADERS
 | 
						||
 | 
						||
 | 
						||
HEADER_STRING *
 | 
						||
FASTCALL
 | 
						||
HTTP_HEADERS::FindFreeSlot(
 | 
						||
    DWORD* piSlot
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds the next free slot in the headers list, or adds some new slots
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    piSlot: returns index of slot found
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    HEADER_STRING *  - pointer to next free slot
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Pointer,
 | 
						||
                 "FindFreeSlot",
 | 
						||
                 NULL
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(FindFreeSlot);
 | 
						||
 | 
						||
    DWORD i;
 | 
						||
    DWORD error;
 | 
						||
    HEADER_STRING * header = NULL;
 | 
						||
 | 
						||
    //
 | 
						||
    // if there are no free slots, allocate some more
 | 
						||
    //
 | 
						||
 | 
						||
    if (_FreeSlots == 0) {
 | 
						||
        i = _TotalSlots;
 | 
						||
        error = AllocateHeaders(HEADERS_INCREMENT);
 | 
						||
    } else {
 | 
						||
        i = 0;
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
        if (!_lpHeaders[_NextOpenSlot].HaveString())
 | 
						||
        {
 | 
						||
            --_FreeSlots;
 | 
						||
            header = &_lpHeaders[_NextOpenSlot];
 | 
						||
            *piSlot = _NextOpenSlot;
 | 
						||
            _NextOpenSlot = (_NextOpenSlot == (_TotalSlots-1)) ? (_TotalSlots-1) : _NextOpenSlot++;
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    if (error == ERROR_SUCCESS) {
 | 
						||
        for (; i < _TotalSlots; ++i) {
 | 
						||
            if (!_lpHeaders[i].HaveString()) {
 | 
						||
                --_FreeSlots;
 | 
						||
                header = &_lpHeaders[i];
 | 
						||
                *piSlot = i;
 | 
						||
                _NextOpenSlot = (i == (_TotalSlots-1)) ? (_TotalSlots-1) : (i+1);
 | 
						||
                break;
 | 
						||
            }
 | 
						||
        }
 | 
						||
        if (header == NULL) {
 | 
						||
 | 
						||
            //
 | 
						||
            // we would have just allocated extra slots if we didn't have
 | 
						||
            // any, so we shouldn't be here
 | 
						||
            //
 | 
						||
 | 
						||
            INET_ASSERT(FALSE);
 | 
						||
 | 
						||
            error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
    _Error = error;
 | 
						||
 | 
						||
    PERF_LEAVE(FindFreeSlot);
 | 
						||
 | 
						||
    DEBUG_LEAVE(header);
 | 
						||
 | 
						||
    return header;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_HEADERS::ShrinkHeader(
 | 
						||
    IN LPBYTE pbBase,
 | 
						||
    IN DWORD  iSlot,
 | 
						||
    IN DWORD  dwOldQueryIndex,
 | 
						||
    IN DWORD  dwNewQueryIndex,
 | 
						||
    IN DWORD  cbNewSize
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Low level function that does a surgical replace of one header with another.
 | 
						||
    This code updates internal structures such as bKnownHeaders and the stored
 | 
						||
    hash value for the new Header.
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    HEADER_STRING* pHeader = _lpHeaders + iSlot;
 | 
						||
 | 
						||
    INET_ASSERT(_bKnownHeaders[dwOldQueryIndex] == (BYTE) iSlot ||
 | 
						||
                dwNewQueryIndex == dwOldQueryIndex );
 | 
						||
 | 
						||
    //
 | 
						||
    // Swap in the new header.  Update Length, Hash, and its cached position
 | 
						||
    //  in the known header array.
 | 
						||
    //
 | 
						||
 | 
						||
    _bKnownHeaders[dwOldQueryIndex] = INVALID_HEADER_INDEX;
 | 
						||
    _bKnownHeaders[dwNewQueryIndex] = (BYTE) iSlot;
 | 
						||
 | 
						||
    pHeader->SetLength (cbNewSize);
 | 
						||
    pHeader->SetHash (GlobalKnownHeaders[dwNewQueryIndex].HashVal);
 | 
						||
}
 | 
						||
 | 
						||
DWORD
 | 
						||
inline
 | 
						||
HTTP_HEADERS::SlowFind(
 | 
						||
    IN LPSTR lpBase,
 | 
						||
    IN LPSTR lpszHeaderName,
 | 
						||
    IN DWORD dwHeaderNameLength,
 | 
						||
    IN DWORD dwIndex,
 | 
						||
    IN DWORD dwHash,
 | 
						||
    OUT DWORD *lpdwQueryIndex,
 | 
						||
    OUT BYTE  **lplpbPrevIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds the next occurance of lpszHeaderName in the header array, uses
 | 
						||
    a cached table of well known headers to accerlate the search if the
 | 
						||
    string is a known header.
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD  - index to Slot in array, or INVALID_HEADER_SLOT if not found
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
 | 
						||
    //
 | 
						||
    // Now see if this is a known header passed in as a string,
 | 
						||
    //   If it is, we save ourselves the loop, and just map it right in to a known header
 | 
						||
    //
 | 
						||
 | 
						||
    DWORD dwKnownQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
 | 
						||
 | 
						||
    *lpdwQueryIndex = INVALID_HEADER_SLOT;
 | 
						||
 | 
						||
    if ( dwKnownQueryIndex != 0 )
 | 
						||
    {
 | 
						||
        dwKnownQueryIndex--;
 | 
						||
 | 
						||
        if ( ((int)dwHeaderNameLength >= GlobalKnownHeaders[dwKnownQueryIndex].Length) &&
 | 
						||
             strnicmp(lpszHeaderName,
 | 
						||
                      GlobalKnownHeaders[dwKnownQueryIndex].Text,
 | 
						||
                      GlobalKnownHeaders[dwKnownQueryIndex].Length) == 0)
 | 
						||
        {
 | 
						||
            *lpdwQueryIndex = dwKnownQueryIndex;
 | 
						||
 | 
						||
            INET_ASSERT((int)(dwHeaderNameLength) == GlobalKnownHeaders[dwKnownQueryIndex].Length);
 | 
						||
 | 
						||
            if ( lplpbPrevIndex )
 | 
						||
            {
 | 
						||
                return FastNukeFind(
 | 
						||
                        dwKnownQueryIndex,
 | 
						||
                        dwIndex,
 | 
						||
                        lplpbPrevIndex
 | 
						||
                        );
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                return FastFind(
 | 
						||
                        dwKnownQueryIndex,
 | 
						||
                        dwIndex
 | 
						||
                        );
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // Otherwise we painfully enumerate the whole array of headers
 | 
						||
    //
 | 
						||
 | 
						||
    for (DWORD i = 0; i < _TotalSlots; ++i)
 | 
						||
    {
 | 
						||
        HEADER_STRING * pString;
 | 
						||
 | 
						||
        pString = &_lpHeaders[i];
 | 
						||
 | 
						||
        if (!pString->HaveString()) {
 | 
						||
            continue;
 | 
						||
        }
 | 
						||
 | 
						||
        if (pString->HashStrnicmp(lpBase,
 | 
						||
                                  lpszHeaderName,
 | 
						||
                                  dwHeaderNameLength,
 | 
						||
                                  dwHash) == 0)
 | 
						||
        {
 | 
						||
 | 
						||
            //
 | 
						||
            // if we haven't reached the required index yet, continue
 | 
						||
            //
 | 
						||
 | 
						||
            if (dwIndex != 0) {
 | 
						||
                --dwIndex;
 | 
						||
                continue;
 | 
						||
            }
 | 
						||
 | 
						||
            return i; // found index/slot
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    return INVALID_HEADER_SLOT; // not found
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
inline
 | 
						||
HTTP_HEADERS::FastFind(
 | 
						||
    IN DWORD  dwQueryIndex,
 | 
						||
    IN DWORD  dwIndex
 | 
						||
    )
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds the next occurance of a known header string in the lpHeaders array.
 | 
						||
    Since this is a known string, an index is used to refer to it.
 | 
						||
    A cached table of well known headers is used to accerlate the search.
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD  - index to Slot in array, or INVALID_HEADER_SLOT if not found
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DWORD dwSlot;
 | 
						||
 | 
						||
    dwSlot = _bKnownHeaders[dwQueryIndex];
 | 
						||
 | 
						||
    while ( (dwIndex > 0) && (dwSlot < INVALID_HEADER_INDEX) )
 | 
						||
    {
 | 
						||
        HEADER_STRING * pString;
 | 
						||
 | 
						||
        pString = &_lpHeaders[dwSlot];
 | 
						||
        dwSlot  = pString->GetNextKnownIndex();
 | 
						||
 | 
						||
        dwIndex--;
 | 
						||
    }
 | 
						||
 | 
						||
    if ( dwSlot >= INVALID_HEADER_INDEX)
 | 
						||
    {
 | 
						||
        return INVALID_HEADER_SLOT;
 | 
						||
    }
 | 
						||
 | 
						||
    return dwSlot; // found it.
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
inline
 | 
						||
HTTP_HEADERS::FastNukeFind(
 | 
						||
    IN DWORD  dwQueryIndex,
 | 
						||
    IN DWORD  dwIndex,
 | 
						||
    OUT BYTE **lplpbPrevIndex
 | 
						||
    )
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds the next occurance of a known header string in the lpHeaders array.
 | 
						||
    Since this is a known string, an index is used to refer to it.
 | 
						||
    A cached table of well known headers is used to accerlate the search.
 | 
						||
    Also provides a ptr to ptr to the slot which directs us to the one found.
 | 
						||
    This is needed for deletion purposes.
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD  - index to Slot in array, or INVALID_HEADER_SLOT if not found
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    BYTE *lpbSlot;
 | 
						||
 | 
						||
    *lplpbPrevIndex = lpbSlot = &_bKnownHeaders[dwQueryIndex];
 | 
						||
    dwIndex++;
 | 
						||
 | 
						||
    while ( (dwIndex > 0) && (*lpbSlot < INVALID_HEADER_INDEX) )
 | 
						||
    {
 | 
						||
        HEADER_STRING * pString;
 | 
						||
 | 
						||
        pString = &_lpHeaders[*lpbSlot];
 | 
						||
        *lplpbPrevIndex = lpbSlot;
 | 
						||
        lpbSlot  = pString->GetNextKnownIndexPtr();
 | 
						||
 | 
						||
        dwIndex--;
 | 
						||
    }
 | 
						||
 | 
						||
    if ( **lplpbPrevIndex >= INVALID_HEADER_INDEX ||
 | 
						||
         dwIndex > 0 )
 | 
						||
    {
 | 
						||
        return INVALID_HEADER_SLOT;
 | 
						||
    }
 | 
						||
 | 
						||
    return ((DWORD) **lplpbPrevIndex); // found it.
 | 
						||
}
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_HEADERS::RemoveAllByIndex(
 | 
						||
    IN DWORD dwQueryIndex
 | 
						||
    )
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Removes all Known Headers found in the header array.
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    dwQueryIndex - index to known header string to remove from array.
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
 | 
						||
{
 | 
						||
    BYTE bSlot;
 | 
						||
    BYTE bPrevSlot;
 | 
						||
 | 
						||
    bSlot = bPrevSlot  = _bKnownHeaders[dwQueryIndex];
 | 
						||
 | 
						||
    while (bSlot < INVALID_HEADER_INDEX)
 | 
						||
    {
 | 
						||
        HEADER_STRING * pString;
 | 
						||
 | 
						||
        bPrevSlot   = bSlot;
 | 
						||
        pString     = &_lpHeaders[bSlot];
 | 
						||
        bSlot       = (BYTE) pString->GetNextKnownIndex();
 | 
						||
 | 
						||
        RemoveHeader(bPrevSlot, dwQueryIndex, &_bKnownHeaders[dwQueryIndex]);
 | 
						||
    }
 | 
						||
 | 
						||
    _bKnownHeaders[dwQueryIndex] = INVALID_HEADER_INDEX;
 | 
						||
 | 
						||
    return;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
BOOL
 | 
						||
inline
 | 
						||
HTTP_HEADERS::HeaderMatch(
 | 
						||
    IN DWORD dwHash,
 | 
						||
    IN LPSTR lpszHeaderName,
 | 
						||
    IN DWORD dwHeaderNameLength,
 | 
						||
    OUT DWORD *lpdwQueryIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Looks up a Known HTTP header string using its Hash value and
 | 
						||
     string contained the name of the header.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    dwHash              - Hash value of header name string
 | 
						||
 | 
						||
    lpszHeaderName      - name of header we are matching
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of header name string
 | 
						||
 | 
						||
    lpdwQueryIndex      - If found, this is the HTTP_QUERY_* based index to the header.
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    BOOL
 | 
						||
        Success - The string and hash matched againsted a known header
 | 
						||
 | 
						||
        Failure - There is no known header for that hash & string pair.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    *lpdwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
 | 
						||
 | 
						||
    if ( *lpdwQueryIndex != 0 )
 | 
						||
    {
 | 
						||
        (*lpdwQueryIndex)--;
 | 
						||
 | 
						||
        if ( ((int)dwHeaderNameLength == GlobalKnownHeaders[*lpdwQueryIndex].Length) &&
 | 
						||
             strnicmp(lpszHeaderName,
 | 
						||
                      GlobalKnownHeaders[*lpdwQueryIndex].Text,
 | 
						||
                      GlobalKnownHeaders[*lpdwQueryIndex].Length) == 0)
 | 
						||
        {
 | 
						||
            return TRUE;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    return FALSE;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
BYTE
 | 
						||
inline
 | 
						||
HTTP_HEADERS::FastAdd(
 | 
						||
    IN DWORD  dwQueryIndex,
 | 
						||
    IN DWORD  dwSlot
 | 
						||
    )
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Rapidly adds a known string to the header array, this function
 | 
						||
     is used to matain coherency of the _bKnownHeaders which
 | 
						||
     contained indexed offsets into the header array for known headers.
 | 
						||
 | 
						||
    Note that this function is used instead of latter listed below
 | 
						||
     in order to maintain proper order in headers received.
 | 
						||
 | 
						||
    N.B. This function MUST be called with the headers already locked
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    dwQueryIndex - index to known header string to remove from array.
 | 
						||
 | 
						||
    dwSlot - Slot in which this header is being added.
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
 | 
						||
{
 | 
						||
    BYTE *lpbSlot;
 | 
						||
 | 
						||
    lpbSlot = &_bKnownHeaders[dwQueryIndex];
 | 
						||
 | 
						||
    while ( (*lpbSlot < INVALID_HEADER_INDEX) )
 | 
						||
    {
 | 
						||
        HEADER_STRING * pString;
 | 
						||
 | 
						||
        pString  = &_lpHeaders[*lpbSlot];
 | 
						||
        lpbSlot  = pString->GetNextKnownIndexPtr();
 | 
						||
    }
 | 
						||
 | 
						||
    INET_ASSERT(*lpbSlot == INVALID_HEADER_INDEX);
 | 
						||
 | 
						||
    *lpbSlot = (BYTE) dwSlot;
 | 
						||
    return INVALID_HEADER_INDEX;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
//BYTE
 | 
						||
//inline
 | 
						||
//HTTP_HEADERS::FastAdd(
 | 
						||
//    IN DWORD  dwQueryIndex,
 | 
						||
//    IN DWORD  dwSlot
 | 
						||
//    )
 | 
						||
//{
 | 
						||
//    BYTE bOldSlot;
 | 
						||
//
 | 
						||
//    bOldSlot = _bKnownHeaders[dwQueryIndex];
 | 
						||
//    _bKnownHeaders[dwQueryIndex] = (BYTE) dwSlot;
 | 
						||
//
 | 
						||
//    return bOldSlot;
 | 
						||
//}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::AddHeader(
 | 
						||
    IN LPSTR lpszHeaderName,
 | 
						||
    IN DWORD dwHeaderNameLength,
 | 
						||
    IN LPSTR lpszHeaderValue,
 | 
						||
    IN DWORD dwHeaderValueLength,
 | 
						||
    IN DWORD dwIndex,
 | 
						||
    IN DWORD dwFlags
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Adds a single header to the array of headers, given the header name and
 | 
						||
    value. Called via HttpOpenRequest()
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeaderName      - pointer to name of header to add, e.g. "Accept:"
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of the header name
 | 
						||
 | 
						||
    lpszHeaderValue     - pointer to value of header to add, e.g. "text/html"
 | 
						||
 | 
						||
    dwHeaderValueLength - length of the header value
 | 
						||
 | 
						||
    dwIndex             - if coalescing headers, index of header to update
 | 
						||
 | 
						||
    dwFlags             - flags controlling function:
 | 
						||
 | 
						||
                            COALESCE_HEADER_WITH_COMMA
 | 
						||
                            COALESCE_HEADER_WITH_SEMICOLON
 | 
						||
                                - headers of the same name can be combined
 | 
						||
 | 
						||
                            CLEAN_HEADER
 | 
						||
                                - header is supplied by user, so we must ensure
 | 
						||
                                  it has correct format
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
                    Ran out of memory allocating string
 | 
						||
 | 
						||
                  ERROR_INVALID_PARAMETER
 | 
						||
                    The header value was bad
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "AddHeader",
 | 
						||
                 "%.*q, %d, %.*q, %d, %d, %#x",
 | 
						||
                 min(dwHeaderNameLength + 1, 80),
 | 
						||
                 lpszHeaderName,
 | 
						||
                 dwHeaderNameLength,
 | 
						||
                 min(dwHeaderValueLength + 1, 80),
 | 
						||
                 lpszHeaderValue,
 | 
						||
                 dwHeaderValueLength,
 | 
						||
                 dwIndex,
 | 
						||
                 dwFlags
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(AddHeader);
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    INET_ASSERT(lpszHeaderName != NULL);
 | 
						||
    INET_ASSERT(*lpszHeaderName != '\0');
 | 
						||
    INET_ASSERT(dwHeaderNameLength != 0);
 | 
						||
    INET_ASSERT(lpszHeaderValue != NULL);
 | 
						||
    INET_ASSERT(*lpszHeaderValue != '\0');
 | 
						||
    INET_ASSERT(dwHeaderValueLength != 0);
 | 
						||
    INET_ASSERT(_FreeSlots <= _TotalSlots);
 | 
						||
 | 
						||
    //
 | 
						||
    // we may have been handed a header with a trailing colon. We don't care
 | 
						||
    // for such nasty imagery
 | 
						||
    //
 | 
						||
 | 
						||
    if (lpszHeaderName[dwHeaderNameLength - 1] == ':') {
 | 
						||
        --dwHeaderNameLength;
 | 
						||
    }
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
    DWORD dwQueryIndex;
 | 
						||
    DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
 | 
						||
 | 
						||
    DWORD i = 0;
 | 
						||
 | 
						||
    //
 | 
						||
    // if we are coalescing headers then find a header with the same name
 | 
						||
    //
 | 
						||
 | 
						||
    if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
 | 
						||
        (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
 | 
						||
    {
 | 
						||
        DWORD dwSlot;
 | 
						||
 | 
						||
        dwSlot = SlowFind(
 | 
						||
                    NULL,
 | 
						||
                    lpszHeaderName,
 | 
						||
                    dwHeaderNameLength,
 | 
						||
                    dwIndex,
 | 
						||
                    dwHash,
 | 
						||
                    &dwQueryIndex,
 | 
						||
                    NULL
 | 
						||
                    );
 | 
						||
 | 
						||
        if (dwSlot != ((DWORD) -1))
 | 
						||
        {
 | 
						||
 | 
						||
            HEADER_STRING * pString;
 | 
						||
 | 
						||
            pString = &_lpHeaders[dwSlot];
 | 
						||
 | 
						||
            //
 | 
						||
            // found what we are looking for. Coalesce it
 | 
						||
            //
 | 
						||
 | 
						||
            pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
 | 
						||
 | 
						||
            pString->Strncat(
 | 
						||
                             (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
 | 
						||
                                 "; " :
 | 
						||
                                 ", ",
 | 
						||
                              2);
 | 
						||
 | 
						||
            pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
 | 
						||
            _HeadersLength += 2 + dwHeaderValueLength;
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
 | 
						||
        //
 | 
						||
        // Check to verify that the header we're adding is a known header,
 | 
						||
        //   If its a known header we use dwQueryIndex to update the known header array
 | 
						||
        //   otherwise, IF ITS NOT, we make sure to set dwQueryIndex to INVALID_...
 | 
						||
        //
 | 
						||
 | 
						||
        if (! HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwQueryIndex) )
 | 
						||
        {
 | 
						||
            dwQueryIndex = INVALID_HEADER_SLOT;
 | 
						||
        }
 | 
						||
 | 
						||
        /*
 | 
						||
        // Perhaps this more efficent ???
 | 
						||
        dwQueryIndex = GlobalHeaderHashs[(dwHash % MAX_HEADER_HASH_SIZE)];
 | 
						||
 | 
						||
        if ( dwQueryIndex != 0 )
 | 
						||
        {
 | 
						||
            dwQueryIndex--;
 | 
						||
 | 
						||
            if ( ((int)dwHeaderNameLength < GlobalKnownHeaders[dwQueryIndex].Length) ||
 | 
						||
                 strnicmp(lpszHeaderName,
 | 
						||
                          GlobalKnownHeaders[dwQueryIndex].Text,
 | 
						||
                          GlobalKnownHeaders[dwQueryIndex].Length) != 0)
 | 
						||
            {
 | 
						||
                dwQueryIndex = INVALID_HEADER_SLOT;
 | 
						||
            }
 | 
						||
        }
 | 
						||
        else
 | 
						||
        {
 | 
						||
            dwQueryIndex = INVALID_HEADER_SLOT;
 | 
						||
        }
 | 
						||
        */
 | 
						||
    }
 | 
						||
 | 
						||
 | 
						||
    //
 | 
						||
    // if we didn't find the header value or we are not coalescing then add the
 | 
						||
    // header
 | 
						||
    //
 | 
						||
 | 
						||
    if (error == ERROR_HTTP_HEADER_NOT_FOUND)
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // find the next slot for this header
 | 
						||
        //
 | 
						||
 | 
						||
        HEADER_STRING * freeHeader;
 | 
						||
        DWORD iSlot;
 | 
						||
 | 
						||
        freeHeader = FindFreeSlot(&iSlot);
 | 
						||
        if (freeHeader == NULL) {
 | 
						||
            error = GetError();
 | 
						||
 | 
						||
            INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
 | 
						||
                                       dwHeaderNameLength,
 | 
						||
                                       dwHeaderNameLength
 | 
						||
                                       + sizeof(": ") - 1
 | 
						||
                                       + dwHeaderValueLength
 | 
						||
                                       + 1 // for extra NULL terminator
 | 
						||
                                       );
 | 
						||
        if (freeHeader->IsError()) {
 | 
						||
            error = ::GetLastError();
 | 
						||
 | 
						||
            INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
        freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
 | 
						||
        freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
 | 
						||
        _HeadersLength += dwHeaderNameLength
 | 
						||
                        + (sizeof(": ") - 1)
 | 
						||
                        + dwHeaderValueLength
 | 
						||
                        + (sizeof("\r\n") - 1)
 | 
						||
                        ;
 | 
						||
        freeHeader->SetHash(dwHash);
 | 
						||
 | 
						||
        if ( dwQueryIndex != INVALID_HEADER_SLOT )
 | 
						||
        {
 | 
						||
            freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
 | 
						||
        }
 | 
						||
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    PERF_LEAVE(AddHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::AddHeader(
 | 
						||
    IN DWORD dwQueryIndex,
 | 
						||
    IN LPSTR lpszHeaderValue,
 | 
						||
    IN DWORD dwHeaderValueLength,
 | 
						||
    IN DWORD dwIndex,
 | 
						||
    IN DWORD dwFlags
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Adds a single header to the array of headers, given the header name and
 | 
						||
    value. Called via HttpOpenRequest()
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    dwQueryIndex        - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
 | 
						||
 | 
						||
    lpszHeaderValue     - pointer to value of header to add, e.g. "text/html"
 | 
						||
 | 
						||
    dwHeaderValueLength - length of the header value
 | 
						||
 | 
						||
    dwIndex             - if coalescing headers, index of header to update
 | 
						||
 | 
						||
    dwFlags             - flags controlling function:
 | 
						||
 | 
						||
                            COALESCE_HEADER_WITH_COMMA
 | 
						||
                            COALESCE_HEADER_WITH_SEMICOLON
 | 
						||
                                - headers of the same name can be combined
 | 
						||
 | 
						||
                            CLEAN_HEADER
 | 
						||
                                - header is supplied by user, so we must ensure
 | 
						||
                                  it has correct format
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
                    Ran out of memory allocating string
 | 
						||
 | 
						||
                  ERROR_INVALID_PARAMETER
 | 
						||
                    The header value was bad
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "AddHeader",
 | 
						||
                 "%q, %u, %.*q, %d, %d, %#x",
 | 
						||
                 GlobalKnownHeaders[dwQueryIndex].Text,
 | 
						||
                 dwQueryIndex,
 | 
						||
                 min(dwHeaderValueLength + 1, 80),
 | 
						||
                 lpszHeaderValue,
 | 
						||
                 dwHeaderValueLength,
 | 
						||
                 dwIndex,
 | 
						||
                 dwFlags
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(AddHeader);
 | 
						||
 | 
						||
    INET_ASSERT(dwQueryIndex <= HTTP_QUERY_MAX);
 | 
						||
    INET_ASSERT(lpszHeaderValue != NULL);
 | 
						||
    INET_ASSERT(*lpszHeaderValue != '\0');
 | 
						||
    INET_ASSERT(dwHeaderValueLength != 0);
 | 
						||
    INET_ASSERT(_FreeSlots <= _TotalSlots);
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
    DWORD i = 0;
 | 
						||
    LPSTR lpszHeaderName;
 | 
						||
    DWORD dwHeaderNameLength;
 | 
						||
    DWORD dwHash;
 | 
						||
 | 
						||
    dwHash             = GlobalKnownHeaders[dwQueryIndex].HashVal;
 | 
						||
    lpszHeaderName     = GlobalKnownHeaders[dwQueryIndex].Text;
 | 
						||
    dwHeaderNameLength = GlobalKnownHeaders[dwQueryIndex].Length;
 | 
						||
 | 
						||
    //
 | 
						||
    // if we are coalescing headers then find a header with the same name
 | 
						||
    //
 | 
						||
 | 
						||
    if ((dwFlags & COALESCE_HEADER_WITH_COMMA) ||
 | 
						||
        (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) )
 | 
						||
    {
 | 
						||
        DWORD dwSlot;
 | 
						||
 | 
						||
        dwSlot = FastFind(
 | 
						||
                    dwQueryIndex,
 | 
						||
                    dwIndex
 | 
						||
                    );
 | 
						||
 | 
						||
        if (dwSlot != INVALID_HEADER_SLOT)
 | 
						||
        {
 | 
						||
 | 
						||
            HEADER_STRING * pString;
 | 
						||
 | 
						||
            pString = &_lpHeaders[dwSlot];
 | 
						||
 | 
						||
            //
 | 
						||
            // found what we are looking for. Coalesce it
 | 
						||
            //
 | 
						||
 | 
						||
            pString->ResizeString((sizeof("; ")-1) + dwHeaderValueLength); // save us from multiple reallocs
 | 
						||
 | 
						||
            pString->Strncat(
 | 
						||
                             (dwFlags & COALESCE_HEADER_WITH_SEMICOLON) ?
 | 
						||
                                 "; " :
 | 
						||
                                 ", ",
 | 
						||
                              2);
 | 
						||
 | 
						||
            pString->Strncat(lpszHeaderValue, dwHeaderValueLength);
 | 
						||
            _HeadersLength += 2 + dwHeaderValueLength;
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
 | 
						||
    //
 | 
						||
    // if we didn't find the header value or we are not coalescing then add the
 | 
						||
    // header
 | 
						||
    //
 | 
						||
 | 
						||
    if (error == ERROR_HTTP_HEADER_NOT_FOUND)
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // find the next slot for this header
 | 
						||
        //
 | 
						||
 | 
						||
        HEADER_STRING * freeHeader;
 | 
						||
        DWORD iSlot;
 | 
						||
 | 
						||
        freeHeader = FindFreeSlot(&iSlot);
 | 
						||
        if (freeHeader == NULL) {
 | 
						||
            error = GetError();
 | 
						||
 | 
						||
            INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        freeHeader->CreateStringBuffer((LPVOID)lpszHeaderName,
 | 
						||
                                       dwHeaderNameLength,
 | 
						||
                                       dwHeaderNameLength
 | 
						||
                                       + sizeof(": ") - 1
 | 
						||
                                       + dwHeaderValueLength
 | 
						||
                                       + 1 // for extra NULL terminator
 | 
						||
                                       );
 | 
						||
        if (freeHeader->IsError()) {
 | 
						||
            error = ::GetLastError();
 | 
						||
 | 
						||
            INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
        freeHeader->Strncat((LPVOID)": ", sizeof(": ") - 1);
 | 
						||
        freeHeader->Strncat((LPVOID)lpszHeaderValue, dwHeaderValueLength);
 | 
						||
        _HeadersLength += dwHeaderNameLength
 | 
						||
                        + (sizeof(": ") - 1)
 | 
						||
                        + dwHeaderValueLength
 | 
						||
                        + (sizeof("\r\n") - 1)
 | 
						||
                        ;
 | 
						||
        freeHeader->SetHash(dwHash);
 | 
						||
        freeHeader->SetNextKnownIndex(FastAdd(dwQueryIndex, iSlot));
 | 
						||
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    PERF_LEAVE(AddHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::ReplaceHeader(
 | 
						||
    IN LPSTR lpszHeaderName,
 | 
						||
    IN DWORD dwHeaderNameLength,
 | 
						||
    IN LPSTR lpszHeaderValue,
 | 
						||
    IN DWORD dwHeaderValueLength,
 | 
						||
    IN DWORD dwIndex,
 | 
						||
    IN DWORD dwFlags
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Replaces a HTTP (request) header. The header can be replaced with a NULL
 | 
						||
    value, meaning that the header is removed
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeaderName      - pointer to the header name
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of the header name
 | 
						||
 | 
						||
    lpszHeaderValue     - pointer to the header value
 | 
						||
 | 
						||
    dwHeaderValueLength - length of the header value
 | 
						||
 | 
						||
    dwIndex             - index of header to replace
 | 
						||
 | 
						||
    dwFlags             - flags controlling function. Allowed flags are:
 | 
						||
 | 
						||
                            COALESCE_HEADER_WITH_COMMA
 | 
						||
                            COALESCE_HEADER_WITH_SEMICOLON
 | 
						||
                                - headers of the same name can be combined
 | 
						||
 | 
						||
                            ADD_HEADER
 | 
						||
                                - if the header-name is not found and there is
 | 
						||
                                  a valid header-value, then the header is added
 | 
						||
 | 
						||
                            ADD_HEADER_IF_NEW
 | 
						||
                                - if the header-name exists then we return an
 | 
						||
                                  error, else we add the header-value
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    The requested header wasn't found
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_ALREADY_EXISTS
 | 
						||
                    The header already exists, and was not added or replaced
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "ReplaceHeader",
 | 
						||
                 "%.*q, %d, %.*q, %d, %d, %#x",
 | 
						||
                 min(dwHeaderNameLength + 1, 80),
 | 
						||
                 lpszHeaderName,
 | 
						||
                 dwHeaderNameLength,
 | 
						||
                 min(dwHeaderValueLength + 1, 80),
 | 
						||
                 lpszHeaderValue,
 | 
						||
                 dwHeaderValueLength,
 | 
						||
                 dwIndex,
 | 
						||
                 dwFlags
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(ReplaceHeader);
 | 
						||
 | 
						||
    INET_ASSERT(lpszHeaderName != NULL);
 | 
						||
    INET_ASSERT(dwHeaderNameLength != 0);
 | 
						||
    INET_ASSERT(lpszHeaderName[dwHeaderNameLength - 1] != ':');
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
    DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
 | 
						||
    DWORD dwSlot;
 | 
						||
    DWORD dwQueryIndex;
 | 
						||
    BYTE *pbPrevByte;
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    dwSlot = SlowFind(
 | 
						||
                NULL,
 | 
						||
                lpszHeaderName,
 | 
						||
                dwHeaderNameLength,
 | 
						||
                dwIndex,
 | 
						||
                dwHash,
 | 
						||
                &dwQueryIndex,
 | 
						||
                &pbPrevByte
 | 
						||
                );
 | 
						||
 | 
						||
    if ( dwSlot != ((DWORD) -1))
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // if ADD_HEADER_IF_NEW is set, then we already have the header
 | 
						||
        //
 | 
						||
 | 
						||
        if (dwFlags & ADD_HEADER_IF_NEW) {
 | 
						||
            error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // for both replace and remove operations, we are going to remove
 | 
						||
        // the current header
 | 
						||
        //
 | 
						||
 | 
						||
        RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
 | 
						||
 | 
						||
        //
 | 
						||
        // if replacing then add the new header value
 | 
						||
        //
 | 
						||
 | 
						||
        if (dwHeaderValueLength != 0)
 | 
						||
        {
 | 
						||
            if ( dwQueryIndex != ((DWORD) -1) )
 | 
						||
            {
 | 
						||
                error = AddHeader(dwQueryIndex,
 | 
						||
                                  lpszHeaderValue,
 | 
						||
                                  dwHeaderValueLength,
 | 
						||
                                  0,
 | 
						||
                                  dwFlags
 | 
						||
                                  );
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                error = AddHeader(lpszHeaderName,
 | 
						||
                                  dwHeaderNameLength,
 | 
						||
                                  lpszHeaderValue,
 | 
						||
                                  dwHeaderValueLength,
 | 
						||
                                  0,
 | 
						||
                                  dwFlags
 | 
						||
                                  );
 | 
						||
            }
 | 
						||
 | 
						||
 | 
						||
        } else {
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // if we didn't find the header but ADD_HEADER is set then we simply add it
 | 
						||
    // but only if the value length is not zero
 | 
						||
    //
 | 
						||
 | 
						||
    if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
 | 
						||
    && (dwHeaderValueLength != 0)
 | 
						||
    && (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
 | 
						||
    {
 | 
						||
        if ( dwQueryIndex != ((DWORD) -1) )
 | 
						||
        {
 | 
						||
            error = AddHeader(dwQueryIndex,
 | 
						||
                              lpszHeaderValue,
 | 
						||
                              dwHeaderValueLength,
 | 
						||
                              0,
 | 
						||
                              dwFlags
 | 
						||
                              );
 | 
						||
        }
 | 
						||
        else
 | 
						||
        {
 | 
						||
            error = AddHeader(lpszHeaderName,
 | 
						||
                              dwHeaderNameLength,
 | 
						||
                              lpszHeaderValue,
 | 
						||
                              dwHeaderValueLength,
 | 
						||
                              0,
 | 
						||
                              dwFlags
 | 
						||
                              );
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    PERF_LEAVE(ReplaceHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::ReplaceHeader(
 | 
						||
    IN DWORD dwQueryIndex,
 | 
						||
    IN LPSTR lpszHeaderValue,
 | 
						||
    IN DWORD dwHeaderValueLength,
 | 
						||
    IN DWORD dwIndex,
 | 
						||
    IN DWORD dwFlags
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Replaces a HTTP (request) header. The header can be replaced with a NULL
 | 
						||
    value, meaning that the header is removed
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeaderValue     - pointer to the header value
 | 
						||
 | 
						||
    dwQueryIndex        - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
 | 
						||
 | 
						||
    dwHeaderValueLength - length of the header value
 | 
						||
 | 
						||
    dwIndex             - index of header to replace
 | 
						||
 | 
						||
    dwFlags             - flags controlling function. Allowed flags are:
 | 
						||
 | 
						||
                            COALESCE_HEADER_WITH_COMMA
 | 
						||
                            COALESCE_HEADER_WITH_SEMICOLON
 | 
						||
                                - headers of the same name can be combined
 | 
						||
 | 
						||
                            ADD_HEADER
 | 
						||
                                - if the header-name is not found and there is
 | 
						||
                                  a valid header-value, then the header is added
 | 
						||
 | 
						||
                            ADD_HEADER_IF_NEW
 | 
						||
                                - if the header-name exists then we return an
 | 
						||
                                  error, else we add the header-value
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    The requested header wasn't found
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_ALREADY_EXISTS
 | 
						||
                    The header already exists, and was not added or replaced
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "ReplaceHeader",
 | 
						||
                 "%q, %u, %.*q, %d, %d, %#x",
 | 
						||
                 GlobalKnownHeaders[dwQueryIndex].Text,
 | 
						||
                 dwQueryIndex,
 | 
						||
                 min(dwHeaderValueLength + 1, 80),
 | 
						||
                 lpszHeaderValue,
 | 
						||
                 dwHeaderValueLength,
 | 
						||
                 dwIndex,
 | 
						||
                 dwFlags
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(ReplaceHeader);
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
    DWORD dwSlot;
 | 
						||
    BYTE *pbPrevByte;
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    dwSlot = FastNukeFind(
 | 
						||
                dwQueryIndex,
 | 
						||
                dwIndex,
 | 
						||
                &pbPrevByte
 | 
						||
                );
 | 
						||
 | 
						||
    if ( dwSlot != INVALID_HEADER_SLOT)
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // if ADD_HEADER_IF_NEW is set, then we already have the header
 | 
						||
        //
 | 
						||
 | 
						||
        if (dwFlags & ADD_HEADER_IF_NEW) {
 | 
						||
            error = ERROR_HTTP_HEADER_ALREADY_EXISTS;
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // for both replace and remove operations, we are going to remove
 | 
						||
        // the current header
 | 
						||
        //
 | 
						||
 | 
						||
        RemoveHeader(dwSlot, dwQueryIndex, pbPrevByte);
 | 
						||
 | 
						||
        //
 | 
						||
        // if replacing then add the new header value
 | 
						||
        //
 | 
						||
 | 
						||
        if (dwHeaderValueLength != 0)
 | 
						||
        {
 | 
						||
            error = AddHeader(dwQueryIndex,
 | 
						||
                              lpszHeaderValue,
 | 
						||
                              dwHeaderValueLength,
 | 
						||
                              0,
 | 
						||
                              dwFlags
 | 
						||
                              );
 | 
						||
        } else {
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // if we didn't find the header but ADD_HEADER is set then we simply add it
 | 
						||
    // but only if the value length is not zero
 | 
						||
    //
 | 
						||
 | 
						||
    if ((error == ERROR_HTTP_HEADER_NOT_FOUND)
 | 
						||
    && (dwHeaderValueLength != 0)
 | 
						||
    && (dwFlags & (ADD_HEADER | ADD_HEADER_IF_NEW)))
 | 
						||
    {
 | 
						||
        error = AddHeader(dwQueryIndex,
 | 
						||
                          lpszHeaderValue,
 | 
						||
                          dwHeaderValueLength,
 | 
						||
                          0,
 | 
						||
                          dwFlags
 | 
						||
                          );
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    PERF_LEAVE(ReplaceHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::FindHeader(
 | 
						||
    IN LPSTR lpBase,
 | 
						||
    IN LPSTR lpszHeaderName,
 | 
						||
    IN DWORD dwHeaderNameLength,
 | 
						||
    IN DWORD dwModifiers,
 | 
						||
    OUT LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength,
 | 
						||
    IN OUT LPDWORD lpdwIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds a request or response header
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBase              - base for offset HEADER_STRINGs
 | 
						||
 | 
						||
    lpszHeaderName      - pointer to header name
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of header name
 | 
						||
 | 
						||
    dwModifiers         - flags which modify returned value
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer for results
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: length of results, or required length of lpBuffer
 | 
						||
 | 
						||
    lpdwIndex           - IN: 0-based index of header to find
 | 
						||
                          OUT: next header index if success returned
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
                    *lpdwBufferLength contains the amount required
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    The specified header (or index of header) was not found
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "HTTP_HEADERS::FindHeader",
 | 
						||
                 "%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
 | 
						||
                 lpszHeaderName,
 | 
						||
                 min(dwHeaderNameLength + 1, 80),
 | 
						||
                 lpszHeaderName,
 | 
						||
                 dwHeaderNameLength,
 | 
						||
                 lpBuffer,
 | 
						||
                 lpdwBufferLength,
 | 
						||
                 *lpdwBufferLength,
 | 
						||
                 dwModifiers,
 | 
						||
                 lpdwIndex,
 | 
						||
                 *lpdwIndex
 | 
						||
                 ));
 | 
						||
 | 
						||
 | 
						||
    PERF_ENTER(FindHeader);
 | 
						||
 | 
						||
 | 
						||
 | 
						||
    INET_ASSERT(lpdwIndex != NULL);
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
    DWORD dwSlot;
 | 
						||
    HEADER_STRING * pString;
 | 
						||
    DWORD dwQueryIndex;
 | 
						||
    DWORD dwHash = CalculateHashNoCase(lpszHeaderName, dwHeaderNameLength);
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    dwSlot = SlowFind(
 | 
						||
                lpBase,
 | 
						||
                lpszHeaderName,
 | 
						||
                dwHeaderNameLength,
 | 
						||
                *lpdwIndex,
 | 
						||
                dwHash,
 | 
						||
                &dwQueryIndex,
 | 
						||
                NULL
 | 
						||
                );
 | 
						||
 | 
						||
    if ( dwSlot != ((DWORD) -1) )
 | 
						||
    {
 | 
						||
        pString = &_lpHeaders[dwSlot];
 | 
						||
 | 
						||
        //
 | 
						||
        // found the header - get to the value
 | 
						||
        //
 | 
						||
 | 
						||
        DWORD stringLen;
 | 
						||
        LPSTR value;
 | 
						||
 | 
						||
        stringLen = pString->StringLength();
 | 
						||
 | 
						||
        INET_ASSERT(stringLen > dwHeaderNameLength);
 | 
						||
 | 
						||
        //
 | 
						||
        // get a pointer to the value string
 | 
						||
        //
 | 
						||
 | 
						||
        value = pString->StringAddress(lpBase) + dwHeaderNameLength;
 | 
						||
        stringLen -= dwHeaderNameLength;
 | 
						||
 | 
						||
        //
 | 
						||
        // the input string could be a substring of a different header
 | 
						||
        //
 | 
						||
 | 
						||
        //INET_ASSERT(*value != ':');
 | 
						||
 | 
						||
        //
 | 
						||
        // find the first non-space character in the value.
 | 
						||
        //
 | 
						||
        // N.B.: Servers can return empty headers, so we may end up with a
 | 
						||
        // zero length string
 | 
						||
        //
 | 
						||
 | 
						||
        do {
 | 
						||
            ++value;
 | 
						||
            --stringLen;
 | 
						||
        } while ((stringLen > 0) && (*value == ' '));
 | 
						||
 | 
						||
        //
 | 
						||
        // get the data in the format requested by the app
 | 
						||
        //
 | 
						||
 | 
						||
        LPVOID lpData;
 | 
						||
        DWORD dwDataSize;
 | 
						||
        DWORD dwRequiredSize;
 | 
						||
        SYSTEMTIME systemTime;
 | 
						||
        DWORD number;
 | 
						||
 | 
						||
        //
 | 
						||
        // error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
 | 
						||
        // really be success either...
 | 
						||
        //
 | 
						||
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
 | 
						||
        if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME) {
 | 
						||
 | 
						||
            char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
 | 
						||
 | 
						||
            if (stringLen < sizeof(buf)) {
 | 
						||
 | 
						||
                //
 | 
						||
                // value probably does not point at a zero-terminated string
 | 
						||
                // which HttpDateToSystemTime() expects, so we make a copy
 | 
						||
                // and terminate it
 | 
						||
                //
 | 
						||
 | 
						||
                memcpy((LPVOID)buf, (LPVOID)value, stringLen);
 | 
						||
                buf[stringLen] = '\0';
 | 
						||
                if (HttpDateToSystemTime(buf, &systemTime)) {
 | 
						||
                    lpData = (LPVOID)&systemTime;
 | 
						||
                    dwRequiredSize = dwDataSize = sizeof(systemTime);
 | 
						||
                } else {
 | 
						||
 | 
						||
                    //
 | 
						||
                    // couldn't convert date/time. Presume header must be bogus
 | 
						||
                    //
 | 
						||
 | 
						||
                    error = ERROR_HTTP_INVALID_QUERY_REQUEST;
 | 
						||
 | 
						||
                    DEBUG_PRINT(HTTP,
 | 
						||
                                ERROR,
 | 
						||
                                ("cannot convert %.40q to SYSTEMTIME\n",
 | 
						||
                                value
 | 
						||
                                ));
 | 
						||
 | 
						||
                }
 | 
						||
            } else {
 | 
						||
 | 
						||
                //
 | 
						||
                // we would break the date/time buffer!
 | 
						||
                //
 | 
						||
 | 
						||
                error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
            }
 | 
						||
        } else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
 | 
						||
            if (isdigit(*value)) {
 | 
						||
                number = 0;
 | 
						||
                for (int i = 0;
 | 
						||
                    (stringLen > 0) && isdigit(value[i]);
 | 
						||
                    ++i, --stringLen) {
 | 
						||
 | 
						||
                    number = number * 10 + (DWORD)(value[i] - '0');
 | 
						||
                }
 | 
						||
                lpData = (LPVOID)&number;
 | 
						||
                dwRequiredSize = dwDataSize = sizeof(number);
 | 
						||
            } else {
 | 
						||
 | 
						||
                //
 | 
						||
                // not a numeric field. Request must be bogus for this header
 | 
						||
                //
 | 
						||
 | 
						||
                error = ERROR_HTTP_INVALID_QUERY_REQUEST;
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            ERROR,
 | 
						||
                            ("cannot convert %.20q to NUMBER\n",
 | 
						||
                            value
 | 
						||
                            ));
 | 
						||
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            lpData = (LPVOID)value;
 | 
						||
            dwDataSize = stringLen;
 | 
						||
            dwRequiredSize = dwDataSize + 1;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // if error == ERROR_SUCCESS then we can attempt to copy the data
 | 
						||
        //
 | 
						||
 | 
						||
        if (error == ERROR_SUCCESS) {
 | 
						||
            if (*lpdwBufferLength < dwRequiredSize) {
 | 
						||
                *lpdwBufferLength = dwRequiredSize;
 | 
						||
                error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
            } else {
 | 
						||
                memcpy(lpBuffer, lpData, dwDataSize);
 | 
						||
                *lpdwBufferLength = dwDataSize;
 | 
						||
 | 
						||
                //
 | 
						||
                // if dwRequiredSize > dwDataSize, then this is a variable-
 | 
						||
                // length item (i.e. a STRING!) so we add a terminating '\0'
 | 
						||
                //
 | 
						||
 | 
						||
                if (dwRequiredSize > dwDataSize) {
 | 
						||
 | 
						||
                    INET_ASSERT(dwRequiredSize - dwDataSize == 1);
 | 
						||
 | 
						||
                    ((LPSTR)lpBuffer)[dwDataSize] = '\0';
 | 
						||
                }
 | 
						||
 | 
						||
                //
 | 
						||
                // successfully retrieved the requested header - bump the
 | 
						||
                // index
 | 
						||
                //
 | 
						||
 | 
						||
                ++*lpdwIndex;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    PERF_LEAVE(FindHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::FindHeader(
 | 
						||
    IN LPSTR lpBase,
 | 
						||
    IN DWORD dwQueryIndex,
 | 
						||
    IN DWORD dwModifiers,
 | 
						||
    OUT LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength,
 | 
						||
    IN OUT LPDWORD lpdwIndex
 | 
						||
    )
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds a request or response header, based on index to the header name we are searching for.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBase              - base for offset HEADER_STRINGs
 | 
						||
 | 
						||
    dwQueryIndex        - a index into a array of known HTTP headers, see wininet.h HTTP_QUERY_* codes
 | 
						||
 | 
						||
    dwModifiers         - flags which modify returned value
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer for results
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: length of results, or required length of lpBuffer
 | 
						||
 | 
						||
    lpdwIndex           - IN: 0-based index of header to find
 | 
						||
                          OUT: next header index if success returned
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
                    *lpdwBufferLength contains the amount required
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    The specified header (or index of header) was not found
 | 
						||
 | 
						||
--*/
 | 
						||
{
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
    LPSTR lpData;
 | 
						||
    DWORD dwDataSize = 0;
 | 
						||
    DWORD dwRequiredSize;
 | 
						||
    SYSTEMTIME systemTime;
 | 
						||
    DWORD number;
 | 
						||
 | 
						||
    error = FastFindHeader(
 | 
						||
                lpBase,
 | 
						||
                dwQueryIndex,
 | 
						||
                (LPVOID *)&lpData,
 | 
						||
                &dwDataSize,
 | 
						||
                *lpdwIndex
 | 
						||
                );
 | 
						||
 | 
						||
    if ( error != ERROR_SUCCESS )
 | 
						||
    {
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // get the data in the format requested by the app
 | 
						||
    //
 | 
						||
 | 
						||
    if (dwModifiers & HTTP_QUERY_FLAG_SYSTEMTIME)
 | 
						||
    {
 | 
						||
        char buf[DATE_AND_TIME_STRING_BUFFER_LENGTH];
 | 
						||
 | 
						||
        if (dwDataSize < sizeof(buf))
 | 
						||
        {
 | 
						||
 | 
						||
            //
 | 
						||
            // value probably does not point at a zero-terminated string
 | 
						||
            // which HttpDateToSystemTime() expects, so we make a copy
 | 
						||
            // and terminate it
 | 
						||
            //
 | 
						||
 | 
						||
            memcpy((LPVOID)buf, (LPVOID)lpData, dwDataSize);
 | 
						||
            buf[dwDataSize] = '\0';
 | 
						||
            if (HttpDateToSystemTime(buf, &systemTime)) {
 | 
						||
                lpData = (LPSTR)&systemTime;
 | 
						||
                dwRequiredSize = dwDataSize = sizeof(systemTime);
 | 
						||
            } else {
 | 
						||
 | 
						||
                //
 | 
						||
                // couldn't convert date/time. Presume header must be bogus
 | 
						||
                //
 | 
						||
 | 
						||
                error = ERROR_HTTP_INVALID_QUERY_REQUEST;
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            ERROR,
 | 
						||
                            ("cannot convert %.40q to SYSTEMTIME\n",
 | 
						||
                            lpData
 | 
						||
                            ));
 | 
						||
 | 
						||
            }
 | 
						||
        }
 | 
						||
        else
 | 
						||
        {
 | 
						||
 | 
						||
            //
 | 
						||
            // we would break the date/time buffer!
 | 
						||
            //
 | 
						||
 | 
						||
            error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    else if (dwModifiers & HTTP_QUERY_FLAG_NUMBER)
 | 
						||
    {
 | 
						||
        if (isdigit(*lpData)) {
 | 
						||
            number = 0;
 | 
						||
            for (int i = 0;
 | 
						||
                (dwDataSize > 0) && isdigit(lpData[i]);
 | 
						||
                ++i, --dwDataSize) {
 | 
						||
 | 
						||
                number = number * 10 + (DWORD)(lpData[i] - '0');
 | 
						||
            }
 | 
						||
            lpData = (LPSTR)&number;
 | 
						||
            dwRequiredSize = dwDataSize = sizeof(number);
 | 
						||
        } else {
 | 
						||
 | 
						||
            //
 | 
						||
            // not a numeric field. Request must be bogus for this header
 | 
						||
            //
 | 
						||
 | 
						||
            error = ERROR_HTTP_INVALID_QUERY_REQUEST;
 | 
						||
 | 
						||
            DEBUG_PRINT(HTTP,
 | 
						||
                        ERROR,
 | 
						||
                        ("cannot convert %.20q to NUMBER\n",
 | 
						||
                        lpData
 | 
						||
                        ));
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
        dwRequiredSize = dwDataSize + 1;
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // if error == ERROR_SUCCESS then we can attempt to copy the data
 | 
						||
    //
 | 
						||
 | 
						||
    if (error == ERROR_SUCCESS)
 | 
						||
    {
 | 
						||
        if (*lpdwBufferLength < dwRequiredSize)
 | 
						||
        {
 | 
						||
            *lpdwBufferLength = dwRequiredSize;
 | 
						||
            error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
        }
 | 
						||
        else
 | 
						||
        {
 | 
						||
            memcpy(lpBuffer, lpData, dwDataSize);
 | 
						||
            *lpdwBufferLength = dwDataSize;
 | 
						||
 | 
						||
            //
 | 
						||
            // if dwRequiredSize > dwDataSize, then this is a variable-
 | 
						||
            // length item (i.e. a STRING!) so we add a terminating '\0'
 | 
						||
            //
 | 
						||
 | 
						||
            if (dwRequiredSize > dwDataSize)
 | 
						||
            {
 | 
						||
                INET_ASSERT(dwRequiredSize - dwDataSize == 1);
 | 
						||
 | 
						||
                ((LPSTR)lpBuffer)[dwDataSize] = '\0';
 | 
						||
            }
 | 
						||
 | 
						||
            //
 | 
						||
            // successfully retrieved the requested header - bump the
 | 
						||
            // index
 | 
						||
            //
 | 
						||
 | 
						||
            ++*lpdwIndex;
 | 
						||
        }
 | 
						||
    }
 | 
						||
quit:
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::FastFindHeader(
 | 
						||
    IN LPSTR lpBase,
 | 
						||
    IN DWORD dwQueryIndex,
 | 
						||
    OUT LPVOID *lplpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength,
 | 
						||
    IN DWORD dwIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Finds a request or response header slightly quicker than its higher level
 | 
						||
     cousin, FindHeader.   Unlike FindHeader this function simply returns
 | 
						||
     a pointer and length, and does not copy header data.
 | 
						||
 | 
						||
 | 
						||
    lpBase              - base address of strings
 | 
						||
 | 
						||
    dwQueryIndex        - a index into a array known HTTP headers, see wininet.h HTTP_QUERY_* codes
 | 
						||
 | 
						||
    lplpBuffer          - pointer to pointer of the actual header to be returned in.
 | 
						||
 | 
						||
    lpdwBufferLength    - OUT: if successful, length of output buffer, minus 1
 | 
						||
                               for any trailing EOS, or if the buffer is not
 | 
						||
                               large enough, the size required
 | 
						||
 | 
						||
    dwIndex             - a index of which header we're asking for, as there can be multiple headers
 | 
						||
                          under the same name.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBase              - base for offset HEADER_STRINGs
 | 
						||
 | 
						||
    lpszHeaderName      - pointer to header name
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of header name
 | 
						||
 | 
						||
    dwModifiers         - flags which modify returned value
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer for results
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: length of results, or required length of lpBuffer
 | 
						||
 | 
						||
    lpdwIndex           - IN: 0-based index of header to find
 | 
						||
                          OUT: next header index if success returned
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
                    *lpdwBufferLength contains the amount required
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    The specified header (or index of header) was not found
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "HTTP_HEADERS::FastFindHeader",
 | 
						||
                 "%q, %#x, %#x [%#x], %u",
 | 
						||
                 GlobalKnownHeaders[dwQueryIndex].Text,
 | 
						||
                 lplpBuffer,
 | 
						||
                 lpdwBufferLength,
 | 
						||
                 *lpdwBufferLength,
 | 
						||
                 dwIndex
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(FindHeader);
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
 | 
						||
    HEADER_STRING * curHeader;
 | 
						||
    DWORD dwSlot;
 | 
						||
 | 
						||
    dwSlot = FastFind(dwQueryIndex, dwIndex);
 | 
						||
 | 
						||
    if ( dwSlot != INVALID_HEADER_SLOT)
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // found the header - get to the value
 | 
						||
        //
 | 
						||
 | 
						||
        DWORD stringLen;
 | 
						||
        LPSTR value;
 | 
						||
 | 
						||
        curHeader = GetSlot(dwSlot);
 | 
						||
 | 
						||
        //
 | 
						||
        // get a pointer to the value string
 | 
						||
        //
 | 
						||
 | 
						||
        value     = curHeader->StringAddress(lpBase) + (GlobalKnownHeaders[dwQueryIndex].Length+1);
 | 
						||
        stringLen = curHeader->StringLength() - (GlobalKnownHeaders[dwQueryIndex].Length+1);
 | 
						||
 | 
						||
        //
 | 
						||
        // find the first non-space character in the value.
 | 
						||
        //
 | 
						||
        // N.B.: Servers can return empty headers, so we may end up with a
 | 
						||
        // zero length string
 | 
						||
        //
 | 
						||
 | 
						||
        while ((stringLen > 0) && (*value == ' '))
 | 
						||
        {
 | 
						||
            ++value;
 | 
						||
            --stringLen;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // get the data in the format requested by the app
 | 
						||
        //
 | 
						||
 | 
						||
        //
 | 
						||
        // error is no longer ERROR_HTTP_HEADER_NOT_FOUND, but it might not
 | 
						||
        // really be success either...
 | 
						||
        //
 | 
						||
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
 | 
						||
        *lplpBuffer = (LPVOID)value;
 | 
						||
        *lpdwBufferLength = stringLen;
 | 
						||
    }
 | 
						||
 | 
						||
    PERF_LEAVE(FindHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::QueryRawHeaders(
 | 
						||
    IN LPSTR lpBase,
 | 
						||
    IN BOOL bCrLfTerminated,
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Returns all the request or response headers in a single buffer. The headers
 | 
						||
    can be returned as ASCIIZ strings, or CR-LF terminated strings
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBase              - base address of strings
 | 
						||
 | 
						||
    bCrLfTerminated     - TRUE if each string is terminated with CR-LF
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer to write headers
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: if successful, length of output buffer, minus 1
 | 
						||
                               for any trailing EOS, or if the buffer is not
 | 
						||
                               large enough, the size required
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    PERF_ENTER(QueryRawHeaders);
 | 
						||
 | 
						||
    DWORD requiredLength = 0;
 | 
						||
    LPSTR lpszBuffer = (LPSTR)lpBuffer;
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
 | 
						||
    for (DWORD i = 0; i < _TotalSlots; ++i) {
 | 
						||
        if (_lpHeaders[i].HaveString()) {
 | 
						||
 | 
						||
            DWORD length;
 | 
						||
 | 
						||
            length = _lpHeaders[i].StringLength();
 | 
						||
 | 
						||
            requiredLength += length + (bCrLfTerminated ? 2 : 1);
 | 
						||
            if (*lpdwBufferLength > requiredLength) {
 | 
						||
                _lpHeaders[i].CopyTo(lpBase, lpszBuffer);
 | 
						||
                lpszBuffer += length;
 | 
						||
                if (bCrLfTerminated) {
 | 
						||
                    *lpszBuffer++ = '\r';
 | 
						||
                    *lpszBuffer++ = '\n';
 | 
						||
                } else {
 | 
						||
                    *lpszBuffer++ = '\0';
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    if (bCrLfTerminated)
 | 
						||
    {
 | 
						||
        requiredLength += 2;
 | 
						||
        if (*lpdwBufferLength > requiredLength)
 | 
						||
        {
 | 
						||
            *lpszBuffer++ = '\r';
 | 
						||
            *lpszBuffer++ = '\n';
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    ++requiredLength;
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    if (*lpdwBufferLength < requiredLength) {
 | 
						||
        error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
    } else {
 | 
						||
        *lpszBuffer = '\0';
 | 
						||
        --requiredLength;   // remove 1 for trailing '\0'
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    }
 | 
						||
    *lpdwBufferLength = requiredLength;
 | 
						||
 | 
						||
    PERF_LEAVE(QueryRawHeaders);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::QueryFilteredRawHeaders(
 | 
						||
    IN LPSTR lpBase,
 | 
						||
    IN LPSTR *lplpFilterList,
 | 
						||
    IN DWORD cListElements,
 | 
						||
    IN BOOL  fExclude,
 | 
						||
    IN BOOL  fSkipVerb,
 | 
						||
    IN BOOL bCrLfTerminated,
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Returns all the request or response headers in a single buffer. The headers
 | 
						||
    can be returned as ASCIIZ strings, or CR-LF terminated strings
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBase              - base address of strings
 | 
						||
 | 
						||
    bCrLfTerminated     - TRUE if each string is terminated with CR-LF
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer to write headers
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: if successful, length of output buffer, minus 1
 | 
						||
                               for any trailing EOS, or if the buffer is not
 | 
						||
                               large enough, the size required
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DWORD error = ERROR_NOT_SUPPORTED;
 | 
						||
 | 
						||
    DWORD requiredLength = 0;
 | 
						||
    LPSTR lpszBuffer = (LPSTR)lpBuffer;
 | 
						||
    BOOL fCopy;
 | 
						||
 | 
						||
    DWORD i = fSkipVerb ? 1 : 0;
 | 
						||
    for (; i < _TotalSlots; ++i) {
 | 
						||
       if (_lpHeaders[i].HaveString()) {
 | 
						||
          fCopy = TRUE;
 | 
						||
          if (lplpFilterList
 | 
						||
             && FMatchList(lplpFilterList, cListElements, _lpHeaders+i, lpBase)) {
 | 
						||
             fCopy = fExclude?FALSE:TRUE;
 | 
						||
          }
 | 
						||
          if (fCopy) {
 | 
						||
              DWORD length;
 | 
						||
 | 
						||
              length = _lpHeaders[i].StringLength();
 | 
						||
              requiredLength += length + (bCrLfTerminated ? 2 : 1);
 | 
						||
              if (*lpdwBufferLength > requiredLength) {
 | 
						||
                    _lpHeaders[i].CopyTo(lpBase, lpszBuffer);
 | 
						||
                   lpszBuffer += length;
 | 
						||
                   if (bCrLfTerminated) {
 | 
						||
                       *lpszBuffer++ = '\r';
 | 
						||
                       *lpszBuffer++ = '\n';
 | 
						||
                    } else {
 | 
						||
                       *lpszBuffer++ = '\0';
 | 
						||
                   }
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    if (bCrLfTerminated)
 | 
						||
    {
 | 
						||
        requiredLength += 2;
 | 
						||
        if (*lpdwBufferLength > requiredLength)
 | 
						||
        {
 | 
						||
            *lpszBuffer++ = '\r';
 | 
						||
            *lpszBuffer++ = '\n';
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    ++requiredLength;
 | 
						||
 | 
						||
 | 
						||
    if (*lpdwBufferLength < requiredLength) {
 | 
						||
        error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
    } else {
 | 
						||
        *lpszBuffer = '\0';
 | 
						||
        --requiredLength;   // remove 1 for trailing '\0'
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    }
 | 
						||
    *lpdwBufferLength = requiredLength;
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::AddRequest(
 | 
						||
    IN LPSTR lpszVerb,
 | 
						||
    IN LPSTR lpszObject,
 | 
						||
    IN LPSTR lpszVersion
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Builds the request line from its constituent parts. The request line is the
 | 
						||
    first (0th) header in the request headers
 | 
						||
 | 
						||
    Assumes:    1. This is the one-and-only call to this method
 | 
						||
                2. lpszObject must already be escaped if necessary
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszVerb    - pointer to HTTP verb, e.g. "GET"
 | 
						||
 | 
						||
    lpszObject  - pointer to HTTP object name, e.g. "/users/albert/~emc2.htm".
 | 
						||
 | 
						||
    lpszVersion - pointer to HTTP version string, e.g. "HTTP/1.0"
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    PERF_ENTER(AddRequest);
 | 
						||
 | 
						||
    //
 | 
						||
    // there must not be a header when this method is called
 | 
						||
    //
 | 
						||
 | 
						||
    INET_ASSERT(_HeadersLength == 0);
 | 
						||
 | 
						||
    DWORD error = ERROR_SUCCESS;
 | 
						||
    int verbLen = lstrlen(lpszVerb);
 | 
						||
    int objectLen = lstrlen(lpszObject);
 | 
						||
    int versionLen = lstrlen(lpszVersion);
 | 
						||
    int len = verbLen       // "GET"
 | 
						||
            + 1             //     ' '
 | 
						||
            + objectLen     //        "/users/albert/~emc2.htm"
 | 
						||
            + 1             //                                 ' '
 | 
						||
            + versionLen    //                                    "HTTP/1.0"
 | 
						||
            + 1             //                                              '\0'
 | 
						||
            ;
 | 
						||
 | 
						||
    //
 | 
						||
    // we are about to start updating the headers for the current
 | 
						||
    // HTTP_REQUEST_HANDLE_OBJECT. Serialize access
 | 
						||
    //
 | 
						||
 | 
						||
    HEADER_STRING * pRequest = GetFirstHeader();
 | 
						||
    HEADER_STRING & request = *pRequest;
 | 
						||
 | 
						||
    if (pRequest == NULL) {
 | 
						||
        error = ERROR_NOT_ENOUGH_MEMORY;
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    INET_ASSERT(!request.HaveString());
 | 
						||
 | 
						||
    _lpszVerb = NULL;
 | 
						||
    _dwVerbLength = 0;
 | 
						||
    _lpszObjectName = NULL;
 | 
						||
    _dwObjectNameLength = 0;
 | 
						||
    _lpszVersion = NULL;
 | 
						||
    _dwVersionLength = 0;
 | 
						||
 | 
						||
    request.CreateStringBuffer((LPVOID)lpszVerb, verbLen, len);
 | 
						||
    if (request.IsError()) {
 | 
						||
        error = GetLastError();
 | 
						||
 | 
						||
        INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
 | 
						||
    } else {
 | 
						||
        request += ' ';
 | 
						||
        request.Strncat((LPVOID)lpszObject, objectLen);
 | 
						||
        request += ' ';
 | 
						||
        request.Strncat((LPVOID)lpszVersion, versionLen);
 | 
						||
 | 
						||
        _HeadersLength = len - 1 + (sizeof("\r\n") - 1);
 | 
						||
 | 
						||
        //
 | 
						||
        // we have used the first free slot in the headers array
 | 
						||
        //
 | 
						||
 | 
						||
        --_FreeSlots;
 | 
						||
 | 
						||
        //
 | 
						||
        // update the component variables in case of a ModifyRequest()
 | 
						||
        //
 | 
						||
 | 
						||
        _lpszVerb = request.StringAddress();
 | 
						||
        _dwVerbLength = verbLen;
 | 
						||
        _lpszObjectName = _lpszVerb + verbLen + 1;
 | 
						||
        _dwObjectNameLength = objectLen;
 | 
						||
        _lpszVersion = _lpszObjectName + objectLen + 1;
 | 
						||
        _dwVersionLength = versionLen;
 | 
						||
        SetRequestVersion();
 | 
						||
        error = request.IsError() ? ::GetLastError() : ERROR_SUCCESS;
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    PERF_LEAVE(AddRequest);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::ModifyRequest(
 | 
						||
    IN HTTP_METHOD_TYPE tMethod,
 | 
						||
    IN LPSTR lpszObjectName,
 | 
						||
    IN DWORD dwObjectNameLength,
 | 
						||
    IN LPSTR lpszVersion OPTIONAL,
 | 
						||
    IN DWORD dwVersionLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Updates the request line. Used in redirection
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    tMethod             - type of new method
 | 
						||
 | 
						||
    lpszObjectName      - pointer to new object name
 | 
						||
 | 
						||
    dwObjectNameLength  - length of new object name
 | 
						||
 | 
						||
    lpszVersion         - optional pointer to version string
 | 
						||
 | 
						||
    dwVersionLength     - length of lpszVersion string if present
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "ModifyRequest",
 | 
						||
                 "%s, %q, %d, %q, %d",
 | 
						||
                 MapHttpMethodType(tMethod),
 | 
						||
                 lpszObjectName,
 | 
						||
                 dwObjectNameLength,
 | 
						||
                 lpszVersion,
 | 
						||
                 dwVersionLength
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(ModifyRequest);
 | 
						||
 | 
						||
    INET_ASSERT(lpszObjectName != NULL);
 | 
						||
    INET_ASSERT(dwObjectNameLength != 0);
 | 
						||
 | 
						||
    //
 | 
						||
    // there must already be a header when this method is called
 | 
						||
    //
 | 
						||
 | 
						||
    INET_ASSERT(_HeadersLength != 0);
 | 
						||
 | 
						||
    //
 | 
						||
    // we are about to start updating the headers for the current
 | 
						||
    // HTTP_REQUEST_HANDLE_OBJECT. Serialize access
 | 
						||
    //
 | 
						||
 | 
						||
    //
 | 
						||
    // BUGBUG [arthurbi] using two HEADER_STRINGs here causes an extra
 | 
						||
    //  ReAlloc when use the Copy operator between the two.
 | 
						||
    //
 | 
						||
 | 
						||
    HEADER_STRING * pRequest = GetFirstHeader();
 | 
						||
    HEADER_STRING & request = *pRequest;
 | 
						||
    HEADER_STRING newRequest;
 | 
						||
    LPCSTR lpcszVerb;
 | 
						||
    DWORD verbLength;
 | 
						||
    DWORD error = ERROR_SUCCESS;
 | 
						||
    DWORD length;
 | 
						||
 | 
						||
    //
 | 
						||
    // there must already be a request line
 | 
						||
    //
 | 
						||
 | 
						||
    if (pRequest == NULL) {
 | 
						||
        error = ERROR_NOT_ENOUGH_MEMORY;
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    INET_ASSERT(request.HaveString());
 | 
						||
 | 
						||
    //
 | 
						||
    // get the verb/method to use.
 | 
						||
    //
 | 
						||
 | 
						||
    if (tMethod == HTTP_METHOD_TYPE_UNKNOWN) {
 | 
						||
 | 
						||
        //
 | 
						||
        // the method is unknown, read the old one out of the string
 | 
						||
        //  and save off, basically we're reusing the previous one.
 | 
						||
        //
 | 
						||
 | 
						||
        lpcszVerb = request.StringAddress();
 | 
						||
 | 
						||
        for (DWORD i = 0; i < request.StringLength(); i++) {
 | 
						||
            if (lpcszVerb[i] == ' ') {
 | 
						||
                break;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        INET_ASSERT((i > 0) && (i < request.StringLength()));
 | 
						||
 | 
						||
        verbLength = i;
 | 
						||
    } else {
 | 
						||
 | 
						||
        //
 | 
						||
        // its one of the normal kind, just map it.
 | 
						||
        //
 | 
						||
 | 
						||
        verbLength = MapHttpMethodType(tMethod, &lpcszVerb);
 | 
						||
    }
 | 
						||
    if (lpszVersion == NULL) {
 | 
						||
        lpszVersion = _lpszVersion;
 | 
						||
        dwVersionLength = _dwVersionLength;
 | 
						||
    }
 | 
						||
 | 
						||
    _lpszVerb = NULL;
 | 
						||
    _dwVerbLength = 0;
 | 
						||
    _lpszObjectName = NULL;
 | 
						||
    _dwObjectNameLength = 0;
 | 
						||
    _lpszVersion = NULL;
 | 
						||
    _dwVersionLength = 0;
 | 
						||
 | 
						||
    //
 | 
						||
    // calculate the new length from the component lengths we originally set
 | 
						||
    // in AddRequest(), and the new object name
 | 
						||
    //
 | 
						||
 | 
						||
    length = verbLength + 1 + dwObjectNameLength + 1 + dwVersionLength + 1;
 | 
						||
 | 
						||
    //
 | 
						||
    // create a new request line
 | 
						||
    //
 | 
						||
 | 
						||
    newRequest.CreateStringBuffer((LPVOID)lpcszVerb, verbLength, length);
 | 
						||
    if (newRequest.IsError()) {
 | 
						||
        error = GetLastError();
 | 
						||
    } else {
 | 
						||
        newRequest += ' ';
 | 
						||
        newRequest.Strncat((LPVOID)lpszObjectName, dwObjectNameLength);
 | 
						||
        newRequest += ' ';
 | 
						||
        newRequest.Strncat((LPVOID)lpszVersion, dwVersionLength);
 | 
						||
 | 
						||
        //
 | 
						||
        // remove the current request line length from the header buffer
 | 
						||
        // aggregate
 | 
						||
        //
 | 
						||
 | 
						||
        _HeadersLength -= request.StringLength();
 | 
						||
 | 
						||
        //
 | 
						||
        // make the current request line the new one
 | 
						||
        //
 | 
						||
 | 
						||
        request = newRequest.StringAddress();
 | 
						||
 | 
						||
        //
 | 
						||
        // and update the address and length variables (version length is the
 | 
						||
        // only thing that stays the same)
 | 
						||
        //
 | 
						||
 | 
						||
        if (!request.IsError()) {
 | 
						||
            _lpszVerb = request.StringAddress();
 | 
						||
            _dwVerbLength = verbLength;
 | 
						||
            _lpszObjectName = _lpszVerb + verbLength + 1;
 | 
						||
            _dwObjectNameLength = dwObjectNameLength;
 | 
						||
            _lpszVersion = _lpszObjectName + dwObjectNameLength + 1;
 | 
						||
            _dwVersionLength = dwVersionLength;
 | 
						||
            SetRequestVersion();
 | 
						||
 | 
						||
        //
 | 
						||
        // and the new request line length to the aggregate header length
 | 
						||
        //
 | 
						||
 | 
						||
            _HeadersLength += request.StringLength();
 | 
						||
        } else {
 | 
						||
            error = GetLastError();
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    PERF_LEAVE(ModifyRequest);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_HEADERS::SetRequestVersion(
 | 
						||
    VOID
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Set _RequestVersionMajor and _RequestVersionMinor based on the HTTP
 | 
						||
    version string
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "HTTP_HEADERS::SetRequestVersion",
 | 
						||
                 NULL
 | 
						||
                 ));
 | 
						||
 | 
						||
    INET_ASSERT(_lpszVersion != NULL);
 | 
						||
 | 
						||
    _RequestVersionMajor = 0;
 | 
						||
    _RequestVersionMinor = 0;
 | 
						||
    if (strncmp(_lpszVersion, "HTTP/", sizeof("HTTP/") - 1) == 0) {
 | 
						||
 | 
						||
        LPSTR pNum = _lpszVersion + sizeof("HTTP/") - 1;
 | 
						||
 | 
						||
        ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMajor);
 | 
						||
        while (!isdigit(*pNum) && (*pNum != '\0')) {
 | 
						||
            ++pNum;
 | 
						||
        }
 | 
						||
        ExtractInt(&pNum, 0, (LPINT)&_RequestVersionMinor);
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("request version = %d.%d\n",
 | 
						||
                    _RequestVersionMajor,
 | 
						||
                    _RequestVersionMinor
 | 
						||
                    ));
 | 
						||
 | 
						||
    } else {
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    WARNING,
 | 
						||
                    ("\"HTTP/\" not found in %q\n",
 | 
						||
                    _lpszVersion
 | 
						||
                    ));
 | 
						||
 | 
						||
    }
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADERS::QueryRequestVersion(
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Get HtttpVersion used in request.
 | 
						||
    
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer to copy version string into
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: size of lpBuffer
 | 
						||
                          OUT: size of version string excluding terminating '\0'
 | 
						||
                               if successful, else required buffer length
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "HTTP_HEADERS::QueryRequestVersion",
 | 
						||
                 NULL
 | 
						||
                 ));
 | 
						||
 | 
						||
    DWORD error = ERROR_HTTP_HEADER_NOT_FOUND;
 | 
						||
 | 
						||
    LockHeaders();
 | 
						||
    
 | 
						||
    if (_lpszVersion != NULL)
 | 
						||
    {
 | 
						||
        DWORD dwLen = lstrlen(_lpszVersion);
 | 
						||
 | 
						||
        if (*lpdwBufferLength > dwLen) {
 | 
						||
            memcpy(lpBuffer, _lpszVersion, dwLen);
 | 
						||
            ((LPSTR)lpBuffer)[dwLen] = '\0';
 | 
						||
            *lpdwBufferLength = dwLen;
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
        } else {
 | 
						||
            *lpdwBufferLength = dwLen + 1;
 | 
						||
            error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    UnlockHeaders();
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
LPSTR
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer(
 | 
						||
    OUT LPDWORD lpdwRequestLength,
 | 
						||
    IN LPVOID lpOptional,
 | 
						||
    IN DWORD dwOptionalLength,
 | 
						||
    IN BOOL bExtraCrLf,
 | 
						||
    IN DWORD dwMaxPacketLength,
 | 
						||
    OUT LPBOOL lpbCombinedData
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Creates a request buffer from the HTTP request and headers
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpdwRequestLength   - pointer to returned buffer length
 | 
						||
 | 
						||
    lpOptional          - pointer to optional data
 | 
						||
 | 
						||
    dwOptionalLength    - length of optional data
 | 
						||
 | 
						||
    bExtraCrLf          - TRUE if we need to add additional CR-LF to buffer
 | 
						||
 | 
						||
    dwMaxPacketLength   - maximum length of buffer
 | 
						||
 | 
						||
    lpbCombinedData     - output TRUE if data successfully combined into one
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    LPSTR
 | 
						||
        Success - pointer to allocated buffer
 | 
						||
 | 
						||
        Failure - NULL
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Pointer,
 | 
						||
                 "HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer",
 | 
						||
                 "%#x, %#x, %d, %B, %d, %#x",
 | 
						||
                 lpdwRequestLength,
 | 
						||
                 lpOptional,
 | 
						||
                 dwOptionalLength,
 | 
						||
                 bExtraCrLf,
 | 
						||
                 dwMaxPacketLength,
 | 
						||
                 lpbCombinedData
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(CreateRequestBuffer);
 | 
						||
 | 
						||
    *lpbCombinedData = FALSE;
 | 
						||
 | 
						||
    _RequestHeaders.LockHeaders();
 | 
						||
 | 
						||
    DWORD headersLength;
 | 
						||
    DWORD requestLength;
 | 
						||
    DWORD optionalLength;
 | 
						||
    HEADER_STRING * pRequest = _RequestHeaders.GetFirstHeader();
 | 
						||
    HEADER_STRING & request = *pRequest;
 | 
						||
    LPSTR requestBuffer = NULL;
 | 
						||
/*
 | 
						||
    WCHAR wszUrl[1024];
 | 
						||
    LPWSTR pwszUrl = NULL;
 | 
						||
    BYTE utf8Url[2048];
 | 
						||
    LPBYTE pbUrl = NULL;
 | 
						||
*/
 | 
						||
    LPSTR pszObject = _RequestHeaders.ObjectName();
 | 
						||
    DWORD dwObjectLength = _RequestHeaders.ObjectNameLength();
 | 
						||
 | 
						||
    if (pRequest == NULL) {
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    INET_ASSERT(request.HaveString());
 | 
						||
 | 
						||
    headersLength = _RequestHeaders.HeadersLength();
 | 
						||
    requestLength = headersLength + (sizeof("\r\n") - 1);
 | 
						||
 | 
						||
/*------------------------------------------------------------------
 | 
						||
    GlobalEnableUtf8Encoding = FALSE;
 | 
						||
    if (GlobalEnableUtf8Encoding
 | 
						||
        && StringContainsHighAnsi(pszObject, dwObjectLength)) {
 | 
						||
 | 
						||
        pwszUrl = wszUrl;
 | 
						||
 | 
						||
        DWORD arrayElements = ARRAY_ELEMENTS(wszUrl);
 | 
						||
 | 
						||
        if (dwObjectLength > ARRAY_ELEMENTS(wszUrl)) {
 | 
						||
            arrayElements = dwObjectLength;
 | 
						||
            pwszUrl = (LPWSTR)ALLOCATE_FIXED_MEMORY(arrayElements * sizeof(*pwszUrl));
 | 
						||
            if (pwszUrl == NULL) {
 | 
						||
                goto utf8_cleanup;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
 | 
						||
        PFNINETMULTIBYTETOUNICODE pfnMBToUnicode;
 | 
						||
        pfnMBToUnicode = GetInetMultiByteToUnicode( );
 | 
						||
        if (pfnMBToUnicode == NULL) {
 | 
						||
            goto utf8_cleanup;
 | 
						||
        }
 | 
						||
 | 
						||
        HRESULT hr;
 | 
						||
        DWORD dwMode;
 | 
						||
        INT nMBChars;
 | 
						||
        INT nWChars;
 | 
						||
 | 
						||
        nMBChars = dwObjectLength;
 | 
						||
        nWChars = arrayElements;
 | 
						||
        dwMode = 0;
 | 
						||
 | 
						||
        hr = pfnMBToUnicode(&dwMode,
 | 
						||
                                GetCodePage(),
 | 
						||
                                pszObject,
 | 
						||
                                &nMBChars,
 | 
						||
                                pwszUrl,
 | 
						||
                                &nWChars
 | 
						||
                               );
 | 
						||
        if (hr != S_OK || nWChars == 0) {
 | 
						||
            goto utf8_cleanup;
 | 
						||
        }
 | 
						||
 | 
						||
        DWORD nBytes;
 | 
						||
 | 
						||
        nBytes = CountUnicodeToUtf8(pwszUrl, (DWORD)nWChars, TRUE);
 | 
						||
        pbUrl = utf8Url;
 | 
						||
        if (nBytes > ARRAY_ELEMENTS(utf8Url)) {
 | 
						||
            pbUrl = (LPBYTE)ALLOCATE_FIXED_MEMORY(nBytes);
 | 
						||
            if (pbUrl == NULL) {
 | 
						||
                goto utf8_cleanup;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        DWORD error;
 | 
						||
 | 
						||
        error = ConvertUnicodeToUtf8(pwszUrl,
 | 
						||
                                     (DWORD)nWChars,
 | 
						||
                                     pbUrl,
 | 
						||
                                     nBytes,
 | 
						||
                                     TRUE
 | 
						||
                                     );
 | 
						||
 | 
						||
        INET_ASSERT(error == ERROR_SUCCESS);
 | 
						||
 | 
						||
        if (error != ERROR_SUCCESS) {
 | 
						||
            goto utf8_cleanup;
 | 
						||
        }
 | 
						||
 | 
						||
        requestLength = requestLength - dwObjectLength + nBytes;
 | 
						||
        headersLength = headersLength - dwObjectLength + nBytes;
 | 
						||
        pszObject = (LPSTR)pbUrl;
 | 
						||
        dwObjectLength = nBytes;
 | 
						||
        goto after_utf8;
 | 
						||
 | 
						||
utf8_cleanup:
 | 
						||
 | 
						||
        if ((pwszUrl != wszUrl) && (pwszUrl != NULL)) {
 | 
						||
            FREE_MEMORY(pwszUrl);
 | 
						||
        }
 | 
						||
        pwszUrl = NULL;
 | 
						||
        if ((pbUrl != utf8Url) && (pbUrl != NULL)) {
 | 
						||
            FREE_MEMORY(pbUrl);
 | 
						||
        }
 | 
						||
        pbUrl = NULL;
 | 
						||
        pszObject = NULL;
 | 
						||
        dwObjectLength = 0;
 | 
						||
    }
 | 
						||
 | 
						||
after_utf8:
 | 
						||
------------------------------------------------------------------*/
 | 
						||
 | 
						||
    optionalLength = (DWORD)(dwOptionalLength + (bExtraCrLf ? (sizeof("\r\n") - 1) : 0));
 | 
						||
    if (requestLength + optionalLength <= dwMaxPacketLength) {
 | 
						||
        requestLength += optionalLength;
 | 
						||
    } else {
 | 
						||
        optionalLength = 0;
 | 
						||
        bExtraCrLf = FALSE;
 | 
						||
    }
 | 
						||
 | 
						||
    requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE);
 | 
						||
    if (requestBuffer != NULL) {
 | 
						||
        if (optionalLength != 0) {
 | 
						||
            *lpbCombinedData = TRUE;
 | 
						||
        }
 | 
						||
    } else if (optionalLength != 0) {
 | 
						||
        requestLength = headersLength + (sizeof("\r\n") - 1);
 | 
						||
        optionalLength = 0;
 | 
						||
        bExtraCrLf = FALSE;
 | 
						||
        requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE);
 | 
						||
    }
 | 
						||
    if (requestBuffer != NULL) {
 | 
						||
 | 
						||
        LPSTR buffer = requestBuffer;
 | 
						||
 | 
						||
        //
 | 
						||
        // copy the headers. Remember: header 0 is the request
 | 
						||
        //
 | 
						||
 | 
						||
//#ifdef COMPRESSED_HEADERS
 | 
						||
//        if (vfCompressedHeaders) {
 | 
						||
//            DEBUG_PRINT(HTTP,
 | 
						||
//                        INFO,
 | 
						||
//                        ("Compressing Headers")
 | 
						||
//                        );
 | 
						||
//            _RequestHeaders.CopyCompressedHeaders(&buffer);
 | 
						||
//
 | 
						||
//        }
 | 
						||
//        else
 | 
						||
//#endif //COMPRESSED_HEADERS
 | 
						||
        {
 | 
						||
            _RequestHeaders.CopyHeaders(&buffer, pszObject, dwObjectLength);
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // terminate the request
 | 
						||
        //
 | 
						||
 | 
						||
        *buffer++ = '\r';
 | 
						||
        *buffer++ = '\n';
 | 
						||
 | 
						||
//#ifdef COMPRESSED_HEADERS
 | 
						||
//        if (vfCompressedHeaders) {
 | 
						||
//
 | 
						||
//            *lpdwRequestLength = ((DWORD)buffer - (DWORD)requestBuffer);
 | 
						||
//
 | 
						||
//            DEBUG_PRINT(HTTP,
 | 
						||
//                        INFO,
 | 
						||
//                        ("Compressed Headers: Old Length=%d, New Length = %d, Saved=%d\n",
 | 
						||
//                        requestLength,
 | 
						||
//                        *lpdwRequestLength,
 | 
						||
//                        requestLength - *lpdwRequestLength
 | 
						||
//                        )
 | 
						||
//                        );
 | 
						||
//        } else {
 | 
						||
//#endif //COMPRESSED_HEADERS
 | 
						||
 | 
						||
        if (optionalLength != 0) {
 | 
						||
            if (dwOptionalLength != 0) {
 | 
						||
                memcpy(buffer, lpOptional, dwOptionalLength);
 | 
						||
                buffer += dwOptionalLength;
 | 
						||
            }
 | 
						||
            if (bExtraCrLf) {
 | 
						||
                *buffer++ = '\r';
 | 
						||
                *buffer++ = '\n';
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        INET_ASSERT((SIZE_T)(buffer-requestBuffer) == requestLength);
 | 
						||
 | 
						||
        *lpdwRequestLength = requestLength;
 | 
						||
 | 
						||
//#ifdef COMPRESSED_HEADERS
 | 
						||
//        }
 | 
						||
//#endif
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    _RequestHeaders.UnlockHeaders();
 | 
						||
 | 
						||
    DEBUG_PRINT(HTTP,
 | 
						||
                INFO,
 | 
						||
                ("request length = %d, combined = %B\n",
 | 
						||
                *lpdwRequestLength,
 | 
						||
                *lpbCombinedData
 | 
						||
                ));
 | 
						||
 | 
						||
/*
 | 
						||
    if ((pbUrl != NULL) && (pbUrl != utf8Url)) {
 | 
						||
        FREE_MEMORY(pbUrl);
 | 
						||
    }
 | 
						||
    if ((pwszUrl != NULL) && (pwszUrl != wszUrl)) {
 | 
						||
        FREE_MEMORY(pwszUrl);
 | 
						||
    }
 | 
						||
*/
 | 
						||
 | 
						||
    PERF_LEAVE(CreateRequestBuffer);
 | 
						||
 | 
						||
    DEBUG_LEAVE(requestBuffer);
 | 
						||
 | 
						||
    return requestBuffer;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader(
 | 
						||
    IN LPSTR lpszHeaderName,
 | 
						||
    IN DWORD dwHeaderNameLength,
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength,
 | 
						||
    IN DWORD dwModifiers,
 | 
						||
    IN OUT LPDWORD lpdwIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Searches for an arbitrary request header and if found, returns its value
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeaderName      - pointer to the name of the header to find
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of the header
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer for results
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: length of the returned header value, or required
 | 
						||
                               length of lpBuffer
 | 
						||
 | 
						||
    dwModifiers         - how to return the data: as number, as SYSTEMTIME
 | 
						||
                          structure, etc.
 | 
						||
 | 
						||
    lpdwIndex           - IN: 0-based index of header to find
 | 
						||
                          OUT: next header index if success returned
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
                    lpBuffer not large enough for results
 | 
						||
 | 
						||
                  ERROR_INTERNET_INCORRECT_FORMAT
 | 
						||
                    Can't convert the data to the requested format
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    Couldn't find the requested header
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "QueryRequestHeader",
 | 
						||
                 "%#x [%.*q], %d, %#x, %#x [%#x], %#x, %#x [%d]",
 | 
						||
                 lpszHeaderName,
 | 
						||
                 min(dwHeaderNameLength + 1, 80),
 | 
						||
                 lpszHeaderName,
 | 
						||
                 dwHeaderNameLength,
 | 
						||
                 lpBuffer,
 | 
						||
                 lpdwBufferLength,
 | 
						||
                 *lpdwBufferLength,
 | 
						||
                 dwModifiers,
 | 
						||
                 lpdwIndex,
 | 
						||
                 *lpdwIndex
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(QueryRequestHeader);
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    error = _RequestHeaders.FindHeader(NULL,
 | 
						||
                                       lpszHeaderName,
 | 
						||
                                       dwHeaderNameLength,
 | 
						||
                                       dwModifiers,
 | 
						||
                                       lpBuffer,
 | 
						||
                                       lpdwBufferLength,
 | 
						||
                                       lpdwIndex
 | 
						||
                                       );
 | 
						||
 | 
						||
    PERF_LEAVE(QueryRequestHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader(
 | 
						||
    IN DWORD dwQueryIndex,
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength,
 | 
						||
    IN DWORD dwModifiers,
 | 
						||
    IN OUT LPDWORD lpdwIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Searches for an arbitrary request header and if found, returns its value
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeaderName      - pointer to the name of the header to find
 | 
						||
 | 
						||
    dwHeaderNameLength  - length of the header
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer for results
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: length of the returned header value, or required
 | 
						||
                               length of lpBuffer
 | 
						||
 | 
						||
    dwModifiers         - how to return the data: as number, as SYSTEMTIME
 | 
						||
                          structure, etc.
 | 
						||
 | 
						||
    lpdwIndex           - IN: 0-based index of header to find
 | 
						||
                          OUT: next header index if success returned
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
                    lpBuffer not large enough for results
 | 
						||
 | 
						||
                  ERROR_INTERNET_INCORRECT_FORMAT
 | 
						||
                    Can't convert the data to the requested format
 | 
						||
 | 
						||
                  ERROR_HTTP_HEADER_NOT_FOUND
 | 
						||
                    Couldn't find the requested header
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "QueryRequestHeader",
 | 
						||
                 "%u, %#x [%#x], %#x, %#x [%d]",
 | 
						||
                 dwQueryIndex,
 | 
						||
                 lpBuffer,
 | 
						||
                 lpdwBufferLength,
 | 
						||
                 *lpdwBufferLength,
 | 
						||
                 dwModifiers,
 | 
						||
                 lpdwIndex,
 | 
						||
                 *lpdwIndex
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(QueryRequestHeader);
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    error = _RequestHeaders.FindHeader(NULL,
 | 
						||
                                       dwQueryIndex,
 | 
						||
                                       dwModifiers,
 | 
						||
                                       lpBuffer,
 | 
						||
                                       lpdwBufferLength,
 | 
						||
                                       lpdwIndex
 | 
						||
                                       );
 | 
						||
 | 
						||
    PERF_LEAVE(QueryRequestHeader);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::AddInternalResponseHeader(
 | 
						||
    IN DWORD dwHeaderIndex,
 | 
						||
    IN LPSTR lpszHeader,
 | 
						||
    IN DWORD dwHeaderLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Adds a created response header to the response header array. Unlike normal
 | 
						||
    response headers, this will be a pointer to an actual string, not an offset
 | 
						||
    into the response buffer.
 | 
						||
 | 
						||
    Even if the address of the response buffer changes, created response headers
 | 
						||
    will remain fixed
 | 
						||
 | 
						||
    N.B. The header MUST NOT have a CR-LF terminator
 | 
						||
    N.B.-2 This function must be called under the header lock.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    dwHeaderIndex   - index into header value we are actually creating
 | 
						||
 | 
						||
    lpszHeader      - pointer to created (internal) header to add
 | 
						||
 | 
						||
    dwHeaderLength  - length of response header, or -1 if ASCIIZ
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "AddInternalResponseHeader",
 | 
						||
                 "%u [%q], %q, %d",
 | 
						||
                 dwHeaderIndex,
 | 
						||
                 GlobalKnownHeaders[dwHeaderIndex].Text,
 | 
						||
                 lpszHeader,
 | 
						||
                 dwHeaderLength
 | 
						||
                 ));
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    if (dwHeaderLength == (DWORD)-1) {
 | 
						||
        dwHeaderLength = lstrlen(lpszHeader);
 | 
						||
    }
 | 
						||
 | 
						||
    INET_ASSERT((lpszHeader[dwHeaderLength - 1] != '\r')
 | 
						||
                && (lpszHeader[dwHeaderLength - 1] != '\n'));
 | 
						||
 | 
						||
    //
 | 
						||
    // find the next slot for this header
 | 
						||
    //
 | 
						||
 | 
						||
    HEADER_STRING * freeHeader;
 | 
						||
 | 
						||
    //
 | 
						||
    // if we already have all the headers (the 'empty' header is the last one
 | 
						||
    // in the array) then change the last header to be the one we are adding
 | 
						||
    // and add a new empty header, else just add this one
 | 
						||
    //
 | 
						||
 | 
						||
    DWORD iSlot;
 | 
						||
    freeHeader = _ResponseHeaders.FindFreeSlot(&iSlot);
 | 
						||
    if (freeHeader == NULL) {
 | 
						||
        error = _ResponseHeaders.GetError();
 | 
						||
 | 
						||
        INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
 | 
						||
    } else {
 | 
						||
 | 
						||
        HEADER_STRING * lastHeader;
 | 
						||
 | 
						||
        lastHeader = _ResponseHeaders.GetEmptyHeader();
 | 
						||
        if (lastHeader != NULL) {
 | 
						||
 | 
						||
            //
 | 
						||
            // make copy of last header - its an offset string
 | 
						||
            //
 | 
						||
 | 
						||
            *freeHeader = *lastHeader;
 | 
						||
 | 
						||
            //
 | 
						||
            // use what was last header as free header
 | 
						||
            //
 | 
						||
 | 
						||
            freeHeader = lastHeader;
 | 
						||
        }
 | 
						||
        freeHeader->MakeCopy(lpszHeader, dwHeaderLength);
 | 
						||
        freeHeader->SetNextKnownIndex(_ResponseHeaders.FastAdd(dwHeaderIndex, iSlot));
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    }
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders(
 | 
						||
    IN OUT LPBOOL lpbEof
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Given the next chunk of the response, updates the response headers. The
 | 
						||
    buffer pointer, buffer length and number of bytes received values are all
 | 
						||
    maintained in this object (_ResponseBuffer, _ResponseBufferLength and
 | 
						||
    _BytesReceived, resp.)
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpbEof  - IN: TRUE if we have reached the end of the response
 | 
						||
              OUT: TRUE if we have reached the end of the response or the end
 | 
						||
                   of the headers
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "HTTP_REQUEST_HANDLE_OBJECT::UpdateResponseHeaders",
 | 
						||
                 "%#x [%.*q], %d, %d, %#x [%B]",
 | 
						||
                 _ResponseBuffer + _ResponseScanned,
 | 
						||
                 min(_ResponseBufferLength + 1, 80),
 | 
						||
                 _ResponseBuffer + _ResponseScanned,
 | 
						||
                 _ResponseBufferLength,
 | 
						||
                 _BytesReceived,
 | 
						||
                 lpbEof,
 | 
						||
                 *lpbEof
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(UpdateResponseHeaders);
 | 
						||
 | 
						||
    LPSTR lpszBuffer = (LPSTR)_ResponseBuffer + _ResponseScanned;
 | 
						||
    DWORD dwBytesReceived = _BytesReceived - _ResponseScanned;
 | 
						||
    DWORD error = ERROR_SUCCESS;
 | 
						||
    BOOL  success = TRUE;
 | 
						||
    HEADER_STRING * statusLine;
 | 
						||
 | 
						||
    //
 | 
						||
    // if input EOF is set then the caller is telling us that the end of the
 | 
						||
    // response has been reached at transport level (the server closed the
 | 
						||
    // connectiion)
 | 
						||
    //
 | 
						||
 | 
						||
    if (*lpbEof) {
 | 
						||
        SetEof(TRUE);
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // lock down the response headers for the duration of this request. The only
 | 
						||
    // way another thread is going to wait on this lock is if the reference on
 | 
						||
    // the HTTP request object goes to zero, which *shouldn't* happen
 | 
						||
    //
 | 
						||
 | 
						||
    _ResponseHeaders.LockHeaders();
 | 
						||
 | 
						||
    //
 | 
						||
    // if input EOF is set then the caller is telling us that the end of the
 | 
						||
    // response has been reached at transport level (the server closed the
 | 
						||
    // connectiion)
 | 
						||
    //
 | 
						||
 | 
						||
    if (*lpbEof) {
 | 
						||
        SetEof(TRUE);
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // if we don't yet know whether we have a HTTP/1.0 (or greater) or HTTP/0.9
 | 
						||
    // response yet, then try to find out.
 | 
						||
    //
 | 
						||
    // Only responses greater than HTTP/0.9 start with the "HTTP/#.#" string
 | 
						||
    //
 | 
						||
 | 
						||
    if (!IsDownLevel() && !IsUpLevel()) {
 | 
						||
 | 
						||
#define MAKE_VERSION_ENTRY(string)  string, sizeof(string) - 1
 | 
						||
 | 
						||
        static struct {
 | 
						||
            LPSTR Version;
 | 
						||
            DWORD Length;
 | 
						||
        } KnownVersionsStrings[] = {
 | 
						||
            MAKE_VERSION_ENTRY("HTTP/"),
 | 
						||
            MAKE_VERSION_ENTRY("S-HTTP/"),
 | 
						||
            MAKE_VERSION_ENTRY("SHTTP/"),
 | 
						||
            MAKE_VERSION_ENTRY("Secure-HTTP/"),
 | 
						||
 | 
						||
            //
 | 
						||
            // allow for servers generating slightly off-the-wall responses
 | 
						||
            //
 | 
						||
 | 
						||
            MAKE_VERSION_ENTRY("HTTP /")
 | 
						||
        };
 | 
						||
 | 
						||
#define NUM_HTTP_VERSIONS   ARRAY_ELEMENTS(KnownVersionsStrings)
 | 
						||
 | 
						||
        //
 | 
						||
        // We know this is the start of a HTTP response, but there may be some
 | 
						||
        // noise at the start from bad HTML authoring, or bad content-length on
 | 
						||
        // the previous response on a keep-alive connection. We will try to sync
 | 
						||
        // up to the HTTP header (we will only look for this - I have never seen
 | 
						||
        // any of the others, and I doubt its worth the increased complexity and
 | 
						||
        // processing time)
 | 
						||
        //
 | 
						||
 | 
						||
        //
 | 
						||
        //  Due to possible DoS attacks outlined in RAID item 510295 we are to
 | 
						||
        //be more stringent as to what sort of 'noise' is allowed before the start
 | 
						||
        //of an HTTP response.  We now allow the noise to consist of at most
 | 
						||
        //8 characters of whitespace or '\0'.  If the content-length is slightly
 | 
						||
        //off because of a sloppy server, this should skip whatever terminators
 | 
						||
        //the server expected us to use.
 | 
						||
        //
 | 
						||
 | 
						||
        const DWORD c_dwSmallestAcceptableStatusLength = ARRAY_ELEMENTS("HTTP/1.1 100\r\n") - 1;
 | 
						||
        const DWORD c_dwMaxNoiseAllowed = 8;
 | 
						||
        const DWORD c_dwMaxPreHTTPLength = ARRAY_ELEMENTS("Secure-HTTP/")-1;
 | 
						||
 | 
						||
        LPSTR lpszBuf;
 | 
						||
        DWORD bytesLeft;
 | 
						||
 | 
						||
        lpszBuf = lpszBuffer;
 | 
						||
        bytesLeft = dwBytesReceived;
 | 
						||
 | 
						||
        //
 | 
						||
        //  Check that we've read enough bytes for a status line to be ready.
 | 
						||
        //
 | 
						||
        if ((dwBytesReceived < c_dwSmallestAcceptableStatusLength) && !IsEof())
 | 
						||
        {
 | 
						||
            goto done;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        //  Allow up to c_dwMaxNoiseAllowed bytes worth of noise
 | 
						||
        //
 | 
						||
        int noiseBytesLeft = min(bytesLeft, c_dwMaxNoiseAllowed);
 | 
						||
        int noiseBytesScanned = 0;
 | 
						||
        while ((noiseBytesLeft > 0) 
 | 
						||
               && (isspace((unsigned char)*lpszBuf)
 | 
						||
                   || *lpszBuffer == '\0'))
 | 
						||
        {
 | 
						||
            ++lpszBuf;
 | 
						||
            --bytesLeft;
 | 
						||
            --noiseBytesLeft;
 | 
						||
            ++noiseBytesScanned;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // scan for the known version strings
 | 
						||
        //
 | 
						||
 | 
						||
        for (int i = 0; i < NUM_HTTP_VERSIONS; ++i) {
 | 
						||
 | 
						||
            LPSTR version = KnownVersionsStrings[i].Version;
 | 
						||
            DWORD length = KnownVersionsStrings[i].Length;
 | 
						||
 | 
						||
            if ((bytesLeft >= length)
 | 
						||
 | 
						||
            //
 | 
						||
            // try the most common case as a direct comparison. memcmp()
 | 
						||
            // should expand to cmpsd && cmpsb on x86 (most common platform
 | 
						||
            // and one on which we are most interested in improving perf)
 | 
						||
            //
 | 
						||
 | 
						||
            && (((i == 0)
 | 
						||
                && (memcmp(lpszBuf, "HTTP/", sizeof("HTTP/") - 1) == 0))
 | 
						||
                //&& (lpszBuf[0] == 'H')
 | 
						||
                //&& (lpszBuf[1] == 'T')
 | 
						||
                //&& (lpszBuf[2] == 'T')
 | 
						||
                //&& (lpszBuf[3] == 'P')
 | 
						||
                //&& (lpszBuf[4] == '/'))
 | 
						||
 | 
						||
                //
 | 
						||
                //  "Clients should be tolerant in parsing the Status-Line"
 | 
						||
                //  quote from HTTP/1.1 spec, therefore we perform a
 | 
						||
                //  case-insensitive string comparison here
 | 
						||
                //
 | 
						||
 | 
						||
                || (_strnicmp(lpszBuf, version, length) == 0))) {
 | 
						||
 | 
						||
                //
 | 
						||
                // it starts with one of the recognized protocol version strings.
 | 
						||
                // We assume its not a down-level server, although it could be,
 | 
						||
                // sending back a plain text document that has e.g. "HTTP/1.0..."
 | 
						||
                // at its start
 | 
						||
                //
 | 
						||
                // According to the HTTP "spec", though, it is mentioned that 0.9
 | 
						||
                // servers typically only return HTML, hence we shouldn't see
 | 
						||
                // even a 0.9 response start with non-HTML data
 | 
						||
                //
 | 
						||
 | 
						||
                SetUpLevel(TRUE);
 | 
						||
                _ResponseScanned += noiseBytesScanned;
 | 
						||
 | 
						||
                //
 | 
						||
                // we have start of this response
 | 
						||
                //
 | 
						||
 | 
						||
                lpszBuffer = lpszBuf;
 | 
						||
                break;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        if (!IsUpLevel())
 | 
						||
        {
 | 
						||
            //
 | 
						||
            // if we didn't find the start of a valid HTTP response and we have
 | 
						||
            // not filled the response buffer sufficiently then allow
 | 
						||
            // re-entry to retry.
 | 
						||
            //
 | 
						||
            // if we didn't find the start of a valid HTTP response and we
 | 
						||
            //have filled the buffer sufficiently to expect the response,
 | 
						||
            //report the response as invalid.
 | 
						||
            //
 | 
						||
 | 
						||
            if ((bytesLeft < c_dwMaxPreHTTPLength ) && !IsEof())
 | 
						||
            {
 | 
						||
                goto done;
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                //
 | 
						||
                // this may be a real down-level server, or it may be the response
 | 
						||
                // from an FTP or gopher server via a proxy, in which case there
 | 
						||
                // will be no headers. We will add some default headers to make
 | 
						||
                // life easier for higher level software
 | 
						||
                //
 | 
						||
 | 
						||
                AddInternalResponseHeader(HTTP_QUERY_STATUS_TEXT, // use non-standard index, since we never query this normally
 | 
						||
                                          "HTTP/1.0 200 OK",
 | 
						||
                                          sizeof("HTTP/1.0 200 OK") - 1
 | 
						||
                                          );
 | 
						||
                _StatusCode = HTTP_STATUS_OK;
 | 
						||
                //SetDownLevel(TRUE);
 | 
						||
 | 
						||
                //
 | 
						||
                // we're now ready for the app to start reading data out
 | 
						||
                //
 | 
						||
 | 
						||
                SetData(TRUE);
 | 
						||
 | 
						||
                //
 | 
						||
                // down-level server: we're done
 | 
						||
                //
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            INFO,
 | 
						||
                            ("Server is down-level\n"
 | 
						||
                            ));
 | 
						||
 | 
						||
                goto done;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    //  WinHTTP only accepts IsUpLevel() type responses.
 | 
						||
    //
 | 
						||
 | 
						||
    INET_ASSERT(IsUpLevel());
 | 
						||
 | 
						||
    //
 | 
						||
    // Note: at this point we can't store pointers into the response buffer
 | 
						||
    // because it might move during a subsequent reallocation. We have to
 | 
						||
    // maintain offsets into the buffer and convert to pointers when we come to
 | 
						||
    // read the data out of the buffer (when the response is complete, or at
 | 
						||
    // least we've finished receiving headers)
 | 
						||
    //
 | 
						||
 | 
						||
    //
 | 
						||
    // if we haven't checked the response yet, then the first thing to
 | 
						||
    // get is the status line
 | 
						||
    //
 | 
						||
 | 
						||
    statusLine = GetStatusLine();
 | 
						||
 | 
						||
    if (statusLine == NULL) {
 | 
						||
        error = ERROR_NOT_ENOUGH_MEMORY;
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    if (!statusLine->HaveString())
 | 
						||
    {
 | 
						||
        BOOL fNeedMoreBuffer;
 | 
						||
        int majorVersion = 0;
 | 
						||
        int minorVersion = 0;
 | 
						||
        BOOL fSupportsHttp1_1;
 | 
						||
 | 
						||
        _StatusCode = 0;
 | 
						||
 | 
						||
        //
 | 
						||
        // Parse the status line.  It has already been checked up to the first '/'
 | 
						||
        //
 | 
						||
 | 
						||
        error = _ResponseHeaders.ParseStatusLine(
 | 
						||
            (LPSTR)_ResponseBuffer,
 | 
						||
            _BytesReceived,
 | 
						||
            IsEof(),
 | 
						||
            &_ResponseScanned,
 | 
						||
            &fNeedMoreBuffer,
 | 
						||
            &_StatusCode,
 | 
						||
            (LPDWORD)&majorVersion,
 | 
						||
            (LPDWORD)&minorVersion
 | 
						||
            );
 | 
						||
 | 
						||
        if (error != ERROR_SUCCESS)
 | 
						||
        {
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
 | 
						||
        if (fNeedMoreBuffer)
 | 
						||
        {
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("Version = %d.%d\n",
 | 
						||
                    majorVersion,
 | 
						||
                    minorVersion
 | 
						||
                    ));
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("_StatusCode = %d\n",
 | 
						||
                    _StatusCode
 | 
						||
                    ));
 | 
						||
 | 
						||
        fSupportsHttp1_1 = FALSE;
 | 
						||
 | 
						||
        if ( majorVersion > 1 )
 | 
						||
        {
 | 
						||
            //
 | 
						||
            // for higher version servers, the 1.1 spec dictates
 | 
						||
            //  that we return the highest version the client
 | 
						||
            //  supports, and in our case that is 1.1.
 | 
						||
            //
 | 
						||
 | 
						||
            fSupportsHttp1_1 = TRUE;
 | 
						||
        }
 | 
						||
        else if (majorVersion == 1
 | 
						||
                 && minorVersion >= 1)
 | 
						||
        {
 | 
						||
            fSupportsHttp1_1 = TRUE;
 | 
						||
        }
 | 
						||
 | 
						||
        SetResponseHttp1_1(fSupportsHttp1_1);
 | 
						||
 | 
						||
        //
 | 
						||
        // record the server HTTP version in the server info object
 | 
						||
        //
 | 
						||
 | 
						||
        CServerInfo * pServerInfo = GetServerInfo();
 | 
						||
 | 
						||
        if (pServerInfo != NULL)
 | 
						||
        {
 | 
						||
            if (fSupportsHttp1_1)
 | 
						||
            {
 | 
						||
                pServerInfo->SetHttp1_1();
 | 
						||
 | 
						||
                //
 | 
						||
                // Set the max connections per HTTP 1.1 server.
 | 
						||
                //
 | 
						||
                pServerInfo->SetNewLimit(GlobalMaxConnectionsPerServer);
 | 
						||
            } else {
 | 
						||
                pServerInfo->SetHttp1_0();
 | 
						||
 | 
						||
                //
 | 
						||
                // up the connection limit from HTTP 1.1 (default 2) to
 | 
						||
                // HTTP 1.0 (default 4)
 | 
						||
                //
 | 
						||
 | 
						||
                pServerInfo->SetNewLimit(GlobalMaxConnectionsPer1_0Server);
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // continue scanning headers here until we have tested all the current
 | 
						||
    // buffer, or we have found the start of the data
 | 
						||
    //
 | 
						||
 | 
						||
    BOOL fFoundEndOfHeaders;
 | 
						||
 | 
						||
    error = _ResponseHeaders.ParseHeaders(
 | 
						||
                (LPSTR)_ResponseBuffer,
 | 
						||
                _BytesReceived,
 | 
						||
                IsEof(),
 | 
						||
                &_ResponseScanned,
 | 
						||
                &success,
 | 
						||
                &fFoundEndOfHeaders
 | 
						||
                );
 | 
						||
 | 
						||
    if ( error != ERROR_SUCCESS )
 | 
						||
    {
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
 | 
						||
    if ( fFoundEndOfHeaders )
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // we found the end of the headers
 | 
						||
        //
 | 
						||
 | 
						||
        SetEof(TRUE);
 | 
						||
 | 
						||
        //
 | 
						||
        // and the start of the data
 | 
						||
        //
 | 
						||
 | 
						||
        SetData(TRUE);
 | 
						||
        _DataOffset = _ResponseScanned;
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("found end of headers. _DataOffset = %d\n",
 | 
						||
                    _DataOffset
 | 
						||
                    ));
 | 
						||
 | 
						||
    }
 | 
						||
 | 
						||
done:
 | 
						||
 | 
						||
    //
 | 
						||
    // if we have reached the end of the headers then we communicate this fact
 | 
						||
    // to the caller
 | 
						||
    //
 | 
						||
 | 
						||
    if (IsData() || IsEof()) {
 | 
						||
        CheckWellKnownHeaders();
 | 
						||
        if (ERROR_SUCCESS != error)
 | 
						||
        {
 | 
						||
            goto quit;
 | 
						||
        }
 | 
						||
        *lpbEof = TRUE;
 | 
						||
 | 
						||
        /*
 | 
						||
 | 
						||
        Set connection persistency based on these rules:
 | 
						||
 | 
						||
        persistent = (1.0Request && Con: K-A && 1.0Response && Con: K-A)
 | 
						||
                     || (1.1Request && Con: K-A && 1.0Response && Con: K-A)
 | 
						||
                     || (1.0Request && Con: K-A && 1.1Response && Con: K-A)
 | 
						||
                     || (1.1Request && !Con: Close && 1.1Response && !Con: Close)
 | 
						||
 | 
						||
        therefore,
 | 
						||
 | 
						||
        persistent = 1.1Request && 1.1Response
 | 
						||
                        ? (!Con: Close in request || response)
 | 
						||
                        : Con: K-A in request && response
 | 
						||
 | 
						||
        */
 | 
						||
 | 
						||
        if (IsRequestHttp1_1() && IsResponseHttp1_1()) {
 | 
						||
 | 
						||
            BOOL bHaveConnCloseRequest;
 | 
						||
 | 
						||
            bHaveConnCloseRequest = FindConnCloseRequestHeader(
 | 
						||
                                        IsRequestUsingProxy()
 | 
						||
                                            ? HTTP_QUERY_PROXY_CONNECTION
 | 
						||
                                            : HTTP_QUERY_CONNECTION
 | 
						||
                                            );
 | 
						||
            if (!(IsConnCloseResponse() || bHaveConnCloseRequest)) {
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            INFO,
 | 
						||
                            ("HTTP/1.1 persistent connection\n"
 | 
						||
                            ));
 | 
						||
 | 
						||
                SetKeepAlive(TRUE);
 | 
						||
                SetPersistentConnection(IsRequestUsingProxy()
 | 
						||
                                        && !IsTalkingToSecureServerViaProxy()
 | 
						||
                                        );
 | 
						||
            } else {
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            INFO,
 | 
						||
                            ("HTTP/1.1 non-persistent connection: close on: request: %B; response: %B\n",
 | 
						||
                            bHaveConnCloseRequest,
 | 
						||
                            IsConnCloseResponse()
 | 
						||
                            ));
 | 
						||
 | 
						||
                SetKeepAlive(FALSE);
 | 
						||
                SetNoLongerKeepAlive();
 | 
						||
                ClearPersistentConnection();
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    error = ERROR_SUCCESS;
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    //
 | 
						||
    // we are finished updating the response headers (no other thread should be
 | 
						||
    // waiting for this if the reference count and object state is correct)
 | 
						||
    //
 | 
						||
 | 
						||
    _ResponseHeaders.UnlockHeaders();
 | 
						||
 | 
						||
    PERF_LEAVE(UpdateResponseHeaders);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders(
 | 
						||
    IN OUT LPSTR* ppszBuffer,
 | 
						||
    IN DWORD      dwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Create the response headers given a buffer containing concatenated headers.
 | 
						||
    Called when we are creating this object from the cache
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszBuffer      - pointer to buffer containing headers
 | 
						||
 | 
						||
    dwBufferLength  - length of lpszBuffer
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_NOT_ENOUGH_MEMORY
 | 
						||
                    Couldn't create headers
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "HTTP_REQUEST_HANDLE_OBJECT::CreateResponseHeaders",
 | 
						||
                 "%.32q, %d",
 | 
						||
                 ppszBuffer,
 | 
						||
                 dwBufferLength
 | 
						||
                 ));
 | 
						||
 | 
						||
    //
 | 
						||
    // there SHOULD NOT already be a response buffer if we're adding an
 | 
						||
    // external buffer
 | 
						||
    //
 | 
						||
 | 
						||
    INET_ASSERT(_ResponseBuffer == NULL);
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
    BOOL eof = FALSE;
 | 
						||
 | 
						||
    _ResponseBuffer = (LPBYTE) *ppszBuffer;
 | 
						||
    _ResponseBufferLength = dwBufferLength;
 | 
						||
    _BytesReceived = dwBufferLength;
 | 
						||
    error = UpdateResponseHeaders(&eof);
 | 
						||
    if (error != ERROR_SUCCESS) {
 | 
						||
 | 
						||
        //
 | 
						||
        // if we failed, we will clean up our variables including clearing
 | 
						||
        // out the response buffer address and length, but leave freeing
 | 
						||
        // the buffer to the caller
 | 
						||
        //
 | 
						||
 | 
						||
        _ResponseBuffer = NULL;
 | 
						||
        _ResponseBufferLength = 0;
 | 
						||
        ResetResponseVariables();
 | 
						||
 | 
						||
    } else {
 | 
						||
 | 
						||
        //
 | 
						||
        // Success - the object owns the buffer so the caller should not free.
 | 
						||
        //
 | 
						||
 | 
						||
        *ppszBuffer = NULL;
 | 
						||
    }
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::QueryResponseVersion(
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Returns the HTTP version string from the status line
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer to copy version string into
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: size of lpBuffer
 | 
						||
                          OUT: size of version string excluding terminating '\0'
 | 
						||
                               if successful, else required buffer length
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    PERF_ENTER(QueryResponseVersion);
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    HEADER_STRING * statusLine = GetStatusLine();
 | 
						||
 | 
						||
    if ((statusLine == NULL) || statusLine->IsError()) {
 | 
						||
        error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    LPSTR string;
 | 
						||
    DWORD length;
 | 
						||
 | 
						||
    //
 | 
						||
    // get a pointer into the response buffer where the status line starts
 | 
						||
    // and its length
 | 
						||
    //
 | 
						||
 | 
						||
    string = statusLine->StringAddress((LPSTR)_ResponseBuffer);
 | 
						||
    length = (DWORD)statusLine->StringLength();
 | 
						||
 | 
						||
    //
 | 
						||
    // the version string is the first token on the line, delimited by spaces
 | 
						||
    //
 | 
						||
 | 
						||
    DWORD index;
 | 
						||
 | 
						||
    for (index = 0; index < length; ++index) {
 | 
						||
 | 
						||
        //
 | 
						||
        // we'll also check for CR and LF, although just space should be
 | 
						||
        // sufficient
 | 
						||
        //
 | 
						||
 | 
						||
        if ((string[index] == ' ')
 | 
						||
        || (string[index] == '\r')
 | 
						||
        || (string[index] == '\n')) {
 | 
						||
            break;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    if (*lpdwBufferLength > index) {
 | 
						||
        memcpy(lpBuffer, (LPVOID)string, index);
 | 
						||
        ((LPSTR)lpBuffer)[index] = '\0';
 | 
						||
        *lpdwBufferLength = index;
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    } else {
 | 
						||
        *lpdwBufferLength = index + 1;
 | 
						||
        error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    PERF_LEAVE(QueryResponseVersion);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::QueryStatusCode(
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength,
 | 
						||
    IN DWORD dwModifiers
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Returns the status code as a string or a number
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer where results written
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of buffer
 | 
						||
                          OUT: size of returned information, or required size'
 | 
						||
                               of buffer
 | 
						||
 | 
						||
    dwModifiers         - flags which modify returned value
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    PERF_ENTER(QueryStatusCode);
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
    DWORD requiredSize;
 | 
						||
 | 
						||
    if (dwModifiers & HTTP_QUERY_FLAG_NUMBER) {
 | 
						||
        requiredSize = sizeof(_StatusCode);
 | 
						||
        if (*lpdwBufferLength >= requiredSize) {
 | 
						||
            *(LPDWORD)lpBuffer = _StatusCode;
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
        } else {
 | 
						||
            error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
        }
 | 
						||
    } else {
 | 
						||
 | 
						||
        //
 | 
						||
        // the number should always be only 3 characters long, but we'll be
 | 
						||
        // flexible (just in case)
 | 
						||
        //
 | 
						||
 | 
						||
        char numBuf[sizeof("4294967296")];
 | 
						||
 | 
						||
        requiredSize = wsprintf(numBuf, "%u", _StatusCode) + 1;
 | 
						||
 | 
						||
#ifdef DEBUG
 | 
						||
        // Debug check to make sure everything is good because the above
 | 
						||
        // used to be ultoa.
 | 
						||
        char debugBuf[sizeof("4294967296")];
 | 
						||
        ultoa(_StatusCode, debugBuf, 10);
 | 
						||
        if (strcmp(debugBuf,numBuf))
 | 
						||
        {
 | 
						||
            INET_ASSERT(FALSE);
 | 
						||
        }
 | 
						||
 | 
						||
        INET_ASSERT(requiredSize == lstrlen(numBuf) + 1);
 | 
						||
#endif
 | 
						||
 | 
						||
        if (*lpdwBufferLength >= requiredSize) {
 | 
						||
            memcpy(lpBuffer, (LPVOID)numBuf, requiredSize);
 | 
						||
            *lpdwBufferLength = requiredSize - 1;
 | 
						||
            error = ERROR_SUCCESS;
 | 
						||
        } else {
 | 
						||
            *lpdwBufferLength = requiredSize;
 | 
						||
            error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    PERF_LEAVE(QueryStatusCode);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::QueryStatusText(
 | 
						||
    IN LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Returns the status text - if any - returned by the server in the status line
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer where status text is written
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: size of lpBuffer
 | 
						||
                          OUT: length of the status text string minus 1 for the
 | 
						||
                               '\0', or the required buffer length if we return
 | 
						||
                               ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure - ERROR_INSUFFICIENT_BUFFER
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    PERF_ENTER(QueryStatusText);
 | 
						||
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    HEADER_STRING * statusLine = GetStatusLine();
 | 
						||
 | 
						||
    if ((statusLine == NULL) || statusLine->IsError()) {
 | 
						||
        error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
        goto quit;
 | 
						||
    }
 | 
						||
 | 
						||
    LPSTR str;
 | 
						||
    DWORD len;
 | 
						||
 | 
						||
    //
 | 
						||
    // find the third token on the status line. The status line has the form
 | 
						||
    //
 | 
						||
    //  "HTTP/1.0 302 Try again\r\n"
 | 
						||
    //
 | 
						||
    //   ^        ^   ^
 | 
						||
    //   |        |   |
 | 
						||
    //   |        |   +- status text
 | 
						||
    //   |        +- status code
 | 
						||
    //   +- version
 | 
						||
    //
 | 
						||
 | 
						||
    str = statusLine->StringAddress((LPSTR)_ResponseBuffer);
 | 
						||
    len = statusLine->StringLength();
 | 
						||
 | 
						||
    DWORD i;
 | 
						||
 | 
						||
    i = 0;
 | 
						||
 | 
						||
    int j;
 | 
						||
 | 
						||
    for (j = 0; j < 2; ++j) {
 | 
						||
        while ((i < len) && (str[i] != ' ')) {
 | 
						||
            ++i;
 | 
						||
        }
 | 
						||
        while ((i < len) && (str[i] == ' ')) {
 | 
						||
            ++i;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    len -= i;
 | 
						||
    if (*lpdwBufferLength > len) {
 | 
						||
        memcpy(lpBuffer, (LPVOID)&str[i], len);
 | 
						||
        ((LPSTR)lpBuffer)[len] = '\0';
 | 
						||
        *lpdwBufferLength = len;
 | 
						||
        error = ERROR_SUCCESS;
 | 
						||
    } else {
 | 
						||
        *lpdwBufferLength = len + 1;
 | 
						||
        error = ERROR_INSUFFICIENT_BUFFER;
 | 
						||
    }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    PERF_LEAVE(QueryStatusText);
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::QueryRawResponseHeaders(
 | 
						||
    IN BOOL bCrLfTerminated,
 | 
						||
    OUT LPVOID lpBuffer,
 | 
						||
    IN OUT LPDWORD lpdwBufferLength
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Gets the raw response headers
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    bCrLfTerminated     - TRUE if we want RAW_HEADERS_CRLF else RAW_HEADERS
 | 
						||
 | 
						||
    lpBuffer            - pointer to buffer where headers returned
 | 
						||
 | 
						||
    lpdwBufferLength    - IN: length of lpBuffer
 | 
						||
                          OUT: returned length of lpBuffer
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - ERROR_SUCCESS
 | 
						||
 | 
						||
        Failure -
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 Dword,
 | 
						||
                 "QueryRawHeaders",
 | 
						||
                 "%B, %#x, %#x [%d]",
 | 
						||
                 bCrLfTerminated,
 | 
						||
                 lpBuffer,
 | 
						||
                 lpdwBufferLength,
 | 
						||
                 *lpdwBufferLength
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(QueryRawHeaders);
 | 
						||
 | 
						||
    DWORD error = _ResponseHeaders.QueryRawHeaders(
 | 
						||
                    (LPSTR)_ResponseBuffer,
 | 
						||
                    bCrLfTerminated,
 | 
						||
                    lpBuffer,
 | 
						||
                    lpdwBufferLength
 | 
						||
                    );
 | 
						||
 | 
						||
    IF_DEBUG_CODE() {
 | 
						||
        if (error == ERROR_INSUFFICIENT_BUFFER) {
 | 
						||
 | 
						||
            DEBUG_PRINT(HTTP,
 | 
						||
                        INFO,
 | 
						||
                        ("*lpdwBufferLength = %d\n",
 | 
						||
                        *lpdwBufferLength
 | 
						||
                        ));
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    PERF_LEAVE(QueryRawHeaders);
 | 
						||
 | 
						||
    DEBUG_LEAVE(error);
 | 
						||
 | 
						||
    return error;
 | 
						||
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
VOID
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::RemoveAllRequestHeadersByName(
 | 
						||
    IN DWORD dwQueryIndex
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Removes all headers of a particular type from the request object
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeaderName  - name of header to remove
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "RemoveAllRequestHeadersByName",
 | 
						||
                 "%q, %u",
 | 
						||
                 GlobalKnownHeaders[dwQueryIndex].Text,
 | 
						||
                 dwQueryIndex
 | 
						||
                 ));
 | 
						||
 | 
						||
    PERF_ENTER(RemoveAllRequestHeadersByName);
 | 
						||
 | 
						||
    _RequestHeaders.RemoveAllByIndex(dwQueryIndex);
 | 
						||
 | 
						||
    PERF_LEAVE(RemoveAllRequestHeadersByName);
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
}
 | 
						||
 | 
						||
//
 | 
						||
// private methods
 | 
						||
//
 | 
						||
 | 
						||
 | 
						||
PRIVATE
 | 
						||
VOID
 | 
						||
HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders(
 | 
						||
    VOID
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Tests for a couple of well-known headers that are important to us as well as
 | 
						||
    the app:
 | 
						||
 | 
						||
        "Connection: Keep-Alive"
 | 
						||
        "Proxy-Connection: Keep-Alive"
 | 
						||
        "Connection: Close"
 | 
						||
        "Proxy-Connection: Close"
 | 
						||
        "Transfer-Encoding: chunked"
 | 
						||
        "Content-Length: ####"
 | 
						||
        "Content-Range: bytes ####-####/####"
 | 
						||
 | 
						||
    The header DOES NOT contain CR-LF. That is, dwHeaderLength will not include
 | 
						||
    any counts for line termination
 | 
						||
 | 
						||
    We need to know if the server honoured a request for a keep-alive connection
 | 
						||
    so that we don't try to receive until we hit the end of the connection. The
 | 
						||
    server will keep it open.
 | 
						||
 | 
						||
    We need to know the content length if we are talking over a persistent (keep
 | 
						||
    alive) connection.
 | 
						||
 | 
						||
    If either header is found, we set the corresponding flag in the HTTP_HEADERS
 | 
						||
    object, and in the case of "Content-Length:" we parse out the length.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DEBUG_ENTER((DBG_HTTP,
 | 
						||
                 None,
 | 
						||
                 "HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders",
 | 
						||
                 NULL
 | 
						||
                 ));
 | 
						||
 | 
						||
    //
 | 
						||
    // check for "Content-Length:" and "Content-Range"
 | 
						||
    //
 | 
						||
 | 
						||
    if ( IsResponseHeaderPresent(HTTP_QUERY_CONTENT_RANGE) )
 | 
						||
    {
 | 
						||
        _iSlotContentRange = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONTENT_RANGE];
 | 
						||
    }
 | 
						||
 | 
						||
    if ( IsResponseHeaderPresent(HTTP_QUERY_CONTENT_LENGTH) )
 | 
						||
    {
 | 
						||
        HEADER_STRING * curHeader;
 | 
						||
        DWORD dwHeaderLength;
 | 
						||
        LPSTR lpszHeader;
 | 
						||
 | 
						||
        _iSlotContentLength = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONTENT_LENGTH];
 | 
						||
        curHeader = _ResponseHeaders.GetSlot(_iSlotContentLength);
 | 
						||
 | 
						||
        lpszHeader     = curHeader->StringAddress((LPSTR)_ResponseBuffer);
 | 
						||
        dwHeaderLength = curHeader->StringLength();
 | 
						||
 | 
						||
        dwHeaderLength -= GlobalKnownHeaders[HTTP_QUERY_CONTENT_LENGTH].Length+1;
 | 
						||
        lpszHeader     += GlobalKnownHeaders[HTTP_QUERY_CONTENT_LENGTH].Length+1;
 | 
						||
 | 
						||
        while (dwHeaderLength && (*lpszHeader == ' ')) {
 | 
						||
            --dwHeaderLength;
 | 
						||
            ++lpszHeader;
 | 
						||
        }
 | 
						||
        while (dwHeaderLength && isdigit(*lpszHeader)) {
 | 
						||
            _ContentLength = _ContentLength * 10 + (*lpszHeader - '0');
 | 
						||
            --dwHeaderLength;
 | 
						||
            ++lpszHeader;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // once we have _ContentLength, we don't modify it (unless
 | 
						||
        // we fix it up when using a 206 partial response to resume
 | 
						||
        // a partial download.)  The header value should be returned
 | 
						||
        // by HttpQueryInfo().  Instead, we keep account of the
 | 
						||
        // amount of keep-alive data left to copy in _BytesRemaining
 | 
						||
        //
 | 
						||
 | 
						||
        _BytesRemaining = _ContentLength;
 | 
						||
 | 
						||
        //
 | 
						||
        // although we said we may be one past the end of the header, in
 | 
						||
        // reality, if we received a buffer with "Content-Length:" then we
 | 
						||
        // expect it to be terminated by CR-LF (or CR-CR-LF or just LF,
 | 
						||
        // depending on the wackiness quotient of the server)
 | 
						||
        //
 | 
						||
 | 
						||
        // darrenmi - commenting out because we're hitting it in stress.
 | 
						||
        // headers coming from the cache have an extra space in them in
 | 
						||
        // some circumstances.  Investigating seperately.
 | 
						||
        // 2/25/00
 | 
						||
 | 
						||
        // INET_ASSERT((*lpszHeader == '\r') || (*lpszHeader == '\n'));
 | 
						||
 | 
						||
 | 
						||
        SetHaveContentLength(TRUE);
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("_ContentLength = %d\n",
 | 
						||
                    _ContentLength
 | 
						||
                    ));
 | 
						||
 | 
						||
        _BytesInSocket = (_ContentLength != 0)
 | 
						||
                ? (_ContentLength - (_BytesReceived - _DataOffset))
 | 
						||
                : 0;
 | 
						||
 | 
						||
        //
 | 
						||
        // we could have multiple responses in the same buffer. If
 | 
						||
        // the amount received is greater than the content length
 | 
						||
        // then we have all the data; there are no bytes left in
 | 
						||
        // the socket for the current response
 | 
						||
        //
 | 
						||
 | 
						||
        if ((int)_BytesInSocket < 0) {
 | 
						||
            _BytesInSocket = 0;
 | 
						||
        }
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("bytes left in socket = %d\n",
 | 
						||
                    _BytesInSocket
 | 
						||
                    ));
 | 
						||
 | 
						||
    }
 | 
						||
 | 
						||
 | 
						||
    if ( IsResponseHeaderPresent(HTTP_QUERY_CONNECTION) ||
 | 
						||
         IsResponseHeaderPresent(HTTP_QUERY_PROXY_CONNECTION) )
 | 
						||
    {
 | 
						||
        //
 | 
						||
        // check for "Connection: Keep-Alive" or "Proxy-Connection: Keep-Alive".
 | 
						||
        // This test protects us against the unlikely
 | 
						||
        // event of a server returning to us a keep-alive response header (because
 | 
						||
        // that would cause problems for the proxy)
 | 
						||
        //
 | 
						||
 | 
						||
        if (IsWantKeepAlive() && (!IsKeepAlive() || IsResponseHttp1_1()))
 | 
						||
        {
 | 
						||
            HEADER_STRING * curHeader;
 | 
						||
            DWORD dwHeaderLength, headerNameLength;
 | 
						||
            LPSTR lpszHeader;
 | 
						||
 | 
						||
 | 
						||
            DWORD iSlot;
 | 
						||
 | 
						||
            char ch;
 | 
						||
 | 
						||
            if (IsRequestUsingProxy() &&
 | 
						||
                IsResponseHeaderPresent(HTTP_QUERY_PROXY_CONNECTION))
 | 
						||
            {
 | 
						||
                iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_PROXY_CONNECTION];
 | 
						||
                headerNameLength = GlobalKnownHeaders[HTTP_QUERY_PROXY_CONNECTION].Length+1;
 | 
						||
            }
 | 
						||
            else if (IsResponseHeaderPresent(HTTP_QUERY_CONNECTION))
 | 
						||
            {
 | 
						||
                iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_CONNECTION];
 | 
						||
                headerNameLength = GlobalKnownHeaders[HTTP_QUERY_CONNECTION].Length+1;
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_PROXY_CONNECTION];
 | 
						||
                headerNameLength = GlobalKnownHeaders[HTTP_QUERY_PROXY_CONNECTION].Length+1;
 | 
						||
                INET_ASSERT(FALSE);
 | 
						||
            }
 | 
						||
 | 
						||
            curHeader      = _ResponseHeaders.GetSlot(iSlot);
 | 
						||
            lpszHeader     = curHeader->StringAddress((LPSTR)_ResponseBuffer);
 | 
						||
            dwHeaderLength = curHeader->StringLength();
 | 
						||
 | 
						||
            dwHeaderLength -= headerNameLength;
 | 
						||
            lpszHeader     += headerNameLength;
 | 
						||
 | 
						||
            while (dwHeaderLength && (*lpszHeader == ' ')) {
 | 
						||
                ++lpszHeader;
 | 
						||
                --dwHeaderLength;
 | 
						||
            }
 | 
						||
 | 
						||
            //
 | 
						||
            // both headers use "Keep-Alive" as header-value ONLY for HTTP 1.0 servers
 | 
						||
            //
 | 
						||
 | 
						||
            if (((int)dwHeaderLength >= KEEP_ALIVE_LEN)
 | 
						||
            && !strnicmp(lpszHeader, KEEP_ALIVE_SZ, KEEP_ALIVE_LEN)) {
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            INFO,
 | 
						||
                            ("Connection: Keep-Alive\n"
 | 
						||
                            ));
 | 
						||
 | 
						||
                //
 | 
						||
                // BUGBUG - we are setting k-a when coming from cache!
 | 
						||
                //
 | 
						||
 | 
						||
                SetKeepAlive(TRUE);
 | 
						||
                SetPersistentConnection(headerNameLength == HTTP_PROXY_CONNECTION_LEN);
 | 
						||
            }
 | 
						||
 | 
						||
            //
 | 
						||
            // also check for "Close" as header-value ONLY for HTTP 1.1 servers
 | 
						||
            //
 | 
						||
 | 
						||
            else if ((*lpszHeader == 'C' || *lpszHeader == 'c')
 | 
						||
                     && ((int)dwHeaderLength >= CLOSE_LEN)
 | 
						||
                     && IsResponseHttp1_1()
 | 
						||
                     && !strnicmp(lpszHeader, CLOSE_SZ, CLOSE_LEN)) {
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            INFO,
 | 
						||
                            ("Connection: Close (HTTP/1.1)\n"
 | 
						||
                            ));
 | 
						||
 | 
						||
                SetConnCloseResponse(TRUE);
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // check for "Refresh"
 | 
						||
    //
 | 
						||
 | 
						||
    if ( IsResponseHeaderPresent(HTTP_QUERY_REFRESH)
 | 
						||
        && !IsRefresh() )
 | 
						||
    {
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("have \"Refresh:\" header\n"
 | 
						||
                    ));
 | 
						||
 | 
						||
        SetRefresh(TRUE);
 | 
						||
    }
 | 
						||
 | 
						||
 | 
						||
    //
 | 
						||
    // check for "Transfer-Encoding:"
 | 
						||
    //
 | 
						||
 | 
						||
    if (IsResponseHeaderPresent(HTTP_QUERY_TRANSFER_ENCODING) &&
 | 
						||
        IsResponseHttp1_1())
 | 
						||
    {
 | 
						||
 | 
						||
        //
 | 
						||
        // If Http 1.1, check for Chunked Transfer
 | 
						||
        //
 | 
						||
 | 
						||
        HEADER_STRING * curHeader;
 | 
						||
        DWORD dwHeaderLength;
 | 
						||
        LPSTR lpszHeader;
 | 
						||
        DWORD iSlot;
 | 
						||
 | 
						||
        iSlot = _ResponseHeaders._bKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING];
 | 
						||
        curHeader = _ResponseHeaders.GetSlot(iSlot);
 | 
						||
 | 
						||
        lpszHeader     = curHeader->StringAddress((LPSTR)_ResponseBuffer);
 | 
						||
        dwHeaderLength = curHeader->StringLength();
 | 
						||
 | 
						||
        dwHeaderLength -= GlobalKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING].Length+1;
 | 
						||
        lpszHeader     += GlobalKnownHeaders[HTTP_QUERY_TRANSFER_ENCODING].Length+1;
 | 
						||
 | 
						||
        while (dwHeaderLength && (*lpszHeader == ' ')) {
 | 
						||
            ++lpszHeader;
 | 
						||
            --dwHeaderLength;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // look for "chunked" entry that confirms that we're doing chunked transfer encoding
 | 
						||
        //
 | 
						||
 | 
						||
        if (((int)dwHeaderLength >= CHUNKED_LEN)
 | 
						||
        && !strnicmp(lpszHeader, CHUNKED_SZ, CHUNKED_LEN))
 | 
						||
        {
 | 
						||
 | 
						||
            SetHaveChunkEncoding(TRUE);
 | 
						||
 | 
						||
            DEBUG_PRINT(HTTP,
 | 
						||
                        INFO,
 | 
						||
                        ("server is sending Chunked Transfer Encoding\n"
 | 
						||
                        ));
 | 
						||
 | 
						||
            //
 | 
						||
            // if both "transfer-encoding: chunked" and "content-length:"
 | 
						||
            // were received then the chunking takes precedence
 | 
						||
            //
 | 
						||
 | 
						||
            INET_ASSERT(!(IsChunkEncoding() && IsContentLength()));
 | 
						||
 | 
						||
            if (IsContentLength()) {
 | 
						||
                SetHaveContentLength(FALSE);
 | 
						||
            }
 | 
						||
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    SetBadNSServer(FALSE);
 | 
						||
 | 
						||
    if (IsResponseHttp1_1())
 | 
						||
    {
 | 
						||
 | 
						||
        //
 | 
						||
        // For IIS 4.0 Servers, and all other normal servers, if we make
 | 
						||
        //  a HEAD request, we should ignore the Content-Length.
 | 
						||
        //
 | 
						||
        // IIS 3.0 servers send an illegal body, and this is a bug in the server.
 | 
						||
        //  since they're not HTTP 1.1 we should be ok here.
 | 
						||
        //
 | 
						||
 | 
						||
        if ( (GetMethodType() == HTTP_METHOD_TYPE_HEAD) &&
 | 
						||
             (_ContentLength > 0) &&
 | 
						||
             IsWantKeepAlive()
 | 
						||
             )
 | 
						||
        {
 | 
						||
 | 
						||
            //
 | 
						||
            // set length to 0
 | 
						||
            //
 | 
						||
 | 
						||
            _ContentLength = 0;
 | 
						||
 | 
						||
        }
 | 
						||
 | 
						||
        if ( IsRequestHttp1_1() )
 | 
						||
        {
 | 
						||
 | 
						||
 | 
						||
            //
 | 
						||
            // check for NS servers that don't return correct HTTP/1.1 responses
 | 
						||
            //
 | 
						||
 | 
						||
            LPSTR buffer;
 | 
						||
            DWORD buflen;
 | 
						||
            DWORD status = FastQueryResponseHeader(HTTP_QUERY_SERVER,
 | 
						||
                                                   (LPVOID*)&buffer,
 | 
						||
                                                   &buflen,
 | 
						||
                                                   0
 | 
						||
                                                   );
 | 
						||
 | 
						||
    #define NSEP    "Netscape-Enterprise/3"
 | 
						||
    #define NSEPLEN (sizeof(NSEP) - 1)
 | 
						||
    #define NSFT    "Netscape-FastTrack/3"
 | 
						||
    #define NSFTLEN (sizeof(NSFT) - 1)
 | 
						||
    #define NSCS    "Netscape-Commerce/3"
 | 
						||
    #define NSCSLEN (sizeof(NSCS) - 1)
 | 
						||
 | 
						||
            if (status == ERROR_SUCCESS) {
 | 
						||
 | 
						||
                BOOL fIsBadServer = ((buflen > NSEPLEN) && !strnicmp(buffer, NSEP, NSEPLEN))
 | 
						||
                                 || ((buflen > NSFTLEN) && !strnicmp(buffer, NSFT, NSFTLEN))
 | 
						||
                                 || ((buflen > NSCSLEN) && !strnicmp(buffer, NSCS, NSCSLEN));
 | 
						||
 | 
						||
                if ( fIsBadServer )
 | 
						||
                {
 | 
						||
                    CServerInfo * pServerInfo = GetServerInfo();
 | 
						||
 | 
						||
                    SetBadNSServer(fIsBadServer);
 | 
						||
 | 
						||
                    if (pServerInfo != NULL)
 | 
						||
                    {
 | 
						||
                        //
 | 
						||
                        // Note this Bad Server info in the server info obj,
 | 
						||
                        //   as we they fail to do keep-alive with SSL properly
 | 
						||
                        //
 | 
						||
 | 
						||
                        pServerInfo->SetBadNSServer();
 | 
						||
                    }
 | 
						||
 | 
						||
 | 
						||
                    DEBUG_PRINT(HTTP,
 | 
						||
                                INFO,
 | 
						||
                                ("IsBadNSServer() == %B\n",
 | 
						||
                                IsBadNSServer()
 | 
						||
                                ));
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // BUGBUG - content-type: multipart/byteranges means we
 | 
						||
        //          also have data
 | 
						||
        //
 | 
						||
 | 
						||
        DWORD statusCode = GetStatusCode();
 | 
						||
 | 
						||
        if (!IsBadNSServer()
 | 
						||
            && !IsContentLength()
 | 
						||
            && !IsChunkEncoding()
 | 
						||
            && (((statusCode >= HTTP_STATUS_CONTINUE)               // 100
 | 
						||
                && (statusCode < HTTP_STATUS_OK))                   // 200
 | 
						||
                || (statusCode == HTTP_STATUS_NO_CONTENT)           // 204
 | 
						||
                || (statusCode == HTTP_STATUS_MOVED)                // 301
 | 
						||
                || (statusCode == HTTP_STATUS_REDIRECT)             // 302
 | 
						||
                || (statusCode == HTTP_STATUS_REDIRECT_METHOD)      // 303
 | 
						||
                || (statusCode == HTTP_STATUS_NOT_MODIFIED)         // 304
 | 
						||
                || (statusCode == HTTP_STATUS_REDIRECT_KEEP_VERB))  // 307
 | 
						||
            || (GetMethodType() == HTTP_METHOD_TYPE_HEAD)) {
 | 
						||
 | 
						||
            DEBUG_PRINT(HTTP,
 | 
						||
                        INFO,
 | 
						||
                        ("header-only HTTP/1.1 response\n"
 | 
						||
                        ));
 | 
						||
 | 
						||
            SetData(FALSE);
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    DEBUG_LEAVE(0);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
//
 | 
						||
// this array has the same order as the HTTP_METHOD_TYPE enum
 | 
						||
//
 | 
						||
 | 
						||
#define MAKE_REQUEST_METHOD_TYPE(Type) \
 | 
						||
    sizeof(# Type) - 1, # Type, HTTP_METHOD_TYPE_ ## Type
 | 
						||
 | 
						||
//
 | 
						||
// darrenmi - need a new macro because *_M-POST isn't a valid enum member.
 | 
						||
// we need a seperate enum type and string value.
 | 
						||
//
 | 
						||
// map HTTP_METHOD_TYPE_MPOST <=> "M-POST"
 | 
						||
//
 | 
						||
 | 
						||
#define MAKE_REQUEST_METHOD_TYPE2(EnumType,Type) \
 | 
						||
    sizeof(# Type) - 1, # Type, HTTP_METHOD_TYPE_ ## EnumType
 | 
						||
 | 
						||
static const struct _REQUEST_METHOD {
 | 
						||
    int Length;
 | 
						||
    LPSTR Name;
 | 
						||
    HTTP_METHOD_TYPE MethodType;
 | 
						||
} MethodNames[] = {
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(GET),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(HEAD),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(POST),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(PUT),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(PROPFIND),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(PROPPATCH),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(LOCK),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(UNLOCK),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(COPY),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(MOVE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(MKCOL),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(CONNECT),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(DELETE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(LINK),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(UNLINK),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(BMOVE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(BCOPY),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(BPROPFIND),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(BPROPPATCH),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(BDELETE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(SUBSCRIBE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(UNSUBSCRIBE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(NOTIFY),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(POLL), 
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(CHECKIN),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(CHECKOUT),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(INVOKE),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(SEARCH),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE(PIN),
 | 
						||
    MAKE_REQUEST_METHOD_TYPE2(MPOST,M-POST)
 | 
						||
};
 | 
						||
 | 
						||
 | 
						||
HTTP_METHOD_TYPE
 | 
						||
MapHttpRequestMethod(
 | 
						||
    IN LPCSTR lpszVerb
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Maps request method string to type. Method names *are* case-sensitive
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszVerb    - method (verb) string
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    HTTP_METHOD_TYPE
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    int verbLen = strlen(lpszVerb);
 | 
						||
 | 
						||
    for (int i = 0; i < ARRAY_ELEMENTS(MethodNames); ++i) {
 | 
						||
        if ((MethodNames[i].Length == verbLen)
 | 
						||
        && (memcmp(lpszVerb, MethodNames[i].Name, verbLen) == 0)) {
 | 
						||
            return MethodNames[i].MethodType;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // we now hande HTTP_METHOD_TYPE_UNKNOWN
 | 
						||
    //
 | 
						||
 | 
						||
    return HTTP_METHOD_TYPE_UNKNOWN;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
MapHttpMethodType(
 | 
						||
    IN HTTP_METHOD_TYPE tMethod,
 | 
						||
    OUT LPCSTR * lplpcszName
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Map a method type to the corresponding name and length
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    tMethod     - to map
 | 
						||
 | 
						||
    lplpcszName - pointer to pointer to returned name
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    DWORD
 | 
						||
        Success - length of method name
 | 
						||
 | 
						||
        Failure - (DWORD)-1
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DWORD length;
 | 
						||
 | 
						||
    if ((tMethod >= HTTP_METHOD_TYPE_FIRST) && (tMethod <= HTTP_METHOD_TYPE_LAST)) {
 | 
						||
        *lplpcszName = MethodNames[tMethod].Name;
 | 
						||
        length = MethodNames[tMethod].Length;
 | 
						||
    } else {
 | 
						||
        length = (DWORD)-1;
 | 
						||
    }
 | 
						||
    return length;
 | 
						||
}
 | 
						||
 | 
						||
#if INET_DEBUG
 | 
						||
 | 
						||
LPSTR
 | 
						||
MapHttpMethodType(
 | 
						||
    IN HTTP_METHOD_TYPE tMethod
 | 
						||
    )
 | 
						||
{
 | 
						||
    return (tMethod == HTTP_METHOD_TYPE_UNKNOWN)
 | 
						||
        ? "Unknown"
 | 
						||
        : MethodNames[tMethod].Name;
 | 
						||
}
 | 
						||
 | 
						||
#endif
 | 
						||
 | 
						||
//
 | 
						||
//DWORD
 | 
						||
//CreateEscapedUrlPath(
 | 
						||
//    IN LPSTR lpszUrlPath,
 | 
						||
//    OUT LPSTR * lplpszEncodedUrlPath
 | 
						||
//    )
 | 
						||
//
 | 
						||
///*++
 | 
						||
//
 | 
						||
//Routine Description:
 | 
						||
//
 | 
						||
//    Given an URL-path, encodes it into a new buffer
 | 
						||
//
 | 
						||
//Arguments:
 | 
						||
//
 | 
						||
//    lpszUrlPath             - URL-path to encode
 | 
						||
//
 | 
						||
//    lplpszEncodedUrlPath    - pointer to returned allocated buffer containing
 | 
						||
//                              escaped URL-path
 | 
						||
//
 | 
						||
//Return Value:
 | 
						||
//
 | 
						||
//    DWORD
 | 
						||
//        Success - ERROR_SUCCESS
 | 
						||
//
 | 
						||
//        Failure -
 | 
						||
//
 | 
						||
//--*/
 | 
						||
//
 | 
						||
//{
 | 
						||
//    LPSTR lpszEncodedUrlPath = NULL;
 | 
						||
//    DWORD urlPathLength;
 | 
						||
//    DWORD encodedUrlPathLength;
 | 
						||
//    DWORD error;
 | 
						||
//
 | 
						||
//    //
 | 
						||
//    // we need to encode the URL-path into a separate buffer (it may grow)
 | 
						||
//    //
 | 
						||
//
 | 
						||
//    urlPathLength = strlen(lpszUrlPath);
 | 
						||
//    encodedUrlPathLength = INTERNET_MAX_PATH_LENGTH;
 | 
						||
//
 | 
						||
//    do {
 | 
						||
//
 | 
						||
//        //
 | 
						||
//        // we allow ourselves to fail due to insufficient buffer (at least once)
 | 
						||
//        //
 | 
						||
//
 | 
						||
//        lpszEncodedUrlPath = (LPSTR)ResizeBuffer(lpszEncodedUrlPath,
 | 
						||
//                                                 encodedUrlPathLength,
 | 
						||
//                                                 FALSE
 | 
						||
//                                                 );
 | 
						||
//        if (lpszEncodedUrlPath != NULL) {
 | 
						||
//
 | 
						||
//            DWORD previousLength = encodedUrlPathLength;
 | 
						||
//
 | 
						||
//            error = EncodeUrlPath(NO_ENCODE_PATH_SEP,
 | 
						||
//
 | 
						||
//                                  //
 | 
						||
//                                  // BUGBUG - assuming HTTP
 | 
						||
//                                  //
 | 
						||
//
 | 
						||
//                                  SCHEME_HTTP,
 | 
						||
//                                  lpszUrlPath,
 | 
						||
//                                  urlPathLength,
 | 
						||
//                                  lpszEncodedUrlPath,
 | 
						||
//                                  &encodedUrlPathLength
 | 
						||
//                                  );
 | 
						||
//
 | 
						||
//            if ((error == ERROR_INSUFFICIENT_BUFFER)
 | 
						||
//            && (previousLength >= encodedUrlPathLength)) {
 | 
						||
//
 | 
						||
//                //
 | 
						||
//                // this should never happen, but we will avoid a loop if it does
 | 
						||
//                //
 | 
						||
//
 | 
						||
//                INET_ASSERT(FALSE);
 | 
						||
//
 | 
						||
//                error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
//            }
 | 
						||
//        } else {
 | 
						||
//
 | 
						||
//            //
 | 
						||
//            // failed to (re)alloc
 | 
						||
//            //
 | 
						||
//
 | 
						||
//            error = ERROR_NOT_ENOUGH_MEMORY;
 | 
						||
//        }
 | 
						||
//    } while (error == ERROR_INSUFFICIENT_BUFFER);
 | 
						||
//
 | 
						||
//    *lplpszEncodedUrlPath = lpszEncodedUrlPath;
 | 
						||
//
 | 
						||
//    return error;
 | 
						||
//}
 | 
						||
 | 
						||
PRIVATE
 | 
						||
BOOL
 | 
						||
FMatchList(
 | 
						||
    LPSTR *lplpList,
 | 
						||
    DWORD cListLen,
 | 
						||
    HEADER_STRING *lpHeader,
 | 
						||
    LPSTR    lpBase
 | 
						||
    )
 | 
						||
{
 | 
						||
    DWORD i;
 | 
						||
    for (i=0; i < cListLen; ++i) {
 | 
						||
       if (!lpHeader->Strnicmp(lpBase, lplpList[i], strlen(lplpList[i]))) {
 | 
						||
          return (TRUE);
 | 
						||
       }
 | 
						||
    }
 | 
						||
    return(FALSE);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef COMPRESSED_HEADERS
 | 
						||
DWORD
 | 
						||
LookupHeadermap(
 | 
						||
    LPSTR   lpszHeader
 | 
						||
)
 | 
						||
{
 | 
						||
    DWORD   top, mid, bottom, ret = 0;
 | 
						||
    int cmp;
 | 
						||
    LPSTR lpszColon;
 | 
						||
 | 
						||
    lpszColon = strchr(lpszHeader, ':');
 | 
						||
 | 
						||
    if (!lpszColon || (lpszColon == lpszHeader)) {
 | 
						||
        return(ret);
 | 
						||
    }
 | 
						||
 | 
						||
    // yuk
 | 
						||
    *lpszColon = 0;
 | 
						||
 | 
						||
    top = 1;
 | 
						||
 | 
						||
    bottom = sizeof(rgsHeaderMap)/sizeof(HEADER_MAP);
 | 
						||
 | 
						||
    INET_ASSERT(bottom >= top);
 | 
						||
 | 
						||
    do {
 | 
						||
 | 
						||
        mid = (bottom+top)/2;
 | 
						||
        if (!(cmp = stricmp(   rgsHeaderMap[mid].lpszLongHeader,
 | 
						||
                                lpszHeader
 | 
						||
                                ))) {
 | 
						||
            // we found a matching header,
 | 
						||
            ret = mid;
 | 
						||
            break;
 | 
						||
        }
 | 
						||
 | 
						||
        if (cmp > 0) {
 | 
						||
            // the mid header is larger than the passed in header
 | 
						||
            // so we must check at the upper end of the sorted array of headers
 | 
						||
            bottom = mid-1;
 | 
						||
        }
 | 
						||
        else {
 | 
						||
            // the mid header is smaller than the passed in header
 | 
						||
            // so we must check at the lower end of the sorted array of headers
 | 
						||
            top = mid+1;
 | 
						||
        }
 | 
						||
    } while (bottom >= top);
 | 
						||
 | 
						||
    *lpszColon = ':';
 | 
						||
    return (ret);
 | 
						||
}
 | 
						||
#endif //COMPRESSED_HEADERS
 | 
						||
 | 
						||
 | 
						||
//
 | 
						||
// HTTP_HEADER_PARSER implementation
 | 
						||
//
 | 
						||
 | 
						||
HTTP_HEADER_PARSER::HTTP_HEADER_PARSER(
 | 
						||
    IN LPSTR szHeaders,
 | 
						||
    IN DWORD cbHeaders
 | 
						||
    ) : HTTP_HEADERS()
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Constructor for the HTTP_HEADER_PARSER object.  Calls ParseHeaders to
 | 
						||
      build a parsed version of the header string passed in.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    szHeaders      - pointer to the headers to parse
 | 
						||
 | 
						||
    cbHeaders      - length of the headers
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
    DWORD dwBytesScaned = 0;
 | 
						||
    BOOL fFoundCompleteLine;
 | 
						||
    BOOL fFoundEndOfHeaders;
 | 
						||
    DWORD error;
 | 
						||
 | 
						||
    error = ParseHeaders(
 | 
						||
        szHeaders,
 | 
						||
        cbHeaders,
 | 
						||
        TRUE, // Eof
 | 
						||
        &dwBytesScaned,
 | 
						||
        &fFoundCompleteLine,
 | 
						||
        &fFoundEndOfHeaders
 | 
						||
        );
 | 
						||
 | 
						||
    INET_ASSERT(error == ERROR_SUCCESS);
 | 
						||
    INET_ASSERT(fFoundCompleteLine);
 | 
						||
    INET_ASSERT(fFoundEndOfHeaders);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/*  //  some test cases which can be used to test ParseStatusLine()
 | 
						||
char bad1[] = "HTTP1.1 200 Description yeah yeah\r\n";
 | 
						||
char bad2[] = "HTTP/1234.1 200 Description yeah yeah\r\n";
 | 
						||
char bad3[] = "HTTP/1.1234 200 Description yeah yeah\r\n";
 | 
						||
char bad4[] = "HTTP/1.1 1234 Description yeah yeah\r\n";
 | 
						||
char bad5[] = "HTTP/    1.1 200 Description yeah yeah\r\n";
 | 
						||
char bad6[] = "HTTP/1.1    200 Description yeah yeah\r\n";
 | 
						||
char bad7[] = "HTTP/1.1 200Description yeah yeah\r\n";
 | 
						||
char bad8[3000] = "HTTP/1.1 200 Description yeah yeah";
 | 
						||
char bad9[] = "HTTP/1 1.1 200 Description yeah yeah\r\n";
 | 
						||
char good1[] = "HTTP/   123.123   200 Description yeah yeah\r\n";
 | 
						||
*/
 | 
						||
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADER_PARSER::ParseStatusLine(
 | 
						||
    IN LPSTR lpHeaderBase,
 | 
						||
    IN DWORD dwBufferLength,
 | 
						||
    IN BOOL fEof,
 | 
						||
    IN OUT DWORD *lpdwBufferLengthScanned,
 | 
						||
    OUT BOOL *lpfNeedMoreBuffer,
 | 
						||
    OUT DWORD *lpdwStatusCode,
 | 
						||
    OUT DWORD *lpdwMajorVersion,
 | 
						||
    OUT DWORD *lpdwMinorVersion
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Parses the Status line of an HTTP server response.  Takes care of adding the status
 | 
						||
     line to HTTP header array.
 | 
						||
 | 
						||
    From HTTP v1.1. spec:
 | 
						||
    {
 | 
						||
      Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 | 
						||
      HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT
 | 
						||
      Status-Code = 1*DIGIT
 | 
						||
      Reason-Phrase = *<TEXT, excluding CR LF>
 | 
						||
      (1*DIGIT means at least one digit, maybe more)
 | 
						||
    }
 | 
						||
 | 
						||
    WinHTTP strictly enforces the status line spec.  The only exception is that up to 3
 | 
						||
    spaces are allowed before the Status-Code and major version number.
 | 
						||
 | 
						||
    To prevent malicious servers from hogging the channel, the integers are limited to
 | 
						||
    3 digits and the Reason-Phrase is limited to GlobalMaxSizeStatusLineResultText characters.
 | 
						||
 | 
						||
    "HTTP" could be other things like "S-HTTP", this is checked by UpdateFromHeaders()
 | 
						||
    before ParseStatusLine() is called.  The existence of the first '/' is verified before
 | 
						||
    ParseStatusLine is called.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeader      - pointer to the header to check
 | 
						||
 | 
						||
    dwHeaderLength  - length of the header
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    BOOL  - TRUE if line was successively parsed and processed, FALSE otherwise
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
{
 | 
						||
 | 
						||
#define BEFORE_VERSION_NUMBERS 0
 | 
						||
#define MAJOR_VERSION_NUMBER   1
 | 
						||
#define MINOR_VERSION_NUMBER   2
 | 
						||
#define STATUS_CODE_NUMBER     3
 | 
						||
#define AFTER_STATUS_CODE      4
 | 
						||
#define MAX_STATUS_INTS        4
 | 
						||
 | 
						||
    LPSTR lpszEnd = lpHeaderBase + dwBufferLength;
 | 
						||
    LPSTR response = lpHeaderBase + *lpdwBufferLengthScanned;
 | 
						||
    DWORD dwBytesScanned = 0;
 | 
						||
    DWORD dwStatusLineLength = 0;
 | 
						||
    LPSTR lpszStatusLine = NULL;
 | 
						||
    int ver_state = BEFORE_VERSION_NUMBERS;
 | 
						||
    BOOL afStatusIntsFound[MAX_STATUS_INTS] = {0};
 | 
						||
    DWORD adwStatusInts[MAX_STATUS_INTS] = {0};
 | 
						||
    DWORD dwStatusPieceLength = 0;
 | 
						||
    BOOL error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
 | 
						||
    lpszStatusLine = response;
 | 
						||
 | 
						||
    //
 | 
						||
    // While walking the Status Line looking for terminating \r\n,
 | 
						||
    //   we extract the Major.Minor Versions and Status Code in that order.
 | 
						||
    //   text and spaces will lie between/before/after the three numbers
 | 
						||
    //   but the idea is to remeber which number we're calculating based on a numeric state
 | 
						||
    //   If all goes well the loop will churn out an array with the 3 numbers plugged in as DWORDs
 | 
						||
    //
 | 
						||
 | 
						||
    while ((response < lpszEnd) && (*response != '\r') && (*response != '\n'))
 | 
						||
    {
 | 
						||
        switch (ver_state)
 | 
						||
        {
 | 
						||
            case BEFORE_VERSION_NUMBERS:
 | 
						||
                //
 | 
						||
                //  We've already matched the status line with something
 | 
						||
                //of the form "****/" in UpdateFromHeaders(), we can ignore everything
 | 
						||
                //through the first '/'.
 | 
						||
                //
 | 
						||
                if (*response == '/')
 | 
						||
                {
 | 
						||
                    INET_ASSERT(ver_state == BEFORE_VERSION_NUMBERS);
 | 
						||
                    ver_state++; // = MAJOR_VERSION_NUMBER
 | 
						||
                    dwStatusPieceLength = 0;  // next piece is either spaces or an int
 | 
						||
                }
 | 
						||
 | 
						||
                break;
 | 
						||
 | 
						||
            case MAJOR_VERSION_NUMBER:
 | 
						||
 | 
						||
                if (*response == '.' && ver_state == MAJOR_VERSION_NUMBER)
 | 
						||
                {
 | 
						||
                    ver_state++; // = MINOR_VERSION_NUMBER
 | 
						||
                    dwStatusPieceLength = 0;  // next piece is an int
 | 
						||
                    break;
 | 
						||
                }
 | 
						||
                // fall through
 | 
						||
 | 
						||
            case MINOR_VERSION_NUMBER:
 | 
						||
 | 
						||
                if (*response == ' ' && ver_state == MINOR_VERSION_NUMBER)
 | 
						||
                {
 | 
						||
                    ver_state++; // = STATUS_CODE_NUMBER
 | 
						||
                    dwStatusPieceLength = 0;  // next piece is either spaces or an int.
 | 
						||
                    break;
 | 
						||
                }
 | 
						||
                // fall through
 | 
						||
 | 
						||
            case STATUS_CODE_NUMBER:
 | 
						||
 | 
						||
                if (isdigit(*response))
 | 
						||
                {
 | 
						||
                    if (!afStatusIntsFound[ver_state])
 | 
						||
                    {
 | 
						||
                        //  transitioning from counting spaces
 | 
						||
                        //to counting integers
 | 
						||
                        dwStatusPieceLength = 0;
 | 
						||
                    }
 | 
						||
 | 
						||
                    //  Allow up to 3 digits per integer.
 | 
						||
                    if (++dwStatusPieceLength > 3)
 | 
						||
                        goto doneInvalidStatusLine;
 | 
						||
                    
 | 
						||
                    int val = *response - '0';
 | 
						||
                    afStatusIntsFound[ver_state] = TRUE;
 | 
						||
                    adwStatusInts[ver_state] = adwStatusInts[ver_state] * 10 + val;
 | 
						||
                }
 | 
						||
                else if ( adwStatusInts[STATUS_CODE_NUMBER] > 0 )
 | 
						||
                {
 | 
						||
                    INET_ASSERT(ver_state == STATUS_CODE_NUMBER);
 | 
						||
                    if (*response != ' ')
 | 
						||
                        goto doneInvalidStatusLine;
 | 
						||
                    ver_state++; // = AFTER_STATUS_CODE
 | 
						||
                    dwStatusPieceLength = 0;  // next piece is the status line
 | 
						||
                    break;
 | 
						||
                }
 | 
						||
                else if (*response == ' ' && !afStatusIntsFound[ver_state])
 | 
						||
                {
 | 
						||
                    //
 | 
						||
                    //  Before processing MAJOR_VERSION_NUMBER or STATUS_CODE_NUMBER,
 | 
						||
                    //allow up to 3 spaces.
 | 
						||
                    //
 | 
						||
                    //  Multiple spaces are being allowed here because it is
 | 
						||
                    //legacy behavior and may therefore be necessary, and being non-strict
 | 
						||
                    //about it doesn't put anything at risk.
 | 
						||
                    //
 | 
						||
                    if (++dwStatusPieceLength > 3)
 | 
						||
                        goto doneInvalidStatusLine;
 | 
						||
                }
 | 
						||
                else
 | 
						||
                {
 | 
						||
                    //  We fail if anything outside the spec is found, except
 | 
						||
                    //for allowing multiple spaces before the status code.
 | 
						||
                    goto doneInvalidStatusLine;
 | 
						||
                }
 | 
						||
 | 
						||
                break;
 | 
						||
 | 
						||
            case AFTER_STATUS_CODE:
 | 
						||
                //
 | 
						||
                //  This will advance to the next CR or LF..
 | 
						||
                //
 | 
						||
                //  We limit Reason-Phrase length to protect against malicious socket hogging
 | 
						||
                //
 | 
						||
 | 
						||
                if (++dwStatusPieceLength > GlobalMaxSizeStatusLineResultText)
 | 
						||
                {
 | 
						||
                    goto doneInvalidStatusLine;
 | 
						||
                }
 | 
						||
                break;
 | 
						||
 | 
						||
        }
 | 
						||
 | 
						||
        ++response;
 | 
						||
        ++dwBytesScanned;
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    //  Verify there is enough reponse left to check for a CRLF..
 | 
						||
    //
 | 
						||
    if (response == lpszEnd
 | 
						||
        || response + 1 == lpszEnd)
 | 
						||
    {
 | 
						||
        //
 | 
						||
        //
 | 
						||
        //  If we're at the end of the connection then the server sent us an
 | 
						||
        //incorrectly formatted response, Invalid status line.
 | 
						||
        //
 | 
						||
        //  If more data may come, indicate to retry it.
 | 
						||
        //
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("found end of short response in status line\n"
 | 
						||
                    ));
 | 
						||
 | 
						||
        if (fEof)
 | 
						||
            goto doneInvalidStatusLine;
 | 
						||
        else
 | 
						||
            goto doneNeedMoreData;
 | 
						||
    }
 | 
						||
 | 
						||
    dwStatusLineLength = dwBytesScanned;
 | 
						||
 | 
						||
    //
 | 
						||
    //  And to finish, verify the CRLF...
 | 
						||
    //
 | 
						||
 | 
						||
    //CR
 | 
						||
    // some servers may not send the CR so give them a pass for compatibility
 | 
						||
    if ( *response == '\r')
 | 
						||
    {
 | 
						||
        ++response;  //  we know its safe to step since we checked if (respone == lpszEnd) above.
 | 
						||
        ++dwBytesScanned;
 | 
						||
    }
 | 
						||
 | 
						||
    //LF
 | 
						||
    if ( *response != '\n')
 | 
						||
        goto doneInvalidStatusLine;
 | 
						||
    ++response;  //  we know its safe to step again since we checked if (respone+1 == lpszEnd) above.
 | 
						||
    ++dwBytesScanned;
 | 
						||
 | 
						||
    //
 | 
						||
    //  Some validation checking
 | 
						||
    //
 | 
						||
    //  All three status ints must have been found.
 | 
						||
    //  I found some code that assumes that if the Status Code == 0, then
 | 
						||
    //the status line hasn't been parsed yet.  To be sure this assumption
 | 
						||
    //remains true, explicitly reject status lines with a 0 status code.
 | 
						||
    //
 | 
						||
    if (afStatusIntsFound[MAJOR_VERSION_NUMBER] != TRUE
 | 
						||
        || afStatusIntsFound[MINOR_VERSION_NUMBER] != TRUE
 | 
						||
        || afStatusIntsFound[STATUS_CODE_NUMBER] != TRUE
 | 
						||
        || adwStatusInts[STATUS_CODE_NUMBER] == 0)
 | 
						||
    {
 | 
						||
        goto doneInvalidStatusLine;
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    // Now we have our parsed header to add to the array
 | 
						||
    //
 | 
						||
 | 
						||
    HEADER_STRING * freeHeader;
 | 
						||
    DWORD iSlot;
 | 
						||
 | 
						||
    freeHeader = FindFreeSlot(&iSlot);
 | 
						||
    if (freeHeader == NULL) {
 | 
						||
        goto doneFailError;
 | 
						||
    } else {
 | 
						||
        INET_ASSERT(iSlot == 0); // status line should always be first
 | 
						||
        freeHeader->CreateOffsetString((DWORD)(lpszStatusLine - lpHeaderBase), dwStatusLineLength);
 | 
						||
        freeHeader->SetHash(0); // status line has no hash value.
 | 
						||
    }
 | 
						||
 | 
						||
    //
 | 
						||
    //  Success..  fill in the output params appropriately.
 | 
						||
    //
 | 
						||
    
 | 
						||
    *lpfNeedMoreBuffer = FALSE;
 | 
						||
    *lpdwStatusCode    = adwStatusInts[STATUS_CODE_NUMBER];
 | 
						||
    *lpdwMajorVersion  = adwStatusInts[MAJOR_VERSION_NUMBER];
 | 
						||
    *lpdwMinorVersion  = adwStatusInts[MINOR_VERSION_NUMBER];
 | 
						||
 | 
						||
    *lpdwBufferLengthScanned += dwBytesScanned;
 | 
						||
 | 
						||
    error = ERROR_SUCCESS;
 | 
						||
    goto exitFinalReturn;
 | 
						||
 | 
						||
doneNeedMoreData:
 | 
						||
    error = ERROR_SUCCESS;
 | 
						||
    *lpfNeedMoreBuffer = TRUE;
 | 
						||
    goto exitFinalReturn;
 | 
						||
 | 
						||
doneInvalidStatusLine:
 | 
						||
    error = ERROR_HTTP_INVALID_SERVER_RESPONSE;
 | 
						||
    *lpfNeedMoreBuffer = FALSE;
 | 
						||
    goto exitFinalReturn;
 | 
						||
 | 
						||
doneFailError:
 | 
						||
    error = ERROR_INTERNET_INTERNAL_ERROR;
 | 
						||
    *lpfNeedMoreBuffer = FALSE;
 | 
						||
    goto exitFinalReturn;
 | 
						||
 | 
						||
exitFinalReturn:
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADER_PARSER::ParseHeaders(
 | 
						||
    IN LPSTR lpHeaderBase,
 | 
						||
    IN DWORD dwBufferLength,
 | 
						||
    IN BOOL fEof,
 | 
						||
    IN OUT DWORD *lpdwBufferLengthScanned,
 | 
						||
    OUT LPBOOL pfFoundCompleteLine,
 | 
						||
    OUT LPBOOL pfFoundEndOfHeaders
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Loads headers into HTTP_HEADERS member for subsequent parsing.
 | 
						||
 | 
						||
    Parses string based headers and adds their parts to an internally stored
 | 
						||
    array of HTTP_HEADERS.
 | 
						||
 | 
						||
    Input is assumed to be well formed Header Name/Value pairs, each deliminated
 | 
						||
    by ':' and '\r\n'.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeader      - pointer to the header to check
 | 
						||
 | 
						||
    dwHeaderLength  - length of the header
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
 | 
						||
{
 | 
						||
 | 
						||
    LPSTR lpszEnd = lpHeaderBase + dwBufferLength;
 | 
						||
    LPSTR response = lpHeaderBase + *lpdwBufferLengthScanned;
 | 
						||
    DWORD dwBytesScanned = 0;
 | 
						||
    BOOL success = FALSE;
 | 
						||
    DWORD error = ERROR_SUCCESS;
 | 
						||
 | 
						||
    *pfFoundEndOfHeaders  = FALSE;
 | 
						||
 | 
						||
    //
 | 
						||
    // Each iteration of the following loop
 | 
						||
    // walks an HTTP header line of the form:
 | 
						||
    //  HeaderName: HeaderValue\r\n
 | 
						||
    //
 | 
						||
 | 
						||
    do
 | 
						||
    {
 | 
						||
        DWORD dwHash = HEADER_HASH_SEED;
 | 
						||
        LPSTR lpszHeaderName;
 | 
						||
        DWORD dwHeaderNameLength = 0;
 | 
						||
        DWORD dwHeaderLineLength = 0;
 | 
						||
        DWORD dwPreviousAmountOfBytesScanned = dwBytesScanned;
 | 
						||
 | 
						||
        //
 | 
						||
        // Remove leading whitespace from header
 | 
						||
        //
 | 
						||
 | 
						||
        while ( (response < lpszEnd) && ((*response == ' ') || (*response == '\t')) )
 | 
						||
        {
 | 
						||
            ++response;
 | 
						||
            ++dwBytesScanned;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // Scan for HeaderName:
 | 
						||
        //
 | 
						||
 | 
						||
        lpszHeaderName = response;
 | 
						||
        dwPreviousAmountOfBytesScanned = dwBytesScanned;
 | 
						||
 | 
						||
        while ((response < lpszEnd) && (*response != ':') && (*response != '\r') && (*response != '\n'))
 | 
						||
        {
 | 
						||
            //
 | 
						||
            // This code incapsulates CalculateHashNoCase as an optimization,
 | 
						||
            //   we attempt to calculate the Hash value as we parse the header.
 | 
						||
            //
 | 
						||
 | 
						||
            CHAR ch = *response;
 | 
						||
 | 
						||
            if ((ch >= 'A') && (ch <= 'Z')) {
 | 
						||
                ch = MAKE_LOWER(ch);
 | 
						||
            }
 | 
						||
            dwHash += (DWORD)(dwHash << 5) + ch;
 | 
						||
 | 
						||
            ++response;
 | 
						||
            ++dwBytesScanned;
 | 
						||
        }
 | 
						||
 | 
						||
        dwHeaderNameLength = (DWORD) (response - lpszHeaderName);
 | 
						||
 | 
						||
        //
 | 
						||
        // catch bogus responses: if we find what looks like one of a (very)
 | 
						||
        // small set of HTML tags, then assume the previous header was the
 | 
						||
        // last
 | 
						||
        //
 | 
						||
 | 
						||
        if ((dwHeaderNameLength >= sizeof("<HTML>") - 1)
 | 
						||
            && (*lpszHeaderName == '<')
 | 
						||
            && (!strnicmp(lpszHeaderName, "<HTML>", sizeof("<HTML>") - 1)
 | 
						||
                || !strnicmp(lpszHeaderName, "<HEAD>", sizeof("<HEAD>") - 1))) {
 | 
						||
            *pfFoundEndOfHeaders  = TRUE;
 | 
						||
            break;
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // Keep scanning till end of the line.
 | 
						||
        //
 | 
						||
 | 
						||
        while ((response < lpszEnd) && (*response != '\r') && (*response != '\n'))
 | 
						||
        {
 | 
						||
            ++response;
 | 
						||
            ++dwBytesScanned;
 | 
						||
        }
 | 
						||
 | 
						||
        dwHeaderLineLength = (DWORD) (response - lpszHeaderName); // note: this headerLINElength
 | 
						||
 | 
						||
        if (response == lpszEnd) {
 | 
						||
 | 
						||
            //
 | 
						||
            // response now points one past the end of the buffer. We may be looking
 | 
						||
            // over the edge...
 | 
						||
            //
 | 
						||
            // if we're at the end of the connection then the server sent us an
 | 
						||
            // incorrectly formatted response. Probably an error.
 | 
						||
            //
 | 
						||
            // Otherwise its a partial response. We need more
 | 
						||
            //
 | 
						||
 | 
						||
 | 
						||
            DEBUG_PRINT(HTTP,
 | 
						||
                        INFO,
 | 
						||
                        ("found end of short response\n"
 | 
						||
                        ));
 | 
						||
 | 
						||
            success = fEof ? TRUE : FALSE;
 | 
						||
 | 
						||
            //
 | 
						||
            // if we really hit the end of the response then update the amount of
 | 
						||
            // headers scanned
 | 
						||
            //
 | 
						||
 | 
						||
            if (!success) {
 | 
						||
                dwBytesScanned = dwPreviousAmountOfBytesScanned;
 | 
						||
            }
 | 
						||
 | 
						||
            break;
 | 
						||
 | 
						||
        }
 | 
						||
        else
 | 
						||
        {
 | 
						||
 | 
						||
            //
 | 
						||
            // we reached a CR or LF. This is the end of this current header. Find
 | 
						||
            // the start of the next one
 | 
						||
            //
 | 
						||
 | 
						||
            //
 | 
						||
            // first, strip off any trailing spaces from the current header. We do
 | 
						||
            // this by simply reducing the string length. We only look for space
 | 
						||
            // and tab characters. Only do this if we have a non-zero length header
 | 
						||
            //
 | 
						||
 | 
						||
            if (dwHeaderLineLength != 0) {
 | 
						||
                for (int i = -1; response[i] == ' ' || response[i] == '\t'; --i) {
 | 
						||
                    --dwHeaderLineLength;
 | 
						||
                }
 | 
						||
            }
 | 
						||
 | 
						||
            INET_ASSERT((int)dwHeaderLineLength >= 0);
 | 
						||
 | 
						||
            //
 | 
						||
            // some servers respond with "\r\r\n". Lame
 | 
						||
            // A new twist: "\r \r\n". Lamer
 | 
						||
            //
 | 
						||
 | 
						||
            while ((response < lpszEnd)
 | 
						||
            && ((*response == '\r') || (*response == ' '))) {
 | 
						||
                ++response;
 | 
						||
                ++dwBytesScanned;
 | 
						||
            }
 | 
						||
            if (response == lpszEnd) {
 | 
						||
 | 
						||
                //
 | 
						||
                // hit end of buffer without finding LF
 | 
						||
                //
 | 
						||
 | 
						||
                success = FALSE;
 | 
						||
 | 
						||
                DEBUG_PRINT(HTTP,
 | 
						||
                            WARNING,
 | 
						||
                            ("hit end of buffer without finding LF\n"
 | 
						||
                            ));
 | 
						||
 | 
						||
                //
 | 
						||
                // get more data, reparse this line
 | 
						||
                //
 | 
						||
 | 
						||
                dwBytesScanned = dwPreviousAmountOfBytesScanned;
 | 
						||
                break;
 | 
						||
            } else if (*response == '\n') {
 | 
						||
                ++response;
 | 
						||
                ++dwBytesScanned;
 | 
						||
 | 
						||
                //
 | 
						||
                // if we found the empty line then we are done
 | 
						||
                //
 | 
						||
 | 
						||
                if (dwHeaderLineLength == 0) {
 | 
						||
                    *pfFoundEndOfHeaders  = TRUE;
 | 
						||
                    break;
 | 
						||
                }
 | 
						||
 | 
						||
                success = TRUE;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        //
 | 
						||
        // Now we have our parsed header to add to the array
 | 
						||
        //
 | 
						||
 | 
						||
        HEADER_STRING * freeHeader;
 | 
						||
        DWORD iSlot;
 | 
						||
 | 
						||
        freeHeader = FindFreeSlot(&iSlot);
 | 
						||
        if (freeHeader == NULL) {
 | 
						||
            error = GetError();
 | 
						||
 | 
						||
            INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
            goto quit;
 | 
						||
 | 
						||
        } else {
 | 
						||
            freeHeader->CreateOffsetString((DWORD) (lpszHeaderName - lpHeaderBase), dwHeaderLineLength);
 | 
						||
            freeHeader->SetHash(dwHash);
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        //CHAR szTemp[256];
 | 
						||
        //
 | 
						||
        //memcpy(szTemp, lpszHeaderName, dwHeaderLineLength);
 | 
						||
        //lpszHeaderName[dwHeaderLineLength] = '\0';
 | 
						||
 | 
						||
        //DEBUG_PRINT(HTTP,
 | 
						||
        //    INFO,
 | 
						||
        //    ("ParseHeaders: adding=%q\n", lpszHeaderName
 | 
						||
        //    ));
 | 
						||
 | 
						||
 | 
						||
        //
 | 
						||
        // Now see if this is a known header we are adding, if so then we note that fact
 | 
						||
        //
 | 
						||
 | 
						||
        DWORD dwKnownQueryIndex;
 | 
						||
 | 
						||
        if (HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwKnownQueryIndex) )
 | 
						||
        {
 | 
						||
            freeHeader->SetNextKnownIndex(FastAdd(dwKnownQueryIndex, iSlot));
 | 
						||
        }
 | 
						||
    } while (TRUE);
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
    *lpdwBufferLengthScanned += dwBytesScanned;
 | 
						||
    *pfFoundCompleteLine = success;
 | 
						||
 | 
						||
    return error;
 | 
						||
}
 | 
						||
 | 
						||
#if 0
 | 
						||
//
 | 
						||
// Slower version of the function above used for performance work!!! Keep around
 | 
						||
//   until we're sure it will never be used.
 | 
						||
//
 | 
						||
 | 
						||
DWORD
 | 
						||
HTTP_HEADER_PARSER::ParseHeaders(
 | 
						||
    IN LPSTR lpHeaderBase,
 | 
						||
    IN DWORD dwBufferLength,
 | 
						||
    IN BOOL fEof,
 | 
						||
    IN OUT DWORD *lpdwBufferLengthScanned,
 | 
						||
    OUT LPBOOL pfFoundCompleteLine,
 | 
						||
    OUT LPBOOL pfFoundEndOfHeaders
 | 
						||
    )
 | 
						||
 | 
						||
/*++
 | 
						||
 | 
						||
Routine Description:
 | 
						||
 | 
						||
    Parses string based headers and adds their parts to an internally stored
 | 
						||
    array of HTTP_HEADERS.
 | 
						||
 | 
						||
 | 
						||
    Input is assumed to be well formed Header Name/Value pairs, each deliminated
 | 
						||
    by ':' and '\r\n'.
 | 
						||
 | 
						||
Arguments:
 | 
						||
 | 
						||
    lpszHeader      - pointer to the header to check
 | 
						||
 | 
						||
    dwHeaderLength  - length of the header
 | 
						||
 | 
						||
Return Value:
 | 
						||
 | 
						||
    None.
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
 | 
						||
{
 | 
						||
 | 
						||
#define HTTP_HEADER_PARSE_LEADING_SPACE 0
 | 
						||
#define HTTP_HEADER_PARSE_NAME 1
 | 
						||
#define HTTP_HEADER_PARSE_VALUE 2
 | 
						||
#define HTTP_HEADER_PARSE_CR 3
 | 
						||
#define HTTP_HEADER_PARSE_LF 4
 | 
						||
 | 
						||
 | 
						||
    LPSTR lpszEnd = lpHeaderBase + dwBufferLength;
 | 
						||
    LPSTR response = lpHeaderBase + *lpdwBufferLengthScanned;
 | 
						||
    DWORD dwBytesScanned = 0;
 | 
						||
    BOOL success = FALSE;
 | 
						||
    DWORD error = ERROR_SUCCESS;
 | 
						||
    DWORD state = HTTP_HEADER_PARSE_LEADING_SPACE;
 | 
						||
 | 
						||
    *pfFoundEndOfHeaders  = FALSE;
 | 
						||
 | 
						||
    //
 | 
						||
    // Each iteration of the following loop
 | 
						||
    // walks an HTTP header line of the form:
 | 
						||
    //  HeaderName: HeaderValue\r\n
 | 
						||
    //
 | 
						||
 | 
						||
        DWORD dwHash = HEADER_HASH_SEED;
 | 
						||
        LPSTR lpszHeaderName;
 | 
						||
        DWORD dwHeaderNameLength = 0;
 | 
						||
        DWORD dwHeaderLineLength = 0;
 | 
						||
        DWORD dwPreviousAmountOfBytesScanned = dwBytesScanned;
 | 
						||
        DWORD dwWhiteSpace = 0;
 | 
						||
 | 
						||
 | 
						||
        while ( (response < lpszEnd) )
 | 
						||
        {
 | 
						||
            switch (state)
 | 
						||
            {
 | 
						||
 | 
						||
                case HTTP_HEADER_PARSE_LEADING_SPACE:
 | 
						||
 | 
						||
                    //
 | 
						||
                    // Remove leading whitespace from header
 | 
						||
                    //
 | 
						||
 | 
						||
                    if ( *response == ' ' ||
 | 
						||
                         *response == '\t' )
 | 
						||
                    {
 | 
						||
                        break;
 | 
						||
                    }
 | 
						||
 | 
						||
                    //
 | 
						||
                    // Scan for HeaderName:
 | 
						||
                    //
 | 
						||
 | 
						||
                    state = HTTP_HEADER_PARSE_NAME;
 | 
						||
                    lpszHeaderName = response;
 | 
						||
                    dwPreviousAmountOfBytesScanned = dwBytesScanned;
 | 
						||
 | 
						||
                    // fall through
 | 
						||
 | 
						||
                case HTTP_HEADER_PARSE_NAME:
 | 
						||
 | 
						||
                    switch (*response)
 | 
						||
                    {
 | 
						||
                        case ':':
 | 
						||
                            //
 | 
						||
                            // Now parse the Header Value
 | 
						||
                            //
 | 
						||
 | 
						||
                            state = HTTP_HEADER_PARSE_VALUE;
 | 
						||
                            dwHeaderNameLength = (DWORD) (response - lpszHeaderName);
 | 
						||
                            break;
 | 
						||
 | 
						||
 | 
						||
                        case '\r':
 | 
						||
                            state = HTTP_HEADER_PARSE_CR;
 | 
						||
                            // note: this is headerLINElength
 | 
						||
                            dwHeaderLineLength = (DWORD) (response - lpszHeaderName);
 | 
						||
                            break;
 | 
						||
 | 
						||
                        case '\n':
 | 
						||
 | 
						||
                            state = HTTP_HEADER_PARSE_LF;
 | 
						||
                            // note: this is headerLINElength
 | 
						||
                            dwHeaderLineLength = (DWORD) (response - lpszHeaderName);
 | 
						||
                            goto Got_LF;
 | 
						||
                            //break;
 | 
						||
 | 
						||
                        default:
 | 
						||
                        {
 | 
						||
                            CHAR ch = *response;
 | 
						||
 | 
						||
                            if ((ch >= 'A') && (ch <= 'Z')) {
 | 
						||
                                ch = MAKE_LOWER(ch);
 | 
						||
                            }
 | 
						||
                            dwHash += (DWORD)(dwHash << 5) + ch;
 | 
						||
 | 
						||
                            break;
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
 | 
						||
                    break;
 | 
						||
 | 
						||
                case HTTP_HEADER_PARSE_VALUE:
 | 
						||
 | 
						||
                    switch ( *response )
 | 
						||
                    {
 | 
						||
                        case '\r':
 | 
						||
                            state = HTTP_HEADER_PARSE_CR;
 | 
						||
                            // note: this is headerLINElength
 | 
						||
                            dwHeaderLineLength = (DWORD) (response - lpszHeaderName) - dwWhiteSpace;
 | 
						||
                            break;
 | 
						||
 | 
						||
                        case '\n':
 | 
						||
 | 
						||
                            state = HTTP_HEADER_PARSE_LF;
 | 
						||
                            // note: this is headerLINElength
 | 
						||
                            dwHeaderLineLength = (DWORD) (response - lpszHeaderName) - dwWhiteSpace;
 | 
						||
                            goto Got_LF;
 | 
						||
 | 
						||
                        case ' ':
 | 
						||
                        case '\t':
 | 
						||
                            // count whitespace at end of the line
 | 
						||
                            dwWhiteSpace++;
 | 
						||
                            break;
 | 
						||
 | 
						||
                        default:
 | 
						||
                            dwWhiteSpace = 0;
 | 
						||
                            break;
 | 
						||
                    }
 | 
						||
 | 
						||
                    break;
 | 
						||
 | 
						||
                case HTTP_HEADER_PARSE_CR:
 | 
						||
 | 
						||
                    if (*response == ' ' ||
 | 
						||
                        *response == '\r' )
 | 
						||
                    {
 | 
						||
                        break;
 | 
						||
                    }
 | 
						||
 | 
						||
                    state = HTTP_HEADER_PARSE_LF;
 | 
						||
                    // fall through
 | 
						||
 | 
						||
                case HTTP_HEADER_PARSE_LF:
 | 
						||
 | 
						||
Got_LF:
 | 
						||
 | 
						||
                    //
 | 
						||
                    // if we found the empty line then we are done
 | 
						||
                    //
 | 
						||
 | 
						||
                    success = TRUE;
 | 
						||
 | 
						||
                    if (dwHeaderLineLength == 0) {
 | 
						||
                        ++dwBytesScanned;
 | 
						||
                        ++response;
 | 
						||
                        *pfFoundEndOfHeaders  = TRUE;
 | 
						||
                        goto quit;
 | 
						||
                    }
 | 
						||
 | 
						||
 | 
						||
                    {
 | 
						||
                        //
 | 
						||
                        // Now we have our parsed header to add to the array
 | 
						||
                        //
 | 
						||
 | 
						||
                        HEADER_STRING * freeHeader;
 | 
						||
                        DWORD iSlot;
 | 
						||
 | 
						||
                        freeHeader = FindFreeSlot(&iSlot);
 | 
						||
                        if (freeHeader == NULL) {
 | 
						||
                            error = GetError();
 | 
						||
 | 
						||
                            INET_ASSERT(error != ERROR_SUCCESS);
 | 
						||
                            goto quit;
 | 
						||
 | 
						||
                        } else {
 | 
						||
                            freeHeader->CreateOffsetString((lpszHeaderName - lpHeaderBase), dwHeaderLineLength);
 | 
						||
                            freeHeader->SetHash(dwHash);
 | 
						||
                        }
 | 
						||
 | 
						||
                        CHAR szTemp[256];
 | 
						||
 | 
						||
                        memcpy(szTemp, lpszHeaderName, dwHeaderLineLength);
 | 
						||
                        szTemp[dwHeaderLineLength] = '\0';
 | 
						||
 | 
						||
                        DEBUG_PRINT(HTTP,
 | 
						||
                            INFO,
 | 
						||
                            ("ParseHeaders: adding=%q\n", lpszHeaderName
 | 
						||
                            ));
 | 
						||
 | 
						||
 | 
						||
                        //
 | 
						||
                        // Now see if this is a known header we are adding, if so then we note that fact
 | 
						||
                        //
 | 
						||
 | 
						||
                        DWORD dwKnownQueryIndex;
 | 
						||
 | 
						||
                        if (HeaderMatch(dwHash, lpszHeaderName, dwHeaderNameLength, &dwKnownQueryIndex) )
 | 
						||
                        {
 | 
						||
                            freeHeader->SetNextKnownIndex(FastAdd(dwKnownQueryIndex, iSlot));
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
 | 
						||
                    //
 | 
						||
                    // Move on to our next header.
 | 
						||
                    //
 | 
						||
 | 
						||
                    dwHash = HEADER_HASH_SEED;
 | 
						||
                    dwHeaderNameLength = 0;
 | 
						||
                    dwHeaderLineLength = 0;
 | 
						||
                    dwPreviousAmountOfBytesScanned = dwBytesScanned;
 | 
						||
                    dwWhiteSpace = 0;
 | 
						||
 | 
						||
                    state = HTTP_HEADER_PARSE_LEADING_SPACE;
 | 
						||
 | 
						||
                    break;
 | 
						||
 | 
						||
            } // switch (state)
 | 
						||
 | 
						||
            ++response;
 | 
						||
            ++dwBytesScanned;
 | 
						||
 | 
						||
        } // while (response < lpszEnd)
 | 
						||
 | 
						||
 | 
						||
        //
 | 
						||
        // response now points one past the end of the buffer. We may be looking
 | 
						||
        // over the edge...
 | 
						||
        //
 | 
						||
        // if we're at the end of the connection then the server sent us an
 | 
						||
        // incorrectly formatted response. Probably an error.
 | 
						||
        //
 | 
						||
        // Otherwise its a partial response. We need more
 | 
						||
        //
 | 
						||
 | 
						||
 | 
						||
        DEBUG_PRINT(HTTP,
 | 
						||
                    INFO,
 | 
						||
                    ("found end of short response\n"
 | 
						||
                    ));
 | 
						||
 | 
						||
        success = fEof ? TRUE : FALSE;
 | 
						||
 | 
						||
        //
 | 
						||
        // if we really hit the end of the response then update the amount of
 | 
						||
        // headers scanned
 | 
						||
        //
 | 
						||
 | 
						||
        if (!success) {
 | 
						||
            dwBytesScanned = dwPreviousAmountOfBytesScanned;
 | 
						||
        }
 | 
						||
 | 
						||
quit:
 | 
						||
 | 
						||
  *lpdwBufferLengthScanned += dwBytesScanned;
 | 
						||
  *pfFoundCompleteLine    = success;
 | 
						||
 | 
						||
  return error;
 | 
						||
}
 | 
						||
 | 
						||
#endif
 |