⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 request.cpp

📁 Windows CE 6.0 Server 源码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
// 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: request.cpp
Abstract: per-Connection thread
--*/


#include "httpd.h"

#define AUTH_FILTER_DONE     0x1000		// no more filter calls to SF_AUTH after the 1st one in a session

BOOL IsLocalFile(PWSTR wszFile);

void CHttpRequest::HandleRequest()  {
	int err = 1;
	RESPONSESTATUS ret = STATUS_BADREQ;
	DWORD dwLength = 0;
	HANDLE hFile = INVALID_HANDLE_VALUE;
	HRINPUT hi;
	BOOL fSendDirectoryList = FALSE;
	WCHAR *wszISAPIPath;
	BOOL  fASP            = FALSE;
	BOOL  fRenegotiateSSL = FALSE;
	BOOL  fDelayAuth      = FALSE;
	
	m_rs = STATUS_OK;

	DEBUGMSG(ZONE_REQUEST,(L"HTTPD: HandleRequest: entry.  Current Authorization Level = %d\r\n",m_AuthLevelGranted));

	if (m_fHandleSSL)  {
		if (! HandleSSLHandShake()) {
			m_fKeepAlive = FALSE;
			return;  // HandleSSLHandshake handles all error codes, logging, et al.
		}
	}
	hi = m_bufRequest.RecvHeaders(m_socket,this);

	// Even if we get an error, continue processing, because we may have read in
	// binary data and have a filter installed that can convert it for us.
	if (hi == INPUT_TIMEOUT || hi == INPUT_ERROR)   {
		// our socket has been closed as part of web server shutdown.  Since we haven't done 
		// any real work at this stage terminate request, don't log.
		if (! IsActive())
			return; 
	
		// Either we have no data, or there's no filter to read in data, return
		if (!g_pVars->m_fFilters || !g_pVars->m_fReadFilters || m_bufRequest.Count() == 0) {
			m_fKeepAlive = FALSE;
			if (hi == INPUT_ERROR && !m_fIsSecurePort) {
				if (m_rs == STATUS_OK) { // if no called into fcn set the error code, set = bad req.
					myretleave(m_rs = STATUS_BADREQ,61);
				}
				else {
					myretleave(m_rs,62);
				}
			}
			else
				return;  // don't send any data across socket on timeout.
		}
	}

	if (g_pVars->m_fFilters &&
	    ! CallFilter(SF_NOTIFY_READ_RAW_DATA))
		myleave(231);
	
	// parse the request headers
	if(!ParseHeaders())
		myleave(50);

	//  There were numerous filter calls in ParseHeaders, make sure none requested end of connection
	if (m_pFInfo && m_pFInfo->m_fFAccept==FALSE)
		myleave(63);  // don't change m_rs, filter set it as appropriate

	// If we received a request for 'OPTIONS *' or 'OPTIONS /', this requires special case
	// handeling to avoid authentication and other virtual root and redirection checks.
	if (m_fOptionsAsterisk || (m_pVRoot && (VERB_OPTIONS==m_idMethod) && IsRootVDir())) {
		if (! m_pWebsite->m_fWebDav)
			myretleave(m_rs = STATUS_NOTIMPLEM,83);

		if (HandleWebDav()) {
			err = 0;
		}
		else {
			DEBUGCHK(!IS_STATUS_2XX(m_rs));
			err = 64;	// force error to be sent
		}
		goto done;
	}

	// If VROOT was configured to auto-redirect, skip all other checks.
	if (m_pVRoot && m_pVRoot->fRedirect) {
		DEBUGMSG(ZONE_REQUEST,(L"HTTPD: VROOT maps to redirect, sending client to URL %a\r\n",m_pVRoot->pszRedirectURL));
		CHttpResponse resp(this,STATUS_MOVED,CONN_CLOSE);
		resp.SendRedirect(m_pVRoot->pszRedirectURL);
		err = 0;
		goto done;
	}

	// check if we successfully mapped the VRoot
	if (!m_wszPath)
		myretleave(m_rs = STATUS_NOTFOUND, 59);

	//
	// Authentication checks come first
	//
	// Check if we're requiring SSL on this VRoot.
	if ((!m_fIsSecurePort && ((GetPerms() & HSE_URL_FLAGS_SSL) || (GetPerms() & HSE_URL_FLAGS_SSL128))) ||
	    (m_fIsSecurePort && (m_SSLInfo.m_dwSessionKeySize < 128) && (GetPerms() & HSE_URL_FLAGS_SSL128)))
	{
		if (g_pVars->m_fFilters)
			CallFilter(SF_NOTIFY_ACCESS_DENIED);
		myretleave(m_rs = STATUS_FORBIDDEN, 55);
	}

	// If we want or require a client cert and we don't have one, we must renegotiate with the client.
	// Only call SSLRenegotiateRequest() once per session, in event that only HSE_URL_FLAGS_NEGO_CERT 
	// is set and client doesn't provide a cert.
	if (!m_SSLInfo.m_pClientCertContext && (GetPerms() & (HSE_URL_FLAGS_REQUIRE_CERT | HSE_URL_FLAGS_NEGO_CERT)) && !m_fPerformedSSLRenegotiateRequest) {
		if (SSLRenegotiateRequest())
			fRenegotiateSSL = TRUE;
		else if (GetPerms() & HSE_URL_FLAGS_REQUIRE_CERT) {
			DEBUGMSG(ZONE_ERROR,(L"HTTPD: SSL renegotiate failed and we require a certificate, fail request\r\n"));
			myleave(236);
		}

		// If we're using client certificates and we're renegotiating, we haven't read in enough
		// information to determine whether to grant or deny access at this point.
		fDelayAuth = TRUE;
	}

	if (!fDelayAuth) {
		if (AllowNTLM() || AllowBasic() || AllowNegotiate() || g_pVars->m_fSSL)
			HandleAuthentication();

		if ( !CheckAuth())
			myretleave(m_rs = STATUS_UNAUTHORIZED, 52);
	}

	// ISAPI filters gets only 1 call per session to notify them of SF_NOTIFY_AUTHENTICATION event.
	if (! CallAuthFilterIfNeeded())
		myleave(294);

	if (!ReadPostData(g_pVars->m_dwPostReadSize,TRUE,fRenegotiateSSL))
		myleave(233); // ReadPostData sets m_rs

	// If content-length is less than # of bytes we've read in, then we have 
	// a garbage request (possibly multiplexing).  CE web server supports 
	// one specific case of multiplexing (allows multiple POSTS in one packet)
	// to work around a known broken HTTP client, however does not handle 
	// general case so fail out if this happens.
	// (-2 because sometimes browsers tack on CRLF to end of POST data but
	// don't count it in body-length).
	if ((int)m_dwContentLength < (int)(m_bufRequest.Count()-2)) {
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: content-length is greater than muber of bytes received, probably HTTP 1.1 pipelined request.  HTTPD does not support this.\r\n"));
		m_rs = STATUS_BADREQ;
		myleave(241);
	}

	// For SSL case we should have client cert by now, so do authentication.
	if (fDelayAuth) {
		DEBUGCHK(IsSecure());

		if (!m_SSLInfo.m_pClientCertContext && (GetPerms() & HSE_URL_FLAGS_REQUIRE_CERT)) {
			DEBUGMSG(ZONE_ERROR,(L"HTTPD: Virtual root set HSE_URL_FLAGS_REQUIRE_CERT but no cert from client, ending request\r\n"));
			m_rs = STATUS_FORBIDDEN;
			myleave(239);
		}
		
		HandleSSLClientCertCheck();

		if (!CheckAuth())
			myretleave(m_rs = STATUS_UNAUTHORIZED, 153);
	}

	//  if we're in middle of authenticating, jump past other stuff to end
	if (m_AuthState.m_Stage == SEC_STATE_PROCESSING)
		myretleave(m_rs = STATUS_UNAUTHORIZED, 65);

	m_dwFileAttributes = GetFileAttributes(m_wszPath);

	if (! (m_wszExt && g_pVars->m_fExtensions && MapScriptExtension(m_wszExt,&wszISAPIPath,&fASP)) && VerbSupportsDirBrowsing() ) {
		if ((-1) == m_dwFileAttributes)
			myretleave(m_rs = GLEtoStatus(), 60);

		if (IsDirectory(m_dwFileAttributes)) {
			if (SendRedirectIfNeeded()) {
				err = 0;
				goto done;
			}
			// If there's no default page then we send dir list (later, after some
			// extra checking).  If there is a default page match m_wszPath is 
			// updated appropriatly.  This must be done before script processing.
			fSendDirectoryList = !MapDirToDefaultPage();
		}
	}

	// HandleScript returns true if page maps to ASP or ISAPI DLL, regardless of whether
	// we have correct permissions, component was included, there was an error, etc.
	// HandleScript sets its own errors.
	if ( !fSendDirectoryList && HandleScript()) {
		err = ! (IS_STATUS_2XX(m_rs) || (m_rs == STATUS_MOVED));  // Only send message on internal error.
		goto done;
	}

	if (m_idMethod != VERB_GET && m_idMethod != VERB_HEAD) {
		// if it's not an ISAPI or ASP or there's no DAV, we can't handle anything but GET and HEAD
		if (m_idMethod == VERB_UNKNOWN || !m_pWebsite->m_fWebDav) {
			DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, received unknown verb.  Non ISAPI/ASP pages can only handle GET+HEAD\r\n"));
			myretleave(m_rs = STATUS_NOTIMPLEM, 54);
		}

		// Let DAV handle all other flavors of verbs.
		// If HandleWebDav() fails and wants HandleRequest() to send error, it will
		// set m_rs appropriatly.  Otherwise HandleWebDav succeeded.
		// In either event no need to do rest of the code in this function, so goto-done.
		if (HandleWebDav()) {
			err = 0;
		}
		else {
			DEBUGCHK(!IS_STATUS_2XX(m_rs));
			err = 64;	// force error to be sent
		}

		goto done;
	}

	// check permissions
	if (!(GetPerms() & HSE_URL_FLAGS_READ))   {
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, vroot does not have HSE_URL_FLAGS_READ permissions\r\n"));
		if (g_pVars->m_fFilters)
			CallFilter(SF_NOTIFY_ACCESS_DENIED);
		myretleave(m_rs = STATUS_FORBIDDEN, 55);
	}

	if (fSendDirectoryList) {
		// In this case there's no default page but directory browsing is turned
		// off.  Return same error code IIS does.
		if (FALSE == AllowDirBrowse())  {
			DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, directory browsing is disabled\r\n"));
			if (g_pVars->m_fFilters)
				CallFilter(SF_NOTIFY_ACCESS_DENIED);
			myretleave(m_rs = STATUS_FORBIDDEN,78);
		}
		
		if(!EmitDirListing())
			myretleave(m_rs = STATUS_INTERNALERR, 53);
		err=0;
		goto done;
	}

	// If we get to here then we're just sending a plain old static file.
	// try to open the file & get the length
	if (INVALID_HANDLE_VALUE == (hFile = MyOpenReadFile(m_wszPath))) {
		DEBUGMSG(ZONE_ERROR,(L"HTTPD: Request fails, unable to open file %s, GLE=0x%08x\r\n",m_wszPath,GetLastError()));
		myretleave(m_rs = GLEtoStatus(), 56);
	}
		
	// get the size
	if (((DWORD)-1) == (dwLength = GetFileSize(hFile, 0)))
		myretleave(m_rs = GLEtoStatus(), 57);

	// if it's a GET check if-modified-since
	if ((m_idMethod==VERB_GET) && IsNotModified(hFile, dwLength))
		myretleave(m_rs = STATUS_NOTMODIFIED, 58);

	// if it's a HTTP/0.9 request, just send back the body. NO headers
	if (m_dwVersion <= MAKELONG(9, 0)) {
		DEBUGMSG(ZONE_RESPONSE, (L"HTTPD: Sending HTTP/0.9 response with NO headers\r\n"));
		SendFile(m_socket, hFile, this);
	}
	else {
	// create a response object & send response
	// if it's a head request, skip the actual body
		CHttpResponse resp(this);
		resp.SetBody(hFile, m_wszExt, dwLength);
		resp.SendResponse();
	}
	DEBUGMSG(ZONE_REQUEST, (L"HTTPD: HTTP Request SUCCEEDED\r\n"));

	err  = 0;
	ret = m_rs = STATUS_OK;   
done:
	MyCloseHandle(hFile);

	if(err) {
		// end this session ASAP if we've encountered an error.
		if (m_rs == STATUS_INTERNALERR || m_rs == STATUS_BADREQ)
			m_fKeepAlive = FALSE;
		
		// if there's been an error but we're doing keep-alives, it's possible
		// there's POST data we haven't read in.  We need to read this
		// before sending response, or next time we recv() HTTP headers we'll
		// start in middle of POST rather than in the new request.
		if (m_fKeepAlive) {
			DEBUGCHK(m_dwContentLength - m_bufRequest.Count() >= 0);

			// If the amount of POST data we'd have to read is greater 
			// than the max we're willing to, then we end the request now.
			if (m_dwContentLength < g_pVars->m_dwPostReadSize) {
				if ((int)(m_dwContentLength - m_bufRequest.Count()) > 0) {
					DEBUGMSG(ZONE_REQUEST,(L"HTTP: HandleRequest: Error occured on keepalive, reading %d POST bytes now\r\n",m_dwContentLength - m_bufRequest.Count()));
					ReadPostData(m_dwContentLength - m_bufRequest.Count(),FALSE,FALSE);
				}
			}
			else {
				DEBUGMSG(ZONE_REQUEST,(L"HTTP: HandleRequest: client requested keep-alive but has content-length set = %d bytes, more than what we will read.  End session\r\n",m_dwContentLength));
				m_fKeepAlive = FALSE;
			}
		}
	
		CHttpResponse resp(this, m_rs);
		resp.SetDefaultBody();
		resp.SendResponse();
		DEBUGMSG(ZONE_REQUEST, (L"HTTPD: HTTP Request FAILED: GLE=%d err=%d status=%d (%d, %a)\r\n", 
			GetLastError(), err, ret, rgStatus[ret].dwStatusNumber, rgStatus[ret].pszStatusText));
	}

	// if in middle of NTLM request, don't do this stuff
	if (m_AuthState.m_Stage != SEC_STATE_PROCESSING) {
		if (g_pVars->m_fFilters)  {
			CallFilter(SF_NOTIFY_END_OF_REQUEST);
			CallFilter(SF_NOTIFY_LOG);
		}
		g_pVars->m_pLog->WriteLog(this);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -