📄 response.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: RESPONSE.CPP
Abstract: Handles response to client.
--*/
#include "httpd.h"
const CHAR cszClose[] = "close";
#define APPENDCRLF(psz) { *psz++ = '\r'; *psz++ = '\n'; }
// ***NOTE***: The order of items in this table must match that in enum RESPONSESTATUS.
// If any additional entries are added they must match this enum, and update httpd.rc as well.
STATUSINFO rgStatus[STATUS_MAX] = {
{ 200, "OK", NULL, FALSE},
{ 302, "Object Moved", NULL, TRUE},
{ 304, "Not Modified", NULL, FALSE},
{ 400, "Bad Request", NULL, TRUE},
{ 401, "Unauthorized", NULL, TRUE},
{ 403, "Forbidden", NULL, TRUE},
{ 404, "Object Not Found", NULL, TRUE},
{ 500, "Internal Server Error", NULL, TRUE},
{ 501, "Not Implemented", NULL, TRUE},
// status codes added for WebDAV support.
{ 201, "Created", NULL, FALSE},
{ 204, "No Content", NULL, FALSE},
{ 207, "Multi-Status", NULL, FALSE},
{ 405, "Method Not Allowed", NULL, TRUE},
{ 409, "Conflict", NULL, TRUE},
{ 411, "Length Required", NULL, TRUE},
{ 412, "Precondition Failed", NULL, TRUE},
{ 413, "Request Entity Too Large", NULL, TRUE},
{ 415, "Unsupported Media Type", NULL, TRUE},
{ 423, "Locked", NULL, TRUE},
{ 507, "Insufficient Storage", NULL, TRUE}
};
#define LCID_USA MAKELANGID(LAND_ENGLISH, SUBLANG_ENGLISH_US);
DWORD GetETagFromFiletime(FILETIME *pft, DWORD dwFileSize, PSTR szWriteBuffer) {
return sprintf (szWriteBuffer,"\"%x%x%x%x%x%x%x%x:%x:%x\"",
(DWORD)(((PUCHAR)pft)[0]),(DWORD)(((PUCHAR)pft)[1]),
(DWORD)(((PUCHAR)pft)[2]),(DWORD)(((PUCHAR)pft)[3]),
(DWORD)(((PUCHAR)pft)[4]),(DWORD)(((PUCHAR)pft)[5]),
(DWORD)(((PUCHAR)pft)[6]),(DWORD)(((PUCHAR)pft)[7]),
dwFileSize,
g_pVars->m_dwSystemChangeNumber);
}
void CHttpResponse::SendHeadersAndDefaultBodyIfAvailable(PCSTR pszExtraHeaders, PCSTR pszNewRespStatus) {
DEBUG_CODE_INIT;
CHAR szBuf[HEADERBUFSIZE - 100]; // Subtract a little to keep stack size of this < 4KB.
PSTR pszBuf = szBuf;
int iLen;
PSTR pszTrav;
DWORD cbNeeded;
SafeInt<DWORD> cbNeededSafe;
if ( g_pVars->m_fFilters &&
! m_pRequest->CallFilter(SF_NOTIFY_SEND_RESPONSE))
myleave(97);
// For HTTP 0.9, we don't send headers back.
if (m_pRequest->m_dwVersion <= MAKELONG(9, 0))
myleave(0);
// We do calculation up front to see if we need a allocate a buffer or not.
// Certain may be set by an ISAPI Extension, Filter, or ASP
// and we must do the check to make sure their sum plus http headers server
// uses will be less than buffer we created.
// Note: we don't use , even though SafeInt throws exceptions
// on error cases, because the entire HTTP request is wrapped in a __try/__
cbNeededSafe = m_pRequest->m_bufRespHeaders.Count();
cbNeededSafe += MyStrlenA(pszExtraHeaders);
cbNeededSafe += MyStrlenA(pszNewRespStatus);
cbNeededSafe += MyStrlenA(m_pszRedirect);
cbNeededSafe += MyStrlenA(m_pRequest->m_pszSecurityOutBuf);
cbNeededSafe += ((m_pRequest->m_pFInfo && m_pRequest->m_rs == STATUS_UNAUTHORIZED) ?
MyStrlenA(m_pRequest->m_pFInfo->m_pszDenyHeader) : 0);
if (VERB_HEAD != m_pRequest->m_idMethod)
cbNeededSafe += MyStrlenA(m_pszBody);
cbNeededSafe += 1000; // Add 1000 for extra HTTP headers this function adds. Always well < this.
cbNeeded = cbNeededSafe.Value(); // Store in a regular DWORD for use outside this block.
// If what we need is > header buf size,
if (cbNeeded > sizeof(szBuf)) {
if (!(pszBuf = MyRgAllocNZ(CHAR,cbNeeded)))
myleave(98);
}
pszTrav = pszBuf;
// the status line
// ServerSupportFunction on SEND_RESPONSE_HEADER can override the server value
// NOTE: using sprintf on user supplied strings is very dangerous as there may be
// sprintf qualifiers in it, so we'll be safe and strcpy.
if (pszNewRespStatus) {
pszTrav += sprintf(pszTrav,"HTTP/1.%d ",m_pRequest->MinorHTTPStatus());
pszTrav = strcpyEx(pszTrav,pszNewRespStatus);
APPENDCRLF(pszTrav);
}
else
pszTrav += sprintf(pszTrav, cszRespStatus,m_pRequest->MinorHTTPStatus(), rgStatus[m_pRequest->m_rs].dwStatusNumber, rgStatus[m_pRequest->m_rs].pszStatusText);
// GENERAL HEADERS
// the Date line
SYSTEMTIME st;
GetSystemTime(&st); // NOTE: Must be GMT!
pszTrav = strcpyEx(pszTrav, cszRespDate);
pszTrav += WriteDateGMT(pszTrav,&st,TRUE);
// Connection: header
if (m_connhdr != CONN_NONE) {
pszTrav += sprintf(pszTrav, cszConnectionResp, (m_connhdr==CONN_KEEP ? cszKeepAlive : cszClose));
}
// RESPONSE HEADERS
// the server line
pszTrav += sprintf(pszTrav, cszRespServer, m_pRequest->m_pWebsite->m_szServerID);
// the Location header (for redirects)
if (m_pszRedirect) {
DEBUGCHK(m_pRequest->m_rs == STATUS_MOVED);
pszTrav = strcpyEx(pszTrav, cszRespLocation);
pszTrav = strcpyEx(pszTrav, m_pszRedirect);
APPENDCRLF(pszTrav);
}
// the WWW-Authenticate line
if (m_pRequest->m_rs == STATUS_UNAUTHORIZED && m_pRequest->SendAuthenticate()) {
// In the middle of an NTLM/negotiate authentication session, send back security context info to client.
if (m_pRequest->m_pszSecurityOutBuf) {
if (m_pRequest->m_AuthState.m_AuthType==AUTHTYPE_NTLM) {
pszTrav += sprintf(pszTrav,NTLM_AUTH_FORMAT " %s\r\n",NTLM_AUTH_STR,m_pRequest->m_pszSecurityOutBuf);
}
else if (m_pRequest->m_AuthState.m_AuthType==AUTHTYPE_NEGOTIATE) {
pszTrav += sprintf(pszTrav,NEGOTIATE_AUTH_FORMAT " %s\r\n",NEGOTIATE_AUTH_STR,m_pRequest->m_pszSecurityOutBuf);
}
else {
DEBUGCHK(0);
}
}
else {
// If we're not in the middle of security session, send back all security session types we support.
if (m_pRequest->AllowBasic())
pszTrav += sprintf(pszTrav,BASIC_AUTH_FORMAT "\r\n",BASIC_AUTH_STR(g_pVars->m_szBasicRealm));
if (m_pRequest->AllowNTLM())
pszTrav += sprintf(pszTrav,NTLM_AUTH_FORMAT "\r\n",NTLM_AUTH_STR);
if (m_pRequest->AllowNegotiate())
pszTrav += sprintf(pszTrav,NEGOTIATE_AUTH_FORMAT "\r\n",NEGOTIATE_AUTH_STR);
}
}
// ENTITY HEADERS
// the Last-Modified line
if (m_hFile) {
WIN32_FILE_ATTRIBUTE_DATA fData;
if( GetFileAttributesEx(m_pRequest->m_wszPath,GetFileExInfoStandard,&fData) &&
FileTimeToSystemTime(&fData.ftLastWriteTime, &st) )
{
pszTrav += sprintf(pszTrav, "%s ",cszRespLastMod);
pszTrav += WriteDateGMT(pszTrav,&st);
APPENDCRLF(pszTrav);
if (m_pRequest->m_pWebsite->m_fWebDav) {
pszTrav = strcpyEx(pszTrav, cszETag);
*pszTrav++ = ' ';
pszTrav += GetETagFromFiletime(&fData.ftLastWriteTime,fData.dwFileAttributes,pszTrav);
APPENDCRLF(pszTrav);
}
}
}
// the Content-Type line
if (m_pszType) {
pszTrav += sprintf(pszTrav,cszRespType,m_pszType);
}
// the Content-Length line
if (m_dwLength) {
// If we have a file that is 0 length, it is set to -1.
if (m_dwLength == -1)
m_dwLength = 0;
pszTrav += sprintf(pszTrav, cszRespLength, m_dwLength);
}
if ((m_pRequest->m_rs == STATUS_UNAUTHORIZED) && m_pRequest->m_pFInfo && m_pRequest->m_pFInfo->m_pszDenyHeader) {
pszTrav = strcpyEx(pszTrav,m_pRequest->m_pFInfo->m_pszDenyHeader);
// It's the script's responsibility to add any \r\n to the headers.
}
if (m_pRequest->m_bufRespHeaders.Data()) {
memcpy(pszTrav,m_pRequest->m_bufRespHeaders.Data(),m_pRequest->m_bufRespHeaders.Count());
pszTrav += m_pRequest->m_bufRespHeaders.Count();
}
if (pszExtraHeaders) {
pszTrav = strcpyEx(pszTrav,pszExtraHeaders);
// It's the script's responsibility to include the final double CRLF, as per IIS.
}
else
APPENDCRLF(pszTrav); // the double CRLF signifies that header data is at an end
if (m_pszBody && (VERB_HEAD != m_pRequest->m_idMethod)) {
// A major HTTP client has a parsing bug where it will wrongly indicate
// a parse error if the HTTP headers and body are sent with different send()
// calls during a 401. Work around this problem by sending
// a body in this call if we have been provided with one at this stage. Note that
// this body will only be a default (eg from rgStatus) or a directory listing.
// ISAPI extensions/ASP, sending raw files back, and WebDAV never set m_pszBody
// but handle their own send() calls.
pszTrav = strcpyEx(pszTrav,m_pszBody);
}
*pszTrav = 0;
iLen = pszTrav - pszBuf;
m_pRequest->SendData(pszBuf,iLen);
done:
DEBUGMSG_ERR(ZONE_ERROR, (L"HTTPD: SendResponse failed, error = %d\r\n"));
DEBUGMSG(ZONE_RESPONSE, (L"HTTPD: Sending RESPONSE HEADER<<%a>>\r\n", pszBuf));
if (pszBuf != szBuf)
MyFree(pszBuf);
}
void SendFile(SOCKET sock, HANDLE hFile, CHttpRequest *pRequest) {
BYTE bBuf[4096];
DWORD dwRead;
while(ReadFile(hFile, bBuf, sizeof(bBuf), &dwRead, 0) && dwRead) {
if (! pRequest->SendData((PSTR) bBuf, dwRead))
return;
}
}
// This function used to display the message "Object moved
void CHttpResponse::SendRedirect(PCSTR pszRedirect, BOOL fFromFilter) {
if (!fFromFilter && m_pRequest->FilterNoResponse())
return;
DEBUGCHK(!m_hFile);
DEBUGCHK(m_pRequest->m_rs==STATUS_MOVED);
// create a body
PSTR pszBody = MySzAllocA(strlen(rgStatus[m_pRequest->m_rs].pszStatusBody)+strlen(pszRedirect)+30);
DWORD dwLen = 0;
if(pszBody) {
sprintf(pszBody, rgStatus[m_pRequest->m_rs].pszStatusBody, pszRedirect);
SetBody(pszBody, cszTextHtml);
}
// set redirect header & send all headers, then the synthetic body
m_pszRedirect = pszRedirect;
SendHeadersAndDefaultBodyIfAvailable(NULL,NULL);
MyFree(pszBody);
}
// Read strings of the bodies to send on web server errors ("Object not found",
// "Server Error",...) using load string. These strings are in wide character
// format, so we first convert them to regular strings, marching along
// pszBodyCodes (which is a buffer size BODYSTRINGSIZE). After the conversion,
// we set each rgStatus[i].pszStatusBody to the pointer in the buffer.
void InitializeResponseCodes(PSTR pszStatusBodyBuf) {
PSTR pszTrav = pszStatusBodyBuf;
UINT i;
int iLen;
WCHAR wszBuf[256];
for(i = 0; i < ARRAYSIZEOF(rgStatus); i++) {
if (!rgStatus[i].fHasDefaultBody)
continue;
if (0 == LoadString(g_hInst,RESBASE_body + i,wszBuf,ARRAYSIZEOF(wszBuf))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: LoadString(%d) failed. GLE=0x%08x\r\n",RESBASE_body + i,GetLastError()));
rgStatus[i].pszStatusBody = NULL;
continue;
}
iLen = MyW2A(wszBuf, pszTrav, BODYSTRINGSIZE - (pszTrav - pszStatusBodyBuf));
if (!iLen)
return;
rgStatus[i].pszStatusBody = pszTrav;
pszTrav += iLen;
}
}
BOOL CHttpRequest::SendData(PSTR pszBuf, DWORD dwLen, BOOL fCopyBuffer) {
DEBUG_CODE_INIT;
BOOL fRet = FALSE;
PSTR pszFilterData = pszBuf;
DWORD dwFilterData = dwLen;
if (g_pVars->m_fFilters &&
! CallFilter(SF_NOTIFY_SEND_RAW_DATA, &pszFilterData, (int*)&dwFilterData))
{
return FALSE;
}
if (!m_fIsSecurePort) {
return (dwLen == (DWORD) send(m_socket,pszFilterData,dwFilterData,0));
}
// If the filter modified the data's pointer then we'll need to copy data, filter
// is the owner of this buffer and we don't want to trounce it.
fCopyBuffer = fCopyBuffer || (pszBuf != pszFilterData);
return SendEncryptedData(pszFilterData,dwFilterData,fCopyBuffer);
}
void CHttpRequest::GetContentTypeOfRequestURI(PSTR szContentType, DWORD ccContentType, const WCHAR *wszExt) {
const WCHAR *wszContentType;
CReg reg;
szContentType[0] = 0;
if (!wszExt)
wszExt = m_wszExt;
if (!wszExt)
goto done;
if (! reg.Open(HKEY_CLASSES_ROOT,wszExt))
goto done;
wszContentType = reg.ValueSZ(L"Content Type");
if (wszContentType) {
if (0 == MyW2A(wszContentType, szContentType, ccContentType))
szContentType[0] = 0;
}
done:
if(! szContentType[0])
strcpy(szContentType,cszOctetStream);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -