|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
headers.cxx
Abstract:
Contents:
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"
VOID HTTP_REQUEST_HANDLE_OBJECT::ReplaceStatusHeader( IN LPCSTR lpszStatus ) /*
Description: Replace the status line in a header (eg. "200 OK") with the specified text. Arguments: lpszStatus - Status text (eg. "200 OK") Return Value: None */ { LockHeaders(); //INET_ASSERT (!_CacheWriteInProgress);
LPSTR pszHeader = _ResponseHeaders.GetHeaderPointer(_ResponseBuffer, 0); INET_ASSERT(pszHeader); LPSTR pszStatus = StrChr (pszHeader, ' '); SKIPWS(pszStatus); INET_ASSERT (!memcmp(pszStatus, "206", 3)); memcpy(pszStatus, lpszStatus, lstrlen(lpszStatus)+1); _ResponseHeaders.ShrinkHeader(_ResponseBuffer, 0, HTTP_QUERY_STATUS_TEXT, HTTP_QUERY_STATUS_TEXT, (DWORD) (pszStatus - pszHeader) + lstrlen(lpszStatus)); UnlockHeaders(); }
LPSTR HTTP_REQUEST_HANDLE_OBJECT::CreateRequestBuffer( OUT LPDWORD lpdwRequestLength, IN LPVOID lpOptional, IN DWORD dwOptionalLength, 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
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, dwMaxPacketLength, lpbCombinedData ));
PERF_ENTER(CreateRequestBuffer);
LPSTR requestBuffer = NULL;
*lpbCombinedData = FALSE;
if (!_RequestHeaders.LockHeaders()) { goto quit; }
DWORD headersLength; DWORD requestLength; DWORD optionalLength; HEADER_STRING * pRequest = _RequestHeaders.GetFirstHeader(); HEADER_STRING & request = *pRequest; /*
WCHAR wszUrl[1024]; LPWSTR pwszUrl = NULL; BYTE utf8Url[2048]; LPBYTE pbUrl = NULL; */ LPSTR pszObject = _RequestHeaders.ObjectName(); DWORD dwObjectLength = _RequestHeaders.ObjectNameLength();
if (pRequest == NULL) { goto Cleanup; } UNREFERENCED_PARAMETER(request); 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 = dwOptionalLength; if (requestLength + optionalLength <= dwMaxPacketLength) { requestLength += optionalLength; } else { optionalLength = 0; }
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; requestBuffer = (LPSTR)ResizeBuffer(NULL, requestLength, FALSE); } if (requestBuffer != NULL) {
LPSTR buffer = requestBuffer;
//
// copy the headers. Remember: header 0 is the request
//
if (ERROR_SUCCESS != _RequestHeaders.CopyHeaders(&buffer, pszObject, dwObjectLength)) { ResizeBuffer(requestBuffer, 0, FALSE); requestBuffer = buffer = NULL; goto Cleanup; }
//
// terminate the request
//
*buffer++ = '\r'; *buffer++ = '\n';
if (optionalLength != 0) { if (dwOptionalLength != 0) { memcpy(buffer, lpOptional, dwOptionalLength); buffer += dwOptionalLength; } }
INET_ASSERT((SIZE_T)(buffer-requestBuffer) == requestLength);
*lpdwRequestLength = requestLength;
}
Cleanup:
_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); } */
quit: PERF_LEAVE(CreateRequestBuffer);
DEBUG_LEAVE(requestBuffer);
return requestBuffer; }
DWORD HTTP_REQUEST_HANDLE_OBJECT::QueryRequestHeader( IN LPCSTR 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_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_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;
//
// 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
//
if (!_ResponseHeaders.LockHeaders()) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; }
//
// 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 { error = ERROR_WINHTTP_INVALID_SERVER_RESPONSE; goto Cleanup; } } }
//
// 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 Cleanup; }
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 Cleanup; }
if (fNeedMoreBuffer) { error = ERROR_SUCCESS; goto Cleanup; }
DEBUG_PRINT(HTTP, INFO, ("Version = %d.%d\n", majorVersion, minorVersion ));
DEBUG_PRINT(HTTP, INFO, ("_StatusCode = %d\n", _StatusCode ));
fSupportsHttp1_1 = FALSE;
if (majorVersion != 1) { // WinHttp shouldn't accept a response with a major version greater than 1.
// The protocol can change drastically between major versions, we'd have
//no idea how to parse a major version of 2. Accepting 1.x responses is OK,
//the protocol won't change in a non-compatible way across minor versions.
//
// WinHttp should not accept a major version of 0 as it is usupported.
//
error = ERROR_HTTP_INVALID_SERVER_RESPONSE; goto Cleanup; } 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(GetMaxConnectionsPerServer(WINHTTP_OPTION_MAX_CONNS_PER_SERVER)); } else { pServerInfo->SetHttp1_0();
//
// Set the max connections per HTTP 1.0 server.
//
pServerInfo->SetNewLimit(GetMaxConnectionsPerServer(WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER)); } } }
//
// 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 > _dwMaxResponseHeaderSize ? _dwMaxResponseHeaderSize : _BytesReceived, _BytesReceived > _dwMaxResponseHeaderSize ? FALSE : IsEof(), &_ResponseScanned, &success, &fFoundEndOfHeaders );
if ( error != ERROR_SUCCESS ) { goto Cleanup; }
if ( !fFoundEndOfHeaders && _BytesReceived > _dwMaxResponseHeaderSize) { error = ERROR_WINHTTP_HEADER_SIZE_OVERFLOW; goto Cleanup; }
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()) { error = CheckWellKnownHeaders(); if (ERROR_SUCCESS != error) { goto Cleanup; } *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(); } } }
Cleanup:
//
// 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();
quit: 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_WINHTTP_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_WINHTTP_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 DWORD 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:
DWORD Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{ DEBUG_ENTER((DBG_HTTP, None, "HTTP_REQUEST_HANDLE_OBJECT::CheckWellKnownHeaders", NULL ));
DWORD dwError = ERROR_SUCCESS;
//
// check for "Content-Length:"
//
if ( IsResponseHeaderPresent(HTTP_QUERY_CONTENT_LENGTH) ) { HEADER_STRING * curHeader; DWORD dwHeaderLength; LPSTR lpszHeader;
DWORD 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)
//
// MSXML3 bug 56001: commenting-out this assert; it's informational
// only and ignorable.
// 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;
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; }
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 "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
//
//
//TODO We really should be able to handle multiple Transfer-Encoding headers.
// (perhaps accepting an 'identity' encoding followed by a 'chunked' encoding
// and recognizing that it is a chunked response, or rejecting a 'chunked' encoding
// followed by an invalid encoding)
//
if (((int)dwHeaderLength >= CHUNKED_LEN) && !strnicmp(lpszHeader, CHUNKED_SZ, CHUNKED_LEN)) { INTERNET_HANDLE_OBJECT* pRoot = GetRootHandle(this); DWORD_PTR dwChunkFilterCtx = 0;
// Now that we know this is a chunked response, allocate
// a decoder context for parsing the data later. If anything
// fails here, the request needs to fail.
if (S_OK != pRoot->_ChunkFilter.RegisterContextEx(&dwChunkFilterCtx, _dwMaxResponseHeaderSize)) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto quit; } else if (!_ResponseFilterList.Insert(&pRoot->_ChunkFilter, dwChunkFilterCtx)) { pRoot->_ChunkFilter.UnregisterContext(dwChunkFilterCtx); dwError = ERROR_NOT_ENOUGH_MEMORY; goto quit; }
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
//
DEBUG_PRINT(HTTP, INFO, ("server sent both Content-Length and Transfer-Encoding: chunked headers\n" ));
if (IsContentLength()) { SetHaveContentLength(FALSE); }
} else if (dwHeaderLength >= (DWORD)IDENTITY_LEN && !strnicmp(lpszHeader, IDENTITY_SZ, IDENTITY_LEN)) { //ignore
} else { //
// We can't be sure when we've reached the end of this request,
//and so we'll disallow keep-alive to prevent the next response
//from being corrupted.
//
SetNoLongerKeepAlive(); } }
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; _BytesInSocket = 0; _BytesRemaining = 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); } }
quit: DEBUG_LEAVE(dwError); return dwError; }
//
// 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
|