1 // This is a part of the Active Template Library.
2 // Copyright (C) Microsoft Corporation
3 // All rights reserved.
5 // This source code is only intended as a supplement to the
6 // Active Template Library Reference and related
7 // electronic documentation provided with the library.
8 // See these sources for detailed information regarding the
9 // Active Template Library product.
11 #ifndef __ATLSTENCIL_H__
12 #include <atlstencil.h>
15 #ifndef __ATLISAPI_H__
16 #define __ATLISAPI_H__
20 #include <time.h> // needed for cookie support
21 #include <httpext.h> // needed for ECB and IIS support
29 #include <atlsrvres.h>
30 #include <atlsiface.h>
32 #include <atlsecurity.h>
42 #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
45 #ifndef _ATL_NO_DEFAULT_LIBS
46 #pragma comment(lib, "winmm.lib")
48 #pragma comment(lib, "msxml2.lib")
50 #endif // !_ATL_NO_DEFAULT_LIBS
55 #pragma warning(disable: 4291) // allow placement new
56 #pragma warning(disable: 4127) // conditional expression is constant
57 #pragma warning(disable: 4511) // copy constructor could not be generated
58 #pragma warning(disable: 4512) // assignment operator could not be generated
59 #pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
60 #pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
61 #pragma warning(disable: 4191) // unsafe conversion from 'functionptr1' to 'functionptr2'
62 #pragma warning(disable: 4702) // unreachable code
65 #include <dbgautoattach.h>
67 #ifndef SESSION_COOKIE_NAME
68 #define SESSION_COOKIE_NAME "SESSIONID"
71 // override this if you want to use a different CLSID for SAX
72 #ifndef ATLS_SAXXMLREADER_CLSID
73 #define ATLS_SAXXMLREADER_CLSID __uuidof(SAXXMLReader)
74 #endif // ATLS_SAXXMLREADER_CLSID
77 // This function is used in CValidateObject to determine if an empty
78 // request parameter really should be empty. You can
79 // specialize this function in your own code such as
80 // the following specialization for type long:
82 // inline bool IsNullByType<long>(long type) throw()
86 // You should provide your own specialization for this
87 // function if the comparison of type==0 is not adequate
88 // to discover whether or not your type is 0.
89 template <class TComp
>
90 inline bool IsNullByType(__in TComp type
) throw()
96 #pragma pack(push,_ATL_PACKING)
99 // Default file extension for server response files
100 #ifndef ATL_DEFAULT_STENCIL_EXTENSION
101 #define ATL_DEFAULT_STENCIL_EXTENSION ".srf"
103 extern __declspec(selectany
) const char * const c_AtlSRFExtension
= ATL_DEFAULT_STENCIL_EXTENSION
;
104 extern __declspec(selectany
) const TCHAR
* const c_tAtlSRFExtension
= _T(ATL_DEFAULT_STENCIL_EXTENSION
);
105 #define ATLS_EXTENSION_LEN (sizeof(ATL_DEFAULT_STENCIL_EXTENSION)-2)
107 // Default file extension for handler DLLs
108 #ifndef ATL_DEFAULT_DLL_EXTENSION
109 #define ATL_DEFAULT_DLL_EXTENSION ".dll"
111 extern __declspec(selectany
) const char * const c_AtlDLLExtension
= ATL_DEFAULT_DLL_EXTENSION
;
112 extern __declspec(selectany
) const TCHAR
* const c_tAtlDLLExtension
= _T(ATL_DEFAULT_DLL_EXTENSION
);
113 #define ATLS_DLL_EXTENSION_LEN (sizeof(ATL_DEFAULT_DLL_EXTENSION)-2)
115 // maximum handler name length
116 #ifndef ATL_MAX_HANDLER_NAME_LEN
117 #define ATL_MAX_HANDLER_NAME_LEN 64
120 #ifndef ATL_HANDLER_NAME_DEFAULT
121 #define ATL_HANDLER_NAME_DEFAULT "Default"
122 #endif // ATL_DEFAULT_HANDLER_NAME
125 // maximum timeout for async guard mutex
126 #ifndef ATLS_ASYNC_MUTEX_TIMEOUT
127 #define ATLS_ASYNC_MUTEX_TIMEOUT 10000
130 #if defined(_M_IA64) || defined (_M_AMD64)
131 #define ATLS_FUNCID_INITIALIZEHANDLERS "InitializeAtlHandlers"
132 #define ATLS_FUNCID_GETATLHANDLERBYNAME "GetAtlHandlerByName"
133 #define ATLS_FUNCID_UNINITIALIZEHANDLERS "UninitializeAtlHandlers"
134 #elif defined(_M_IX86)
135 #define ATLS_FUNCID_INITIALIZEHANDLERS "_InitializeAtlHandlers@8"
136 #define ATLS_FUNCID_GETATLHANDLERBYNAME "_GetAtlHandlerByName@12"
137 #define ATLS_FUNCID_UNINITIALIZEHANDLERS "_UninitializeAtlHandlers@0"
139 #error Unknown Platform.
142 #define ATL_MAX_COOKIE_LEN 2048
143 #define ATL_MAX_COOKIE_ELEM 1024
146 // Defines a small value used for comparing the equality of floating point numbers.
148 #define ATL_EPSILON .0001
151 #ifndef ATL_DEFAULT_PRECISION
152 #define ATL_DEFAULT_PRECISION 6
155 // Call this function to URL-encode a buffer and have the result appended to a CString passed by reference.
157 // A space in the input string is encoded as a plus sign (+).
158 // Other unsafe characters (as determined by AtlIsUnsafeUrlChar) are encoded as escaped octets.
159 // An escaped octet is a percent sign (%) followed by two digits representing the hexadecimal code of the character.
161 // string A CStringA reference to which will be appended the encoded version of szBuf.
163 // szBuf The string to be URL-encoded.
164 ATL_NOINLINE
inline bool EscapeToCString(__inout CStringA
& string
, __in LPCSTR szBuf
)
166 ATLENSURE( szBuf
!= NULL
);
171 LPSTR pszStr
= szEscaped
;
176 if (dwLen
+4 >= _countof(szEscaped
))
179 string
.Append(szEscaped
, dwLen
);
183 if (AtlIsUnsafeUrlChar(*szBuf
))
192 LPSTR pszTmp
= pszStr
;
194 unsigned char ch
= (unsigned char)*szBuf
;
199 Checked::ultoa_s((unsigned char)ch
, pszTmp
, szEscaped
+ _countof(szEscaped
) - pszTmp
, 16);
200 pszStr
+= sizeof("%FF")-1;
201 dwLen
+= sizeof("%FF")-1;
213 string
.Append(szEscaped
, dwLen
);
223 // UNICODE overload for EscapeToCString
224 // follow specifications detailed in RFC document on
225 // Internationalized Uniform Resource Identifiers (IURI)
226 inline bool EscapeToCString(__inout CStringA
& string
, __in_z LPCWSTR wszBuf
) throw()
230 // convert string to UTF8
231 CFixedStringT
<CStringA
, 2048> strConvert
;
233 // get the required length for conversion
234 int nSrcLen
= (int) wcslen(wszBuf
);
235 int nLen
= AtlUnicodeToUTF8(wszBuf
, nSrcLen
, NULL
, 0);
241 // allocate MBCS conversion string
242 LPSTR sz
= strConvert
.GetBuffer(nLen
+1);
248 // do the UNICODE to UTF8 conversion
249 nLen
= AtlUnicodeToUTF8(wszBuf
, nSrcLen
, sz
, nLen
);
258 // delegate to ANSI version of EscapeToCString
259 if (!EscapeToCString(string
, sz
))
264 strConvert
.ReleaseBuffer(nLen
);
274 struct CDefaultErrorProvider
276 struct HTTP_ERROR_TEXT
278 UINT uHttpError
; // the Http Error value
279 UINT uHttpSubError
; // Allows for customization of error text based on srf specific errors.
280 LPCSTR szHeader
; // the string that should appear in the http response header
281 UINT uResId
; // the resource id of the string to send back as the body
285 // GetErrorText retrieves the http response header string
286 // and a resource id of the response body for a given
288 // uError: Http error code to retrieve information for
289 // ppszHeader: pointer to LPCSTR that receives the response header string
290 // ppszHeader is optional
291 // puResId: pointer to UINT that receives the response body resource id
292 // puResId is optional
293 static BOOL
GetErrorText(__in UINT uError
, __in UINT uSubErr
, __deref_out_opt LPCSTR
*ppszHeader
, __out_opt UINT
*puResId
) throw()
295 static const HTTP_ERROR_TEXT s_Errors
[] =
297 { 200, SUBERR_NONE
, "OK", 0 },
298 { 201, SUBERR_NONE
, "Created", 0 },
299 { 202, SUBERR_NONE
, "Accepted", 0 },
300 { 203, SUBERR_NONE
, "Non-Authoritative Information", 0 },
301 { 204, SUBERR_NONE
, "No Content", 0 },
302 { 204, DBG_SUBERR_ALREADY_DEBUGGING
, "Already being debugged by another user", 0},
303 { 204, DBG_SUBERR_NOT_DEBUGGING
, "Not currently debugging a process", 0},
304 { 204, DBG_SUBERR_INVALID_SESSION
, "Requested DebugSessionID does not match current DebugSessionID", 0},
305 { 204, DBG_SUBERR_BAD_ID
, "DebugSessionID corrupted or not provided", 0 },
306 { 204, DBG_SUBERR_COCREATE
, "Could not CoCreate the debugger", 0 },
307 { 204, DBG_SUBERR_ATTACH
, "Could not attach to process", 0 },
308 { 205, SUBERR_NONE
, "Reset Content", 0 },
309 { 206, SUBERR_NONE
, "Partial Content", 0 },
310 { 300, SUBERR_NONE
, "Multiple Choices", 0 },
311 { 301, SUBERR_NONE
, "Moved Permanently", 0 },
312 { 302, SUBERR_NONE
, "Found", 0 },
313 { 303, SUBERR_NONE
, "See Other", 0 },
314 { 304, SUBERR_NONE
, "Not Modified", 0 },
315 { 305, SUBERR_NONE
, "Use Proxy", 0 },
316 { 306, SUBERR_NONE
, "(Unused)", 0 },
317 { 307, SUBERR_NONE
, "Temporary Redirect", 0 },
318 { 400, SUBERR_NONE
, "Bad Request", IDS_ATLSRV_BAD_REQUEST
},
319 { 401, SUBERR_NONE
, "Unauthorized", IDS_ATLSRV_AUTH_REQUIRED
},
320 { 402, SUBERR_NONE
, "Payment Required", 0 },
321 { 403, SUBERR_NONE
, "Forbidden", IDS_ATLSRV_FORBIDDEN
},
322 { 404, SUBERR_NONE
, "Not Found", IDS_ATLSRV_NOT_FOUND
},
323 { 405, SUBERR_NONE
, "Method Not Allowed", 0 },
324 { 406, SUBERR_NONE
, "Not Acceptable", 0 },
325 { 407, SUBERR_NONE
, "Proxy Authentication Required", 0 },
326 { 408, SUBERR_NONE
, "Request Timeout", 0 },
327 { 409, SUBERR_NONE
, "Conflict", 0 },
328 { 410, SUBERR_NONE
, "Gone", 0 },
329 { 411, SUBERR_NONE
, "Length Required", 0 },
330 { 412, SUBERR_NONE
, "Precondition Failed", 0 },
331 { 413, SUBERR_NONE
, "Request Entity Too Long", 0 },
332 { 414, SUBERR_NONE
, "Request-URI Too Long", 0 },
333 { 415, SUBERR_NONE
, "Unsupported Media Type", 0 },
334 { 416, SUBERR_NONE
, "Requested Range Not Satisfiable", 0 },
335 { 417, SUBERR_NONE
, "Expectation Failed", 0 },
336 { 500, SUBERR_NONE
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR
},
337 { 500, ISE_SUBERR_BADSRF
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_BADSRF
},
338 { 500, ISE_SUBERR_HNDLFAIL
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HNDLFAIL
},
339 { 500, ISE_SUBERR_SYSOBJFAIL
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL
},
340 { 500, ISE_SUBERR_READFILEFAIL
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_READFILEFAIL
},
341 { 500, ISE_SUBERR_LOADFILEFAIL
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL
},
342 { 500, ISE_SUBERR_LOADLIB
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LOADLIB
},
343 { 500, ISE_SUBERR_HANDLERIF
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HANDLERIF
},
344 { 500, ISE_SUBERR_OUTOFMEM
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_OUTOFMEM
},
345 { 500, ISE_SUBERR_UNEXPECTED
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_UNEXPECTED
},
346 { 500, ISE_SUBERR_STENCIL_PARSE_FAIL
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL
},
347 { 500, ISE_SUBERR_STENCIL_LOAD_FAIL
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL
},
348 { 500, ISE_SUBERR_HANDLER_NOT_FOUND
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND
},
349 { 500, ISE_SUBERR_BAD_HANDLER_TAG
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG
},
350 { 500, ISE_SUBERR_LONGMETHODNAME
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME
},
351 { 500, ISE_SUBERR_LONGHANDLERNAME
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME
},
352 { 500, ISE_SUBERR_NO_HANDLER_TAG
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG
},
353 { 500, ISE_SUBERR_IMPERSONATIONFAILED
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED
},
354 { 500, ISE_SUBERR_ISAPISTARTUPFAILED
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED
},
355 { 500, ISE_SUBERR_SOAPNOSOAPACTION
, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION
},
357 { 501, SUBERR_NONE
, "Not Implemented", IDS_ATLSRV_NOT_IMPLEMENTED
},
358 { 502, SUBERR_NONE
, "Bad Gateway", IDS_ATLSRV_BAD_GATEWAY
},
359 { 503, SUBERR_NONE
, "Service Unavailable", IDS_ATLSRV_SERVICE_NOT_AVAILABLE
},
360 { 504, SUBERR_NONE
, "Gateway Timeout", 0 },
361 { 505, SUBERR_NONE
, "HTTP Version Not Supported", 0 },
364 // look for the error
365 for (int i
=0; i
<sizeof(s_Errors
)/sizeof(s_Errors
[0]); i
++)
367 if ((s_Errors
[i
].uHttpError
== uError
) && (s_Errors
[i
].uHttpSubError
== uSubErr
))
370 *ppszHeader
= s_Errors
[i
].szHeader
;
372 *puResId
= s_Errors
[i
].uResId
;
380 }; // CDefaultErrorProvider
382 template<class HttpUserErrorTextProvider
>
383 void GetStatusHeader(__inout CStringA
&strStatus
, __in DWORD dwStatus
, __in DWORD dwSubStatus
, __in HttpUserErrorTextProvider
* pErrorProvider
, __out_opt UINT
*puResId
= NULL
) throw(...)
385 ATLENSURE( pErrorProvider
!= NULL
);
387 LPCSTR szHeadErr
= NULL
;
388 // First, we check for the error text in the extension's user error text provider
389 BOOL bRet
= pErrorProvider
->GetErrorText(dwStatus
, dwSubStatus
, &szHeadErr
, puResId
);
396 Checked::itoa_s(dwStatus
, szBuf
, _countof(szBuf
), 10);
398 // add the space after the 3 digit response code
401 strStatus
.SetString(szBuf
, 4);
402 strStatus
.Append(szHeadErr
);
405 template<class HttpUserErrorTextProvider
>
406 void RenderError(__in IHttpServerContext
*pServerContext
, __in DWORD dwStatus
, __in DWORD dwSubStatus
, __in HttpUserErrorTextProvider
* pErrorProvider
)
408 ATLENSURE( pServerContext
!= NULL
);
409 ATLENSURE( pErrorProvider
!= NULL
);
414 CFixedStringT
<CStringA
, 256> strStatus
;
415 GetStatusHeader(strStatus
, dwStatus
, dwSubStatus
, pErrorProvider
, &uResId
);
416 pServerContext
->SendResponseHeader(NULL
, strStatus
, FALSE
);
418 LPCSTR szBody
= strStatus
;
419 DWORD dwBodyLen
= strStatus
.GetLength();
420 CFixedStringT
<CStringA
, 1024> strBody
;
423 // load the body string from a resource
424 if (strBody
.LoadString(uResId
))
427 dwBodyLen
= strBody
.GetLength();
431 pServerContext
->WriteClient((void *) szBody
, &dwBodyLen
);
435 // last resort message when low on memory
438 bRes
= CDefaultErrorProvider::GetErrorText(dwStatus
, dwSubStatus
, &szError
, 0);
440 bRes
= CDefaultErrorProvider::GetErrorText(dwStatus
, SUBERR_NONE
, &szError
, 0);
442 bRes
= CDefaultErrorProvider::GetErrorText(500, SUBERR_NONE
, &szError
, 0);
445 szError
="Unknown Error"; // last resort, can't localize
447 DWORD dwBodyLen
= (DWORD
) strlen(szError
);
448 pServerContext
->WriteClient((void *) szError
, &dwBodyLen
);
452 // Call this function to retrieve the full canonical physical path
453 // of a file relative to the current script.
455 // Returns TRUE on success, FALSE on error.
457 // szFile A file path relative to the current script directory for which
458 // you are trying to retrieve the full path.
460 // szFullFileName A caller-allocated buffer of at least MAX_PATH characters in length.
461 // On success, contains the the full canonical path of szFile.
463 // pServerContext The context for the current request. The context is used to obtain the
464 // current script directory.
465 inline BOOL
GetScriptFullFileName(
467 __in_ecount(MAX_PATH
) LPSTR szFullFileName
,
468 __in IHttpServerContext
* pServerContext
) throw(...)
470 ATLENSURE( szFile
!= NULL
);
471 ATLASSERT( szFullFileName
!= NULL
);
472 ATLENSURE( pServerContext
!= NULL
);
474 char szTmpScriptPath
[MAX_PATH
];
475 LPCSTR szTmp
= pServerContext
->GetScriptPathTranslated();
482 if (!SafeStringCopy(szTmpScriptPath
, szTmp
))
488 CHAR
*szScriptPath
= szTmpScriptPath
;
493 szBackslash
= strrchr(szScriptPath
, '\\');
495 ATLASSERT( *(szScriptPath
+strlen(szScriptPath
)) != '\\');
502 // handle case where szFile is of the form \directory\etc\etc
503 szBackslash
= strchr(szScriptPath
, '\\');
509 int nScriptPathLen
= (int)(szBackslash
? strlen(szScriptPath
) : 0);
510 int nFileLen
= (int) strlen(szFile
);
511 int newLen
= nScriptPathLen
+ nFileLen
;
513 if ((newLen
< nScriptPathLen
) || (newLen
< nFileLen
) || (newLen
> MAX_PATH
-1))
517 CHAR szTemp
[MAX_PATH
];
520 Checked::memcpy_s(szTemp
, MAX_PATH
, szScriptPath
, nScriptPathLen
);
522 Checked::memcpy_s(szTemp
+ nScriptPathLen
, MAX_PATH
-nScriptPathLen
, szFile
, nFileLen
);
523 *(szTemp
+ newLen
) = 0;
525 return PathCanonicalizeA(szFullFileName
, szTemp
);
530 ATLSRV_STATE_BEGIN
, // The request has just arrived, and the type has not been determined
531 ATLSRV_STATE_CONTINUE
, // The request is a continuation of an async request
532 ATLSRV_STATE_DONE
, // The request is a continuation of an async request, but the server is done with it
533 ATLSRV_STATE_CACHE_DONE
// The request is the callback of a cached page
536 enum ATLSRV_REQUESTTYPE
538 ATLSRV_REQUEST_UNKNOWN
=-1, // The request type isn't known yet
539 ATLSRV_REQUEST_STENCIL
, // The request is for a .srf file
540 ATLSRV_REQUEST_DLL
// The request is for a .dll file
543 // Flags the InitRequest can return in dwStatus
544 #define ATLSRV_INIT_USECACHE 1
545 #define ATLSRV_INIT_USEASYNC 2
546 #define ATLSRV_INIT_USEASYNC_EX 4 // required for use of NOFLUSH status
548 typedef HTTP_CODE (IRequestHandler::*PFnHandleRequest
)(AtlServerRequest
*pRequestInfo
, IServiceProvider
*pProvider
);
549 typedef void (*PFnAsyncComplete
)(AtlServerRequest
*pRequestInfo
, DWORD cbIO
, DWORD dwError
);
551 struct AtlServerRequest
553 DWORD cbSize
; // For future compatibility
554 IHttpServerContext
*pServerContext
; // Necessary because it wraps the ECB
555 ATLSRV_REQUESTTYPE dwRequestType
; // See the ATLSRV variables above
556 // Indicates whether it was called through an .srf file or through a .dll file
557 ATLSRV_STATE dwRequestState
; // See the ATLSRV variables above
558 // Indicates what state of completion the request is in
559 IRequestHandler
*pHandler
; // Necessary because the callback (for async calls) must know where to
561 HINSTANCE hInstDll
; // Necessary in order to release the dll properly (for async calls)
562 IIsapiExtension
*pExtension
; // Necessary to requeue the request (for async calls)
563 IDllCache
* pDllCache
; // Necessary to release the dll in async callback
567 IFileCache
* pFileCache
;
569 HANDLE m_hMutex
; // necessary to syncronize calls to HandleRequest
570 // if HandleRequest could potientially make an
571 // async call before returning. only used
572 // if indicated with ATLSRV_INIT_USEASYNC_EX
574 DWORD dwStartTicks
; // Tick count when the request was received
575 EXTENSION_CONTROL_BLOCK
*pECB
;
576 PFnHandleRequest pfnHandleRequest
;
577 PFnAsyncComplete pfnAsyncComplete
;
578 LPCSTR pszBuffer
; // buffer to be flushed asyncronously
579 DWORD dwBufferLen
; // length of data in pszBuffer
580 void* pUserData
; // value that can be used to pass user data between parent and child handlers
583 inline void _ReleaseAtlServerRequest(__inout AtlServerRequest
* pRequest
)
585 ATLENSURE(pRequest
!=NULL
);
586 if (pRequest
->pHandler
)
587 pRequest
->pHandler
->Release();
588 if (pRequest
->pServerContext
)
589 pRequest
->pServerContext
->Release();
590 if (pRequest
->pDllCache
&& pRequest
->hInstDll
)
591 pRequest
->pDllCache
->ReleaseModule(pRequest
->hInstDll
);
592 if (pRequest
->m_hMutex
)
593 CloseHandle(pRequest
->m_hMutex
);
596 typedef BOOL (__stdcall
*GETATLHANDLERBYNAME
)(LPCSTR szHandlerName
, IIsapiExtension
*pExtension
, IUnknown
**ppHandler
);
597 typedef BOOL (__stdcall
*INITIALIZEATLHANDLERS
)(IHttpServerContext
*, IIsapiExtension
*);
598 typedef void (__stdcall
*UNINITIALIZEATLHANDLERS
)();
600 // initial size of thread worker heap (per thread)
601 // The heap is growable. The default initial is 16KB
602 #ifndef ATLS_WORKER_HEAP_SIZE
603 #define ATLS_WORKER_HEAP_SIZE 16384
609 typedef AtlServerRequest
* RequestType
;
612 CComPtr
<ISAXXMLReader
> m_spReader
;
615 CIsapiWorker() throw()
620 virtual ~CIsapiWorker() throw()
622 ATLASSUME(m_hHeap
== NULL
);
625 virtual BOOL
Initialize(__inout
__crt_typefix(IIsapiExtension
*) void *pvParam
)
627 IIsapiExtension
* pExtension
= (IIsapiExtension
*) pvParam
;
628 ATLENSURE(pExtension
);
629 if (!(pExtension
->OnThreadAttach()))
632 m_hHeap
= HeapCreate(HEAP_NO_SERIALIZE
, ATLS_WORKER_HEAP_SIZE
, 0);
636 if (FAILED(m_spReader
.CoCreateInstance(ATLS_SAXXMLREADER_CLSID
, NULL
, CLSCTX_INPROC_SERVER
)))
639 ATLTRACE( atlTraceISAPI
, 0, _T("MSXML3 is not installed -- web services will not work.") );
642 return pExtension
->SetThreadWorker(this);
645 virtual void Terminate(__inout_opt
__crt_typefix(IIsapiExtension
*) void* pvParam
) throw()
649 if (HeapDestroy(m_hHeap
))
658 m_spReader
.Release();
661 (static_cast<IIsapiExtension
*>(pvParam
))->OnThreadTerminate();
664 void Execute(__inout AtlServerRequest
*pRequestInfo
, __inout
__crt_typefix(IIsapiExtension
*) void *pvParam
, __reserved OVERLAPPED
*pOverlapped
)
666 ATLENSURE(pRequestInfo
!= NULL
);
667 ATLENSURE(pvParam
!= NULL
);
668 (pOverlapped
); // unused
669 ATLASSUME(m_hHeap
!= NULL
);
670 // any exceptions thrown at this point should have been caught in an
671 // override of DispatchStencilCall. They will not be thrown out of this
675 (static_cast<IIsapiExtension
*>(pvParam
))->DispatchStencilCall(pRequestInfo
);
679 ATLTRACE(_T("Warning. An uncaught exception was thrown from DispatchStencilCall\n"));
684 virtual BOOL
GetWorkerData(DWORD
/*dwParam*/, void ** /*ppvData*/) throw()
690 inline void _AtlGetScriptPathTranslated(
691 __in LPCSTR szPathTranslated
,
692 __inout CFixedStringT
<CStringA
, MAX_PATH
>& strScriptPathTranslated
)
694 ATLENSURE(szPathTranslated
!=NULL
);
695 LPCSTR szEnd
= szPathTranslated
;
699 while (*szEnd
!= '.' && *szEnd
!= '\0')
707 if (!AsciiStrnicmp(szEnd
, c_AtlDLLExtension
+1, ATLS_DLL_EXTENSION_LEN
))
708 nLen
= ATLS_DLL_EXTENSION_LEN
;
709 else if (!AsciiStrnicmp(szEnd
, c_AtlSRFExtension
+1, ATLS_EXTENSION_LEN
))
710 nLen
= ATLS_EXTENSION_LEN
;
715 if (!*szEnd
|| *szEnd
== '/' || *szEnd
== '\\' || *szEnd
== '?' || *szEnd
== '#')
720 DWORD dwResult
= (DWORD
)(szEnd
- szPathTranslated
);
721 char *szScriptPathTranslated
= NULL
;
722 ATLTRY(szScriptPathTranslated
= strScriptPathTranslated
.GetBuffer(dwResult
));
723 if (szScriptPathTranslated
)
725 Checked::memcpy_s(szScriptPathTranslated
, dwResult
, szPathTranslated
, dwResult
);
726 szScriptPathTranslated
[dwResult
] = '\0';
727 strScriptPathTranslated
.ReleaseBuffer(dwResult
);
734 CStencilState() throw()
744 AtlServerRequest
* pIncludeInfo
;
745 AtlServerRequest
* pParentInfo
;
748 class CWrappedServerContext
:
749 public IHttpServerContext
752 CComPtr
<IHttpServerContext
> m_spParent
;
754 CWrappedServerContext() throw()
758 virtual ~CWrappedServerContext() throw()
762 CWrappedServerContext(__in IHttpServerContext
*pParent
) throw()
764 m_spParent
= pParent
;
767 LPCSTR
GetRequestMethod()
769 ATLENSURE(m_spParent
);
770 return m_spParent
->GetRequestMethod();
773 LPCSTR
GetQueryString()
775 ATLENSURE(m_spParent
);
776 return m_spParent
->GetQueryString();
781 ATLENSURE(m_spParent
);
782 return m_spParent
->GetPathInfo();
785 LPCSTR
GetScriptPathTranslated()
787 ATLENSURE(m_spParent
);
788 return m_spParent
->GetScriptPathTranslated();
791 LPCSTR
GetPathTranslated()
793 ATLENSURE(m_spParent
);
794 return m_spParent
->GetPathTranslated();
797 DWORD
GetTotalBytes()
799 ATLENSURE(m_spParent
);
800 return m_spParent
->GetTotalBytes();
803 DWORD
GetAvailableBytes()
805 ATLENSURE(m_spParent
);
806 return m_spParent
->GetAvailableBytes();
809 BYTE
*GetAvailableData()
811 ATLENSURE(m_spParent
);
812 return m_spParent
->GetAvailableData();
815 LPCSTR
GetContentType()
817 ATLENSURE(m_spParent
);
818 return m_spParent
->GetContentType();
821 __checkReturn BOOL
GetServerVariable(__in_z LPCSTR pszVariableName
, __out_ecount_part(*pdwSize
,*pdwSize
) LPSTR pvBuffer
, __inout DWORD
*pdwSize
)
823 ATLENSURE(m_spParent
);
824 return m_spParent
->GetServerVariable(pszVariableName
, pvBuffer
, pdwSize
);
827 __checkReturn BOOL
WriteClient(__in_bcount(*pdwBytes
) void *pvBuffer
, __inout DWORD
*pdwBytes
)
829 ATLENSURE(m_spParent
);
830 return m_spParent
->WriteClient(pvBuffer
, pdwBytes
);
833 __checkReturn BOOL
AsyncWriteClient(__in_bcount(*pdwBytes
) void * pvBuffer
, __inout DWORD
* pdwBytes
)
835 ATLENSURE(m_spParent
);
836 return m_spParent
->AsyncWriteClient(pvBuffer
, pdwBytes
);
839 __checkReturn BOOL
ReadClient(__out_bcount_part(*pdwSize
,*pdwSize
) void * pvBuffer
, __inout DWORD
* pdwSize
)
841 ATLENSURE(m_spParent
);
842 return m_spParent
->ReadClient(pvBuffer
, pdwSize
);
845 __checkReturn BOOL
AsyncReadClient(__out_bcount_part(*pdwSize
,*pdwSize
) void * pvBuffer
, __inout DWORD
* pdwSize
)
847 ATLENSURE(m_spParent
);
848 return m_spParent
->AsyncReadClient(pvBuffer
, pdwSize
);
851 __checkReturn BOOL
SendRedirectResponse(__in LPCSTR pszRedirectUrl
)
853 ATLENSURE(m_spParent
);
854 return m_spParent
->SendRedirectResponse(pszRedirectUrl
);
857 __checkReturn BOOL
GetImpersonationToken(__out HANDLE
* pToken
)
859 ATLENSURE(m_spParent
);
860 return m_spParent
->GetImpersonationToken(pToken
);
863 __checkReturn BOOL
SendResponseHeader(__in LPCSTR pszHeader
, __in LPCSTR pszStatusCode
, __in BOOL fKeepConn
)
865 ATLENSURE(m_spParent
);
866 return m_spParent
->SendResponseHeader(pszHeader
, pszStatusCode
, fKeepConn
);
869 __checkReturn BOOL
DoneWithSession(__in DWORD dwHttpStatusCode
)
871 ATLENSURE(m_spParent
);
872 return m_spParent
->DoneWithSession(dwHttpStatusCode
);
875 __checkReturn BOOL
RequestIOCompletion(__in PFN_HSE_IO_COMPLETION pfn
, DWORD
* pdwContext
)
877 ATLENSURE(m_spParent
);
878 return m_spParent
->RequestIOCompletion(pfn
, pdwContext
);
881 BOOL
TransmitFile(__in HANDLE hFile
, __in_opt PFN_HSE_IO_COMPLETION pfn
, void * pContext
,
882 __in LPCSTR szStatusCode
, __in DWORD dwBytesToWrite
, __in DWORD dwOffset
, __in_bcount_opt(dwHeadLen
) void * pvHead
,
883 __in DWORD dwHeadLen
, __in_bcount_opt(dwTailLen
) void * pvTail
, __in DWORD dwTailLen
, __in DWORD dwFlags
)
885 ATLENSURE(m_spParent
);
886 return m_spParent
->TransmitFile(hFile
, pfn
, pContext
, szStatusCode
,
887 dwBytesToWrite
, dwOffset
, pvHead
, dwHeadLen
, pvTail
, dwTailLen
,
891 BOOL
AppendToLog(__in LPCSTR szMessage
, __in_opt DWORD
* pdwLen
)
893 ATLENSURE(m_spParent
);
894 return m_spParent
->AppendToLog(szMessage
, pdwLen
);
897 BOOL
MapUrlToPathEx(__in_bcount(dwLen
) LPCSTR szLogicalPath
, __in DWORD dwLen
, __out HSE_URL_MAPEX_INFO
*pumInfo
)
899 ATLENSURE(m_spParent
);
900 return m_spParent
->MapUrlToPathEx(szLogicalPath
, dwLen
, pumInfo
);
902 }; // class CWrappedServerContext
904 // Wraps the EXTENSION_CONTROL_BLOCK structure used by IIS to provide
905 // an ISAPI extension with information about the current request and
906 // access to the web server's functionality.
907 class CServerContext
:
908 public CComObjectRootEx
<CComMultiThreadModel
>,
909 public IHttpServerContext
912 BEGIN_COM_MAP(CServerContext
)
913 COM_INTERFACE_ENTRY(IHttpServerContext
)
916 CServerContext() throw()
919 m_bHeadersHaveBeenSent
= false;
921 virtual ~CServerContext() throw()
925 void Initialize(__in EXTENSION_CONTROL_BLOCK
*pECB
)
930 // Initialize the translated script path
931 _AtlGetScriptPathTranslated(GetPathTranslated(), m_strScriptPathTranslated
);
934 // Returns a nul-terminated string that contains the HTTP method of the current request.
935 // Examples of common HTTP methods include "GET" and "POST".
936 // Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
937 LPCSTR
GetRequestMethod()
940 return m_pECB
->lpszMethod
;
943 // Returns a nul-terminated string that contains the query information.
944 // This is the part of the URL that appears after the question mark (?).
945 // Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
946 LPCSTR
GetQueryString()
949 return m_pECB
->lpszQueryString
;
952 // Returns a nul-terminated string that contains the path of the current request.
953 // This is the part of the URL that appears after the server name, but before the query string.
954 // Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
958 return m_pECB
->lpszPathInfo
;
961 // Call this function to retrieve a nul-terminated string containing the physical path of the script.
963 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
965 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
966 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the
967 // buffer (including the nul-terminating byte).
968 // The script path is the same as GetPathTranslated up to the first .srf or .dll.
969 // For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
970 // then this function returns "c:\inetpub\vcisapi\hello.srf".
971 LPCSTR
GetScriptPathTranslated()
974 return m_strScriptPathTranslated
;
978 // Returns a nul-terminated string that contains the translated path of the requested resource.
979 // This is the path of the resource on the local server.
980 // Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
981 LPCSTR
GetPathTranslated()
984 return m_pECB
->lpszPathTranslated
;
987 // Returns the total number of bytes to be received from the client.
988 // If this value is 0xffffffff, then there are four gigabytes or more of available data.
989 // In this case, ReadClient or AsyncReadClient should be called until no more data is returned.
990 // Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
991 DWORD
GetTotalBytes()
994 return m_pECB
->cbTotalBytes
;
997 // Returns the number of bytes available in the request buffer accessible via GetAvailableData.
998 // If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
999 // Otherwise, the remaining data should be read from the client using ReadClient or AsyncReadClient.
1000 // Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
1001 DWORD
GetAvailableBytes()
1004 return m_pECB
->cbAvailable
;
1007 // Returns a pointer to the request buffer containing the data sent by the client.
1008 // The size of the buffer can be determined by calling GetAvailableBytes.
1009 // Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
1010 BYTE
*GetAvailableData()
1013 return m_pECB
->lpbData
;
1016 // Returns a nul-terminated string that contains the content type of the data sent by the client.
1017 // Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
1018 LPCSTR
GetContentType()
1021 return m_pECB
->lpszContentType
;
1024 // Call this function to retrieve a nul-terminated string containing the value of the requested server variable.
1025 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
1026 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
1027 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
1028 // Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
1029 __checkReturn BOOL
GetServerVariable(
1030 __in LPCSTR pszVariableName
,
1031 __out_ecount_part(*pdwSize
,*pdwSize
) LPSTR pvBuffer
,
1032 __inout DWORD
*pdwSize
)
1035 ATLASSERT(pszVariableName
);
1038 if (pszVariableName
&& pdwSize
)
1040 return m_pECB
->GetServerVariable(m_pECB
->ConnID
, (LPSTR
) pszVariableName
,
1046 // Synchronously sends the data present in the given buffer to the client that made the request.
1047 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
1048 // Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_SYNC).
1049 __checkReturn BOOL
WriteClient(__in_bcount(*pdwBytes
) void *pvBuffer
, __inout DWORD
*pdwBytes
)
1052 ATLASSERT(pvBuffer
);
1053 ATLASSERT(pdwBytes
);
1055 if (pvBuffer
&& pdwBytes
)
1057 return m_pECB
->WriteClient(m_pECB
->ConnID
, pvBuffer
, pdwBytes
, HSE_IO_SYNC
| HSE_IO_NODELAY
);
1062 // Asynchronously sends the data present in the given buffer to the client that made the request.
1063 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
1064 // Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_ASYNC).
1065 __checkReturn BOOL
AsyncWriteClient(__in_bcount(*pdwBytes
) void *pvBuffer
, __inout DWORD
*pdwBytes
)
1068 ATLASSERT(pvBuffer
);
1069 ATLASSERT(pdwBytes
);
1071 if (pvBuffer
&& pdwBytes
)
1073 return m_pECB
->WriteClient(m_pECB
->ConnID
, pvBuffer
, pdwBytes
, HSE_IO_ASYNC
| HSE_IO_NODELAY
);
1078 // Call this function to synchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
1079 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
1080 // Equivalent to EXTENSION_CONTROL_BLOCK::ReadClient.
1081 __checkReturn BOOL
ReadClient(__out_ecount_part(*pdwSize
,*pdwSize
) void *pvBuffer
, __inout DWORD
*pdwSize
)
1084 ATLASSERT(pvBuffer
);
1087 if (pvBuffer
&& pdwSize
)
1089 return m_pECB
->ReadClient(m_pECB
->ConnID
, pvBuffer
, pdwSize
);
1094 // Call this function to asynchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
1095 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
1096 // Equivalent to the HSE_REQ_ASYNC_READ_CLIENT server support function.
1097 __checkReturn BOOL
AsyncReadClient(__out_bcount_part(*pdwSize
,*pdwSize
) void *pvBuffer
, __inout DWORD
*pdwSize
)
1099 // To call this function successfully someone has to have already
1100 // called RequestIOCompletion specifying the callback function
1101 // to be used for IO completion.
1103 ATLASSERT(pvBuffer
);
1106 if (pvBuffer
&& pdwSize
)
1108 DWORD dwFlag
= HSE_IO_ASYNC
;
1109 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1110 HSE_REQ_ASYNC_READ_CLIENT
, pvBuffer
, pdwSize
,
1116 // Call this function to redirect the client to the specified URL.
1117 // The client receives a 302 (Found) HTTP status code.
1118 // Returns TRUE on success, and FALSE on failure.
1119 // Equivalent to the HSE_REQ_SEND_URL_REDIRECT_RESP server support function.
1120 __checkReturn BOOL
SendRedirectResponse(__in LPCSTR pszRedirectUrl
)
1123 ATLENSURE(pszRedirectUrl
);
1127 DWORD dwSize
= (DWORD
) strlen(pszRedirectUrl
);
1128 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1129 HSE_REQ_SEND_URL_REDIRECT_RESP
,
1130 (void *) pszRedirectUrl
, &dwSize
, NULL
);
1135 // Call this function to retrieve a handle to the impersonation token for this request.
1136 // An impersonation token represents a user context. You can use the handle in calls to ImpersonateLoggedOnUser or SetThreadToken.
1137 // Do not call CloseHandle on the handle.
1138 // Returns TRUE on success, and FALSE on failure.
1139 // Equivalent to the HSE_REQ_GET_IMPERSONATION_TOKEN server support function.
1140 __checkReturn BOOL
GetImpersonationToken(__out HANDLE
* pToken
)
1145 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1146 HSE_REQ_GET_IMPERSONATION_TOKEN
, pToken
,
1152 // Call this function to send an HTTP response header to the client including the HTTP status, server version, message time, and MIME version.
1153 // Returns TRUE on success, and FALSE on failure.
1154 // Equivalent to the HSE_REQ_SEND_RESPONSE_HEADER_EX server support function.
1155 __checkReturn BOOL
SendResponseHeader(
1156 __in LPCSTR pszHeader
= "Content-Type: text/html\r\n\r\n",
1157 __in LPCSTR pszStatusCode
= "200 OK",
1158 __in BOOL fKeepConn
=FALSE
)
1162 if (m_bHeadersHaveBeenSent
)
1165 HSE_SEND_HEADER_EX_INFO hex
;
1166 hex
.pszStatus
= pszStatusCode
;
1167 hex
.pszHeader
= pszHeader
;
1168 hex
.cchStatus
= (DWORD
)(pszStatusCode
? strlen(pszStatusCode
) : 0);
1169 hex
.cchHeader
= (DWORD
)(pszHeader
? strlen(pszHeader
) : 0);
1170 hex
.fKeepConn
= fKeepConn
;
1172 m_bHeadersHaveBeenSent
= true;
1174 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1175 HSE_REQ_SEND_RESPONSE_HEADER_EX
,
1179 // Call this function to terminate the session for the current request.
1180 // Returns TRUE on success, and FALSE on failure.
1181 // Equivalent to the HSE_REQ_DONE_WITH_SESSION server support function.
1182 __checkReturn BOOL
DoneWithSession(__in DWORD dwHttpStatusCode
)
1186 m_pECB
->dwHttpStatusCode
= dwHttpStatusCode
;
1188 DWORD dwStatusCode
= (dwHttpStatusCode
>= 400) ? HSE_STATUS_ERROR
: HSE_STATUS_SUCCESS
;
1190 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1191 HSE_REQ_DONE_WITH_SESSION
, &dwStatusCode
, NULL
, NULL
);
1194 // Call this function to set a special callback function that will be used for handling the completion of asynchronous I/O operations.
1195 // Returns TRUE on success, and FALSE on failure.
1196 // Equivalent to the HSE_REQ_IO_COMPLETION server support function.
1197 __checkReturn BOOL
RequestIOCompletion(__in PFN_HSE_IO_COMPLETION pfn
, DWORD
*pdwContext
)
1204 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1205 HSE_REQ_IO_COMPLETION
, pfn
, NULL
, pdwContext
);
1210 // Call this function to transmit a file asynchronously to the client.
1211 // Returns TRUE on success, and FALSE on failure.
1212 // Equivalent to the HSE_REQ_TRANSMIT_FILE server support function.
1215 __in_opt PFN_HSE_IO_COMPLETION pfn
,
1217 __in LPCSTR szStatusCode
,
1218 __in DWORD dwBytesToWrite
,
1219 __in DWORD dwOffset
,
1220 __in_bcount_opt(dwHeadLen
) void *pvHead
,
1221 __in DWORD dwHeadLen
,
1222 __in_bcount_opt(dwTailLen
) void *pvTail
,
1223 __in DWORD dwTailLen
,
1230 tf
.BytesToWrite
= dwBytesToWrite
;
1231 tf
.Offset
= dwOffset
;
1232 tf
.pContext
= pContext
;
1235 tf
.HeadLength
= dwHeadLen
;
1237 tf
.TailLength
= dwTailLen
;
1238 tf
.pszStatusCode
= szStatusCode
;
1239 tf
.dwFlags
= dwFlags
;
1240 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1241 HSE_REQ_TRANSMIT_FILE
, &tf
, NULL
, NULL
);
1244 // Appends the string szMessage to the web server log for the current
1246 // Returns TRUE on success, FALSE on failure.
1247 // Equivalent to the HSE_APPEND_LOG_PARAMETER server support function.
1248 BOOL
AppendToLog(__in LPCSTR szMessage
, __in_opt DWORD
*pdwLen
)
1257 dwLen
= (DWORD
)strlen(szMessage
);
1264 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
,
1265 HSE_APPEND_LOG_PARAMETER
, (void *)szMessage
,
1269 // Maps a logical Url Path to a physical path
1270 // Returns TRUE on success, FALSE on failure.
1271 // Equivalent to the HSE_REQ_MAP_URL_TO_PATH_EX server support function.
1272 // you can pass 0 for dwLen if szLogicalPath is null terminated
1273 BOOL
MapUrlToPathEx(__in_bcount(dwLen
) LPCSTR szLogicalPath
, __in DWORD dwLen
, __out HSE_URL_MAPEX_INFO
*pumInfo
)
1275 ATLENSURE(m_pECB
!=NULL
);
1277 dwLen
= (DWORD
) strlen(szLogicalPath
);
1278 return m_pECB
->ServerSupportFunction(m_pECB
->ConnID
, HSE_REQ_MAP_URL_TO_PATH_EX
, (void *) szLogicalPath
,
1279 &dwLen
, (DWORD
*) pumInfo
);
1283 // The pointer to the extension control block provided by IIS.
1284 EXTENSION_CONTROL_BLOCK
*m_pECB
;
1285 bool m_bHeadersHaveBeenSent
;
1287 // The translated script path
1288 CFixedStringT
<CStringA
, MAX_PATH
> m_strScriptPathTranslated
;
1290 }; // class CServerContext
1292 class CPageCachePeer
1302 static BOOL
Add(__inout PeerInfo
* pDest
, __in PeerInfo
* pSrc
) throw()
1306 PeerInfo
*pIn
= (PeerInfo
*)pSrc
;
1307 pDest
->strHeader
= pIn
->strHeader
;
1308 pDest
->strStatus
= pIn
->strStatus
;
1317 static BOOL
Remove(const PeerInfo
* /*pDest*/) throw()
1324 class CCacheServerContext
:
1325 public CComObjectRootEx
<CComMultiThreadModel
>,
1326 public CWrappedServerContext
,
1327 public IPageCacheControl
1331 CAtlTemporaryFile m_cacheFile
;
1332 CComPtr
<IFileCache
> m_spCache
;
1333 char m_szFullUrl
[ATL_URL_MAX_URL_LENGTH
+ 1];
1334 FILETIME m_ftExpiration
;
1336 CPageCachePeer::PeerInfo m_Headers
;
1340 BEGIN_COM_MAP(CCacheServerContext
)
1341 COM_INTERFACE_ENTRY(IHttpServerContext
)
1342 COM_INTERFACE_ENTRY(IPageCacheControl
)
1345 CCacheServerContext() throw()
1348 virtual ~CCacheServerContext() throw()
1352 BOOL
Initialize(__in IHttpServerContext
*pParent
, __in IFileCache
*pCache
) throw()
1357 if (pParent
== NULL
|| pCache
== NULL
)
1360 m_spParent
= pParent
;
1363 if (FAILED(m_cacheFile
.Create()))
1366 LPCSTR szPathInfo
= pParent
->GetPathInfo();
1367 LPCSTR szQueryString
= pParent
->GetQueryString();
1368 if ( szPathInfo
== NULL
|| szQueryString
== NULL
)
1371 LPSTR szTo
= m_szFullUrl
;
1373 while (*szPathInfo
&& nSize
< ATL_URL_MAX_URL_LENGTH
)
1375 *szTo
++ = *szPathInfo
++;
1378 if (nSize
>= ATL_URL_MAX_URL_LENGTH
)
1384 while (*szQueryString
&& nSize
< ATL_URL_MAX_URL_LENGTH
)
1386 *szTo
++ = *szQueryString
++;
1389 if (nSize
>= ATL_URL_MAX_URL_LENGTH
)
1395 memset(&m_ftExpiration
, 0x00, sizeof(FILETIME
));
1401 // IPageCacheControl methods
1402 HRESULT
GetExpiration(__out FILETIME
*pftExpiration
) throw()
1404 ATLASSERT(pftExpiration
);
1406 return E_INVALIDARG
;
1408 *pftExpiration
= m_ftExpiration
;
1413 HRESULT
SetExpiration(__in FILETIME ftExpiration
) throw()
1415 m_ftExpiration
= ftExpiration
;
1420 __checkReturn BOOL
IsCached() throw()
1425 BOOL
Cache(__in BOOL bCache
) throw()
1427 BOOL bRet
= m_bIsCached
;
1428 m_bIsCached
= bCache
;
1432 __checkReturn BOOL
WriteClient(__in_bcount(*pdwBytes
) void *pvBuffer
, __inout DWORD
*pdwBytes
)
1434 ATLENSURE(pvBuffer
);
1435 ATLENSURE(pdwBytes
);
1437 if (S_OK
!= m_cacheFile
.Write(pvBuffer
, *pdwBytes
))
1439 ATLENSURE(m_spParent
);
1440 return m_spParent
->WriteClient(pvBuffer
, pdwBytes
);
1443 __checkReturn BOOL
DoneWithSession(__in DWORD dwHttpStatusCode
)
1445 ATLENSURE(m_spParent
);
1451 CT2CA
strFileName(m_cacheFile
.TempFileName());
1452 m_cacheFile
.HandsOff();
1453 m_spCache
->AddFile(m_szFullUrl
, strFileName
, &m_ftExpiration
, &m_Headers
, NULL
);
1456 m_cacheFile
.Close();
1460 m_cacheFile
.Close();
1463 return m_spParent
->DoneWithSession(dwHttpStatusCode
);
1466 __checkReturn BOOL
GetImpersonationToken(__out HANDLE
* pToken
)
1468 ATLTRACE(atlTraceISAPI
, 0, _T("Getting impersonation token for cached page")
1469 _T(" -- Caching a page that requires special privileges to build is a possible security problem. ")
1470 _T("Future hits may get a cached page without going through the security checks done during the page creation process"));
1471 ATLENSURE(m_spParent
);
1473 return m_spParent
->GetImpersonationToken(pToken
);
1476 __checkReturn BOOL
AppendToLog(__in LPCSTR szMessage
, __in_opt DWORD
* pdwLen
)
1478 ATLTRACE(atlTraceISAPI
, 0, _T("Logging on cached page -- future hits will not log"));
1479 ATLENSURE(m_spParent
);
1480 return m_spParent
->AppendToLog(szMessage
, pdwLen
);
1483 __checkReturn BOOL
SendResponseHeader(
1484 __in LPCSTR pszHeader
= "Content-Type: text/html\r\n\r\n",
1485 __in LPCSTR pszStatusCode
= "200 OK",
1486 __in BOOL fKeepConn
=FALSE
)
1488 ATLENSURE(m_spParent
);
1490 m_Headers
.strHeader
= pszHeader
;
1491 m_Headers
.strStatus
= pszStatusCode
;
1493 return m_spParent
->SendResponseHeader(pszHeader
, pszStatusCode
, fKeepConn
);
1496 // The methods below this point are actions that should not be performed on cached
1497 // pages, as they will not behave correctly.
1498 __checkReturn BOOL
AsyncWriteClient(void * /*pvBuffer*/, DWORD
* /*pdwBytes*/)
1500 // Asynchronous calls will not work
1505 __checkReturn BOOL
ReadClient(void * /*pvBuffer*/, DWORD
* /*pdwSize*/)
1507 // Nobody should be reading from this client if the page is being cached
1508 // Also, only GET's are cached anyway
1513 __checkReturn BOOL
AsyncReadClient(void * /*pvBuffer*/, DWORD
* /*pdwSize*/)
1519 __checkReturn BOOL
SendRedirectResponse(LPCSTR
/*pszRedirectUrl*/)
1526 __checkReturn BOOL
RequestIOCompletion(PFN_HSE_IO_COMPLETION
/*pfn*/, DWORD
* /*pdwContext*/)
1532 __checkReturn BOOL
TransmitFile(
1534 PFN_HSE_IO_COMPLETION
/*pfn*/,
1535 void * /*pContext*/,
1536 LPCSTR
/*szStatusCode*/,
1537 DWORD
/*dwBytesToWrite*/,
1540 DWORD
/*dwHeadLen*/,
1542 DWORD
/*dwTailLen*/,
1551 // This class represents a collection of validation failures.
1552 // Use this class in combination with CValidateObject to validate
1553 // forms, cookies, or query strings and build up a collection of
1554 // failures. If appropriate, use the information in the collection
1555 // to return detailed responses to the client to help them correct the failures.
1558 class CValidateContext
1561 enum { ATL_EMPTY_PARAMS_ARE_FAILURES
= 0x00000001 };
1563 CValidateContext(__in DWORD dwFlags
=0) throw()
1565 m_bFailures
= false;
1566 m_dwFlags
= dwFlags
;
1569 bool SetResultAt(__in LPCSTR szName
, __in DWORD type
)
1573 if (!VALIDATION_SUCCEEDED(type
) ||
1574 (type
== VALIDATION_S_EMPTY
&& (m_dwFlags
& ATL_EMPTY_PARAMS_ARE_FAILURES
)))
1577 return TRUE
== m_results
.SetAt(szName
,type
);
1587 // Call this function to add a validation result to the collection managed by this object.
1588 // Each result is identified by a name and the type of result that occurred.
1589 // The result codes are the VALIDATION_ codes defined at the top of this file.
1590 // The bOnlyFailure parameter below is used to only allow failure results to
1591 // be added to the list of failures. The reason you'd want to do this is that
1592 // success codes should be the common case in validation routines so you can
1593 // use bOnlyFailures to limit the number of allocations by this class's base
1594 // map for mapping success results if you don't care about iterating successes.
1596 bool AddResult(__in LPCSTR szName
, __in DWORD type
, __in
bool bOnlyFailures
= true) throw()
1600 if (!VALIDATION_SUCCEEDED(type
) ||
1601 (type
== VALIDATION_S_EMPTY
&& (m_dwFlags
& ATL_EMPTY_PARAMS_ARE_FAILURES
)))
1605 return TRUE
== m_results
.Add(szName
, type
); // add everything
1607 else if (bOnlyFailures
&&
1608 (!VALIDATION_SUCCEEDED(type
) ||
1609 (type
== VALIDATION_S_EMPTY
&& (m_dwFlags
& ATL_EMPTY_PARAMS_ARE_FAILURES
))))
1610 return TRUE
== m_results
.Add(szName
, type
); // only add failures
1619 // Returns true if there are no validation failures in the collection,
1620 // returns false otherwise.
1621 __checkReturn
bool ParamsOK() throw()
1623 return !m_bFailures
;
1626 // Returns the number of validation results in the collection.
1627 __checkReturn
int GetResultCount() throw()
1629 return m_results
.GetSize();
1632 // Call this function to retrieve the name and type of a
1633 // validation result based on its index in the collection.
1634 // Returns true on success, false on failure.
1636 // i The index of a result managed by this collection.
1638 // strName On success, the name of the result with index i.
1640 // type On success, the type of the result with index i.
1641 __checkReturn
bool GetResultAt(__in
int i
, __out CStringA
& strName
, __out DWORD
& type
) throw()
1643 if ( i
>= 0 && i
< m_results
.GetSize())
1647 strName
= m_results
.GetKeyAt(i
);
1648 type
= m_results
.GetValueAt(i
);
1661 CSimpleMap
<CStringA
, DWORD
> m_results
;
1663 }; // CValidateContext
1670 template <class T
, class TCompType
>
1671 static DWORD
Validate(
1673 __in TCompType nMinValue
,
1674 __in TCompType nMaxValue
) throw()
1676 DWORD dwRet
= VALIDATION_S_OK
;
1677 if (value
< static_cast<T
>(nMinValue
))
1678 dwRet
= VALIDATION_E_LENGTHMIN
;
1679 else if (value
> static_cast<T
>(nMaxValue
))
1680 dwRet
= VALIDATION_E_LENGTHMAX
;
1684 static DWORD
Validate( __in LPCSTR pszValue
, __in
int nMinChars
, __in
int nMaxChars
) throw()
1686 DWORD dwRet
= VALIDATION_S_OK
;
1689 return VALIDATION_E_FAIL
;
1691 int nChars
= (int) strlen(pszValue
);
1692 if (nChars
< nMinChars
)
1693 dwRet
= VALIDATION_E_LENGTHMIN
;
1694 else if (nChars
> nMaxChars
)
1695 dwRet
= VALIDATION_E_LENGTHMAX
;
1698 static DWORD
Validate( __in
double dblValue
, __in
double dblMinValue
, __in
double dblMaxValue
) throw()
1700 DWORD dwRet
= VALIDATION_S_OK
;
1701 if ( dblValue
< (dblMinValue
- ATL_EPSILON
) )
1702 dwRet
= VALIDATION_E_LENGTHMIN
;
1703 else if ( dblValue
> (dblMaxValue
+ ATL_EPSILON
) )
1704 dwRet
= VALIDATION_E_LENGTHMAX
;
1709 // This class provides functions for retrieving and validating named values.
1711 // The named values are expected to be provided in string form by the class used as
1712 // the template parameter. CValidateObject provides the means of
1713 // retrieving these values converted to data types chosen by you. You can validate the values
1714 // by specifying a range for numeric values or by specifying a minimum and maximum length
1715 // for string values.
1717 // Call one of the Exchange overloads to retrieve a named value converted to your chosen data type.
1718 // Call one of the Validate overloads to retrieve a named value converted to your chosen data type
1719 // and validated against a minimum and maximum value or length supplied by you.
1721 // To add validation functionality to the class TLookupClass, derive that class from CValidateObject<TLookupClass>
1722 // and provide a Lookup function that takes a name as a string and returns the corresponding value
1723 // also as a string:
1724 // LPCSTR Lookup(LPCSTR szName);
1725 template <class TLookupClass
, class TValidator
= CAtlValidator
>
1726 class CValidateObject
1729 // Exchange Routines
1731 // Call this function to retrieve a named value converted to your chosen data type.
1732 // Returns one of the following validation status codes:
1733 // VALIDATION_S_OK The named value was found and could be converted successfully
1734 // VALIDATION_S_EMPTY The name was present, but the value was empty
1735 // VALIDATION_E_PARAMNOTFOUND The named value was not found
1736 // VALIDATION_E_INVALIDPARAM The name was present, but the value could not be converted to the requested data type
1737 // VALIDATION_E_FAIL An unspecified error occurred
1738 // Pass a pointer to a validation context object if you want to add
1739 // failures to the collection managed by that object.
1741 ATL_NOINLINE __checkReturn DWORD
Exchange(
1742 __in LPCSTR szParam
,
1744 __inout_opt CValidateContext
*pContext
= NULL
) const throw()
1746 DWORD dwRet
= VALIDATION_E_PARAMNOTFOUND
;
1751 const TLookupClass
*pT
= static_cast<const TLookupClass
*>(this);
1752 LPCSTR szValue
= pT
->Lookup(szParam
);
1756 dwRet
= VALIDATION_S_EMPTY
;
1759 dwRet
= ConvertNumber(szValue
, pValue
);
1765 return VALIDATION_E_FAIL
;
1769 dwRet
= VALIDATION_E_FAIL
; // invalid input
1772 pContext
->AddResult(szParam
, dwRet
);
1777 ATL_NOINLINE __checkReturn DWORD
Exchange(
1778 __in LPCSTR szParam
,
1779 __out_opt CString
* pstrValue
,
1780 __in_opt CValidateContext
*pContext
) const throw()
1784 LPCSTR pszValue
= NULL
;
1785 DWORD dwRet
= VALIDATION_E_PARAMNOTFOUND
;
1788 dwRet
= Exchange(szParam
, &pszValue
, pContext
);
1789 if (VALIDATION_SUCCEEDED(dwRet
) && pstrValue
!= NULL
)
1790 *pstrValue
= CA2T(pszValue
);
1794 dwRet
= VALIDATION_E_FAIL
; // invalid input
1796 pContext
->AddResult(szParam
, dwRet
);
1803 return VALIDATION_E_FAIL
;
1808 ATL_NOINLINE __checkReturn DWORD
Exchange(
1809 __in LPCSTR szParam
,
1810 __deref_out_opt LPCSTR
* ppszValue
,
1811 __inout_opt CValidateContext
*pContext
) const throw()
1813 DWORD dwRet
= VALIDATION_E_PARAMNOTFOUND
;
1819 const TLookupClass
*pT
= static_cast<const TLookupClass
*>(this);
1820 LPCSTR szValue
= pT
->Lookup(szParam
);
1824 dwRet
= VALIDATION_S_EMPTY
;
1827 *ppszValue
= szValue
;
1828 dwRet
= VALIDATION_S_OK
;
1834 return VALIDATION_E_FAIL
;
1838 dwRet
= VALIDATION_E_FAIL
; // invalid input
1841 pContext
->AddResult(szParam
, dwRet
);
1846 ATL_NOINLINE __checkReturn DWORD
Exchange(
1847 __in LPCSTR szParam
,
1849 __inout_opt CValidateContext
*pContext
) const throw()
1851 DWORD dwRet
= VALIDATION_E_PARAMNOTFOUND
;
1856 const TLookupClass
*pT
= static_cast<const TLookupClass
*>(this);
1857 LPCSTR szValue
= pT
->Lookup(szParam
);
1861 dwRet
= VALIDATION_S_EMPTY
;
1864 if (S_OK
!= CLSIDFromString(CA2W(szValue
), pValue
))
1866 dwRet
= VALIDATION_E_INVALIDPARAM
;
1869 dwRet
= VALIDATION_S_OK
;
1875 return VALIDATION_E_FAIL
;
1879 dwRet
= VALIDATION_E_FAIL
; // invalid input
1882 pContext
->AddResult(szParam
, dwRet
);
1887 ATL_NOINLINE __checkReturn DWORD
Exchange(
1888 __in LPCSTR szParam
,
1889 __out
bool* pbValue
,
1890 __inout_opt CValidateContext
*pContext
) const throw()
1892 DWORD dwRet
= VALIDATION_S_OK
;
1897 const TLookupClass
*pT
= static_cast<const TLookupClass
*>(this);
1898 LPCSTR szValue
= pT
->Lookup(szParam
);
1902 if (*szValue
!= '\0')
1908 return VALIDATION_E_FAIL
;
1912 dwRet
= VALIDATION_E_FAIL
; // invalid input
1915 pContext
->AddResult(szParam
, dwRet
);
1920 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out ULONGLONG
*pnVal
) const throw()
1923 return VALIDATION_E_FAIL
;
1927 return VALIDATION_E_FAIL
;
1930 errno_t errnoValue
= AtlStrToNum(&n
, szVal
, &pEnd
, 10);
1931 if (pEnd
== szVal
|| errnoValue
== ERANGE
)
1933 return VALIDATION_E_INVALIDPARAM
;
1936 return VALIDATION_S_OK
;
1939 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out LONGLONG
*pnVal
) const throw()
1942 return VALIDATION_E_FAIL
;
1946 return VALIDATION_E_FAIL
;
1949 errno_t errnoValue
= AtlStrToNum(&n
, szVal
, &pEnd
, 10);
1950 if (pEnd
== szVal
|| errnoValue
== ERANGE
)
1952 return VALIDATION_E_INVALIDPARAM
;
1955 return VALIDATION_S_OK
;
1958 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
double *pdblVal
) const throw()
1961 return VALIDATION_E_FAIL
;
1965 return VALIDATION_E_FAIL
;
1968 errno_t errnoValue
= AtlStrToNum(&d
, szVal
, &pEnd
);
1969 if (pEnd
== szVal
|| errnoValue
== ERANGE
)
1971 return VALIDATION_E_INVALIDPARAM
;
1974 return VALIDATION_S_OK
;
1977 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
int *pnVal
) const throw()
1979 return ConvertNumber(szVal
, (long*)pnVal
);
1982 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
unsigned int *pnVal
) const throw()
1984 return ConvertNumber(szVal
, (unsigned long*)pnVal
);
1987 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
long *pnVal
) const throw()
1990 return VALIDATION_E_FAIL
;
1994 return VALIDATION_E_FAIL
;
1997 errno_t errnoValue
= AtlStrToNum(&n
, szVal
, &pEnd
, 10);
1998 if (pEnd
== szVal
|| errnoValue
== ERANGE
)
2000 return VALIDATION_E_INVALIDPARAM
;
2003 return VALIDATION_S_OK
;
2006 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
unsigned long *pnVal
) const throw()
2009 return VALIDATION_E_FAIL
;
2013 return VALIDATION_E_FAIL
;
2015 unsigned long n
= 0;
2016 errno_t errnoValue
= AtlStrToNum(&n
, szVal
, &pEnd
, 10);
2017 if (pEnd
== szVal
|| errnoValue
== ERANGE
)
2019 return VALIDATION_E_INVALIDPARAM
;
2022 return VALIDATION_S_OK
;
2025 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
short *pnVal
) const throw()
2028 return VALIDATION_E_FAIL
;
2032 return VALIDATION_E_FAIL
;
2034 DWORD dwRet
= ConvertNumber(szVal
, &nVal
);
2035 if (dwRet
== VALIDATION_S_OK
)
2037 // clamp to the size of a short
2038 if(nVal
<= SHRT_MAX
&&
2041 *pnVal
= (short)nVal
;
2045 dwRet
= VALIDATION_E_INVALIDPARAM
;
2051 __checkReturn DWORD
ConvertNumber(__in LPCSTR szVal
, __out
unsigned short *pnVal
) const throw()
2054 return VALIDATION_E_FAIL
;
2058 return VALIDATION_E_FAIL
;
2059 unsigned long nVal
= 0;
2060 DWORD dwRet
= ConvertNumber(szVal
, &nVal
);
2061 if (dwRet
== VALIDATION_S_OK
)
2063 // clamp to the size of a short
2064 if(nVal
<= USHRT_MAX
&&
2067 *pnVal
= (unsigned short)nVal
;
2071 dwRet
= VALIDATION_E_INVALIDPARAM
;
2077 // Call this function to retrieve a named value converted to your chosen data type
2078 // and validated against a minimum and maximum value or length supplied by you.
2080 // Returns one of the following validation status codes:
2081 // VALIDATION_S_OK The named value was found and could be converted successfully
2082 // VALIDATION_S_EMPTY The name was present, but the value was empty
2083 // VALIDATION_E_PARAMNOTFOUND The named value was not found
2084 // VALIDATION_E_INVALIDPARAM The name was present, but the value could not be converted to the requested data type
2085 // VALIDATION_E_LENGTHMIN The name was present and could be converted to the requested data type, but the value was too small
2086 // VALIDATION_E_LENGTHMAX The name was present and could be converted to the requested data type, but the value was too large
2087 // VALIDATION_E_FAIL An unspecified error occurred
2089 // Validate can be used to convert and validate name-value pairs
2090 // such as those associated with HTTP requests (query string, form fields, or cookie values).
2091 // The numeric specializations validate the minimum and maximum value.
2092 // The string specializations validate the minimum and maximum length.
2094 // Pass a pointer to a validation context object if you want to add
2095 // failures to the collection managed by that object.
2097 // Note that you can validate the value of a parameter without
2098 // storing its value by passing NULL for the second parameter. However
2099 // if you pass NULL for the second parameter, make sure you cast the NULL to a
2100 // type so that the compiler will call the correct specialization of Validate.
2101 template <class T
, class TCompType
>
2102 ATL_NOINLINE __checkReturn DWORD
Validate(
2104 __out_opt T
*pValue
,
2105 __in TCompType nMinValue
,
2106 __in TCompType nMaxValue
,
2107 __inout_opt CValidateContext
*pContext
= NULL
) const throw()
2110 DWORD dwRet
= Exchange(Param
, &value
, pContext
);
2111 if ( dwRet
== VALIDATION_S_OK
)
2115 dwRet
= TValidator::Validate(value
, nMinValue
, nMaxValue
);
2116 if (pContext
&& dwRet
!= VALIDATION_S_OK
)
2117 pContext
->AddResult(Param
, dwRet
);
2119 else if (dwRet
== VALIDATION_S_EMPTY
&&
2120 !IsNullByType(nMinValue
))
2122 dwRet
= VALIDATION_E_LENGTHMIN
;
2125 pContext
->SetResultAt(Param
, VALIDATION_E_LENGTHMIN
);
2132 // Specialization for strings. Comparison is for number of characters.
2134 ATL_NOINLINE __checkReturn DWORD
Validate(
2136 __deref_opt_out LPCSTR
* ppszValue
,
2139 __inout_opt CValidateContext
*pContext
) const throw()
2141 LPCSTR pszValue
= NULL
;
2142 DWORD dwRet
= Exchange(Param
, &pszValue
, pContext
);
2143 if (dwRet
== VALIDATION_S_OK
)
2146 *ppszValue
= pszValue
;
2147 dwRet
= TValidator::Validate(pszValue
, nMinChars
, nMaxChars
);
2148 if (pContext
&& dwRet
!= VALIDATION_S_OK
)
2149 pContext
->AddResult(Param
, dwRet
);
2151 else if (dwRet
== VALIDATION_S_EMPTY
&&
2154 dwRet
= VALIDATION_E_LENGTHMIN
;
2157 pContext
->SetResultAt(Param
, VALIDATION_E_LENGTHMIN
);
2165 // Specialization for CString so caller doesn't have to cast CString
2167 ATL_NOINLINE __checkReturn DWORD
Validate(
2169 __out_opt CString
* pstrValue
,
2172 __inout_opt CValidateContext
*pContext
) const throw()
2177 DWORD dwRet
= Validate(Param
, &szValue
, nMinChars
, nMaxChars
, pContext
);
2178 if (pstrValue
&& dwRet
== VALIDATION_S_OK
)
2179 *pstrValue
= szValue
;
2184 return VALIDATION_E_FAIL
;
2188 // Specialization for doubles, uses a different comparison.
2190 ATL_NOINLINE __checkReturn DWORD
Validate(
2192 __out_opt
double* pdblValue
,
2193 __in
double dblMinValue
,
2194 __in
double dblMaxValue
,
2195 __inout_opt CValidateContext
*pContext
) const throw()
2198 DWORD dwRet
= Exchange(Param
, &dblValue
, pContext
);
2199 if (dwRet
== VALIDATION_S_OK
)
2202 *pdblValue
= dblValue
;
2203 dwRet
= TValidator::Validate(dblValue
, dblMinValue
, dblMaxValue
);
2204 if (pContext
&& dwRet
!= VALIDATION_S_OK
)
2205 pContext
->AddResult(Param
, dwRet
);
2207 else if (dwRet
== VALIDATION_S_EMPTY
&&
2208 (dblMinValue
< -ATL_EPSILON
||
2209 dblMinValue
> ATL_EPSILON
))
2211 dwRet
= VALIDATION_E_LENGTHMIN
;
2214 pContext
->SetResultAt(Param
, VALIDATION_E_LENGTHMIN
);
2221 // Cookies provide a way for a server to store a small amount of data on a client
2222 // and have that data returned to it on each request the client makes.
2223 // Use this class to represent a cookie to be sent from the server to a client
2224 // or to represent a cookie that has been returned by a client to the originating server.
2226 // At the HTTP level, a cookie is an application-defined name-value pair
2227 // plus some standard attribute-value pairs that describe the way in which the user agent (web browser)
2228 // should interact with the cookie. The HTTP format of a cookie is described in RFC 2109.
2230 // The CCookie class provides methods to set and get the application-defined name and value
2231 // as well as methods for the standard attributes. In addition, CCookie provides an abstraction
2232 // on top of the application-defined value that allows it to be treated as a collection of name-value
2233 // pairs if that model makes sense to you. Cookies with a single value are known as single-valued cookies.
2234 // Cookies whose value consists of name-value pairs are known as multi-valued cookies or dictionary cookies.
2236 // You can set the name of a cookie by calling SetName or using the appropriate constructor.
2237 // The name of a cookie can be 0 or more characters.
2239 // You can set the value of a cookie by calling SetValue or using the appropriate constructor.
2240 // If the cookie has a value set, it is a single-valued cookie and attempts to add a name-value pair will fail.
2241 // You can remove the value of a cookie by calling SetValue(NULL).
2243 // You can add a name-value pair to a cookie by calling AddValue.
2244 // If the cookie has any name-value pairs, it is a multi-valued cookie and attempts to set the primary value will fail.
2245 // You can remove all the name-value pairs of a cookie by calling RemoveAllValues.
2247 // Class CCookie follows the same rules for creating cookies as ASP does.
2249 public CValidateObject
<CCookie
>
2251 typedef CAtlMap
<CStringA
, CStringA
, CStringElementTraits
<CStringA
>,
2252 CStringElementTraits
<CStringA
> > mapType
;
2254 const static DWORD ATLS_MAX_HTTP_DATE
= 64;
2257 // Constructs a named cookie.
2258 CCookie(__in LPCSTR szName
) throw(...)
2260 ATLENSURE(SetName(szName
));
2263 // Constructs a single-valued cookie.
2264 CCookie(__in LPCSTR szName
, __in_opt LPCSTR szValue
) throw(...)
2266 ATLENSURE(SetName(szName
));
2267 ATLENSURE(SetValue(szValue
));
2270 CCookie(__in
const CCookie
& thatCookie
) throw(...)
2275 CCookie
& operator=(__in
const CCookie
& thatCookie
) throw(...)
2277 if(this!=&thatCookie
)
2279 return Copy(thatCookie
);
2289 __checkReturn BOOL
IsEmpty() const throw()
2291 return m_strName
.IsEmpty();
2294 // Call this function to set the name of this cookie.
2295 // Returns TRUE on success, FALSE on failure.
2296 // The name of a cookie cannot contain whitespace, semicolons or commas.
2297 // The name should not begin with a dollar sign ($) since such names are reserved for future use.
2298 __checkReturn BOOL
SetName(__in LPCSTR szName
) throw()
2302 if (szName
&& *szName
)
2314 // Call this function to retrieve the name of this cookie.
2315 // Returns TRUE on success, FALSE on failure.
2316 __checkReturn BOOL
GetName(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) const throw()
2318 return CopyCString(m_strName
, szBuff
, pdwSize
);
2321 // Call this function to retrieve the name of this cookie.
2322 // Returns TRUE on success, FALSE on failure.
2323 __checkReturn BOOL
GetName(__out CStringA
&strName
) const throw()
2327 strName
= m_strName
;
2336 // Call this function to set the value of this cookie.
2337 // Returns TRUE on success, FALSE on failure.
2338 // Will fail if the cookie is multi-valued.
2339 // Pass NULL to remove the cookie's value.
2340 __checkReturn BOOL
SetValue(__in_opt LPCSTR szValue
) throw()
2344 if (m_Values
.GetCount())
2345 return FALSE
; //already dictionary values in the cookie
2350 m_strValue
= szValue
;
2360 // Call this function to retrieve the value of this cookie.
2361 // Returns TRUE on success, FALSE on failure.
2362 // Returns TRUE if there is no value or the value is of zero length.
2363 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
2364 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
2365 __checkReturn BOOL
GetValue(__out_ecount(*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) const throw()
2367 return CopyCString(m_strValue
, szBuff
, pdwSize
);
2370 // Call this function to retrieve the value of this cookie.
2371 // Returns TRUE on success, FALSE on failure.
2372 __checkReturn BOOL
GetValue(__out CStringA
&strValue
) const throw()
2376 strValue
= m_strValue
;
2385 // Call this function to add a name-value pair to the cookie.
2386 // Returns TRUE on success, FALSE on failure.
2387 // Will fail if the cookie is single-valued.
2388 // If the named value is already present in the cookie, calling this function
2389 // will modify the current value, otherwise a new name-value pair is added to the cookie.
2390 // Call RemoveValue or RemoveAllValues to remove the name-value pairs
2391 // added by this function.
2392 __checkReturn BOOL
AddValue(__in LPCSTR szName
, __in_opt LPCSTR szValue
) throw()
2394 if (m_strValue
.GetLength())
2398 return m_Values
.SetAt(szName
, szValue
) != NULL
;
2406 // Call this function to modify a name-value pair associated with the cookie.
2407 // Returns TRUE on success, FALSE on failure.
2408 // Will fail if the cookie is single-valued.
2409 // This function just calls AddValue so the name-value pair will be added if not already present.
2410 // Use this function instead of AddValue to document the intentions of your call.
2411 __checkReturn BOOL
ModifyValue(__in LPCSTR szName
, __in LPCSTR szValue
) throw()
2413 return AddValue(szName
, szValue
);
2416 // Call this function to remove a name-value pair from the collection managed by this cookie.
2417 // Returns TRUE on success, FALSE on failure.
2418 __checkReturn BOOL
RemoveValue(__in LPCSTR szName
) throw()
2420 return m_Values
.RemoveKey(szName
);
2423 // Call this function to remove all the name-value pairs from the collection managed by this cookie.
2424 void RemoveAllValues() throw()
2426 m_Values
.RemoveAll();
2429 // Call this function to add an attribute-value pair to the collection of attributes for this cookie.
2430 // Returns TRUE on success, FALSE on failure.
2431 // This function is equivalent to calling ModifyAttribute.
2432 // Both functions will add the attribute if not already present or
2433 // change its value if it has already been applied to the cookie.
2434 __checkReturn BOOL
AddAttribute(__in LPCSTR szName
, __in LPCSTR szValue
) throw()
2436 if (!szName
|| !*szName
|| !szValue
)
2441 return (m_Attributes
.SetAt(szName
, szValue
) != NULL
);
2450 // Call this function to modify an attribute-value pair associated with the cookie.
2451 // Returns TRUE on success, FALSE on failure.
2452 // This function is equivalent to calling AddAttribute.
2453 // Both functions will add the attribute if not already present or
2454 // change its value if it has already been applied to the cookie.
2455 __checkReturn BOOL
ModifyAttribute(__in LPCSTR szName
, __in LPCSTR szValue
) throw()
2457 return AddAttribute(szName
, szValue
);
2460 // Call this function to remove an attribute-value pair from the collection of attributes managed by this cookie.
2461 // Returns TRUE on success, FALSE on failure.
2462 __checkReturn BOOL
RemoveAttribute(__in LPCSTR szName
) throw()
2464 return m_Attributes
.RemoveKey(szName
);
2467 // Call this function to remove all the attribute-value pairs from the collection of attributes managed by this cookie.
2468 void RemoveAllAttributes() throw()
2470 m_Attributes
.RemoveAll();
2474 // Call this function to set the Comment attribute of the cookie.
2475 // Returns TRUE on success, FALSE on failure.
2476 // The Comment attribute allows a web server to document its
2477 // intended use of a cookie. This information may be displayed
2478 // by supporting browsers so that the user of the web site can
2479 // decide whether to initiate or continue a session with this cookie.
2480 // This attribute is optional.
2481 // Version 1 attribute.
2482 __checkReturn BOOL
SetComment(__in LPCSTR szComment
) throw()
2484 BOOL bRet
= SetVersion(1);
2486 bRet
= AddAttribute("comment", szComment
);
2490 // Call this function to set the CommentUrl attribute of the cookie.
2491 // Returns TRUE on success, FALSE on failure.
2492 // The CommentUrl attribute allows a web server to document its intended
2493 // use of a cookie via a URL that the user of the web site can navigate to.
2494 // The URL specified here should not send further cookies to the client to
2495 // avoid frustrating the user.
2496 // This attribute is optional.
2497 // Version 1 attribute.
2498 __checkReturn BOOL
SetCommentUrl(__in LPCSTR szUrl
) throw()
2500 BOOL bRet
= SetVersion(1);
2502 bRet
= AddAttribute("commenturl", szUrl
);
2506 // Call this function to add or remove the Discard attribute of the cookie.
2507 // Returns TRUE on success, FALSE on failure.
2508 // The Discard attribute does not have a value.
2509 // Call SetDiscard(TRUE) to add the Discard attribute
2510 // or SetDiscard(FALSE) to remove the Discard attribute.
2511 // Setting the Discard attribute tells a web browser that it should
2512 // discard this cookie when the browser exits regardless of the
2513 // value of the Max-Age attribute.
2514 // This attribute is optional.
2515 // When omitted, the default behavior is that the Max-Age attribute
2516 // controls the lifetime of the cookie.
2517 // Version 1 attribute.
2518 __checkReturn BOOL
SetDiscard(__in BOOL bDiscard
) throw()
2521 LPCSTR szKey
= "Discard";
2522 bRet
= SetVersion(1);
2527 bRet
= m_Attributes
.RemoveKey(szKey
);
2533 bRet
= m_Attributes
.SetAt(szKey
, " ") != 0;
2544 // Call this function to set the Domain attribute of the cookie.
2545 // Returns TRUE on success, FALSE on failure.
2546 // The Domain attribute is used to indicate the domain to which the current
2547 // cookie applies. Browsers should only send cookies back to the relevant domains.
2548 // This attribute is optional.
2549 // When omitted, the default behavior is for
2550 // browsers to use the full domain of the server originating the cookie. You can
2551 // set this attribute value explicitly if you want to share cookies among several servers.
2552 // Version 0 & Version 1 attribute.
2553 __checkReturn BOOL
SetDomain(__in LPCSTR szDomain
) throw()
2555 BOOL bRet
= SetVersion(1);
2557 bRet
= AddAttribute("domain", szDomain
);
2561 // Call this function to set the Max-Age attribute of the cookie.
2562 // Returns TRUE on success, FALSE on failure.
2563 // The value of the Max-Age attribute is a lifetime in seconds for the cookie.
2564 // When the time has expired, compliant browsers will discard this cookie
2565 // (if they haven't already done so as a result of the Discard attribute).
2566 // If Max-Age is set to zero, the browser discards the cookie immediately.
2567 // This attribute is the Version 1 replacement for the Expires attribute.
2568 // This attribute is optional.
2569 // When omitted, the default behavior is for browsers to discard cookies
2570 // when the user closes the browser.
2571 // Version 1 attribute.
2572 __checkReturn BOOL
SetMaxAge(__in UINT nMaxAge
) throw()
2575 bRet
= SetVersion(1);
2579 if (0 == _itoa_s(nMaxAge
, buff
, _countof(buff
), 10))
2581 bRet
= AddAttribute("max-age", buff
);
2587 // Call this function to set the Path attribute of the cookie.
2588 // Returns TRUE on success, FALSE on failure.
2589 // The Path attribute specifies the subset of URLs to which this cookie applies.
2590 // Only URLs that contain that path are allowed to read or modify the cookie.
2591 // This attribute is optional.
2592 // When omitted the default behavior is for browsers to treat the path of a cookie
2593 // as the path of the request URL that generated the Set-Cookie response, up to,
2594 // but not including, the right-most /.
2595 // Version 0 & Version 1 attribute.
2596 __checkReturn BOOL
SetPath(__in LPCSTR szPath
) throw()
2598 BOOL bRet
= SetVersion(1);
2600 bRet
= AddAttribute("path", szPath
);
2604 // Call this function to set the Port attribute of the cookie.
2605 // Returns TRUE on success, FALSE on failure.
2606 // The Port attribute specifies the port to which this cookie applies.
2607 // Only URLs accessed via that port are allowed to read or modify the cookie.
2608 // This attribute is optional.
2609 // When omitted the default behavior is for browsers to return the cookie via any port.
2610 // Version 1 attribute.
2611 __checkReturn BOOL
SetPort(__in LPCSTR szPort
) throw()
2613 BOOL bRet
= SetVersion(1);
2615 bRet
= AddAttribute("port", szPort
);
2619 // Call this function to add or remove the Secure attribute of the cookie.
2620 // Returns TRUE on success, FALSE on failure.
2621 // The Secure attribute does not have a value.
2622 // Call SetSecure(TRUE) to add the Secure attribute
2623 // or SetSecure(FALSE) to remove the Secure attribute.
2624 // Setting the Secure attribute tells a browser that it should
2625 // transmit the cookie to the web server only via secure means such as HTTPS.
2626 // This attribute is optional.
2627 // When omitted, the default behavior is that the cookie
2628 // will be sent via unsecured protocols.
2629 // Version 0 & Version 1 attribute.
2630 __checkReturn BOOL
SetSecure(__in BOOL bSecure
) throw()
2633 LPCSTR szKey
= "secure";
2634 bRet
= SetVersion(1);
2639 bRet
= m_Attributes
.RemoveKey(szKey
);
2645 bRet
= m_Attributes
.SetAt(szKey
, " ") != 0;
2656 // Call this function to set the Version attribute of the cookie.
2657 // Returns TRUE on success, FALSE on failure.
2658 // This attribute is required for Version 1 cookies by RFC 2109 and must have a value of 1.
2659 // However, you do not need to call SetVersion explicitly from your own code unless you need to
2660 // force RFC 2109 compliance. CCookie will automatically set this attribute whenever
2661 // you use a Version 1 attribute in your cookie.
2662 // Version 1 attribute.
2663 __checkReturn BOOL
SetVersion(__in UINT nVersion
) throw()
2667 if (0 == _itoa_s(nVersion
, buff
, _countof(buff
), 10))
2669 bRet
= AddAttribute("version", buff
);
2674 // Call this function to set the Expires attribute of the cookie.
2675 // Returns TRUE on success, FALSE on failure.
2676 // The Expires attribute specifies an absolute date and time at which this cookie
2677 // should be discarded by web browsers. Pass a SYSTEMTIME holding a Greenwich Mean Time (GMT)
2678 // value or a string in the following format:
2679 // Wdy, DD-Mon-YY HH:MM:SS GMT
2680 // This attribute is optional.
2681 // When omitted, the default behavior is for browsers to discard cookies
2682 // when the user closes the browser.
2683 // This attribute has been superceded in Version 1 by the Max-Age attribute,
2684 // but you should continue to use this attribute for Version 0 clients.
2685 // Version 0 attribute.
2686 __checkReturn BOOL
SetExpires(__in LPCSTR szExpires
) throw()
2688 return AddAttribute("expires", szExpires
);
2691 __checkReturn BOOL
SetExpires(__in
const SYSTEMTIME
&st
) throw()
2695 CFixedStringT
<CStringA
, ATLS_MAX_HTTP_DATE
> strTime
;
2696 SystemTimeToHttpDate(st
, strTime
);
2697 return SetExpires(strTime
);
2705 // Call this function to look up the value of a name-value pair applied to this cookie.
2706 // Returns the requested value if present or NULL if the name was not found.
2707 __checkReturn LPCSTR
Lookup(__in_opt LPCSTR szName
=NULL
) const throw()
2712 if (m_strValue
.GetLength())
2714 ATLASSERT(szName
== NULL
);
2718 if (m_Values
.GetCount())
2720 ATLENSURE_RETURN_VAL(szName
, NULL
);
2721 const mapType::CPair
*pPair
= NULL
;
2722 ATLTRY(pPair
= m_Values
.Lookup(szName
));
2724 return (LPCSTR
)pPair
->m_value
;
2730 // Call this function to clear the cookie of all content
2731 // including name, value, name-value pairs, and attributes.
2732 void Empty() throw()
2736 m_Attributes
.RemoveAll();
2737 m_Values
.RemoveAll();
2740 // Call this function to create a CCookie from a buffer.
2741 // The passed in buffer contains a cookie header retrieved
2742 // from the HTTP_COOKIE request variable
2743 bool ParseValue(__in
const char *pstart
)
2746 if (!pstart
|| *pstart
== '\0')
2749 // could be just a value or could be an array of name=value pairs
2750 LPCSTR pEnd
= pstart
;
2751 LPCSTR pStart
= pstart
;
2752 CStringA name
, value
;
2754 while (*pEnd
!= '\0')
2756 while (*pEnd
&& *pEnd
!= '=' && *pEnd
!= '&')
2759 if (*pEnd
== '\0' || *pEnd
== '&')
2762 CopyToCString(value
, pStart
, pEnd
);
2770 return true; // we're done;
2772 else if (*pEnd
== '=' )
2774 // we have name=value
2777 CopyToCString(name
, pStart
, pEnd
);
2786 // skip '=' and go for value
2789 while (*pEnd
&& *pEnd
!= '&' && *pEnd
!= '=')
2792 CopyToCString(value
, pStart
, pEnd
);
2794 ATLENSURE(AddValue(name
, value
));
2806 // Call this function to render this cookie
2807 // into a buffer. Returns TRUE on success, FALSE on failure.
2808 // On entry, pdwLen should point to a DWORD that indicates
2809 // the size of the buffer in bytes. On exit, the DWORD contains
2810 // the number of bytes transferred or available to be transferred
2811 // into the buffer (including the nul-terminating byte). On
2812 // success, the buffer will contain the correct HTTP
2813 // representation of the current cookie suitable for sending to
2814 // a client as the body of a Set-Cookie header.
2815 __success(return==true) ATL_NOINLINE __checkReturn BOOL
Render(__out_ecount_part_opt(*pdwLen
,*pdwLen
) LPSTR szCookieBuffer
, __inout DWORD
*pdwLen
) const throw()
2820 CStringA name
, value
;
2821 DWORD dwLenBuff
= *pdwLen
;
2824 // A cookie must have a name!
2825 if (!m_strName
.GetLength())
2832 strCookie
= m_strName
;
2833 int nValSize
= (int) m_Values
.GetCount();
2837 POSITION pos
= m_Values
.GetStartPosition();
2838 for (int i
=0; pos
; i
++)
2840 m_Values
.GetNextAssoc(pos
, name
, value
);
2842 if (value
.GetLength())
2847 if (i
<= nValSize
-2)
2854 if (m_strValue
.GetLength())
2855 strCookie
+= m_strValue
;
2858 CStringA strAttributes
;
2859 if (!RenderAttributes(strAttributes
))
2861 if (strAttributes
.GetLength() > 0)
2864 strCookie
+= strAttributes
;
2867 DWORD dwLenCookie
= strCookie
.GetLength() + 1;
2868 *pdwLen
= dwLenCookie
;
2869 if (!szCookieBuffer
)
2870 return TRUE
; // caller just wanted the length
2872 // see if buffer is big enough
2873 if (dwLenCookie
> dwLenBuff
)
2874 return FALSE
; //buffer wasn't big enough
2877 Checked::strcpy_s(szCookieBuffer
, *pdwLen
, strCookie
);
2886 POSITION
GetFirstAttributePos() const throw()
2888 return m_Attributes
.GetStartPosition();
2891 const CStringA
& GetNextAttributeName(__inout POSITION
& pos
) const throw()
2893 return m_Attributes
.GetNextKey(pos
);
2896 const CStringA
& GetAttributeValueAt(__in POSITION pos
) const throw()
2898 return m_Attributes
.GetValueAt(pos
);
2901 BOOL
GetNextAttrAssoc(__inout POSITION
& pos
, __out CStringA
& key
,
2902 __out CStringA
& val
) const throw()
2906 m_Attributes
.GetNextAssoc(pos
, key
, val
);
2915 POSITION
GetFirstValuePos() const throw()
2917 return m_Values
.GetStartPosition();
2920 const CStringA
& GetNextValueName(__inout POSITION
& pos
) const throw()
2922 return m_Values
.GetNextKey(pos
);
2925 const CStringA
& GetValueAt(__in POSITION pos
) const throw()
2927 return m_Values
.GetValueAt(pos
);
2930 BOOL
GetNextValueAssoc(__inout POSITION
& pos
, __out CStringA
& key
,
2931 __out CStringA
& val
) const throw()
2935 m_Values
.GetNextAssoc(pos
, key
, val
);
2946 BOOL
RenderAttributes(__out CStringA
& strAttributes
) const throw()
2950 strAttributes
.Empty();
2952 POSITION pos
= m_Attributes
.GetStartPosition();
2954 for (int i
=0; pos
; i
++)
2957 strAttributes
+= ";";
2958 m_Attributes
.GetNextAssoc(pos
, key
, val
);
2959 strAttributes
+= key
;
2960 strAttributes
+= '=';
2961 strAttributes
+= val
;
2971 CCookie
& Copy(__in
const CCookie
& thatCookie
) throw(...)
2973 m_strName
= thatCookie
.m_strName
;
2974 m_strValue
= thatCookie
.m_strValue
;
2975 POSITION pos
= NULL
;
2976 CStringA strName
, strValue
;
2977 if (!thatCookie
.m_Attributes
.IsEmpty())
2979 pos
= thatCookie
.m_Attributes
.GetStartPosition();
2982 thatCookie
.m_Attributes
.GetNextAssoc(pos
, strName
, strValue
);
2983 m_Attributes
.SetAt(strName
, strValue
);
2986 if (!thatCookie
.m_Values
.IsEmpty())
2990 pos
= thatCookie
.m_Values
.GetStartPosition();
2993 thatCookie
.m_Values
.GetNextAssoc(pos
, strName
, strValue
);
2994 m_Values
.SetAt(strName
, strValue
);
3000 // Call this function to copy a substring to a CString reference and ensure nul-termination.
3001 void CopyToCString(__out CStringA
& string
, __in_ecount(pEnd
-pStart
) LPCSTR pStart
, __in LPCSTR pEnd
) throw( ... )
3003 ATLENSURE( pStart
!= NULL
);
3004 ATLENSURE( pEnd
!= NULL
);
3006 string
.SetString(pStart
, (int)(pEnd
-pStart
));
3012 // These are implementation only, use at your own risk!
3013 // Map of attribute-value pairs applied to this cookie.
3014 mapType m_Attributes
;
3016 // Map of name-value pairs applied to this cookie.
3019 // The name of this cookie.
3022 // The value of this cookie.
3023 CStringA m_strValue
;
3027 class CSessionCookie
: public CCookie
3030 CSessionCookie() throw(...)
3032 if (!SetName(SESSION_COOKIE_NAME
) ||
3034 AtlThrow(E_OUTOFMEMORY
);
3037 CSessionCookie(LPCSTR szSessionID
) throw(...)
3039 if (!SetName(SESSION_COOKIE_NAME
) ||
3041 !SetSessionID(szSessionID
) )
3042 AtlThrow(E_OUTOFMEMORY
);
3045 BOOL
SetSessionID(LPCSTR szSessionID
) throw()
3047 ATLASSERT(szSessionID
&& szSessionID
[0]);
3048 return SetValue(szSessionID
);
3050 }; // class CSessionCookie
3053 class CElementTraits
< CCookie
> :
3054 public CElementTraitsBase
< CCookie
>
3057 typedef const CCookie
& INARGTYPE
;
3058 typedef CCookie
& OUTARGTYPE
;
3060 static ULONG
Hash( __in INARGTYPE cookie
)
3062 return CStringElementTraits
<CStringA
>::Hash( cookie
.m_strName
);
3065 static bool CompareElements( __in INARGTYPE cookie1
, __in INARGTYPE cookie2
)
3067 return( cookie1
.m_strName
== cookie2
.m_strName
);
3070 static int CompareElementsOrdered( __in INARGTYPE cookie1
, __in INARGTYPE cookie2
)
3072 return( cookie1
.m_strName
.Compare( cookie2
.m_strName
) );
3077 ///////////////////////////////////////////////////////////////////////////////
3078 // Request and response classes and support functions
3081 // This class is a wrapper for CAtlMap that allows maps to be chained.
3082 // It simply adds a bool that tells whether or not a map shares values
3083 template <typename K
, typename V
, typename KTraits
=CElementTraits
<K
>, typename VTraits
=CElementTraits
<V
> >
3088 #ifdef ATL_HTTP_PARAM_MULTIMAP
3089 typedef CRBMultiMap
<K
, V
, KTraits
, VTraits
> MAPTYPE
;
3091 typedef CAtlMap
<K
, V
, KTraits
, VTraits
> MAPTYPE
;
3092 #endif // ATL_HTTP_PARAM_MULTIMAP
3096 typedef typename
KTraits::INARGTYPE KINARGTYPE
;
3097 typedef typename
KTraits::OUTARGTYPE KOUTARGTYPE
;
3098 typedef typename
VTraits::INARGTYPE VINARGTYPE
;
3099 typedef typename
VTraits::OUTARGTYPE VOUTARGTYPE
;
3101 typedef typename
MAPTYPE::CPair CPair
;
3120 inline bool IsShared() const throw()
3125 inline void SetShared(__in
bool bShared
) throw()
3127 m_bShared
= bShared
;
3131 // exposed lookup and iteration functionality
3134 inline size_t GetCount() const throw()
3136 return m_map
.GetCount();
3139 inline bool IsEmpty() const throw()
3141 return m_map
.IsEmpty();
3144 inline POSITION
GetStartPosition() const throw()
3146 #ifdef ATL_HTTP_PARAM_MULTIMAP
3147 return m_map
.GetHeadPosition();
3149 return m_map
.GetStartPosition();
3150 #endif // ATL_HTTP_PARAM_MULTIMAP
3154 bool Lookup( __in KINARGTYPE key
, __out VOUTARGTYPE value
) const throw()
3158 #ifdef ATL_HTTP_PARAM_MULTIMAP
3159 CPair
*p
= Lookup(key
);
3167 return m_map
.Lookup(key
, value
);
3168 #endif // ATL_HTTP_PARAM_MULTIMAP
3176 const CPair
* Lookup( __in KINARGTYPE key
) const throw()
3178 #ifdef ATL_HTTP_PARAM_MULTIMAP
3179 POSITION pos
= m_map
.FindFirstWithKey(key
);
3182 return m_map
.GetAt(pos
);
3186 return m_map
.Lookup(key
);
3187 #endif // ATL_HTTP_PARAM_MULTIMAP
3190 CPair
* Lookup( __in KINARGTYPE key
) throw()
3192 #ifdef ATL_HTTP_PARAM_MULTIMAP
3193 POSITION pos
= m_map
.FindFirstWithKey(key
);
3196 return m_map
.GetAt(pos
);
3200 return m_map
.Lookup(key
);
3201 #endif // ATL_HTTP_PARAM_MULTIMAP
3204 // iteration wrappers
3205 void GetNextAssoc( __inout POSITION
& pos
, __out KOUTARGTYPE key
, __out VOUTARGTYPE value
) const throw(...)
3207 m_map
.GetNextAssoc(pos
, key
, value
);
3210 const CPair
* GetNext( __inout POSITION
& pos
) const throw()
3212 return m_map
.GetNext(pos
);
3215 CPair
* GetNext( __inout POSITION
& pos
) throw()
3217 return m_map
.GetNext(pos
);
3220 const K
& GetNextKey( __inout POSITION
& pos
) const throw()
3222 return m_map
.GetNextKey(pos
);
3225 const V
& GetNextValue( __inout POSITION
& pos
) const throw()
3227 return m_map
.GetNextValue(pos
);
3230 V
& GetNextValue( __inout POSITION
& pos
) throw()
3232 return m_map
.GetNextValue(pos
);
3235 void GetAt( __in POSITION pos
, __out KOUTARGTYPE key
, __out VOUTARGTYPE value
) const throw(...)
3237 return m_map
.GetAt(pos
, key
, value
);
3240 CPair
* GetAt( __in POSITION pos
) throw()
3242 return m_map
.GetAt(pos
);
3245 const CPair
* GetAt( __in POSITION pos
) const throw()
3247 return m_map
.GetAt(pos
);
3250 const K
& GetKeyAt( __in POSITION pos
) const throw()
3252 return m_map
.GetKeyAt(pos
);
3255 const V
& GetValueAt( __in POSITION pos
) const throw()
3257 return m_map
.GetValueAt(pos
);
3260 V
& GetValueAt( __in POSITION pos
) throw()
3262 return m_map
.GetValueAt(pos
);
3265 // modification wrappers
3266 POSITION
SetAt( __in KINARGTYPE key
, __in_opt VINARGTYPE value
) throw(...)
3268 #ifdef ATL_HTTP_PARAM_MULTIMAP
3269 return m_map
.Insert(key
, value
);
3271 return m_map
.SetAt(key
, value
);
3272 #endif // ATL_HTTP_PARAM_MULTIMAP
3275 bool RemoveKey( __in KINARGTYPE key
) throw()
3277 #ifdef ATL_HTTP_PARAM_MULTIMAP
3278 return (m_map
.RemoveKey(key
) != 0);
3280 return m_map
.RemoveKey(key
);
3281 #endif // ATL_HTTP_PARAM_MULTIMAP
3284 virtual void RemoveAll()
3290 // This class is a wrapper for CHttpMap that assumes it's values are pointers that
3291 // should be deleted on RemoveAll
3292 template <typename K
, typename V
, typename KTraits
=CElementTraits
<K
>, typename VTraits
=CElementTraits
<V
> >
3293 class CHttpPtrMap
: public CHttpMap
<K
, V
, KTraits
, VTraits
>
3296 typedef CHttpMap
<K
, V
, KTraits
, VTraits
> Base
;
3302 POSITION pos
= GetStartPosition();
3305 GetNextValue(pos
)->Free();
3317 // This class represents a collection of request parameters - the name-value pairs
3318 // found, for example, in a query string or in the data provided when a form is submitted to the server.
3319 // Call Parse to build the collection from a string of URL-encoded data.
3320 // Use the standard collection methods of the CHttpMap base class to retrieve the
3321 // decoded names and values.
3322 // Use the methods of the CValidateObject base class to validate the parameters.
3323 class CHttpRequestParams
:
3324 #if (defined(ATL_HTTP_PARAM_MAP_CASEINSENSITIVE))
3325 public CHttpMap
<CStringA
, CStringA
, CStringElementTraitsI
<CStringA
>, CStringElementTraitsI
<CStringA
> >,
3327 public CHttpMap
<CStringA
, CStringA
, CStringElementTraits
<CStringA
>, CStringElementTraits
<CStringA
> >,
3329 public CValidateObject
<CHttpRequestParams
>
3332 #if (defined(ATL_HTTP_PARAM_MAP_CASEINSENSITIVE))
3333 typedef CHttpMap
<CStringA
, CStringA
, CStringElementTraitsI
<CStringA
>, CStringElementTraitsI
<CStringA
> > BaseMap
;
3335 typedef CHttpMap
<CStringA
, CStringA
, CStringElementTraits
<CStringA
>, CStringElementTraits
<CStringA
> > BaseMap
;
3338 LPCSTR
Lookup(__in LPCSTR szName
) const throw()
3345 const CPair
*p
= BaseMap::Lookup(szName
);
3357 // Call this function to build a collection of name-value pairs from a string of URL-encoded data.
3358 // Returns TRUE on success, FALSE on failure.
3359 // URL-encoded data:
3360 // Each name-value pair is separated from the next by an ampersand (&)
3361 // Each name is separated from its corresponding value by an equals signs (=)
3362 // The end of the data to be parsed is indicated by a nul character (\0) or a pound symbol (#)
3363 // A plus sign (+) in the input will be decoded as a space
3364 // A percent sign (%) in the input signifies the start of an escaped octet.
3365 // The next two digits represent the hexadecimal code of the character.
3366 // For example, %21 is the escaped encoding for the US-ASCII exclamation mark and will be decoded as !.
3367 // Common sources of URL-encoded data are query strings and the bodies of POST requests with content type of application/x-www-form-urlencoded.
3369 // Parse and Render are complementary operations.
3370 // Parse creates a collection from a string.
3371 // Render creates a string from a collection.
3372 ATL_NOINLINE __checkReturn BOOL
Parse(__inout LPSTR szQueryString
) throw()
3374 while (szQueryString
&& *szQueryString
)
3376 LPSTR szUrlCurrent
= szQueryString
;
3377 LPSTR szName
= szUrlCurrent
;
3380 while (*szQueryString
)
3382 if (*szQueryString
== '=')
3387 if (*szQueryString
== '&')
3391 if (*szQueryString
== '+')
3392 *szUrlCurrent
= ' ';
3393 else if (*szQueryString
== '%')
3395 // if there is a % without two characters
3396 // at the end of the url we skip it
3397 if (*(szQueryString
+1) && *(szQueryString
+2))
3399 short nFirstDigit
= AtlHexValue(szQueryString
[1]);
3400 short nSecondDigit
= AtlHexValue(szQueryString
[2]);
3402 if( nFirstDigit
< 0 || nSecondDigit
< 0 )
3406 *szUrlCurrent
= static_cast<CHAR
>(16*nFirstDigit
+nSecondDigit
);
3410 *szUrlCurrent
= '\0';
3413 *szUrlCurrent
= *szQueryString
;
3419 if (*szUrlCurrent
== '&')
3421 *szUrlCurrent
++ = '\0';
3428 *szUrlCurrent
++ = '\0';
3430 // we have the property name
3431 szPropValue
= szUrlCurrent
;
3432 while (*szQueryString
&& *szQueryString
!= '#')
3434 if (*szQueryString
== '&')
3439 if (*szQueryString
== '+')
3440 *szUrlCurrent
= ' ';
3441 else if (*szQueryString
== '%')
3443 // if there is a % without two characters
3444 // at the end of the url we skip it
3445 if (*(szQueryString
+1) && *(szQueryString
+2))
3447 short nFirstDigit
= AtlHexValue(szQueryString
[1]);
3448 short nSecondDigit
= AtlHexValue(szQueryString
[2]);
3450 if( nFirstDigit
< 0 || nSecondDigit
< 0 )
3454 *szUrlCurrent
= static_cast<CHAR
>(16*nFirstDigit
+nSecondDigit
);
3458 *szUrlCurrent
= '\0';
3461 *szUrlCurrent
= *szQueryString
;
3465 // we have the value
3466 *szUrlCurrent
= '\0';
3472 SetAt(szName
, szPropValue
);
3482 // Call this function to render the map of names and values into a buffer as a URL-encoded string.
3483 // Returns TRUE on success, FALSE on failure.
3484 // On entry, pdwLen should point to a DWORD that indicates the size of the buffer in bytes.
3485 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
3486 // On success, the buffer will contain the correct URL-encoded representation of the current object
3487 // suitable for sending to a server as a query string or in the body of a form.
3489 // Each name-value pair is separated from the next by an ampersand (&)
3490 // Each name is separated from its corresponding value by an equals signs (=)
3491 // A space is encoded as a plus sign (+).
3492 // Other unsafe characters (as determined by AtlIsUnsafeUrlChar) are encoded as escaped octets.
3493 // An escaped octet is a percent sign (%) followed by two digits representing the hexadecimal code of the character.
3495 // Parse and Render are complementary operations.
3496 // Parse creates a collection from a string.
3497 // Render creates a string from a collection.
3498 ATL_NOINLINE __checkReturn BOOL
Render(__out_ecount(pdwLen
) LPSTR szParameters
, __inout LPDWORD pdwLen
)
3500 ATLENSURE(szParameters
);
3504 if (GetCount() == 0)
3506 *szParameters
= '\0';
3512 POSITION pos
= GetStartPosition();
3515 LPCSTR szBuf
= GetKeyAt(pos
);
3516 EscapeToCString(strParams
, szBuf
);
3517 szBuf
= GetValueAt(pos
);
3521 EscapeToCString(strParams
, szBuf
);
3527 DWORD dwLen
= strParams
.GetLength();
3528 strParams
.Delete(dwLen
-1);
3530 if (dwLen
> *pdwLen
)
3537 Checked::memcpy_s(szParameters
, *pdwLen
, static_cast<LPCSTR
>(strParams
), dwLen
);
3538 szParameters
[dwLen
] = '\0';
3550 }; // class CHttpRequestParams
3552 #ifndef MAX_TOKEN_LENGTH
3553 #define MAX_TOKEN_LENGTH (MAX_PATH)
3556 // This class represents the information about a file that has been uploaded to the web server.
3557 class CHttpRequestFile
: public IHttpFile
3561 // The name of the form field used to upload the file.
3562 CHAR m_szParamName
[MAX_TOKEN_LENGTH
];
3564 // The original file name of the uploaded file as set by the client.
3565 CHAR m_szFileName
[MAX_PATH
];
3567 // The original path and file name of the uploaded file as set by the client.
3568 CHAR m_szFullFileName
[MAX_PATH
];
3570 // The MIME type of the uploaded file.
3571 CHAR m_szContentType
[MAX_TOKEN_LENGTH
];
3573 // The name of the uploaded file on the server.
3574 CHAR m_szTempFileName
[MAX_PATH
];
3576 // The size of the file in bytes.
3577 ULONGLONG m_nFileSize
;
3581 CHttpRequestFile() throw()
3583 *m_szParamName
= '\0';
3584 *m_szFileName
= '\0';
3585 *m_szFullFileName
= '\0';
3586 *m_szContentType
= '\0';
3590 __checkReturn BOOL
Initialize(
3591 __in_opt LPCSTR pParamName
,
3592 __in LPCSTR pFileName
,
3593 __in_opt LPCSTR pTempFileName
,
3594 __in_opt LPCSTR pContentType
,
3595 __in
const ULONGLONG
&nFileSize
)
3597 ATLENSURE( pFileName
!= NULL
);
3599 if (!SafeStringCopy(m_szFullFileName
, pFileName
))
3605 if (pParamName
&& *pParamName
)
3607 if (!SafeStringCopy(m_szParamName
, pParamName
))
3614 if (pTempFileName
&& *pTempFileName
)
3616 if (!SafeStringCopy(m_szTempFileName
, pTempFileName
))
3623 if (pContentType
&& *pContentType
)
3625 if (!SafeStringCopy(m_szContentType
, pContentType
))
3632 // Set m_szFileName to be the file name without the path.
3633 // This is likely to be the most meaningful part of the
3634 // original file name once the file reaches the server.
3636 LPSTR szTmp
= m_szFullFileName
;
3637 LPSTR szFile
= m_szFileName
;
3638 LPSTR pszLastCharFileBuf
=m_szFileName
+ _countof(m_szFileName
) - 1;
3643 szFile
= m_szFileName
;
3647 if (szFile
> pszLastCharFileBuf
)
3655 if (szFile
> pszLastCharFileBuf
)
3661 m_nFileSize
= nFileSize
;
3666 //=======================================
3667 // IHttpFile interface
3668 //=======================================
3669 __checkReturn LPCSTR
GetParamName()
3671 return m_szParamName
;
3674 __checkReturn LPCSTR
GetFileName()
3676 return m_szFileName
;
3679 __checkReturn LPCSTR
GetFullFileName()
3681 return m_szFullFileName
;
3684 __checkReturn LPCSTR
GetContentType()
3686 return m_szContentType
;
3689 __checkReturn LPCSTR
GetTempFileName()
3691 return m_szTempFileName
;
3694 __checkReturn ULONGLONG
GetFileSize()
3704 }; // class CHttpRequestFile
3707 // utility function to ReadData from a ServerContext
3709 __checkReturn BOOL
ReadClientData(__inout IHttpServerContext
*pServerContext
, __out_ecount_part(*pdwLen
,*pdwLen
) LPSTR pbDest
, __inout LPDWORD pdwLen
, __in DWORD dwBytesRead
)
3711 ATLENSURE(pServerContext
!= NULL
);
3712 ATLENSURE(pdwLen
!= NULL
);
3713 ATLENSURE(pbDest
!= NULL
);
3717 DWORD dwToRead
= *pdwLen
;
3718 DWORD dwAvailableBytes
= pServerContext
->GetAvailableBytes();
3721 // Read from available data first
3722 if (dwBytesRead
< dwAvailableBytes
)
3724 LPBYTE pbAvailableData
= pServerContext
->GetAvailableData();
3725 pbAvailableData
+= dwBytesRead
;
3726 DWORD dwAvailableToRead
= __min(dwToRead
, dwAvailableBytes
-dwBytesRead
);
3727 Checked::memcpy_s(pbDest
, *pdwLen
, pbAvailableData
, dwAvailableToRead
);
3728 dwBytesRead
+= dwAvailableToRead
;
3729 dwToRead
-= dwAvailableToRead
;
3730 pbDest
+= dwAvailableToRead
;
3731 dwRead
+= dwAvailableToRead
;
3734 DWORD dwTotalBytes
= pServerContext
->GetTotalBytes();
3736 // If there is still more to read after the available data is exhausted
3737 if (dwToRead
&& dwBytesRead
< dwTotalBytes
)
3739 DWORD dwClientBytesToRead
= __min(pServerContext
->GetTotalBytes()-dwBytesRead
, dwToRead
);
3740 DWORD dwClientBytesRead
= 0;
3742 // keep on reading until we've read the amount requested
3745 dwClientBytesRead
= dwClientBytesToRead
;
3746 if (!pServerContext
->ReadClient(pbDest
, &dwClientBytesRead
))
3750 dwClientBytesToRead
-= dwClientBytesRead
;
3751 dwRead
+= dwClientBytesRead
;
3752 pbDest
+= dwClientBytesRead
;
3754 } while (dwClientBytesToRead
!= 0 && dwClientBytesRead
!= 0);
3769 #ifndef MAX_MIME_BOUNDARY_LEN
3770 #define MAX_MIME_BOUNDARY_LEN 128
3775 ATL_FORM_FLAG_NONE
= 0,
3776 ATL_FORM_FLAG_IGNORE_FILES
= 1,
3777 ATL_FORM_FLAG_REFUSE_FILES
= 2,
3778 ATL_FORM_FLAG_IGNORE_EMPTY_FILES
= 4,
3779 ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS
= 8,
3782 // Use this class to read multipart/form-data from the associated server context
3783 // and generate files as necessary from the data in the body of the request.
3784 class CMultiPartFormParser
3791 CHAR m_szBoundary
[MAX_MIME_BOUNDARY_LEN
+2];
3792 DWORD m_dwBoundaryLen
;
3794 CComPtr
<IHttpServerContext
> m_spServerContext
;
3798 typedef CHttpMap
<CStringA
, IHttpFile
*, CStringElementTraits
<CStringA
> > FILEMAPTYPE
;
3800 CMultiPartFormParser(__in IHttpServerContext
* pServerContext
) throw() :
3806 m_spServerContext(pServerContext
)
3808 *m_szBoundary
= '\0';
3811 ~CMultiPartFormParser() throw()
3815 // free memory if necessary
3816 if (m_spServerContext
->GetTotalBytes() > m_spServerContext
->GetAvailableBytes())
3826 // Call this function to read multipart/form-data from the current HTTP request,
3827 // allowing files to be uploaded to the web server.
3829 // Returns TRUE on success, FALSE on failure.
3831 // Forms can be sent to a web server using one of two encodings: application/x-www-form-urlencoded or multipart/form-data.
3832 // In addition to the simple name-value pairs typically associated with
3833 // application/x-www-form-urlencoded form data, multipart/form-data (as
3834 // described in RFC 2388) can also contain files to be uploaded
3835 // to the web server.
3837 // This function will generate a physical file for each file contained in the multipart/form-data request body.
3838 // The generated files are stored in the server's temporary directory as returned by the
3839 // GetTempPath API and are named using the GetTempFileName API.
3840 // The information about each file can be obtained from the elements of the Files array.
3841 // You can retrieve the original name of the file on the client, the name of the generated file on the server,
3842 // the MIME content type of the uploaded file, the name of the form field associated with that file, and the size in
3843 // bytes of the file. All this information is exposed by the CHttpRequestFile objects in the array.
3845 // In addition to generating files and populating the Files array with information about them,
3846 // this function also populates the pQueryParams array with the names and values of the other form fields
3847 // contained in the current request. The file fields are also added to this array. The value of these fields
3848 // is the full name of the generated file on the server.
3850 // Note that files can be generated even if this function returns FALSE unless you specify either the
3851 // ATL_FORM_FLAG_IGNORE_FILES or the ATL_FORM_FLAG_REFUSE_FILES flag. If you don't specify one of these
3852 // flags, you should always check the Files array for generated files and delete any that are no longer
3853 // needed to prevent your web server from running out of disk space.
3855 // dwFlags can be a combination of one or more of the following values:
3856 // ATL_FORM_FLAG_NONE Default behavior.
3857 // ATL_FORM_FLAG_IGNORE_FILES Any attempt to upload files is ignored.
3858 // ATL_FORM_FLAG_REFUSE_FILES Any attempt to upload files is treated as a failure. The function will return FALSE.
3859 // ATL_FORM_FLAG_IGNORE_EMPTY_FILES Files with a size of zero bytes are ignored.
3860 // ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS Fields with no content are ignored.
3861 ATL_NOINLINE BOOL
GetMultiPartData(
3862 __inout FILEMAPTYPE
& Files
,
3863 __inout CHttpRequestParams
* pQueryParams
,
3864 __in DWORD dwFlags
=ATL_FORM_FLAG_NONE
) throw()
3868 if (!InitializeParser())
3873 //Get to the first boundary
3874 if (!ReadUntilBoundary())
3879 CStringA strParamName
;
3880 CStringA strFileName
;
3881 CStringA strContentType
;
3885 while (!m_bFinished
)
3887 // look for "name" field
3888 if (!GetMimeData(strParamName
, "name=", sizeof("name=")-1, &bFound
, TRUE
) || !bFound
)
3890 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed Form-Data"));
3894 // see if it's a file
3895 if (!GetMimeData(strFileName
, "filename=", sizeof("filename=")-1, &bFound
, TRUE
))
3897 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed Form-Data"));
3903 if (dwFlags
& ATL_FORM_FLAG_REFUSE_FILES
)
3908 if (!strFileName
.GetLength())
3910 if(!ReadUntilBoundary())
3917 if (!GetMimeData(strContentType
, "Content-Type:", sizeof("Content-Type:")-1, &bFound
, TRUE
))
3919 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed Form-Data"));
3923 // move to the actual uploaded data
3926 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed Form-Data"));
3930 // if the user doesn't want files, don't save the file
3931 if (dwFlags
& ATL_FORM_FLAG_IGNORE_FILES
)
3933 if (!ReadUntilBoundary(NULL
, NULL
))
3940 CAtlTemporaryFile ctf
;
3941 HRESULT hr
= ctf
.Create();
3945 if (!ReadUntilBoundary(NULL
, &ctf
))
3950 ULONGLONG nFileSize
= 0;
3951 if (ctf
.GetSize(nFileSize
) != S_OK
)
3954 if ((dwFlags
& ATL_FORM_FLAG_IGNORE_EMPTY_FILES
) && nFileSize
== 0)
3960 CAutoPtr
<CHttpRequestFile
> spFile
;
3962 CT2AEX
<MAX_PATH
+1> szTempFileNameA(ctf
.TempFileName());
3964 ATLTRY(spFile
.Attach(new CHttpRequestFile()));
3970 if (!spFile
->Initialize(strParamName
, strFileName
, szTempFileNameA
, strContentType
, nFileSize
))
3972 // one of the strings was too long
3976 if (!Files
.SetAt(szTempFileNameA
, spFile
))
3983 if (!pQueryParams
|| !pQueryParams
->SetAt(strParamName
, szTempFileNameA
))
3993 // move to the actual uploaded data
3996 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed Form-Data"));
4000 if (!ReadUntilBoundary(&strData
))
4005 if ((dwFlags
& ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS
) && strData
.GetLength() == 0)
4008 if (!pQueryParams
|| !pQueryParams
->SetAt(strParamName
, strData
))
4027 // case insensitive substring search -- does not handle multibyte characters (unlike tolower)
4028 // allows searching up to a maximum point in a string
4029 inline char AtlCharLower(__in
char ch
) throw()
4031 if (ch
> 64 && ch
< 91)
4039 inline __checkReturn
char * _stristr (__in
const char * str1
, __in
const char * str2
)
4041 ATLENSURE(str1
!=NULL
);
4042 ATLENSURE(str2
!=NULL
);
4043 char *cp
= (char *) str1
;
4047 return((char *)str1
);
4054 while ( *s1
&& *s2
&& !(AtlCharLower(*s1
)-AtlCharLower(*s2
)) )
4071 inline __checkReturn
char * _stristrex (__in
const char * str1
, __in
const char * str2
, __in
const char * str1End
)
4073 ATLENSURE(str1
!=NULL
);
4074 ATLENSURE(str2
!=NULL
);
4075 char *cp
= (char *) str1
;
4079 return((char *)str1
);
4081 while (cp
!= str1End
)
4086 while ( s1
!= str1End
&& *s2
&& !(AtlCharLower(*s1
)-AtlCharLower(*s2
)) )
4107 inline __checkReturn
char * _strstrex (__in
const char * str1
, __in
const char * str2
, __in
const char * str1End
)
4109 ATLENSURE(str1
!=NULL
);
4110 ATLENSURE(str2
!=NULL
);
4111 char *cp
= (char *) str1
;
4115 return((char *)str1
);
4117 while (cp
!= str1End
)
4122 while ( s1
!= str1End
&& *s2
&& !((*s1
)-(*s2
)) )
4143 ATL_NOINLINE __checkReturn BOOL
InitializeParser() throw()
4145 ATLASSUME( m_spServerContext
!= NULL
);
4149 DWORD dwBytesTotal
= m_spServerContext
->GetTotalBytes();
4151 // if greater than bytes available, allocate necessary space
4152 if (dwBytesTotal
> m_spServerContext
->GetAvailableBytes())
4154 m_pStart
= (LPSTR
) malloc(dwBytesTotal
);
4159 m_pCurrent
= m_pStart
;
4160 DWORD dwLen
= dwBytesTotal
;
4161 if (!ReadClientData(m_spServerContext
, m_pStart
, &dwLen
, 0) || dwLen
!= dwBytesTotal
)
4168 m_pStart
= (LPSTR
) m_spServerContext
->GetAvailableData();
4171 m_pCurrent
= m_pStart
;
4172 m_pEnd
= m_pCurrent
+ dwBytesTotal
;
4175 LPCSTR pszContentType
= m_spServerContext
->GetContentType();
4176 ATLASSERT(pszContentType
!= NULL
);
4178 LPCSTR pszTmp
= _stristr(pszContentType
, "boundary=");
4181 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed Form-Data"));
4185 pszTmp
+= sizeof("boundary=")-1;
4186 BOOL bInQuote
= FALSE
;
4187 if (*pszTmp
== '\"')
4193 LPSTR pszMimeBoundary
= m_szBoundary
;
4194 *pszMimeBoundary
++ = '-';
4195 *pszMimeBoundary
++ = '-';
4196 m_dwBoundaryLen
= 2;
4197 while (*pszTmp
&& (bInQuote
|| IsStandardBoundaryChar(*pszTmp
)))
4199 if (m_dwBoundaryLen
>= MAX_MIME_BOUNDARY_LEN
)
4201 ATLTRACE(atlTraceISAPI
, 0, _T("Malformed MIME boundary"));
4205 if (*pszTmp
== '\r' || *pszTmp
== '\n')
4214 if (bInQuote
&& *pszTmp
== '"')
4219 *pszMimeBoundary
++ = *pszTmp
++;
4223 *pszMimeBoundary
= '\0';
4233 inline __checkReturn BOOL
MoveToData() throw()
4235 LPSTR szEnd
= _strstrex(m_pCurrent
, "\r\n\r\n", m_pEnd
);
4241 m_pCurrent
= szEnd
+4;
4242 if (m_pCurrent
>= m_pEnd
)
4250 inline __checkReturn BOOL
GetMimeData(__inout CStringA
&str
, __in_ecount(dwFieldLen
) LPCSTR szField
, __in DWORD dwFieldLen
, __out LPBOOL pbFound
, __in BOOL bIgnoreCase
= FALSE
) throw()
4254 ATLASSERT( szField
!= NULL
);
4255 ATLENSURE( pbFound
!= NULL
);
4259 LPSTR szEnd
= _strstrex(m_pCurrent
, "\r\n\r\n", m_pEnd
);
4265 LPSTR szDataStart
= NULL
;
4269 szDataStart
= _strstrex(m_pCurrent
, szField
, szEnd
);
4273 szDataStart
= _stristrex(m_pCurrent
, szField
, szEnd
);
4278 szDataStart
+= dwFieldLen
;
4279 if (szDataStart
>= m_pEnd
)
4284 BOOL bInQuote
= FALSE
;
4285 if (*szDataStart
== '\"')
4291 LPSTR szDataEnd
= szDataStart
;
4292 while (!bInQuote
&& (szDataEnd
< m_pEnd
) && (*szDataEnd
== ' ' || *szDataEnd
== '\t'))
4297 if (szDataEnd
>= m_pEnd
)
4302 while (szDataEnd
< m_pEnd
)
4304 if (!IsValidTokenChar(*szDataEnd
))
4306 if (*szDataEnd
== '\"' || !bInQuote
)
4314 if (szDataEnd
>= m_pEnd
)
4319 str
.SetString(szDataStart
, (int)(szDataEnd
-szDataStart
));
4331 ATL_NOINLINE __checkReturn BOOL
ReadUntilBoundary(__inout_opt CStringA
* pStrData
=NULL
, __inout_opt CAtlTemporaryFile
* pCtf
=NULL
) throw()
4335 LPSTR szBoundaryStart
= m_pCurrent
;
4336 LPSTR szBoundaryEnd
= NULL
;
4340 szBoundaryStart
= _strstrex(szBoundaryStart
, m_szBoundary
, m_pEnd
);
4341 if (szBoundaryStart
)
4343 if ((szBoundaryStart
-m_pStart
) >= 2)
4345 if (*(szBoundaryStart
-1) != 0x0a || *(szBoundaryStart
-2) != 0x0d)
4351 szBoundaryEnd
= szBoundaryStart
+m_dwBoundaryLen
;
4352 if (szBoundaryEnd
+2 >= m_pEnd
)
4356 if (szBoundaryEnd
[0] == '\r' && szBoundaryEnd
[1] == '\n')
4360 if (szBoundaryEnd
[0] == '-' && szBoundaryEnd
[1] == '-')
4367 } while (szBoundaryStart
&& szBoundaryStart
< m_pEnd
);
4369 if (!szBoundaryStart
|| szBoundaryStart
>= m_pEnd
)
4374 if ((szBoundaryStart
-m_pStart
) >= 2)
4376 szBoundaryStart
-= 2;
4380 pStrData
->SetString(m_pCurrent
, (int)(szBoundaryStart
-m_pCurrent
));
4384 if (FAILED(pCtf
->Write(m_pCurrent
, (DWORD
)(szBoundaryStart
-m_pCurrent
))))
4392 m_pCurrent
= szBoundaryEnd
+2;
4393 if (m_pCurrent
>= m_pEnd
)
4407 static inline __checkReturn BOOL
IsStandardBoundaryChar(__in CHAR ch
) throw()
4409 if ( (ch
>= 'A' && ch
<= 'Z') ||
4410 (ch
>= 'a' && ch
<= 'z') ||
4411 (ch
>= '0' && ch
<= '9') ||
4425 inline __checkReturn BOOL
IsValidTokenChar(__in CHAR ch
) throw()
4427 return ( (ch
!= 0) && (ch
!= 0xd) && (ch
!= 0xa) && (ch
!= ' ') && (ch
!= '\"') );
4431 // Prevents copying.
4432 CMultiPartFormParser(__in
const CMultiPartFormParser
& /*that*/) throw()
4437 const CMultiPartFormParser
& operator=(__in
const CMultiPartFormParser
& /*that*/) throw()
4442 }; // class CMultiPartFormParser
4445 // 48K max form size
4446 #ifndef DEFAULT_MAX_FORM_SIZE
4447 #define DEFAULT_MAX_FORM_SIZE 49152
4450 // This class provides access to the information contained in an HTTP request submitted to a web server.
4452 // CHttpRequest provides access to the query string parameters, form fields, cookies, and files
4453 // that make up an HTTP request, as well as many other important properties of the request.
4454 class CHttpRequest
: public IHttpRequestLookup
4457 // Implementation: Array used to map an HTTP request method (for example, "GET" or "POST")
4458 // from a string to a numeric constant from the HTTP_METHOD enum (HTTP_METHOD_GET or HTTP_METHOD_HEAD).
4459 static const char* const m_szMethodStrings
[];
4461 // Implementation: The server context.
4462 CComPtr
<IHttpServerContext
> m_spServerContext
;
4464 // Implementation: The number of bytes read from the body of the request.
4465 DWORD m_dwBytesRead
;
4467 // Implementation: TRUE if the request method was POST and the encoding was
4468 // multipart/form-data, FALSE otherwise.
4471 CCookie m_EmptyCookie
;
4473 // Implementation: Constructor function used to reinitialize all data members.
4474 void Construct() throw()
4476 m_spServerContext
.Release();
4477 m_bMultiPart
= FALSE
;
4479 if (m_pFormVars
!= &m_QueryParams
)
4483 m_pFormVars
= &m_QueryParams
;
4484 m_QueryParams
.RemoveAll();
4485 m_QueryParams
.SetShared(false);
4487 ClearFilesAndCookies();
4490 void ClearFilesAndCookies() throw()
4492 m_Files
.RemoveAll();
4493 m_Files
.SetShared(false);
4494 m_requestCookies
.RemoveAll();
4495 m_requestCookies
.SetShared(false);
4500 // Implementation: The collection of query parameters (name-value pairs) obtained from the query string.
4501 CHttpRequestParams m_QueryParams
;
4503 // Implementation: The collection of form fields (name-value pairs).
4504 // The elements of this collection are obtained from the query string for a GET request,
4505 // or from the body of the request for a POST request.
4506 CHttpRequestParams
*m_pFormVars
;
4508 // The array of CHttpRequestFiles obtained from the current request.
4509 // See CHttpRequest::Initialize and CMultiPartFormParser::GetMultiPartData for more information.
4510 typedef CHttpPtrMap
<CStringA
, IHttpFile
*, CStringElementTraits
<CStringA
> > FileMap
;
4513 // Implementation: The array of cookies obtained from the current request.
4514 typedef CHttpMap
<CStringA
, CCookie
, CStringElementTraits
<CStringA
> > CookieMap
;
4515 CookieMap m_requestCookies
;
4517 // Numeric constants for the HTTP request methods (such as GET and POST).
4520 HTTP_METHOD_UNKNOWN
=-1,
4527 HTTP_METHOD_DEBUG
// Debugging support for VS7
4530 // The collection of query parameters (name-value pairs) obtained from the query string.
4531 // A read-only property.
4532 __declspec(property(get
=GetQueryParams
)) const CHttpRequestParams
& QueryParams
;
4534 // Returns a reference to the collection of query parameters(name-value pairs)
4535 // obtained from the query string.
4536 const CHttpRequestParams
& GetQueryParams() const throw()
4538 return m_QueryParams
;
4541 // The collection of form fields (name-value pairs).
4542 // The elements of this collection are obtained from the query string for a GET request,
4543 // or from the body of the request for a POST request.
4544 // A read-only property.
4545 __declspec(property(get
=GetFormVars
)) const CHttpRequestParams
& FormVars
;
4547 // Returns a reference to the collection of form fields (name-value pairs)
4548 // obtained from the query string for a GET request,
4549 // or from the body of the request for a POST request.
4550 const CHttpRequestParams
& GetFormVars() const throw()
4552 return *m_pFormVars
;
4555 CHttpRequest() throw()
4557 m_bMultiPart
= FALSE
;
4559 m_pFormVars
= &m_QueryParams
;
4562 virtual ~CHttpRequest() throw()
4566 if (m_pFormVars
!= &m_QueryParams
)
4573 // Constructs and initializes the object.
4575 __in IHttpServerContext
*pServerContext
,
4576 __in DWORD dwMaxFormSize
=DEFAULT_MAX_FORM_SIZE
,
4577 __in DWORD dwFlags
=ATL_FORM_FLAG_NONE
) throw(...)
4581 if (!Initialize(pServerContext
, dwMaxFormSize
, dwFlags
))
4585 CHttpRequest(__in IHttpRequestLookup
*pRequestLookup
) throw(...)
4588 if (!Initialize(pRequestLookup
)) // Calls Construct for you
4592 //=========================================================================================
4593 // BEGIN IHttpRequestLoookup interface
4594 //=========================================================================================
4595 __success(return != NULL
) __checkReturn POSITION
GetFirstQueryParam(__deref_out LPCSTR
*ppszName
, __deref_out LPCSTR
*ppszValue
)
4597 POSITION pos
= m_QueryParams
.GetStartPosition();
4600 ATLENSURE(ppszName
!= NULL
);
4601 ATLENSURE(ppszValue
!= NULL
);
4602 *ppszName
= m_QueryParams
.GetKeyAt(pos
);
4603 *ppszValue
= m_QueryParams
.GetValueAt(pos
);
4609 __success(return != NULL
) __checkReturn POSITION
GetNextQueryParam(__in POSITION pos
, __deref_out LPCSTR
*ppszName
, __deref_out LPCSTR
*ppszValue
)
4611 ATLENSURE(pos
!= NULL
);
4614 POSITION
posNext(pos
);
4615 m_QueryParams
.GetNext(posNext
);
4616 if (posNext
!= NULL
)
4618 ATLENSURE(ppszName
!= NULL
);
4619 ATLENSURE(ppszValue
!= NULL
);
4620 *ppszName
= m_QueryParams
.GetKeyAt(posNext
);
4621 *ppszValue
= m_QueryParams
.GetValueAt(posNext
);
4627 __success(return != NULL
) __checkReturn POSITION
GetFirstFormVar(__deref_out LPCSTR
*ppszName
, __deref_out LPCSTR
*ppszValue
)
4629 // if no form vars and just pointing to the query params,
4631 if (m_pFormVars
== &m_QueryParams
)
4634 POSITION pos
= m_pFormVars
->GetStartPosition();
4637 ATLENSURE(ppszName
!= NULL
);
4638 ATLENSURE(ppszValue
!= NULL
);
4639 *ppszName
= m_pFormVars
->GetKeyAt(pos
);
4640 *ppszValue
= m_pFormVars
->GetValueAt(pos
);
4646 __success(return != NULL
) __checkReturn POSITION
GetNextFormVar(__in POSITION pos
, __deref_out LPCSTR
*ppszName
, __deref_out LPCSTR
*ppszValue
)
4648 ATLASSERT(pos
!= NULL
);
4649 ATLASSERT(ppszName
!= NULL
);
4650 ATLASSERT(ppszValue
!= NULL
);
4652 POSITION
posNext(pos
);
4653 m_pFormVars
->GetNext(posNext
);
4654 if (posNext
!= NULL
)
4656 *ppszName
= m_pFormVars
->GetKeyAt(posNext
);
4657 *ppszValue
= m_pFormVars
->GetValueAt(posNext
);
4663 POSITION
GetFirstFile(__in LPCSTR
*ppszName
, __deref_out IHttpFile
**ppFile
)
4665 ATLASSERT(ppszName
!= NULL
);
4666 ATLASSERT(ppFile
!= NULL
);
4668 POSITION pos
= m_Files
.GetStartPosition();
4671 *ppszName
= m_Files
.GetKeyAt(pos
);
4672 *ppFile
= m_Files
.GetValueAt(pos
);
4678 __success(return != NULL
) __checkReturn POSITION
GetNextFile(__in POSITION pos
, __deref_out LPCSTR
*ppszName
, __deref_out IHttpFile
**ppFile
)
4680 ATLASSERT(pos
!= NULL
);
4681 ATLASSERT(ppszName
!= NULL
);
4682 ATLASSERT(ppFile
!= NULL
);
4684 m_Files
.GetNext(pos
);
4687 *ppszName
= m_Files
.GetKeyAt(pos
);
4688 *ppFile
= m_Files
.GetValueAt(pos
);
4694 // Returns a pointer to the IHttpServerContext interface for the current request.
4695 HRESULT
GetServerContext(__deref_out_opt IHttpServerContext
** ppOut
)
4697 return m_spServerContext
.CopyTo(ppOut
);
4699 //=========================================================================================
4700 // END IHttpRequestLookup interface
4701 //=========================================================================================
4703 __success(return != NULL
) __checkReturn POSITION
GetFirstCookie(__deref_out LPCSTR
*ppszName
, __deref_out
const CCookie
**ppCookie
) throw()
4705 ATLASSERT(ppszName
!= NULL
);
4706 ATLASSERT(ppCookie
!= NULL
);
4707 POSITION pos
= NULL
;
4708 if (GetRequestCookies())
4710 pos
= m_requestCookies
.GetStartPosition();
4713 *ppszName
= m_requestCookies
.GetKeyAt(pos
);
4714 *ppCookie
= &(m_requestCookies
.GetValueAt(pos
));
4720 __success(return != NULL
) __checkReturn POSITION
GetNextCookie(__in POSITION pos
, __deref_out LPCSTR
*ppszName
, __deref_out
const CCookie
**ppCookie
) throw()
4722 ATLASSERT(pos
!= NULL
);
4723 ATLASSERT(ppszName
!= NULL
);
4724 ATLASSERT(ppCookie
!= NULL
);
4726 POSITION
posNext(pos
);
4727 m_requestCookies
.GetNext(posNext
);
4728 if (posNext
!= NULL
)
4730 *ppszName
= m_requestCookies
.GetKeyAt(posNext
);
4731 *ppCookie
= &(m_requestCookies
.GetValueAt(posNext
));
4736 void SetServerContext(__in IHttpServerContext
*pServerContext
) throw()
4738 m_spServerContext
= pServerContext
;
4741 BOOL
Initialize(__in IHttpRequestLookup
*pRequestLookup
) throw()
4745 ATLASSERT(pRequestLookup
!= NULL
);
4746 // if there's no pRequestLookup, just return
4747 if (!pRequestLookup
)
4751 HRESULT hr
= pRequestLookup
->GetServerContext(&m_spServerContext
);
4755 LPCSTR
szName(NULL
);
4756 LPCSTR
szValue(NULL
);
4758 // Initialize query params from the IHttpRequestLookup*
4759 POSITION
pos(pRequestLookup
->GetFirstQueryParam(&szName
, &szValue
));
4762 m_QueryParams
.SetAt(szName
, szValue
);
4763 pos
= pRequestLookup
->GetNextQueryParam(pos
, &szName
, &szValue
);
4765 m_QueryParams
.SetShared(true);
4767 // Initialize the form vars from the IHttpRequestLookup*
4768 pos
= pRequestLookup
->GetFirstFormVar(&szName
, &szValue
);
4772 ATLTRY(m_pFormVars
= new CHttpRequestParams
);
4778 m_pFormVars
->SetAt(szName
, szValue
);
4779 pos
= pRequestLookup
->GetNextFormVar(pos
, &szName
, &szValue
);
4781 m_pFormVars
->SetShared(true);
4785 m_pFormVars
= &m_QueryParams
;
4788 // Initialize the files from the IHttpRequestLookup*
4789 IHttpFile
*pFile(NULL
);
4790 pos
= pRequestLookup
->GetFirstFile(&szName
, &pFile
);
4793 m_Files
.SetAt(szName
, pFile
);
4794 pos
= pRequestLookup
->GetNextFile(pos
, &szName
, &pFile
);
4796 m_Files
.SetShared(true);
4798 // Initialzie the cookies form the IHttpRequestLookup*
4800 CStringA strCookies
;
4801 bRet
= GetCookies(strCookies
);
4804 bRet
= Parse(strCookies
);
4806 m_requestCookies
.SetShared(false);
4815 // Call this function to initialize the object with information about the current request.
4817 // Returns TRUE on success, FALSE on failure.
4819 // Call Initialize directly or via the appropriate constructor before using the methods and
4820 // properties of the request object.
4822 // Initialize does the following:
4824 // Parses and decodes the query string into a collection of name-value pairs.
4825 // This collection is accessible via the GetQueryParams method or the QueryParams property.
4827 // Sets m_bMultiPart to TRUE if the request is a POST request with multipart/form-data encoding.
4829 // Parses the body of a POST request if the size of the request data is less than or equal to dwMaxFormSize.
4830 // The body of the request will consist of simple form fields and may also contain files if the request is encoded as multipart/form-data.
4831 // In that case, the dwFlags parameter is passed to CMultiPartFormParser::GetMultiPartData to control the creation of the files.
4832 // The collection of form fields is accessible via the GetFormVars method or the FormVars property.
4833 // The collection of files is accessible via the m_Files member.
4835 // Note that Initialize does not parse the cookies associated with a request.
4836 // Cookies are not processed until an attempt is made to access a cookie in the collection.
4838 __in IHttpServerContext
*pServerContext
,
4839 __in DWORD dwMaxFormSize
=DEFAULT_MAX_FORM_SIZE
,
4840 __in DWORD dwFlags
=ATL_FORM_FLAG_NONE
) throw()
4844 ATLASSERT(pServerContext
!= NULL
);
4845 if (!pServerContext
)
4850 m_spServerContext
= pServerContext
;
4852 HTTP_METHOD httpMethod
= GetMethod();
4853 LPCSTR pszQueryString
= GetQueryString();
4854 if (pszQueryString
&& *pszQueryString
)
4856 // Parse the query string.
4857 CHAR szQueryString
[ATL_URL_MAX_URL_LENGTH
];
4858 if (!SafeStringCopy(szQueryString
, pszQueryString
))
4863 if (!m_QueryParams
.Parse(szQueryString
))
4869 if (m_QueryParams
.IsShared())
4874 // If this is a GET request, the collection of form fields
4875 // is the same as the collection of query parameters.
4876 if (httpMethod
== HTTP_METHOD_GET
)
4878 m_pFormVars
= &m_QueryParams
;
4881 else if (httpMethod
== HTTP_METHOD_POST
)
4883 LPCSTR szContentType
= GetContentType();
4887 // Don't parse the form data if the size is bigger than the maximum specified.
4888 if (m_spServerContext
->GetTotalBytes() > dwMaxFormSize
)
4890 if (strncmp(szContentType
, "multipart/form-data", 19) == 0)
4891 m_bMultiPart
= TRUE
;
4898 // If POSTed data is urlencoded, call InitFromPost.
4899 if (strncmp(szContentType
, "application/x-www-form-urlencoded", 33) == 0 && !m_pFormVars
->IsShared())
4900 return InitFromPost();
4902 // If POSTed data is encoded as multipart/form-data, use CMultiPartFormParser.
4903 if (strncmp(szContentType
, "multipart/form-data", 19) == 0 && !m_pFormVars
->IsShared())
4905 if (m_pFormVars
!= &m_QueryParams
)
4909 CMultiPartFormParser
FormParser(m_spServerContext
);
4910 ATLTRY(m_pFormVars
= new CHttpRequestParams
);
4914 BOOL bRet
= FormParser
.GetMultiPartData(m_Files
, m_pFormVars
, dwFlags
);
4918 // else initialize m_dwBytesRead for ReadData
4931 // Implementation: Call this function to initialize the collection of form fields
4932 // from the body of an application/x-www-form-urlencoded POST request.
4933 ATL_NOINLINE BOOL
InitFromPost() throw()
4937 ATLASSUME(m_spServerContext
!= NULL
);
4939 // create our m_pFormVars
4940 if (m_pFormVars
== NULL
|| m_pFormVars
== &m_QueryParams
)
4942 ATLTRY(m_pFormVars
= new CHttpRequestParams
);
4943 if (m_pFormVars
== NULL
)
4949 // read the form data into a buffer
4950 DWORD dwBytesTotal
= m_spServerContext
->GetTotalBytes();
4951 CAutoVectorPtr
<CHAR
> szBuff
;
4952 if ((dwBytesTotal
+1 < dwBytesTotal
) || (dwBytesTotal
< 0))
4956 if (!szBuff
.Allocate(dwBytesTotal
+1))
4960 // first copy the available
4961 BOOL bRet
= ReadClientData(m_spServerContext
, szBuff
, &dwBytesTotal
, 0);
4964 szBuff
[dwBytesTotal
] = '\0';
4965 bRet
= m_pFormVars
->Parse(szBuff
);
4976 // Call this function to remove the files listed in m_Files from the web server's hard disk.
4977 // Returns the number of files deleted.
4978 int DeleteFiles() throw()
4981 POSITION pos
= m_Files
.GetStartPosition();
4984 LPCSTR szTempFile
= m_Files
.GetKeyAt(pos
);
4985 if (szTempFile
&& DeleteFileA(szTempFile
))
4989 m_Files
.GetNext(pos
);
4995 // Read a specified amount of data into pbDest and return the bytes read in pdwLen.
4996 // Returns TRUE on success, FALSE on failure.
4997 BOOL
ReadData(__out_ecount_part(*pdwLen
,*pdwLen
) LPSTR pDest
, __inout LPDWORD pdwLen
)
5002 BOOL bRet
= ReadClientData(m_spServerContext
, pDest
, pdwLen
, m_dwBytesRead
);
5004 m_dwBytesRead
+= *pdwLen
;
5008 // Returns the number of bytes available in the request buffer accessible via GetAvailableData.
5009 // If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
5010 // Otherwise, the remaining data should be read from the client using ReadData.
5011 // Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
5012 __checkReturn DWORD
GetAvailableBytes() throw(...)
5014 ATLENSURE(m_spServerContext
);
5015 return m_spServerContext
->GetAvailableBytes();
5018 // Returns the total number of bytes to be received from the client.
5019 // If this value is 0xffffffff, then there are four gigabytes or more of available data.
5020 // In this case, ReadData should be called until no more data is returned.
5021 // Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
5022 __checkReturn DWORD
GetTotalBytes() throw(...)
5024 ATLENSURE(m_spServerContext
);
5025 return m_spServerContext
->GetTotalBytes();
5028 // Returns a pointer to the request buffer containing the data sent by the client.
5029 // The size of the buffer can be determined by calling GetAvailableBytes.
5030 // Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
5031 __checkReturn LPBYTE
GetAvailableData() throw(...)
5033 ATLENSURE(m_spServerContext
);
5034 return m_spServerContext
->GetAvailableData();
5038 // Returns a nul-terminated string that contains the query information.
5039 // This is the part of the URL that appears after the question mark (?).
5040 // Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
5041 __checkReturn LPCSTR
GetQueryString() throw(...)
5043 ATLENSURE(m_spServerContext
);
5044 return m_spServerContext
->GetQueryString();
5047 // Returns a nul-terminated string that contains the HTTP method of the current request.
5048 // Examples of common HTTP methods include "GET" and "POST".
5049 // Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
5050 __checkReturn LPCSTR
GetMethodString() throw(...)
5052 ATLENSURE(m_spServerContext
);
5053 return m_spServerContext
->GetRequestMethod();
5056 // Returns an HTTP_METHOD enum value corresponding to the HTTP method of the current request.
5057 // Returns HTTP_METHOD_UNKNOWN if the request method is not one of the following methods:
5064 __checkReturn HTTP_METHOD
GetMethod() throw(...)
5066 LPCSTR szMethod
= GetMethodString();
5068 return HTTP_METHOD_UNKNOWN
;
5069 for (int i
=0; m_szMethodStrings
[i
]; i
++)
5071 if (strcmp(szMethod
, m_szMethodStrings
[i
]) == 0)
5072 return (HTTP_METHOD
) i
;
5074 return HTTP_METHOD_UNKNOWN
;
5077 // Returns a nul-terminated string that contains the content type of the data sent by the client.
5078 // Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
5079 __checkReturn LPCSTR
GetContentType() throw(...)
5081 ATLENSURE(m_spServerContext
);
5082 return m_spServerContext
->GetContentType();
5086 // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_USER" server variable.
5088 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5090 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5091 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5092 __checkReturn BOOL
GetAuthUserName(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5094 ATLENSURE(m_spServerContext
);
5095 return m_spServerContext
->GetServerVariable("AUTH_USER", szBuff
, pdwSize
);
5098 // Call this function to retrieve a nul-terminated string containing the value of the "APPL_PHYSICAL_PATH" server variable.
5100 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5102 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5103 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5104 __checkReturn BOOL
GetPhysicalPath(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5106 ATLENSURE(m_spServerContext
);
5107 return m_spServerContext
->GetServerVariable("APPL_PHYSICAL_PATH", szBuff
, pdwSize
);
5110 // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_PASSWORD" server variable.
5112 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5114 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5115 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5116 __checkReturn BOOL
GetAuthUserPassword(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5118 ATLENSURE(m_spServerContext
);
5119 return m_spServerContext
->GetServerVariable("AUTH_PASSWORD", szBuff
, pdwSize
);
5122 // Call this function to retrieve a nul-terminated string containing the value of the "URL" server variable.
5124 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5126 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5127 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5128 __checkReturn BOOL
GetUrl(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, DWORD
*pdwSize
) throw(...)
5130 ATLENSURE(m_spServerContext
);
5131 return m_spServerContext
->GetServerVariable("URL", szBuff
, pdwSize
);
5134 // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_HOST" server variable.
5136 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5138 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5139 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5140 __checkReturn BOOL
GetUserHostName(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5142 ATLENSURE(m_spServerContext
);
5143 return m_spServerContext
->GetServerVariable("REMOTE_HOST", szBuff
, pdwSize
);
5146 // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_ADDR" server variable.
5148 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5150 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5151 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5152 __checkReturn BOOL
GetUserHostAddress(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5154 ATLENSURE(m_spServerContext
);
5155 return m_spServerContext
->GetServerVariable("REMOTE_ADDR", szBuff
, pdwSize
);
5158 // Call this function to retrieve a nul-terminated string containing the physical path of the script.
5160 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5162 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5163 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5164 // The script path is the same as GetPathTranslated up to the first .srf or .dll.
5165 // For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
5166 // then this function returns "c:\inetpub\vcisapi\hello.srf".
5167 __checkReturn LPCSTR
GetScriptPathTranslated() throw(...)
5169 ATLENSURE(m_spServerContext
);
5170 return m_spServerContext
->GetScriptPathTranslated();
5173 // Returns a nul-terminated string that contains the physical path of the requested resource on the local server.
5174 // Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
5175 __checkReturn LPCSTR
GetPathTranslated() throw(...)
5177 ATLENSURE(m_spServerContext
);
5178 return m_spServerContext
->GetPathTranslated();
5181 // Returns a nul-terminated string that contains the path of the current request.
5182 // This is the part of the URL that appears after the server name, but before the query string.
5183 // Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
5184 __checkReturn LPCSTR
GetPathInfo() throw(...)
5186 ATLENSURE(m_spServerContext
);
5187 return m_spServerContext
->GetPathInfo();
5190 // Call this function to determine whether the current request was authenticated.
5191 __checkReturn BOOL
GetAuthenticated() throw(...)
5193 // We assume that if we get an authentication type from IIS,
5194 // then the user has been authenticated.
5195 CStringA strAuthType
;
5196 if (GetAuthenticationType(strAuthType
) &&
5197 strAuthType
.GetLength() > 0)
5203 // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_TYPE" server variable.
5205 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5207 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5208 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5209 __checkReturn BOOL
GetAuthenticationType(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5211 ATLENSURE(m_spServerContext
);
5212 return m_spServerContext
->GetServerVariable("AUTH_TYPE", szBuff
, pdwSize
);
5215 // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_USER" server variable.
5217 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5219 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5220 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5221 __checkReturn BOOL
GetUserName(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, DWORD __inout
*pdwSize
) throw(...)
5223 ATLENSURE(m_spServerContext
);
5224 return m_spServerContext
->GetServerVariable("REMOTE_USER", szBuff
, pdwSize
);
5227 // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_USER_AGENT" server variable.
5229 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5231 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5232 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5233 __checkReturn BOOL
GetUserAgent(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5235 ATLENSURE(m_spServerContext
);
5236 return m_spServerContext
->GetServerVariable("HTTP_USER_AGENT", szBuff
, pdwSize
);
5239 // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT_LANGUAGE" server variable.
5241 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5243 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5244 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5245 __checkReturn BOOL
GetUserLanguages(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5247 ATLENSURE(m_spServerContext
);
5248 return m_spServerContext
->GetServerVariable("HTTP_ACCEPT_LANGUAGE", szBuff
, pdwSize
);
5251 // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT" server variable.
5253 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5255 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5256 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5257 __checkReturn BOOL
GetAcceptTypes(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5259 ATLENSURE(m_spServerContext
);
5260 return m_spServerContext
->GetServerVariable("HTTP_ACCEPT", szBuff
, pdwSize
);
5263 // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT_ENCODING" server variable.
5265 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5267 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5268 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5269 __checkReturn BOOL
GetAcceptEncodings(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5271 ATLENSURE(m_spServerContext
);
5272 return m_spServerContext
->GetServerVariable("HTTP_ACCEPT_ENCODING", szBuff
, pdwSize
);
5276 // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_REFERER" server variable.
5278 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5280 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5281 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5282 __checkReturn BOOL
GetUrlReferer(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5284 ATLENSURE(m_spServerContext
);
5285 return m_spServerContext
->GetServerVariable("HTTP_REFERER", szBuff
, pdwSize
);
5288 // Call this function to retrieve a nul-terminated string containing the value of the "SCRIPT_NAME" server variable.
5290 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5292 // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
5293 // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
5294 __checkReturn BOOL
GetScriptName(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuff
, __inout DWORD
*pdwSize
) throw(...)
5296 ATLENSURE(m_spServerContext
);
5297 return m_spServerContext
->GetServerVariable("SCRIPT_NAME", szBuff
, pdwSize
);
5300 // Fills a buffer with the contents of the HTTP_COOKIE headers sent
5301 // from the browser.
5302 __checkReturn BOOL
GetCookies(__out_ecount_part(*pdwSize
,*pdwSize
) LPSTR szBuf
, __inout LPDWORD pdwSize
) const throw(...)
5304 ATLASSERT(szBuf
!= NULL
);
5307 if (GetCookies(strCookie
))
5309 ATLENSURE(pdwSize
!= NULL
);
5310 if (pdwSize
&& *pdwSize
> (DWORD
)strCookie
.GetLength())
5312 Checked::strcpy_s(szBuf
, *pdwSize
, strCookie
);
5313 *pdwSize
= strCookie
.GetLength();
5320 // Fills a CStringA with the contents of the HTTP_COOKIE headers sent
5321 // from the browser.
5322 __checkReturn BOOL
GetCookies(__inout CStringA
& strBuff
) const throw()
5324 return GetServerVariable("HTTP_COOKIE", strBuff
);
5327 // Call this function to retrieve a reference to the specified cookie.
5328 // Returns a CCookie reference to the specified cookie or a
5329 // reference to an empty cookie if the name can not be found.
5330 ATL_NOINLINE
const CCookie
& Cookies(__in LPCSTR szName
) throw(...)
5332 if (GetRequestCookies())
5334 // p->m_value is a const CCookie&
5335 CookieMap::CPair
*p
= NULL
;
5336 ATLTRY(p
= m_requestCookies
.Lookup(szName
));
5342 m_EmptyCookie
.Empty(); // make sure it is cleared.
5343 return m_EmptyCookie
;
5347 // Call this function to retrieve the session cookie.
5348 const CCookie
& GetSessionCookie() throw(...)
5350 return Cookies(SESSION_COOKIE_NAME
);
5353 // Call this function to retrieve the value of the requested server variable in a CStringA object.
5354 // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
5355 // Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
5356 BOOL
GetServerVariable(__in LPCSTR szVariable
, __out CStringA
&str
) const
5358 ATLENSURE(m_spServerContext
);
5364 m_spServerContext
->GetServerVariable(szVariable
, NULL
, &dwSize
);
5365 bRet
= m_spServerContext
->GetServerVariable(szVariable
, str
.GetBuffer(dwSize
), &dwSize
);
5368 str
.ReleaseBuffer(dwSize
);
5377 // Call this function to retrieve the value of the "APPL_PHYSICAL_PATH" server variable.
5378 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5379 __checkReturn BOOL
GetPhysicalPath(__out CStringA
&str
) throw()
5381 return GetServerVariable("APPL_PHYSICAL_PATH", str
);
5384 // Call this function to retrieve the value of the "REMOTE_HOST" server variable.
5385 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5386 __checkReturn BOOL
GetUserHostName(__out CStringA
&str
) throw()
5388 return GetServerVariable("REMOTE_HOST", str
);
5391 // Call this function to retrieve the value of the "REMOTE_ADDR" server variable.
5392 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5393 __checkReturn BOOL
GetUserHostAddress(__out CStringA
&str
) throw()
5395 return GetServerVariable("REMOTE_ADDR", str
);
5398 // Call this function to retrieve the value of the "AUTH_TYPE" server variable.
5399 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5400 __checkReturn BOOL
GetAuthenticationType(__out CStringA
&str
) throw()
5402 return GetServerVariable("AUTH_TYPE", str
);
5405 // Call this function to retrieve the value of the "REMOTE_USER" server variable.
5406 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5407 __checkReturn BOOL
GetUserName(__out CStringA
&str
) throw()
5409 return GetServerVariable("REMOTE_USER", str
);
5412 // Call this function to retrieve the value of the "HTTP_USER_AGENT" server variable.
5413 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5414 __checkReturn BOOL
GetUserAgent(__out CStringA
&str
) throw()
5416 return GetServerVariable("HTTP_USER_AGENT", str
);
5419 // Call this function to retrieve the value of the "HTTP_ACCEPT_LANGUAGE" server variable.
5420 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5421 __checkReturn BOOL
GetUserLanguages(__out CStringA
&str
) throw()
5423 return GetServerVariable("HTTP_ACCEPT_LANGUAGE", str
);
5426 // Call this function to retrieve the value of the "AUTH_USER" server variable.
5427 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5428 __checkReturn BOOL
GetAuthUserName(__out CStringA
&str
) throw()
5430 return GetServerVariable("AUTH_USER", str
);
5433 // Call this function to retrieve the value of the "AUTH_PASSWORD" server variable.
5434 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5435 __checkReturn BOOL
GetAuthUserPassword(__out CStringA
&str
) throw()
5437 return GetServerVariable("AUTH_PASSWORD", str
);
5440 // Call this function to retrieve the value of the "URL" server variable.
5441 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5442 __checkReturn BOOL
GetUrl(__out CStringA
&str
) throw()
5444 return GetServerVariable("URL", str
);
5447 // Call this function to retrieve the value of the "HTTP_ACCEPT" server variable.
5448 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5449 __checkReturn BOOL
GetAcceptTypes(__out CStringA
&str
) throw()
5451 return GetServerVariable("HTTP_ACCEPT", str
);
5454 // Call this function to retrieve the value of the "HTTP_ACCEPT_ENCODING" server variable.
5455 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5456 __checkReturn BOOL
GetAcceptEncodings(__out CStringA
& str
) throw()
5458 return GetServerVariable("HTTP_ACCEPT_ENCODING", str
);
5461 // Call this function to retrieve the value of the "HTTP_REFERER" server variable.
5462 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5463 __checkReturn BOOL
GetUrlReferer(__out CStringA
&str
) throw()
5465 return GetServerVariable("HTTP_REFERER", str
);
5468 // Call this function to retrieve the value of the "SCRIPT_NAME" server variable.
5469 // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
5470 __checkReturn BOOL
GetScriptName(__out CStringA
&str
) throw()
5472 return GetServerVariable("SCRIPT_NAME", str
);
5475 // Implementation: Call this function to populate the collection
5476 // of CCookie objects with the cookies in the current request.
5477 // Returns TRUE on success, FALSE on failure.
5478 __checkReturn BOOL
GetRequestCookies() throw()
5482 if (m_requestCookies
.GetCount())
5483 return TRUE
; // we already got the cookies!
5485 CStringA strCookies
;
5486 if (GetCookies(strCookies
))
5488 bRet
= Parse(strCookies
);
5496 Pair(__in
const CStringA
&n
, __in
const CStringA
& v
)
5502 Pair(__in
const Pair
& rhs
)
5511 static const int INVALID_COOKIE_VERSION
= -1;
5514 // Implementation: Call this function to populate m_requestCookies
5515 // with a collection of CCookie objects which represents the
5516 // cookies contained in szCookie header sent from the browser.
5517 BOOL
Parse(__in LPCSTR szCookieIn
)
5519 ATLENSURE(szCookieIn
!=NULL
);
5520 UINT acp
= GetACP();
5523 CStringA name
, value
;
5524 CAtlList
<Pair
> pair_list
;
5526 // create a list of name=value pairs
5527 // these are separated by or ;
5528 szStart
= szCookieIn
;
5534 szStart
= SkipSpace(szStart
, (WORD
)acp
);
5535 if (*szStart
== '\0')
5539 while (*szEnd
&& *szEnd
!= '=' && *szEnd
!= ';' )
5542 if (szEnd
<= szStart
)
5551 break; // no name or error
5554 CopyToCString(name
, szStart
, szEnd
);
5555 if (*szEnd
== '\0' || *szEnd
== ';' )
5557 // no value expected;
5558 pair_list
.AddTail(Pair(name
, value
));
5561 else if ( *szEnd
== '=')
5563 szEnd
++; // skip '='
5565 while (*szEnd
&& *szEnd
!= ';')
5568 if (szEnd
<= szStart
)
5577 pair_list
.AddTail(Pair(name
,value
));
5581 CopyToCString(value
, szStart
, szEnd
);
5582 pair_list
.AddTail(Pair(name
,value
));
5587 // now make cookies out of the list
5588 // The first item could be $Version, which would
5589 // be the version for all cookies in the list.
5590 // any other $Version's found in the list will be ignored.
5591 if (pair_list
.GetCount() <= 0)
5592 return TRUE
; // nothing in the list
5594 // check for $Version
5595 static const char szVersion
[] = "$Version";
5596 int nVersion
= INVALID_COOKIE_VERSION
;
5597 if (!strncmp(pair_list
.GetHead().name
, szVersion
, sizeof(szVersion
)+1))
5600 errno_t errnoValue
= AtlStrToNum
<int, char>(&nVersion
, pair_list
.GetHead().value
, &pStop
, 10);
5601 if (errnoValue
== ERANGE
)
5602 nVersion
= INVALID_COOKIE_VERSION
; // $Version contained garbage
5603 pair_list
.RemoveHead(); // Remove $Version
5606 POSITION pos
= pair_list
.GetHeadPosition();
5607 bool bInCookie
= false;
5612 const Pair
&p
= pair_list
.GetNext(pos
);
5613 if (p
.name
[0] == '$')
5616 return FALSE
; // invalid cookie string
5619 //add attribute to current cookie
5620 if(!cookie
.AddAttribute(p
.name
.Right(p
.name
.GetLength()-1),p
.value
))
5631 cookie
.SetName(p
.name
);
5632 cookie
.ParseValue(p
.value
);
5633 if (nVersion
!= INVALID_COOKIE_VERSION
)
5634 cookie
.SetVersion(nVersion
);
5638 // add previous cookie
5639 CStringA strPrevName
;
5640 if(!cookie
.GetName(strPrevName
))
5644 m_requestCookies
.SetAt(strPrevName
, cookie
);
5646 // empty current cookie and start over
5648 cookie
.SetName(p
.name
);
5649 cookie
.ParseValue(p
.value
);
5650 if (nVersion
!= INVALID_COOKIE_VERSION
)
5651 cookie
.SetVersion(nVersion
);
5657 if (!cookie
.IsEmpty())
5660 if(!cookie
.GetName(strName
))
5664 m_requestCookies
.SetAt(strName
, cookie
);
5671 // Call this function to copy a substring to a CString reference and ensure nul-termination.
5672 inline void CopyToCString(__out CStringA
& string
, __in_ecount(pEnd
-pStart
) LPCSTR pStart
, __in LPCSTR pEnd
) throw( ... )
5674 ATLENSURE( pStart
!= NULL
);
5675 ATLENSURE( pEnd
!= NULL
);
5676 ATLENSURE( pEnd
>=pStart
);
5678 string
.SetString(pStart
, (int)(pEnd
-pStart
));
5682 inline LPCSTR
SkipSpace(__in LPCSTR sz
, __in WORD nCodePage
) throw()
5687 while (isspace(static_cast<unsigned char>(*sz
)))
5688 sz
= CharNextExA(nCodePage
, sz
, 0);
5694 __success(SUCCEEDED(return)) __checkReturn HRESULT STDMETHODCALLTYPE
QueryInterface(__in REFIID riid
, __deref_out
void **ppv
)
5698 if (InlineIsEqualGUID(riid
, __uuidof(IHttpRequestLookup
)))
5700 *ppv
= static_cast<IUnknown
*>(static_cast<IHttpRequestLookup
*>(this));
5704 if (InlineIsEqualGUID(riid
, __uuidof(IUnknown
)))
5706 *ppv
= static_cast<IUnknown
*>(this);
5710 return E_NOINTERFACE
;
5713 ULONG STDMETHODCALLTYPE
AddRef()
5718 ULONG STDMETHODCALLTYPE
Release()
5722 }; // class CHttpRequest
5724 __declspec(selectany
) const char* const CHttpRequest::m_szMethodStrings
[] = {
5731 "DEBUG", // Debugging support for VS7
5735 // This class provides type conversions via the Write method
5736 // and overloaded left shift << operator for writing
5737 // data to the IWriteStream interface. The IWriteStream interface
5738 // only accepts strings, but this helper class allows you to transparently
5739 // pass many different types by providing automatic type conversions.
5741 // Notes on Type Conversions:
5742 // Numeric types are converted to their decimal representations.
5743 // Floating point values are output with a precision of 6 decimal places.
5744 // Currency values are converted according to the locale settings of the current thread.
5745 class CWriteStreamHelper
5748 // Implementation: The IWriteStream interface.
5749 IWriteStream
*m_pStream
;
5753 CWriteStreamHelper() throw()
5756 m_ulACP
= _AtlGetConversionACP();
5759 CWriteStreamHelper(__in_opt IWriteStream
*pStream
) throw()
5761 m_pStream
= pStream
;
5762 m_ulACP
= _AtlGetConversionACP();
5765 // Attach a IWriteStream
5766 IWriteStream
*Attach(__in IWriteStream
*pStream
) throw()
5768 IWriteStream
*pRetStream
= m_pStream
;
5769 m_pStream
= pStream
;
5773 // Call this function to write data to the IWriteStream interface managed by this object.
5774 // Returns TRUE on success, FALSE on failure.
5775 BOOL
Write(__in_z LPCSTR szOut
) throw()
5777 ATLASSUME(m_pStream
!= NULL
);
5783 return SUCCEEDED(m_pStream
->WriteStream(szOut
, (int) strlen(szOut
), &dwWritten
));
5786 // Call this function to write data to the IWriteStream interface managed by this object.
5787 // Returns TRUE on success, FALSE on failure.
5788 BOOL
Write(__in
int n
) throw()
5791 Checked::itoa_s(n
, szTmp
, _countof(szTmp
), 10);
5792 return Write(szTmp
);
5795 // Call this function to write data to the IWriteStream interface managed by this object.
5796 // Returns TRUE on success, FALSE on failure.
5797 BOOL
Write(__in
unsigned int u
) throw()
5800 Checked::ultoa_s(u
, szTmp
, _countof(szTmp
), 10);
5801 return Write(szTmp
);
5804 // Call this function to write data to the IWriteStream interface managed by this object.
5805 // Returns TRUE on success, FALSE on failure.
5806 BOOL
Write(__in
short int w
) throw()
5808 return Write((int) w
);
5811 // Call this function to write data to the IWriteStream interface managed by this object.
5812 // Returns TRUE on success, FALSE on failure.
5813 BOOL
Write(__in
unsigned short int w
) throw()
5815 return Write((unsigned int) w
);
5818 // Call this function to write data to the IWriteStream interface managed by this object.
5819 // Returns TRUE on success, FALSE on failure.
5820 BOOL
Write(__in
long int dw
) throw()
5823 Checked::ltoa_s(dw
, szTmp
, _countof(szTmp
), 10);
5824 return Write(szTmp
);
5827 // Call this function to write data to the IWriteStream interface managed by this object.
5828 // Returns TRUE on success, FALSE on failure.
5829 BOOL
Write(__in
unsigned long int dw
) throw()
5832 Checked::ultoa_s(dw
, szTmp
, _countof(szTmp
), 10);
5833 return Write(szTmp
);
5836 // Call this function to write data to the IWriteStream interface managed by this object.
5837 // Returns TRUE on success, FALSE on failure.
5838 BOOL
Write(__in
double d
, __in
int nDigitCount
=ATL_DEFAULT_PRECISION
) throw()
5840 ATLASSUME(m_pStream
!= NULL
);
5845 bool fWriteDec
=true;
5846 if (0 != _fcvt_s(szTmp
, _countof(szTmp
), d
, nDigitCount
, &nDec
, &nSign
))
5853 m_pStream
->WriteStream("-", 1, NULL
);
5857 m_pStream
->WriteStream("0.", 2, NULL
);
5858 for (int i
=0;i
<nDec
;i
++)
5860 m_pStream
->WriteStream("0", 1, NULL
);
5869 // if the decimal lies at the end of the number
5870 // (no digits to the right of the decimal, we don't
5872 if (nDec
== 0 && fWriteDec
)
5873 m_pStream
->WriteStream(".", 1, NULL
);
5874 m_pStream
->WriteStream(p
, 1, NULL
);
5881 // Call this function to write data to the IWriteStream interface managed by this object.
5882 // Returns TRUE on success, FALSE on failure.
5883 BOOL
Write(__in __int64 i
) throw()
5886 Checked::i64toa_s(i
, szTmp
, _countof(szTmp
), 10);
5887 return Write(szTmp
);
5890 // Call this function to write data to the IWriteStream interface managed by this object.
5891 // Returns TRUE on success, FALSE on failure.
5892 BOOL
Write(__in
unsigned __int64 i
) throw()
5895 Checked::ui64toa_s(i
, szTmp
, _countof(szTmp
), 10);
5896 return Write(szTmp
);
5899 // Call this function to write data to the IWriteStream interface managed by this object.
5900 // Returns TRUE on success, FALSE on failure.
5901 BOOL
Write(__in CURRENCY c
) throw()
5906 Checked::i64toa_s(c
.int64
, szNumber
, _countof(szNumber
), 10);
5907 int nLen
= (int) strlen(szNumber
);
5910 // prepend ascii zeros
5911 Checked::memmove_s(szNumber
+5-nLen
, 32-(5-nLen
), szNumber
, nLen
+1);
5912 memset(szNumber
, '0', 5-nLen
);
5916 Checked::memmove_s(szNumber
+nLen
-3, 32-(nLen
-3), szNumber
+nLen
-4, 5);
5917 szNumber
[nLen
-4] = '.';
5919 int nRet
= GetCurrencyFormatA(GetThreadLocale(), 0, szNumber
, NULL
, szDest
, sizeof(szDest
));
5921 return Write(szDest
);
5923 ATLASSERT(GetLastError()==ERROR_INSUFFICIENT_BUFFER
);
5925 nRet
= GetCurrencyFormatA(GetThreadLocale(), 0, szNumber
, NULL
, NULL
, 0);
5926 ATLASSERT(nRet
> 0);
5931 CAutoVectorPtr
<CHAR
> szBuffer
;
5932 if (!szBuffer
.Allocate(nRet
))
5934 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
5937 nRet
= GetCurrencyFormatA(GetThreadLocale(), 0, szNumber
, NULL
, szBuffer
, nRet
);
5939 ATLASSERT(nRet
> 0);
5942 bRet
= Write(szBuffer
);
5947 // Call this function to write data to the IWriteStream interface managed by this object.
5948 // Returns TRUE on success, FALSE on failure.
5949 BOOL
Write(__in LPCWSTR wsz
) throw()
5951 ATLASSUME(m_pStream
!= NULL
);
5957 CW2A
sz(wsz
, m_ulACP
);
5965 bRet
= SUCCEEDED(m_pStream
->WriteStream(sz
, (int) strlen(sz
), &dwWritten
));
5975 // Use this operator to write data to the IWriteStream interface managed by this object.
5976 CWriteStreamHelper
& operator<<(__in LPCSTR szStr
) throw(...)
5983 // Use this operator to write data to the IWriteStream interface managed by this object.
5984 CWriteStreamHelper
& operator<<(__in LPCWSTR wszStr
) throw(...)
5991 // Use this operator to write data to the IWriteStream interface managed by this object.
5992 CWriteStreamHelper
& operator<<(__in
int n
) throw(...)
5999 // Use this operator to write data to the IWriteStream interface managed by this object.
6000 CWriteStreamHelper
& operator<<(__in
short int w
) throw(...)
6007 // Use this operator to write data to the IWriteStream interface managed by this object.
6008 CWriteStreamHelper
& operator<<(__in
unsigned int u
) throw(...)
6015 // Use this operator to write data to the IWriteStream interface managed by this object.
6016 CWriteStreamHelper
& operator<<(__in
long int dw
) throw(...)
6023 // Use this operator to write data to the IWriteStream interface managed by this object.
6024 CWriteStreamHelper
& operator<<(__in
unsigned long int dw
) throw(...)
6031 // Use this operator to write data to the IWriteStream interface managed by this object.
6032 CWriteStreamHelper
& operator<<(__in
double d
) throw(...)
6039 // Use this operator to write data to the IWriteStream interface managed by this object.
6040 CWriteStreamHelper
& operator<<(__in __int64 i
) throw(...)
6047 // Use this operator to write data to the IWriteStream interface managed by this object.
6048 CWriteStreamHelper
& operator<<(__in
unsigned __int64 i
) throw(...)
6055 // Use this operator to write data to the IWriteStream interface managed by this object.
6056 CWriteStreamHelper
& operator<<(__in CURRENCY c
) throw(...)
6063 UINT
SetConversionCodePage(__in UINT nNewCP
)
6065 UINT nOldCP
= m_ulACP
;
6072 template <DWORD dwSizeT
=ATL_ISAPI_BUFFER_SIZE
>
6073 class CAtlIsapiBuffer
6076 char m_szBuffer
[dwSizeT
];
6083 CAtlIsapiBuffer() throw()
6088 m_pBuffer
= m_szBuffer
;
6090 m_dwAlloc
= dwSizeT
;
6091 m_hProcHeap
= GetProcessHeap();
6094 CAtlIsapiBuffer(__in LPCSTR sz
)
6096 m_pBuffer
= m_szBuffer
;
6098 m_dwAlloc
= dwSizeT
;
6099 m_hProcHeap
= GetProcessHeap();
6102 AtlThrow(E_OUTOFMEMORY
);
6105 ~CAtlIsapiBuffer() throw()
6110 BOOL
Alloc(__in DWORD dwSize
) throw()
6112 if (m_dwAlloc
>= dwSize
)
6116 if (m_pBuffer
!= m_szBuffer
)
6118 HeapFree(m_hProcHeap
, 0, m_pBuffer
);
6122 m_pBuffer
= (LPSTR
)HeapAlloc(m_hProcHeap
, 0, dwSize
);
6131 BOOL
ReAlloc(__in DWORD dwNewSize
) throw()
6133 if (dwNewSize
<= m_dwAlloc
)
6136 if (m_pBuffer
== m_szBuffer
)
6138 BOOL bRet
= Alloc(dwNewSize
);
6141 Checked::memcpy_s(m_pBuffer
, dwNewSize
, m_szBuffer
, m_dwLen
);
6146 LPSTR pvNew
= (LPSTR
)HeapReAlloc(m_hProcHeap
, 0, m_pBuffer
, dwNewSize
);
6150 m_dwAlloc
= dwNewSize
;
6158 if (m_pBuffer
!= m_szBuffer
)
6160 HeapFree(m_hProcHeap
,0 , m_pBuffer
);
6161 m_dwAlloc
= dwSizeT
;
6162 m_pBuffer
= m_szBuffer
;
6167 void Empty() throw()
6176 DWORD
GetLength() throw()
6181 BOOL
Append(__in LPCSTR sz
, __in
int nLen
= -1) throw()
6187 nLen
= (int) strlen(sz
);
6189 DWORD newLen
= m_dwLen
+ nLen
+ 1;
6190 if (newLen
<= m_dwLen
|| newLen
<= (DWORD
)nLen
)
6194 if (newLen
> m_dwAlloc
)
6196 if (!ReAlloc(m_dwAlloc
+ (nLen
+1 > ATL_ISAPI_BUFFER_SIZE
? nLen
+1 : ATL_ISAPI_BUFFER_SIZE
)))
6199 Checked::memcpy_s(m_pBuffer
+ m_dwLen
, m_dwAlloc
-m_dwLen
, sz
, nLen
);
6201 m_pBuffer
[m_dwLen
]=0;
6205 operator LPCSTR() throw()
6210 CAtlIsapiBuffer
& operator+=(__in LPCSTR sz
)
6213 AtlThrow(E_OUTOFMEMORY
);
6216 }; // class CAtlIsapiBuffer
6218 // This class represents the response that the web server will send back to the client.
6220 // CHttpResponse provides friendly functions for building up the headers, cookies, and body of an HTTP response.
6221 // The class derives from IWriteStream and CWriteStreamHelper, allowing you to call those classes' methods
6222 // to build up the body of the response. By default, the class improves performance by buffering the response until it is complete before sending it back to the client.
6223 class CHttpResponse
: public IWriteStream
, public CWriteStreamHelper
6227 // Implementation: A map of HTTP response headers.
6228 // The key is the name of the response header.
6229 // The value is the data for the response header.
6230 CSimpleMap
<CStringA
, CStringA
> m_headers
;
6232 // Implementation: Determines whether the response is currently being buffered.
6233 BOOL m_bBufferOutput
;
6235 // Implementation: Determines whether any output should be sent to the client.
6236 // Intended mainly for HEAD requests, where the client should get the same headers
6237 // (i.e. Content-Length) as for a GET request
6240 // Implementation: The limit in bytes of the response buffer.
6241 // When the limit is reached, the buffer is automatically flushed
6242 // and data is sent to the client. You can set this to ULONG_MAX
6243 // to enable full buffering (this is the default, and is required
6244 // for enabling keep alive connections).
6245 DWORD m_dwBufferLimit
;
6247 // Implementation: The server context.
6248 CComPtr
<IHttpServerContext
> m_spServerContext
;
6250 // Implementation: The HTTP status code for the response.
6253 // Implementation: Determines whether the response headers have already been sent to the client.
6254 BOOL m_bHeadersSent
;
6256 // Implementation: Handle of the file being transmitted so it can be closed
6257 // when the async I/O completes
6261 // Implementation: The buffer used to store the response before
6262 // the data is sent to the client.
6263 CAtlIsapiBuffer
<> m_strContent
;
6265 // Numeric constants for the HTTP status codes used for redirecting client requests.
6268 HTTP_REDIRECT_MULTIPLE
=300,
6269 HTTP_REDIRECT_MOVED
=301,
6270 HTTP_REDIRECT_FOUND
=302,
6271 HTTP_REDIRECT_SEE_OTHER
=303,
6272 HTTP_REDIRECT_NOT_MODIFIED
=304,
6273 HTTP_REDIRECT_USE_PROXY
=305,
6274 HTTP_REDIRECT_TEMPORARY_REDIRECT
=307
6277 CHttpResponse() throw()
6279 m_bBufferOutput
= TRUE
;
6280 m_dwBufferLimit
= ULONG_MAX
;
6281 m_nStatusCode
= 200;
6283 m_bHeadersSent
= FALSE
;
6284 m_bSendOutput
= TRUE
;
6285 m_hFile
= INVALID_HANDLE_VALUE
;
6288 CHttpResponse(__in IHttpServerContext
*pServerContext
)
6290 m_bBufferOutput
= TRUE
;
6291 m_dwBufferLimit
= ULONG_MAX
;
6292 m_nStatusCode
= 200;
6294 m_bHeadersSent
= FALSE
;
6295 ATLENSURE(Initialize(pServerContext
));
6296 m_bSendOutput
= TRUE
;
6297 m_hFile
= INVALID_HANDLE_VALUE
;
6300 // The destructor flushes the buffer if there is content that
6301 // hasn't yet been sent to the client.
6302 virtual ~CHttpResponse() throw()
6305 if (m_hFile
&& m_hFile
!= INVALID_HANDLE_VALUE
)
6306 CloseHandle(m_hFile
);
6309 // Call this function to initialize the response object with a pointer to the server context.
6310 // Returns TRUE on success, FALSE on failure.
6311 __checkReturn BOOL
Initialize(__in IHttpServerContext
*pServerContext
) throw()
6313 ATLASSERT(pServerContext
!= NULL
);
6314 if (!pServerContext
)
6317 m_spServerContext
= pServerContext
;
6322 // This is called to initialize the CHttpResponse for a child handler. By default, it
6323 // assumes the parent will be responsible for sending the headers.
6324 __checkReturn BOOL
Initialize(__in IHttpRequestLookup
*pLookup
) throw(...)
6330 CComPtr
<IHttpServerContext
> spContext
;
6331 HRESULT hr
= pLookup
->GetServerContext(&spContext
);
6335 if (!Initialize(spContext
))
6338 m_bHeadersSent
= TRUE
;
6343 // Returns a pointer to the IHttpServerContext interface for the current request.
6344 __checkReturn HRESULT
GetServerContext(__deref_out_opt IHttpServerContext
** ppOut
) throw()
6346 return m_spServerContext
.CopyTo(ppOut
);
6351 m_spServerContext
.Release();
6352 HaveSentHeaders(TRUE
);
6354 // Call this function to set buffering options for the response.
6356 // This function allows you to turn buffering on or off, and to set a size limit
6357 // on the amount of data that will be buffered before being sent to the client.
6359 // When you turn off buffering, the current contents of the buffer will be sent to the client.
6360 // If you need to clear the buffer without sending the contents to the client, call ClearContent instead.
6362 // When the size of the buffer is reduced below the current size of the buffered content,
6363 // the entire buffer is flushed.
6364 void SetBufferOutput(__in BOOL bBufferOutput
, __in DWORD dwBufferLimit
=ATL_ISAPI_BUFFER_SIZE
) throw()
6366 if (m_bBufferOutput
&& !bBufferOutput
)
6368 // before turning off buffering, flush
6369 // the current contents
6372 SetBufferLimit(dwBufferLimit
);
6374 m_bBufferOutput
= bBufferOutput
;
6377 // Call this function to determine whether data written to the response object is being buffered or not.
6378 // Returns TRUE if output is being buffered, FALSE otherwise.
6379 __checkReturn BOOL
GetBufferOutput() throw()
6381 return m_bBufferOutput
;
6384 // Call this function to determine whether the response headers have been sent
6385 // Returns TRUE if headers have been sent, FALSE otherwise.
6386 __checkReturn BOOL
HaveSentHeaders() throw()
6388 return m_bHeadersSent
;
6391 // Call this function to override the m_bHeadersSent state. This is useful
6392 // when you want child handlers (e.g. from an include or subhandler) to send the headers
6393 void HaveSentHeaders(__in BOOL bSent
) throw()
6395 m_bHeadersSent
= bSent
;
6398 // Call this function to set a size limit on the amount of data buffered by the reponse object.
6399 // When the size of the buffer is reduced below the current size of the buffered content,
6400 // the entire buffer is flushed.
6401 // See GetBufferLimit.
6402 void SetBufferLimit(__in DWORD dwBufferLimit
) throw()
6404 if (m_bBufferOutput
)
6406 if (m_strContent
.GetLength() >= dwBufferLimit
)
6408 // new buffer limit is less than the
6409 // size currently buffered. So flush
6410 // the current buffer
6414 m_dwBufferLimit
= dwBufferLimit
;
6417 // Returns the current size limit of the buffer in bytes.
6418 // See SetBufferLimit.
6419 DWORD
GetBufferLimit() throw()
6421 return m_dwBufferLimit
;
6424 // Returns the current value of the Content-Type header if present, otherwise returns NULL.
6425 LPCSTR
GetContentType() throw()
6427 // return the content type from the
6428 // header collection if any
6431 CStringA
strKey("Content-Type");
6433 int nIndex
= m_headers
.FindKey(strKey
);
6435 return m_headers
.GetValueAt(nIndex
);
6443 // Call this function to set the Content-Type of the HTTP response.
6444 // Examples of common MIME content types include text/html and text/plain.
6445 BOOL
SetContentType(__in_opt LPCSTR szContentType
) throw()
6449 if (!m_headers
.SetAt("Content-Type", szContentType
))
6450 return m_headers
.Add("Content-Type", szContentType
);
6458 // Call this function to set the HTTP status code of the response.
6459 // If not set explicitly, the default status code is 200 (OK).
6460 // See GetStatusCode.
6461 void SetStatusCode(__in
int nCode
) throw()
6463 m_nStatusCode
= nCode
;
6466 // Returns the current HTTP status code of the response.
6467 // See SetStatusCode.
6468 int GetStatusCode() throw()
6470 return m_nStatusCode
;
6473 // Call this function to set the Cache-Control http header of the response.
6474 // Examples of common Cache-Control header values: public, private, max-age=delta-seconds
6475 BOOL
SetCacheControl(__in_opt LPCSTR szCacheControl
) throw()
6479 if (!m_headers
.SetAt("Cache-Control", szCacheControl
))
6480 return m_headers
.Add("Cache-Control", szCacheControl
);
6488 // Call this function to set the Expires HTTP header to the absolute date/time
6489 // specified in the stExpires parameter
6490 BOOL
SetExpiresAbsolute(__in
const SYSTEMTIME
& stExpires
) throw()
6494 CStringA strExpires
;
6495 SystemTimeToHttpDate(stExpires
, strExpires
);
6497 if (!m_headers
.SetAt("Expires", strExpires
))
6498 return m_headers
.Add("Expires", strExpires
);
6506 // Call this function to set the Expires HTTP header to a relative date/time
6507 // value specified in minutes;
6508 BOOL
SetExpires(__in
long lMinutes
) throw()
6511 GetSystemTimeAsFileTime(&ft
);
6513 // add the specified number of minutes
6514 ft
+= CFileTimeSpan(((ULONGLONG
)lMinutes
)*60*10000000);
6517 FileTimeToSystemTime(&ft
, &st
);
6518 return SetExpiresAbsolute(st
);
6521 // Call this function to set whether or not to output to client.
6522 // Intended primarily for HEAD requests
6523 BOOL
SetWriteToClient(__in BOOL bSendOutput
) throw()
6525 m_bSendOutput
= bSendOutput
;
6529 // Call this function to determine whether or not the data is
6530 // sent to the client. Intended primarily for HEAD requests
6531 BOOL
GetWriteToClient() throw()
6533 return m_bSendOutput
;
6536 // Call this function to write data to the response object.
6538 // Returns S_OK on success, E_INVALIDARG or E_FAIL on failure.
6540 // See WriteClient for comments on buffering.
6542 // szOut A pointer to the first byte of the data to be written.
6544 // nLen The number of bytes to write. If this parameter is -1,
6545 // szOut is assumed to be a nul-terminated string and the
6546 // whole string will be written.
6548 // pdwWritten A DWORD pointer that can be used to get the number of bytes written.
6549 // This parameter can be NULL.
6550 __checkReturn HRESULT
WriteStream(__in_z LPCSTR szOut
, __in
int nLen
, __out_opt DWORD
*pdwWritten
)
6552 ATLASSUME(m_spServerContext
!= NULL
);
6559 return E_INVALIDARG
;
6560 nLen
= (int) strlen(szOut
);
6562 BOOL bRet
= WriteLen(szOut
, nLen
);
6565 return AtlHresultFromLastError();
6572 // Call this function to write data to the response object.
6574 // Returns TRUE on success. FALSE on failure.
6576 // If buffering is disabled, data is written directly to the client.
6577 // If buffering is enabled, this function attempts to write to the buffer.
6578 // If the buffer is too small to contain its existing data and the new data,
6579 // the current contents of the buffer are flushed.
6580 // If the buffer is still too small to contain the new data, that data is written
6581 // directly to the client. Otherwise the new data is written to the buffer.
6583 // Any headers that have been set in the response will be sent just before the
6584 // data is written to the client if no headers have been sent up to that point.
6586 // szOut A pointer to the first byte of the data to be written.
6588 // nLen The number of bytes to write.
6589 __checkReturn BOOL
WriteLen(__in_ecount(dwLen
) LPCSTR szOut
, __in DWORD dwLen
) throw()
6591 ATLASSUME(m_spServerContext
!= NULL
);
6595 if (m_bBufferOutput
)
6597 if (m_strContent
.GetLength()+dwLen
>= m_dwBufferLimit
)
6602 if (dwLen
<= m_dwBufferLimit
)
6603 return m_strContent
.Append(szOut
, dwLen
);
6605 BOOL bRet
= SendHeadersInternal();
6607 if (bRet
&& m_bSendOutput
)
6608 bRet
= m_spServerContext
->WriteClient((void *) szOut
, &dwLen
);
6613 // Call this function to redirect the client to a different resource.
6615 // Returns TRUE on success, FALSE on failure.
6617 // szURL A nul-terminated string specifying the resource the client should navigate to.
6619 // statusCode An HTTP status code from the HTTP_REDIRECT enumeration describing the reason
6620 // for the redirection.
6622 // bSendBody Specifies whether to generate and send a response body with the headers.
6624 // This function allows (and RFC 2616 encourages) a response body to be sent
6625 // with the following redirect types:
6626 // HTTP_REDIRECT_MOVED
6627 // HTTP_REDIRECT_FOUND
6628 // HTTP_REDIRECT_SEE_OTHER
6629 // HTTP_REDIRECT_TEMPORARY_REDIRECT
6630 // No body will be sent with other redirect types.
6632 // The response body contains a short hypertext note with a hyperlink to the new resource.
6633 // A meta refresh tag is also included to allow browsers to automatically redirect
6634 // the user to the resource even if they don't understand the redirect header.
6636 // See RFC 2616 section 10.3 for more information on redirection.
6637 BOOL
Redirect(__in LPCSTR szUrl
, __in HTTP_REDIRECT statusCode
=HTTP_REDIRECT_MOVED
, __in BOOL bSendBody
=TRUE
) throw(...)
6640 LPCSTR szBody
= NULL
;
6642 (HTTP_REDIRECT_MOVED
== statusCode
|| HTTP_REDIRECT_FOUND
== statusCode
||
6643 HTTP_REDIRECT_SEE_OTHER
== statusCode
|| HTTP_REDIRECT_TEMPORARY_REDIRECT
== statusCode
))
6647 ATLENSURE(szUrl
!=NULL
);
6651 "<meta http-equiv=\"refresh\" content=\"0; url=%s\">\r\n"
6653 "<body>Please use the following link to access this resource:"
6654 " <a href=\"%s\">%s</a>\r\n"
6657 szUrl
, szUrl
, szUrl
);
6663 szBody
= (LPCSTR
) strBody
;
6665 return Redirect(szUrl
, szBody
, statusCode
);
6668 // Call this function to redirect the client to a different resource.
6670 // Returns TRUE on success, FALSE on failure.
6672 // szURL A nul-terminated string specifying the resource the client should navigate to.
6674 // szBody A nul-terminated string containing the body of the response to be sent to the client.
6676 // statusCode An HTTP status code from the HTTP_REDIRECT enumeration describing the reason
6677 // for the redirection.
6679 // This function allows (and RFC 2616 encourages) a response body to be sent
6680 // with the following redirect types:
6681 // HTTP_REDIRECT_MOVED
6682 // HTTP_REDIRECT_FOUND
6683 // HTTP_REDIRECT_SEE_OTHER
6684 // HTTP_REDIRECT_TEMPORARY_REDIRECT
6685 // No body will be sent with other redirect types.
6687 // The response body should contain a short hypertext note with a hyperlink to the new resource.
6688 // You can include a meta refresh tag to allow browsers to automatically redirect
6689 // the user to the resource even if they don't understand the redirect header.
6691 // See RFC 2616 section 10.3 for more information on redirection.
6692 BOOL
Redirect(__in LPCSTR szUrl
, __in LPCSTR szBody
, __in HTTP_REDIRECT statusCode
=HTTP_REDIRECT_MOVED
) throw()
6694 SetStatusCode(statusCode
);
6695 AppendHeader("Location", szUrl
);
6699 if (!SendHeadersInternal())
6708 (HTTP_REDIRECT_MOVED
== statusCode
|| HTTP_REDIRECT_FOUND
== statusCode
||
6709 HTTP_REDIRECT_SEE_OTHER
== statusCode
|| HTTP_REDIRECT_TEMPORARY_REDIRECT
== statusCode
))
6717 // Call this function to append a header to the collection of HTTP headers managed by this object.
6719 // szName A nul-teminated string containing the name of the HTTP header.
6721 // szValue A nul-teminated string containing the value of the HTTP header.
6722 BOOL
AppendHeader(__in LPCSTR szName
, __in_opt LPCSTR szValue
) throw()
6728 bRet
= m_headers
.Add(szName
, szValue
);
6737 // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
6739 // pCookie A pointer to a CCookie object describing the cookie to be sent to the client.
6740 BOOL
AppendCookie(__in
const CCookie
*pCookie
)
6743 return AppendCookie((const CCookie
&)*pCookie
);
6746 // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
6748 // cookie A reference to a CCookie object describing the cookie to be sent to the client.
6749 BOOL
AppendCookie(__in
const CCookie
& cookie
) throw()
6751 CHAR szCookie
[ATL_MAX_COOKIE_LEN
];
6752 DWORD dwBuffSize
= ATL_MAX_COOKIE_LEN
;
6754 bRet
= cookie
.Render(szCookie
, &dwBuffSize
);
6759 bRet
= m_headers
.Add("Set-Cookie", szCookie
);
6768 if (!bRet
&& dwBuffSize
> 0 && dwBuffSize
+1 > dwBuffSize
) //static buffer wasn't big enough.
6770 //We'll have to try dynamically allocating it
6772 CAutoVectorPtr
<CHAR
> sz
;
6773 if (sz
.Allocate(dwBuffSize
+1))
6775 DWORD dwSizeNew
= dwBuffSize
+ 1;
6776 if (cookie
.Render(sz
, &dwSizeNew
))
6780 bRet
= m_headers
.Add("Set-Cookie", (const char *) sz
);
6792 // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
6794 // szName A nul-terminated string containing the name of the cookie to be sent to the client.
6796 // szValue A nul-terminated string containing the value of the cookie to be sent to the client.
6797 BOOL
AppendCookie(__in LPCSTR szName
, __in_opt LPCSTR szValue
) throw()
6803 CCookie
c(szName
, szValue
);
6804 bRet
= AppendCookie(c
);
6813 // Call this function to add a Set-Cookie header that removes a cookie value
6814 // to the collection of HTTP headers managed by this object.
6816 // szName A nul-terminated string containing the name of the cookie to be deleted
6817 BOOL
DeleteCookie(__in LPCSTR szName
) throw()
6823 CCookie
cookie(szName
, "");
6824 bRet
=cookie
.SetMaxAge(0);
6827 bRet
= AppendCookie(cookie
);
6838 // Call this function to clear the collection of HTTP response headers maintained by this object.
6840 // Note that clearing the headers includes removing all cookies associated with the response
6841 // object. Cookies are sent to the client as Set-Cookie HTTP headers.
6842 void ClearHeaders() throw()
6844 m_headers
.RemoveAll();
6847 // Call this function to clear theresponse buffer without sending the contents to the client.
6848 // If you need to empty the buffer but you do want the current contents sent to the client, call Flush instead.
6849 void ClearContent() throw()
6851 m_strContent
.Empty();
6854 // Call this function to send the current headers associated with this object to the client.
6856 // Returns TRUE on success, FALSE on failure.
6858 // The response headers are sent to the client using the current status code for the
6859 // response object. See SetStatusCode and GetStatusCode.
6860 BOOL
SendHeadersInternal(__in BOOL fKeepConn
=FALSE
)
6865 ATLENSURE(m_spServerContext
!= NULL
);
6867 CStringA strHeaders
;
6868 RenderHeaders(strHeaders
);
6873 if (m_nStatusCode
== 200)
6875 bRet
= m_spServerContext
->SendResponseHeader(strHeaders
, "200 OK", fKeepConn
);
6878 m_bHeadersSent
= TRUE
;
6883 CFixedStringT
<CStringA
, 256> strStatus
;
6884 CDefaultErrorProvider prov
;
6885 GetStatusHeader(strStatus
, m_nStatusCode
, SUBERR_NONE
, &prov
);
6886 bRet
= m_spServerContext
->SendResponseHeader(strHeaders
, strStatus
, fKeepConn
);
6889 m_bHeadersSent
= TRUE
;
6899 // Call this function to get a string containing all the HTTP headers associated with
6900 // this object in a format suitable for sending to a client.
6902 // strHeaders A CStringA reference to which will be appended the HTTP headers.
6903 void RenderHeaders(CStringA
& strHeaders
) throw()
6907 strHeaders
.Preallocate(::ATL::AtlMultiplyThrow(m_headers
.GetSize(),64));
6908 for (int i
=0; i
<m_headers
.GetSize(); i
++)
6910 strHeaders
+= m_headers
.GetKeyAt(i
);
6911 strHeaders
.Append(": ", sizeof(": ")-1);
6912 strHeaders
+= m_headers
.GetValueAt(i
);
6913 strHeaders
.Append("\r\n", sizeof("\r\n")-1);
6915 strHeaders
.Append("\r\n", sizeof("\r\n")-1);
6922 // Call this function to empty the response buffer and send its current
6923 // contents to the client.
6925 // Returns S_OK on success, or an error HRESULT on failure.
6926 HRESULT
FlushStream()
6929 return AtlHresultFromLastError();
6933 // Call this function to empty the response buffer and send its current
6934 // contents to the client.
6936 // Returns TRUE on success, or FALSE on failure.
6938 // Any headers that have been set in the response will be sent just before the
6939 // data is written to the client if no headers have been sent up to that point.
6940 BOOL
Flush(BOOL bFinal
=FALSE
) throw()
6942 if (!m_spServerContext
)
6949 // if the headers haven't been sent,
6952 if (!m_bHeadersSent
)
6954 char szProtocol
[ATL_URL_MAX_URL_LENGTH
];
6955 DWORD dwProtocolLen
= sizeof(szProtocol
);
6957 if (bFinal
&& m_bBufferOutput
&& m_dwBufferLimit
==ULONG_MAX
)
6959 if (m_spServerContext
->GetServerVariable("SERVER_PROTOCOL", szProtocol
, &dwProtocolLen
) &&
6960 !strcmp(szProtocol
, "HTTP/1.0"))
6961 AppendHeader("Connection", "Keep-Alive");
6962 Checked::itoa_s(m_strContent
.GetLength(), szProtocol
, _countof(szProtocol
), 10);
6963 AppendHeader("Content-Length", szProtocol
);
6964 bRet
= SendHeadersInternal(TRUE
);
6967 bRet
= SendHeadersInternal();
6969 if (m_bBufferOutput
)
6973 dwLen
= m_strContent
.GetLength();
6976 if (m_bSendOutput
&&
6977 m_spServerContext
->WriteClient((void *) (LPCSTR
) m_strContent
, &dwLen
) != TRUE
)
6979 m_strContent
.Empty();
6982 m_strContent
.Empty();
6993 // Call this function to clear the response object of any headers
6994 // and the contents of the buffer.
6995 void ClearResponse() throw()
6997 m_strContent
.Empty();
6998 m_headers
.RemoveAll();
7001 BOOL
AsyncPrep(__in BOOL fKeepConn
=FALSE
) throw()
7003 ATLASSUME(m_spServerContext
!= NULL
);
7005 return SendHeadersInternal(fKeepConn
);
7008 BOOL
AsyncFlush() throw()
7010 ATLASSUME(m_spServerContext
!= NULL
);
7012 BOOL bRet
= SendHeadersInternal();
7014 if (bRet
&& m_bBufferOutput
)
7018 dwLen
= m_strContent
.GetLength();
7023 if (m_spServerContext
->AsyncWriteClient((void *) (LPCSTR
) m_strContent
, &dwLen
) != TRUE
)
7038 BOOL
TransmitFile(__in HANDLE hFile
, __in LPCSTR szContentType
="text/plain") throw()
7040 ATLASSUME(m_spServerContext
!= NULL
);
7041 ATLASSERT(hFile
!= NULL
&& hFile
!= INVALID_HANDLE_VALUE
);
7043 SetContentType(szContentType
);
7045 if (m_strContent
.GetLength())
7049 BOOL bRet
= SendHeadersInternal();
7054 bRet
= m_spServerContext
->TransmitFile(hFile
, NULL
, NULL
, NULL
,
7055 0, 0, NULL
, 0, NULL
, 0, HSE_IO_ASYNC
| HSE_IO_NODELAY
);
7065 }; // class CHttpResponse
7069 #define ATLS_FLAG_NONE 0
7070 #define ATLS_FLAG_ASYNC 1 // handler might do some async handling
7072 // push_macro/pop_macro doesn't work in a template definition.
7073 #pragma push_macro("new")
7076 class PerThreadWrapper
: public CComObjectNoLock
<T
>
7079 void *operator new(size_t /*size*/, void *p
) throw()
7084 void operator delete(void * /*p*/) throw()
7088 STDMETHOD_(ULONG
, Release
)() throw()
7090 ULONG l
= InternalRelease();
7093 T
*pT
= static_cast<T
*>(this);
7094 ATLASSERT(pT
->m_spExtension
!= NULL
);
7095 CIsapiWorker
*pWorker
= pT
->m_spExtension
->GetThreadWorker();
7101 HeapFree(pWorker
->m_hHeap
, HEAP_NO_SERIALIZE
, this);
7108 template <typename THandler
>
7109 inline BOOL
CreateRequestHandlerSync(__in IIsapiExtension
*pExtension
, __deref_out_opt IUnknown
**ppOut
)
7111 ATLENSURE(pExtension
);
7114 CIsapiWorker
*pWorker
= pExtension
->GetThreadWorker();
7116 void *pv
= HeapAlloc(pWorker
->m_hHeap
, HEAP_NO_SERIALIZE
, sizeof(PerThreadWrapper
<THandler
>));
7120 PerThreadWrapper
<THandler
> *pHandler
= new(pv
) PerThreadWrapper
<THandler
>;
7121 *ppOut
= static_cast<IRequestHandler
*>(pHandler
);
7122 pHandler
->m_spExtension
= pExtension
;
7128 #pragma pop_macro("new")
7130 #define DECLARE_ASYNC_HANDLER() \
7131 static DWORD GetHandlerFlags() \
7133 return ATLS_FLAG_ASYNC; \
7135 DWORD GetAsyncFlags() \
7137 return ATLSRV_INIT_USEASYNC; \
7140 #define DECLARE_ASYNC_HANDLER_EX() \
7141 static DWORD GetHandlerFlags() \
7143 return ATLS_FLAG_ASYNC; \
7145 DWORD GetAsyncFlags() \
7147 return (ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX); \
7151 template <typename THandler
>
7152 class IRequestHandlerImpl
: public IRequestHandler
7155 HINSTANCE m_hInstHandler
;
7156 CComPtr
<IServiceProvider
> m_spServiceProvider
;
7157 CComPtr
<IHttpServerContext
> m_spServerContext
;
7158 CComPtr
<IIsapiExtension
> m_spExtension
;
7159 DWORD m_dwAsyncFlags
;
7161 IRequestHandlerImpl()
7162 :m_hInstHandler(NULL
)
7166 virtual ~IRequestHandlerImpl()
7170 HTTP_CODE
GetFlags(__out DWORD
*pdwStatus
)
7172 ATLASSERT(pdwStatus
);
7173 THandler
*pT
= static_cast<THandler
*>(this);
7175 *pdwStatus
= pT
->GetAsyncFlags();
7176 if (pT
->CachePage())
7177 *pdwStatus
|= ATLSRV_INIT_USECACHE
;
7180 if (*pdwStatus
& (ATLSRV_INIT_USEASYNC
|ATLSRV_INIT_USEASYNC_EX
))
7181 ATLASSERT(pT
->GetHandlerFlags() & ATLS_FLAG_ASYNC
);
7184 return HTTP_SUCCESS
;
7187 HTTP_CODE
InitializeHandler(__in AtlServerRequest
*pRequestInfo
, __in IServiceProvider
*pProvider
)
7189 ATLENSURE(pRequestInfo
!= NULL
);
7190 ATLENSURE(pProvider
!= NULL
);
7191 ATLENSURE(pRequestInfo
->hInstDll
!= NULL
);
7192 ATLENSURE(pRequestInfo
->pServerContext
!= NULL
);
7194 // Initialize our internal references to required services
7195 m_hInstHandler
= pRequestInfo
->hInstDll
;
7196 m_spServiceProvider
= pProvider
;
7197 m_spServerContext
= pRequestInfo
->pServerContext
;
7199 return HTTP_SUCCESS
;
7202 HTTP_CODE
InitializeChild(__in AtlServerRequest
*pRequestInfo
, __in IServiceProvider
*pProvider
, IHttpRequestLookup
* /*pLookup*/)
7204 return InitializeHandler(pRequestInfo
, pProvider
);
7207 void UninitializeHandler()
7211 HTTP_CODE
HandleRequest(
7212 AtlServerRequest
* /*pRequestInfo*/,
7213 IServiceProvider
* /*pServiceProvider*/)
7215 return HTTP_SUCCESS
;
7218 DWORD
GetAsyncFlags()
7220 return m_dwAsyncFlags
;
7223 void SetAsyncFlags(__in DWORD dwAsyncFlags
)
7225 ATLASSERT((dwAsyncFlags
& ~(ATLSRV_INIT_USEASYNC
|ATLSRV_INIT_USEASYNC_EX
)) == 0);
7226 m_dwAsyncFlags
= dwAsyncFlags
;
7234 static DWORD
GetHandlerFlags()
7236 return ATLS_FLAG_NONE
;
7239 // Used to create new instance of this object. A pointer to this
7240 // function is stored in the handler map in user's code.
7241 static BOOL
CreateRequestHandler(__in IIsapiExtension
*pExtension
, __deref_out_opt IUnknown
**ppOut
)
7243 ATLASSERT(ppOut
!= NULL
);
7249 if (THandler::GetHandlerFlags() & ATLS_FLAG_ASYNC
)
7251 THandler
*pHandler
= NULL
;
7252 ATLTRY(pHandler
= new CComObjectNoLock
<THandler
>);
7255 *ppOut
= static_cast<IRequestHandler
*>(pHandler
);
7256 pHandler
->m_spExtension
= pExtension
;
7261 if (!CreateRequestHandlerSync
<THandler
>(pExtension
, ppOut
))
7268 // Used to initialize the class
7269 // function is stored in the handler map in user's code.
7270 static BOOL
InitRequestHandlerClass(IHttpServerContext
*pContext
, IIsapiExtension
*pExt
)
7272 (pContext
); // unused
7277 // Used to uninitialize the class
7278 // function is stored in the handler map in user's code.
7279 static void UninitRequestHandlerClass()
7285 struct CRequestStats
7287 long m_lTotalRequests
;
7288 long m_lFailedRequests
;
7289 __int64 m_liTotalResponseTime
;
7290 long m_lAvgResponseTime
;
7291 long m_lCurrWaiting
;
7293 long m_lActiveThreads
;
7295 CRequestStats() throw()
7297 m_lTotalRequests
= 0;
7298 m_lFailedRequests
= 0;
7299 m_liTotalResponseTime
= 0;
7300 m_lAvgResponseTime
= 0;
7303 m_lActiveThreads
= 0;
7306 void RequestHandled(__in AtlServerRequest
*pRequestInfo
, __in BOOL bSuccess
)
7308 ATLENSURE(pRequestInfo
);
7310 InterlockedIncrement(&m_lTotalRequests
);
7312 InterlockedIncrement(&m_lFailedRequests
);
7316 #ifndef ATL_NO_MMSYS
7317 lTicks
= (long) (timeGetTime() - pRequestInfo
->dwStartTicks
);
7319 lTicks
= (long) (GetTickCount() - pRequestInfo
->dwStartTicks
);
7321 __int64 liTotalResponseTime
= Add64(&m_liTotalResponseTime
, lTicks
);
7322 long lAv
= (long) (liTotalResponseTime
/ m_lTotalRequests
);
7323 InterlockedExchange(&m_lAvgResponseTime
, lAv
);
7325 InterlockedDecrement(&m_lActiveThreads
);
7328 long GetTotalRequests() throw()
7330 return m_lTotalRequests
;
7333 long GetFailedRequests() throw()
7335 return m_lFailedRequests
;
7338 long GetAvgResponseTime() throw()
7340 return m_lAvgResponseTime
;
7343 void OnRequestReceived() throw()
7345 long nCurrWaiting
= InterlockedIncrement(&m_lCurrWaiting
);
7346 AtlInterlockedUpdateMax(nCurrWaiting
, &m_lMaxWaiting
);
7349 void OnRequestDequeued() throw()
7351 InterlockedDecrement(&m_lCurrWaiting
);
7352 InterlockedIncrement(&m_lActiveThreads
);
7355 long GetCurrWaiting() throw()
7357 return m_lCurrWaiting
;
7360 long GetMaxWaiting() throw()
7362 return m_lMaxWaiting
;
7365 long GetActiveThreads() throw()
7367 return m_lActiveThreads
;
7371 // not actually atomic, but it will add safely.
7373 // the returned value is not 100% guaranteed to be
7374 // correct, but should be correct more often than
7375 // just reading the __int64
7376 // the 2 cases where the return value will not be
7377 // a valid result value from an add are:
7378 // * multiple threads wrap the low part in rapid succession
7379 // * different threads are adding values with different signs
7381 // this is good enough for our use in RequestHandled -
7382 // we always add positive values and we shouldn't wrap 32-bits often
7383 inline __int64
Add64(__inout __int64
* pn
, long nAdd
)
7385 ATLENSURE(pn
!= NULL
);
7387 #if defined(_WIN64) && defined(_M_CEE)
7389 // Use System::Threading::Interlocked::Add because InterlockedExchangeAdd is an intrisinc not supported in managed code
7390 // with 64bits compilers.
7391 // Also, System::Threading::Interlocked::Add support 64bits operands.
7392 return System::Threading::Interlocked::Add(*pn
, nAdd
);
7396 long* pnLow
= (long*)(LPBYTE(pn
) + offsetof(LARGE_INTEGER
, LowPart
));
7397 long* pnHigh
= (long*)(LPBYTE(pn
) + offsetof(LARGE_INTEGER
, HighPart
));
7399 long nOrigLow
= InterlockedExchangeAdd(pnLow
, nAdd
);
7400 long nNewLow
= nOrigLow
+ nAdd
;
7401 long nNewHigh
= *pnHigh
;
7402 if (nAdd
> 0 && nNewLow
< nOrigLow
)
7403 nNewHigh
= InterlockedIncrement(pnHigh
);
7404 else if (nAdd
< 0 && nNewLow
> nOrigLow
)
7405 nNewHigh
= InterlockedDecrement(pnHigh
);
7408 li
.LowPart
= nNewLow
;
7409 li
.HighPart
= nNewHigh
;
7416 class CStdRequestStats
: public CRequestStats
7420 HRESULT
Initialize() throw()
7425 void Uninitialize() throw()
7430 #define PERF_REQUEST_OBJECT 100
7432 struct CPerfRequestStatObject
: public CPerfObject
,
7433 public CRequestStats
7435 DECLARE_PERF_CATEGORY_EX(PERF_REQUEST_OBJECT
, IDS_PERFMON_REQUEST
, IDS_PERFMON_REQUEST_HELP
, PERF_DETAIL_NOVICE
, 0, sizeof(CPerfRequestStatObject
), MAX_PATH
, -1);
7436 BEGIN_COUNTER_MAP(CPerfRequestStatObject
)
7437 DEFINE_COUNTER(m_lTotalRequests
, IDS_PERFMON_REQUEST_TOTAL
, IDS_PERFMON_REQUEST_TOTAL_HELP
, PERF_COUNTER_RAWCOUNT
, -1)
7438 DEFINE_COUNTER(m_lFailedRequests
, IDS_PERFMON_REQUEST_FAILED
, IDS_PERFMON_REQUEST_FAILED_HELP
, PERF_COUNTER_RAWCOUNT
, -1)
7439 DEFINE_COUNTER(m_lTotalRequests
, IDS_PERFMON_REQUEST_RATE
, IDS_PERFMON_REQUEST_RATE_HELP
, PERF_COUNTER_COUNTER
, -1)
7440 DEFINE_COUNTER(m_lAvgResponseTime
, IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME
, IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP
, PERF_COUNTER_RAWCOUNT
, -1)
7441 DEFINE_COUNTER(m_lCurrWaiting
, IDS_PERFMON_REQUEST_CURR_WAITING
, IDS_PERFMON_REQUEST_CURR_WAITING_HELP
, PERF_COUNTER_RAWCOUNT
, -1)
7442 DEFINE_COUNTER(m_lMaxWaiting
, IDS_PERFMON_REQUEST_MAX_WAITING
, IDS_PERFMON_REQUEST_MAX_WAITING_HELP
, PERF_COUNTER_RAWCOUNT
, -1)
7443 DEFINE_COUNTER(m_lActiveThreads
, IDS_PERFMON_REQUEST_ACTIVE_THREADS
, IDS_PERFMON_REQUEST_ACTIVE_THREADS
, PERF_COUNTER_RAWCOUNT
, -1)
7447 class CRequestPerfMon
: public CPerfMon
7450 BEGIN_PERF_MAP(_T("ATL Server:Request"))
7451 CHAIN_PERF_CATEGORY(CPerfRequestStatObject
)
7455 class CPerfMonRequestStats
7457 CRequestPerfMon m_PerfMon
;
7458 CPerfRequestStatObject
* m_pPerfObjectInstance
;
7459 CPerfRequestStatObject
* m_pPerfObjectTotal
;
7462 CPerfMonRequestStats() throw()
7464 m_pPerfObjectInstance
= NULL
;
7465 m_pPerfObjectTotal
= NULL
;
7468 HRESULT
Initialize() throw()
7472 m_pPerfObjectInstance
= NULL
;
7473 m_pPerfObjectTotal
= NULL
;
7475 hr
= m_PerfMon
.Initialize();
7478 CPerfLock
lock(&m_PerfMon
);
7479 if (FAILED(hr
= lock
.GetStatus()))
7484 HINSTANCE hInst
= _AtlBaseModule
.GetModuleInstance();
7485 WCHAR szName
[MAX_PATH
];
7486 if (GetModuleFileNameW(hInst
, szName
, MAX_PATH
) == 0)
7490 szName
[MAX_PATH
-1] = 0;
7492 hr
= m_PerfMon
.CreateInstanceByName(L
"_Total", &m_pPerfObjectTotal
);
7498 hr
= m_PerfMon
.CreateInstanceByName(szName
, &m_pPerfObjectInstance
);
7501 m_PerfMon
.ReleaseInstance(m_pPerfObjectTotal
);
7502 m_pPerfObjectTotal
= NULL
;
7510 void Uninitialize() throw()
7512 if (m_pPerfObjectInstance
!= NULL
)
7513 m_PerfMon
.ReleaseInstance(m_pPerfObjectInstance
);
7514 if (m_pPerfObjectTotal
!= NULL
)
7515 m_PerfMon
.ReleaseInstance(m_pPerfObjectTotal
);
7517 m_pPerfObjectInstance
= NULL
;
7518 m_pPerfObjectTotal
= NULL
;
7520 m_PerfMon
.UnInitialize();
7523 void RequestHandled(__in AtlServerRequest
*pRequestInfo
, __in BOOL bSuccess
) throw()
7525 if (m_pPerfObjectInstance
!= NULL
)
7526 m_pPerfObjectInstance
->RequestHandled(pRequestInfo
, bSuccess
);
7527 if (m_pPerfObjectTotal
!= NULL
)
7528 m_pPerfObjectTotal
->RequestHandled(pRequestInfo
, bSuccess
);
7531 long GetTotalRequests() throw()
7533 if (m_pPerfObjectInstance
!= NULL
)
7534 return m_pPerfObjectInstance
->GetTotalRequests();
7539 long GetFailedRequests() throw()
7541 if (m_pPerfObjectInstance
!= NULL
)
7542 return m_pPerfObjectInstance
->GetFailedRequests();
7547 long GetAvgResponseTime() throw()
7549 if (m_pPerfObjectInstance
!= NULL
)
7550 return m_pPerfObjectInstance
->GetAvgResponseTime();
7555 void OnRequestReceived() throw()
7557 if (m_pPerfObjectInstance
!= NULL
)
7558 m_pPerfObjectInstance
->OnRequestReceived();
7559 if (m_pPerfObjectTotal
!= NULL
)
7560 m_pPerfObjectTotal
->OnRequestReceived();
7563 void OnRequestDequeued() throw()
7565 if (m_pPerfObjectInstance
!= NULL
)
7566 m_pPerfObjectInstance
->OnRequestDequeued();
7567 if (m_pPerfObjectTotal
!= NULL
)
7568 m_pPerfObjectTotal
->OnRequestDequeued();
7571 long GetCurrWaiting() throw()
7573 if (m_pPerfObjectInstance
!= NULL
)
7574 return m_pPerfObjectInstance
->GetCurrWaiting();
7579 long GetMaxWaiting() throw()
7581 if (m_pPerfObjectInstance
!= NULL
)
7582 return m_pPerfObjectInstance
->GetMaxWaiting();
7587 long GetActiveThreads() throw()
7589 if (m_pPerfObjectInstance
!= NULL
)
7590 return m_pPerfObjectInstance
->GetActiveThreads();
7596 class CNoRequestStats
7602 HRESULT
Initialize() throw()
7607 void Uninitialize() throw()
7611 void RequestHandled(AtlServerRequest
* /*pRequestInfo*/, BOOL
/*bSuccess*/) throw()
7615 long GetTotalRequests() throw()
7620 long GetFailedRequests() throw()
7625 long GetAvgResponseTime() throw()
7630 void OnRequestReceived() throw()
7634 void OnRequestDequeued() throw()
7638 long GetCurrWaiting() throw()
7643 long GetMaxWaiting() throw()
7648 long GetActiveThreads() throw()
7654 struct ATLServerDllInfo
7656 GETATLHANDLERBYNAME pfnGetHandler
;
7657 UNINITIALIZEATLHANDLERS pfnUninitHandlers
;
7658 INITIALIZEATLHANDLERS pfnInitHandlers
;
7659 IIsapiExtension
*pExtension
;
7660 IHttpServerContext
*pContext
;
7666 struct DllInfo
: public ATLServerDllInfo
7668 DllInfo
& operator=(__in
const DllInfo
& right
) throw()
7672 pfnGetHandler
= right
.pfnGetHandler
;
7673 pfnUninitHandlers
= right
.pfnUninitHandlers
;
7674 pfnInitHandlers
= right
.pfnInitHandlers
;
7675 pExtension
= right
.pExtension
;
7676 pContext
= right
.pContext
;
7682 BOOL
Add(__in HINSTANCE hInst
, __out DllInfo
*pInfo
) throw(...)
7684 ATLENSURE(pInfo
!=NULL
);
7685 pInfo
->pfnInitHandlers
= (INITIALIZEATLHANDLERS
) GetProcAddress(hInst
, ATLS_FUNCID_INITIALIZEHANDLERS
);
7687 pInfo
->pfnGetHandler
= (GETATLHANDLERBYNAME
) GetProcAddress(hInst
, ATLS_FUNCID_GETATLHANDLERBYNAME
);
7688 if (!pInfo
->pfnGetHandler
)
7691 pInfo
->pfnUninitHandlers
= (UNINITIALIZEATLHANDLERS
) GetProcAddress(hInst
, ATLS_FUNCID_UNINITIALIZEHANDLERS
);
7693 if (pInfo
->pfnInitHandlers
)
7695 pInfo
->pfnInitHandlers(pInfo
->pContext
, pInfo
->pExtension
);
7696 pInfo
->pContext
= NULL
; // won't be valid after this call
7702 void Remove(HINSTANCE
/*hInst*/, __in DllInfo
*pInfo
) throw(...)
7704 ATLENSURE(pInfo
!=NULL
);
7705 if (pInfo
->pfnUninitHandlers
)
7706 (*pInfo
->pfnUninitHandlers
)();
7711 inline bool operator==(__in
const CDllCachePeer::DllInfo
& left
, __in
const CDllCachePeer::DllInfo
& right
) throw()
7713 return ( (left
.pfnGetHandler
== right
.pfnGetHandler
) &&
7714 (left
.pfnUninitHandlers
== right
.pfnUninitHandlers
) &&
7715 (left
.pfnInitHandlers
== right
.pfnInitHandlers
) &&
7716 (left
.pExtension
== right
.pExtension
) &&
7717 (left
.pContext
== right
.pContext
)
7723 // Helper function to impersonate the client
7724 // on the current thread
7725 inline BOOL
AtlImpersonateClient(__in IHttpServerContext
*pServerContext
)
7727 ATLENSURE(pServerContext
);
7729 // impersonate the calling client on the current thread
7733 if (!pServerContext
->GetImpersonationToken(&hToken
))
7741 if (!SetThreadToken(NULL
, hToken
))
7746 // Helper class to set the thread impersonation token
7747 // This is mainly used internally to ensure that we
7748 // don't forget to revert to the process impersonation
7750 class CSetThreadToken
7753 CSetThreadToken() : m_bShouldRevert(FALSE
) {}
7755 BOOL
Initialize(__in AtlServerRequest
*pRequestInfo
)
7757 ATLENSURE(pRequestInfo
);
7758 m_bShouldRevert
= AtlImpersonateClient(pRequestInfo
->pServerContext
);
7759 return m_bShouldRevert
;
7762 ~CSetThreadToken() throw()
7764 if( m_bShouldRevert
&& !RevertToSelf() )
7766 _AtlRaiseException( (DWORD
)EXCEPTION_ACCESS_VIOLATION
);
7770 BOOL m_bShouldRevert
;
7774 // push_macro/pop_macro doesn't work in a template definition.
7775 #pragma push_macro("new")
7779 //Base is the user's class that derives from CComObjectRoot and whatever
7780 //interfaces the user wants to support on the object
7781 template <class Base
>
7782 class _CComObjectHeapNoLock
: public Base
7785 typedef Base _BaseClass
;
7788 _CComObjectHeapNoLock(void* = NULL
, HANDLE hHeap
= NULL
)
7792 // Set refcount to -(LONG_MAX/2) to protect destruction and
7793 // also catch mismatched Release in debug builds
7794 ~_CComObjectHeapNoLock()
7796 m_dwRef
= -(LONG_MAX
/2);
7798 #ifdef _ATL_DEBUG_INTERFACES
7799 _AtlDebugInterfacesModule
.DeleteNonAddRefThunk(_GetRawUnknown());
7803 //If InternalAddRef or InternalRelease is undefined then your class
7804 //doesn't derive from CComObjectRoot
7805 STDMETHOD_(ULONG
, AddRef
)() throw() {return InternalAddRef();}
7806 STDMETHOD_(ULONG
, Release
)() throw()
7808 ULONG l
= InternalRelease();
7811 HANDLE hHeap
= m_hHeap
;;
7812 this->~_CComObjectHeapNoLock();
7815 HeapFree(hHeap
, 0, this);
7820 //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
7821 STDMETHOD(QueryInterface
)(REFIID iid
, void ** ppvObject
) throw()
7822 {return _InternalQueryInterface(iid
, ppvObject
);}
7824 static HRESULT WINAPI
CreateInstance(_CComObjectHeapNoLock
<Base
>** pp
, HANDLE hHeap
) throw();
7829 template <class Base
>
7830 HRESULT WINAPI _CComObjectHeapNoLock
<Base
>::CreateInstance(__deref_out _CComObjectHeapNoLock
<Base
>** pp
, __in HANDLE hHeap
) throw()
7832 ATLASSERT(pp
!= NULL
);
7837 HRESULT hRes
= E_OUTOFMEMORY
;
7838 // Allocate a fixed block size to avoid fragmentation
7839 void *pv
= HeapAlloc(hHeap
, HEAP_ZERO_MEMORY
,
7840 __max(sizeof(AtlServerRequest
), sizeof(_CComObjectHeapNoLock
<CServerContext
>)));
7845 #pragma warning(push)
7846 #pragma warning(disable: 6280)
7847 /* prefast noise VSW 493229 */
7848 _CComObjectHeapNoLock
<Base
>* p
= new(pv
) _CComObjectHeapNoLock
<CServerContext
>(NULL
, hHeap
);
7849 #pragma warning(pop)
7852 p
->InternalFinalConstructAddRef();
7853 hRes
= p
->_AtlInitialConstruct();
7854 if (SUCCEEDED(hRes
))
7855 hRes
= p
->FinalConstruct();
7856 if (SUCCEEDED(hRes
))
7857 hRes
= p
->_AtlFinalConstruct();
7858 p
->InternalFinalConstructRelease();
7861 p
->~_CComObjectHeapNoLock();
7862 #pragma warning(push)
7863 #pragma warning(disable: 6280)
7864 /* prefast noise VSW 493229 */
7865 HeapFree(hHeap
, 0, p
);
7866 #pragma warning(pop)
7873 inline CServerContext
* CreateServerContext(__in HANDLE hRequestHeap
) throw()
7875 _CComObjectHeapNoLock
<CServerContext
>* pContext
;
7876 _CComObjectHeapNoLock
<CServerContext
>::CreateInstance(&pContext
, hRequestHeap
);
7879 #pragma pop_macro("new")
7881 // _AtlGetHandlerName
7882 // get handler name from stencil file. Ignore all server side comments
7883 // szFileName - the file from which to extract the handler name
7884 // szHandlerName - buffer into which handler name will be copied,
7885 // it is assumed to be of size MAX_PATH+ATL_MAX_HANDLER_NAME+2
7886 inline HTTP_CODE
_AtlGetHandlerName(__in LPCSTR szFileName
, __out_ecount(MAX_PATH
+ATL_MAX_HANDLER_NAME
+2) LPSTR szHandlerName
)
7888 ATLASSERT(szFileName
);
7889 ATLENSURE(szHandlerName
);
7891 szHandlerName
[0] = '\0';
7897 hr
= cfFile
.Create(CA2CTEX
<MAX_PATH
>(szFileName
), GENERIC_READ
, FILE_SHARE_READ
, OPEN_EXISTING
);
7898 if (FAILED(hr
) || cfFile
.m_h
== NULL
|| GetFileType(cfFile
.m_h
) != FILE_TYPE_DISK
)
7900 if (hr
== AtlHresultFromWin32(ERROR_FILE_NOT_FOUND
))
7901 return HTTP_NOT_FOUND
;
7902 else if (hr
== AtlHresultFromWin32(ERROR_ACCESS_DENIED
))
7903 return HTTP_UNAUTHORIZED
;
7905 return AtlsHttpError(500, IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL
);
7910 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM
); // CA2CTEX threw
7913 HTTP_CODE hcErr
= HTTP_SUCCESS
;
7915 LPCSTR szHandler
= "handler";
7916 LPCSTR pszHandlerPos
= NULL
;
7917 LPSTR pszHandlerName
= szHandlerName
;
7919 LPSTR szCurly
= NULL
;
7920 LPSTR pszBuf
= NULL
;
7921 bool bInQuote
= false;
7924 // 0 = default/unknown
7926 // 2 = have "{{" -- skip spaces
7927 // 3 = have "{{" -- check "handler"
7928 // 4 = have "handler" -- skip spaces
7929 // 5 = have "handler" -- get name
7930 // 6 = scan until first '}'
7931 // 7 = better be '}'
7937 hr
= cfFile
.Read(szBuf
, _countof(szBuf
)-1, dwRead
);
7940 return AtlsHttpError(500, ISE_SUBERR_READFILEFAIL
); // failed reading
7943 szBuf
[dwRead
] = '\0';
7946 while (*pszBuf
&& nState
!= 8)
7951 // 0 = default/unknown
7953 // look for the first curly
7954 szCurly
= strchr(pszBuf
, '{');
7957 // skip to the end of the buffer
7958 pszBuf
= szBuf
+dwRead
-1;
7974 nState
= 0; // if the next character is not a '{', start over
7978 if (!isspace(static_cast<unsigned char>(*pszBuf
)))
7980 pszHandlerPos
= szHandler
;
7986 // 3 = partial handler "h..."
7987 if (*pszBuf
!= *pszHandlerPos
)
7989 // not a handler, skip tag
7995 if (!*pszHandlerPos
) // at the end of the "handler" part
8000 // 4 = have "handler" -- skip spaces
8001 if (!isspace(static_cast<unsigned char>(*pszBuf
)))
8003 if (*pszBuf
== '\"')
8015 // 5 = have "handler" -- get name
8016 if (isspace(static_cast<unsigned char>(*pszBuf
)) && !bInQuote
)
8018 if (*(pszHandlerName
-1) != '/')
8020 // end of the name -- jump to getting the first '}'
8028 else if (*pszBuf
== '}')
8030 // end of the name -- jump to getting the second '}'
8033 else if (*pszBuf
== '\"')
8047 // ensure we don't overwrite the buffer
8048 if (pszHandlerName
-szHandlerName
>= MAX_PATH
+ATL_MAX_HANDLER_NAME_LEN
+1)
8055 *pszHandlerName
++ = *pszBuf
;
8060 // 6 = scan until first '}'
8065 // 7 = better be '}'
8068 hcErr
= AtlsHttpError(500, ISE_SUBERR_BAD_HANDLER_TAG
);
8078 return HTTP_INTERNAL_SERVER_ERROR
;
8083 } while (dwRead
!= 0 && nState
!= 8);
8085 *pszHandlerName
= '\0';
8090 // _AtlCrackHandler cracks a request path of the form dll_path/handler_name into its
8091 // constituent parts
8092 // szHandlerDllName - the full handler path of the form "dll_path/handler_name"
8093 // szDllPath - the DLL path (should be of length MAX_PATH)
8094 // szHandlerName - the handler name (should be of length ATL_MAX_HANDLER_NAME_LEN+1)
8096 inline BOOL
_AtlCrackHandler(
8097 __in_z LPCSTR szHandlerDllName
,
8098 __out_ecount_part(*pdwszDllPathLen
,*pdwszDllPathLen
) LPSTR szDllPath
,
8099 __inout LPDWORD pdwDllPathLen
,
8100 __out_ecount_part(*pdwHandlerNameLen
,*pdwHandlerNameLen
) LPSTR szHandlerName
,
8101 __inout LPDWORD pdwHandlerNameLen
)
8103 ATLENSURE( szHandlerDllName
!= NULL
);
8104 ATLASSERT( szDllPath
!= NULL
);
8105 ATLENSURE( pdwDllPathLen
!= NULL
);
8106 ATLASSERT( szHandlerName
!= NULL
);
8107 ATLASSERT( pdwHandlerNameLen
!= NULL
);
8111 // skip leading spaces
8112 while (*szHandlerDllName
&& isspace(static_cast<unsigned char>(*szHandlerDllName
)))
8115 // get the handler name
8116 LPCSTR szSlash
= strchr(szHandlerDllName
, '/');
8117 LPCSTR szEnd
= NULL
;
8118 LPCSTR szSlashEnd
= NULL
;
8120 // if it is of the form <dll_name>/<handler_name>
8125 // skip trailing spaces on <dll_name>
8126 while (szEnd
>szHandlerDllName
&& isspace(static_cast<unsigned char>(*(szEnd
-1))))
8130 // skip leading whitespace
8131 while (*szSlash
&& isspace(static_cast<unsigned char>(*szSlash
)))
8134 // right trim szSlash;
8135 szSlashEnd
= szSlash
;
8136 while (*szSlashEnd
&& !isspace(static_cast<unsigned char>(*szSlashEnd
)))
8139 else // only the <dll_name>
8141 szSlash
= ATL_HANDLER_NAME_DEFAULT
;
8142 szSlashEnd
= szSlash
+sizeof(ATL_HANDLER_NAME_DEFAULT
)-1;
8144 // do it this way to handle paths with spaces
8145 // (e.g. "some path\subdirectory one\subdirectory two\dll_name.dll")
8146 szEnd
= szHandlerDllName
+strlen(szHandlerDllName
);
8148 // skip trailing spaces
8149 while (szEnd
>szHandlerDllName
&& isspace(static_cast<unsigned char>(*(szEnd
-1))))
8153 // if the dll path is quoted, strip the quotes
8154 if (*szHandlerDllName
== '\"' && *(szEnd
-1) == '\"' && szEnd
> szHandlerDllName
+2)
8160 if (*pdwDllPathLen
> (DWORD
)(szEnd
-szHandlerDllName
) && (szEnd
-szHandlerDllName
>= 0))
8162 Checked::memcpy_s(szDllPath
, *pdwDllPathLen
, szHandlerDllName
, szEnd
-szHandlerDllName
);
8163 szDllPath
[szEnd
-szHandlerDllName
] = '\0';
8164 *pdwDllPathLen
= (DWORD
)(szEnd
-szHandlerDllName
);
8168 *pdwDllPathLen
= (DWORD
)(szEnd
-szHandlerDllName
)+1;
8172 if (*pdwHandlerNameLen
> (DWORD
)(szSlashEnd
-szSlash
) && (szSlashEnd
-szSlash
>= 0))
8174 Checked::memcpy_s(szHandlerName
, *pdwHandlerNameLen
, szSlash
, (szSlashEnd
-szSlash
));
8175 szHandlerName
[szSlashEnd
-szSlash
] = '\0';
8176 *pdwHandlerNameLen
= (DWORD
)(szSlashEnd
-szSlash
);
8180 *pdwHandlerNameLen
= (DWORD
)(szSlashEnd
-szSlash
)+1;
8187 inline __checkReturn
__success(return==HTTP_SUCCESS
) HTTP_CODE
_AtlLoadRequestHandler(
8188 __in LPCSTR szDllPath
,
8189 __in LPCSTR szHandlerName
,
8190 __in IHttpServerContext
*pServerContext
,
8191 __out HINSTANCE
*phInstance
,
8192 __deref_out_opt IRequestHandler
**ppHandler
,
8193 __in IIsapiExtension
*pExtension
,
8194 __in IDllCache
*pDllCache
) throw(...)
8196 ATLENSURE(phInstance
!=NULL
);
8197 ATLENSURE(ppHandler
!=NULL
);
8198 ATLENSURE(pDllCache
!=NULL
);
8202 ATLServerDllInfo DllInfo
;
8203 DllInfo
.pExtension
= pExtension
;
8204 DllInfo
.pContext
= pServerContext
;
8205 if (!IsFullPathA(szDllPath
))
8207 CHAR szFileName
[MAX_PATH
];
8208 if (!GetScriptFullFileName(szDllPath
, szFileName
, pServerContext
))
8213 *phInstance
= pDllCache
->Load(szFileName
, (void *)&DllInfo
);
8217 *phInstance
= pDllCache
->Load(szDllPath
, (void *)&DllInfo
);
8221 ATLTRACE( "LoadLibrary failed: '%s' with error: %d\r\n", szDllPath
, GetLastError() );
8222 return AtlsHttpError(500, ISE_SUBERR_LOADLIB
);
8225 CComPtr
<IUnknown
> spUnk
;
8227 if (!DllInfo
.pfnGetHandler
||
8228 !DllInfo
.pfnGetHandler(szHandlerName
, pExtension
, &spUnk
) ||
8229 FAILED(spUnk
->QueryInterface(ppHandler
)))
8231 pDllCache
->Free(*phInstance
);
8233 return AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND
);
8236 return HTTP_SUCCESS
;
8237 } // _AtlLoadRequestHandler
8240 class CTransferServerContext
: public CComObjectRootEx
<CComMultiThreadModel
>,
8241 public CWrappedServerContext
8244 char m_szFileName
[MAX_PATH
];
8245 char m_szQueryString
[ATL_URL_MAX_PATH_LENGTH
+1];
8247 IWriteStream
*m_pStream
;
8249 BEGIN_COM_MAP(CTransferServerContext
)
8250 COM_INTERFACE_ENTRY(IHttpServerContext
)
8253 CTransferServerContext() throw()
8258 BOOL
Initialize(__in CTransferServerContext
*pOtherContext
)
8260 ATLENSURE(pOtherContext
!=NULL
);
8261 return Initialize(pOtherContext
->m_strUrl
, pOtherContext
->m_pStream
, pOtherContext
->m_spParent
);
8264 BOOL
Initialize(__in LPCSTR szUrl
, __in IWriteStream
*pStream
, __in IHttpServerContext
*pParent
) throw()
8266 m_pStream
= pStream
;
8267 m_spParent
= pParent
;
8271 m_strUrl
= szUrl
; // we store the URL in case we need to initialize another context from this context
8278 long nUrlLen
= m_strUrl
.GetLength();
8279 m_szFileName
[0] = '\0';
8281 if (!IsFullPathA(szUrl
))
8283 DWORD dwLen
= MAX_PATH
;
8287 bRet
= m_spParent
->GetServerVariable(
8288 "APPL_PHYSICAL_PATH",
8303 // check for query params
8304 LPCSTR szMark
= strchr(szUrl
, '?');
8307 size_t nPathLen
= szMark
- szUrl
;
8310 if ((nPathLen
>= 0) && (nPathLen
< MAX_PATH
))
8312 nLen
= strlen(m_szFileName
) + nPathLen
;
8313 if (nLen
< MAX_PATH
)
8315 #pragma warning(push)
8316 #pragma warning(disable: 22008)
8317 /* Prefast false warning about unbound nPathLen in the below fragment -
8318 we already have necessary checks few lines above
8320 if (m_szFileName
[0])
8322 Checked::strcat_s(m_szFileName
, MAX_PATH
-nLen
, szUrl
);
8326 Checked::memcpy_s(m_szFileName
, MAX_PATH
, szUrl
, nPathLen
);
8327 m_szFileName
[nPathLen
] = '\0';
8329 #pragma warning(pop)
8333 return FALSE
; // path would overwrite buffer
8338 return FALSE
; // path would overwrite buffer
8341 // save query params
8342 nLen
= strlen(szMark
+ 1);
8343 if (nLen
< ATL_URL_MAX_PATH_LENGTH
)
8345 Checked::strcpy_s(m_szQueryString
, ATL_URL_MAX_PATH_LENGTH
, szMark
+1);
8349 return FALSE
; // url would overwrite buffer
8355 size_t nLen
= strlen(m_szFileName
) + nUrlLen
;
8356 if (nLen
< MAX_PATH
)
8358 if (m_szFileName
[0])
8360 Checked::strcat_s(m_szFileName
, MAX_PATH
-nLen
, szUrl
);
8364 Checked::strcpy_s(m_szFileName
, MAX_PATH
, szUrl
);
8369 return FALSE
; // path would be too long
8371 m_szQueryString
[0] = '\0';
8376 BOOL
WriteClient(__in_bcount(*pdwBytes
) void *pvBuffer
, __inout DWORD
*pdwBytes
)
8378 ATLENSURE(m_pStream
!= NULL
);
8383 m_pStream
->WriteStream((LPCSTR
) pvBuffer
, *pdwBytes
, pdwBytes
);
8390 return SUCCEEDED(hr
);
8393 LPCSTR
GetQueryString()
8395 ATLASSUME(m_spParent
);
8396 return m_szQueryString
;
8399 LPCSTR
GetScriptPathTranslated()
8401 ATLASSUME(m_spParent
);
8402 return m_szFileName
;
8405 LPCSTR
GetPathTranslated()
8407 ATLASSUME(m_spParent
);
8408 return m_szFileName
;
8411 // Asynchronous writes will not work properly in a child handler
8412 BOOL
AsyncWriteClient(void * /*pvBuffer*/, DWORD
* /*pdwBytes*/)
8418 // These next few methods are to protect against attempting to parse form data twice
8419 // We tell the new handler that it was a GET request
8420 LPCSTR
GetRequestMethod()
8422 ATLASSUME(m_spParent
);
8426 // The handler should not query these methods -- they are only useful if attempting to
8427 // parse form data, which is not allowed in child handlers.
8428 BOOL
ReadClient(void * /*pvBuffer*/, DWORD
* /*pdwSize*/)
8433 BOOL
AsyncReadClient(void * /*pvBuffer*/, DWORD
* /*pdwSize*/)
8438 DWORD
GetTotalBytes()
8444 DWORD
GetAvailableBytes()
8450 BYTE
*GetAvailableData()
8456 LPCSTR
GetContentType()
8463 class CAllocContextBase
8466 virtual HTTP_CODE
Alloc(IHttpServerContext
**ppNewContext
) = 0;
8469 ATL_NOINLINE
inline HTTP_CODE
_AtlRenderInclude(
8470 __in IHttpServerContext
*pServerContextNew
,
8471 __in LPCSTR szFileName
,
8472 __in LPCSTR szQueryParams
,
8473 __in WORD wCodePage
,
8474 __in CAllocContextBase
*pAllocContext
,
8475 __in IServiceProvider
*pServiceProvider
,
8476 __in_opt IHttpRequestLookup
*pLookup
,
8477 __inout_opt CStencilState
* pState
= NULL
)
8479 ATLENSURE(pServiceProvider
!=NULL
);
8480 AtlServerRequest
* pRequestInfo
= NULL
;
8481 HTTP_CODE hcErr
= HTTP_SUCCESS
;
8483 // get a pointer to the ISAPI extension
8484 CComPtr
<IIsapiExtension
> spExtension
;
8485 if (S_OK
!= pServiceProvider
->QueryInterface(&spExtension
) ||
8488 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED
);
8491 // get a pointer to the extension's dll cache
8492 CComPtr
<IDllCache
> spDllCache
;
8493 if (S_OK
!= pServiceProvider
->QueryService(__uuidof(IDllCache
),
8494 __uuidof(IDllCache
),
8495 (void**)&spDllCache
) ||
8498 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED
);
8502 bool bAsyncAllowed
= false;
8504 if (pState
&& pState
->pIncludeInfo
)
8506 pRequestInfo
= pState
->pIncludeInfo
;
8507 pState
->pIncludeInfo
= NULL
;
8509 bAsyncAllowed
= true;
8514 ATLASSERT(spDllCache
);
8515 ATLASSERT(spExtension
);
8517 pRequestInfo
= spExtension
->CreateRequest();
8518 if (pRequestInfo
== NULL
)
8520 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM
);
8523 pRequestInfo
->dwRequestState
= ATLSRV_STATE_BEGIN
;
8524 pRequestInfo
->dwRequestType
= ATLSRV_REQUEST_STENCIL
;
8525 pRequestInfo
->pDllCache
= spDllCache
;
8526 pRequestInfo
->pExtension
= spExtension
;
8527 pRequestInfo
->pServerContext
= pServerContextNew
;
8528 if (pState
&& pState
->pParentInfo
)
8530 pRequestInfo
->pUserData
= pState
->pParentInfo
->pUserData
;
8533 // Extract the file extension of the included file by searching
8534 // for the first '.' from the right.
8535 // Can't use _tcsrchr because we have to use the stencil's codepage
8536 LPCSTR szDot
= NULL
;
8537 LPCSTR szMark
= szFileName
;
8538 ATLENSURE(szMark
!=NULL
);
8544 LPCSTR szNext
= CharNextExA(wCodePage
, szMark
, 0);
8545 if (szNext
== szMark
)
8548 pRequestInfo
->pServerContext
= NULL
;
8549 spExtension
->FreeRequest(pRequestInfo
);
8555 if (szDot
&& AsciiStricmp(szDot
, c_AtlSRFExtension
) == 0)
8557 hcErr
= spExtension
->LoadDispatchFile(szFileName
, pRequestInfo
);
8560 pRequestInfo
->pServerContext
= NULL
;
8561 spExtension
->FreeRequest(pRequestInfo
);
8565 else if (szDot
&& AsciiStricmp(szDot
, c_AtlDLLExtension
) == 0)
8567 // Get the handler name if they used the asdf.dll?Handler=Default notation
8568 char szHandlerName
[ATL_MAX_HANDLER_NAME_LEN
+1] = { '\0' };
8570 LPCSTR szStart
= strstr(szQueryParams
, "Handler=");
8572 ((szStart
== szQueryParams
) ||
8573 ((szStart
> szQueryParams
) && (*(szStart
-1) == '&'))))
8575 szStart
+= 8; // Skip past "Handler" and the "="
8576 LPCSTR szEnd
= strchr(szStart
, '&');
8579 Checked::memcpy_s(szHandlerName
, ATL_MAX_HANDLER_NAME_LEN
+1, szStart
, __min((szEnd
-szStart
), ATL_MAX_HANDLER_NAME_LEN
));
8580 szHandlerName
[__min((szEnd
-szStart
), ATL_MAX_HANDLER_NAME_LEN
)] = '\0';
8584 if (!SafeStringCopy(szHandlerName
, szStart
))
8586 // handler name too long
8587 pRequestInfo
->pServerContext
= NULL
;
8588 spExtension
->FreeRequest(pRequestInfo
);
8595 ATLASSERT( ATL_MAX_HANDLER_NAME_LEN
>= sizeof(ATL_HANDLER_NAME_DEFAULT
) );
8596 Checked::memcpy_s(szHandlerName
, ATL_MAX_HANDLER_NAME_LEN
+1, ATL_HANDLER_NAME_DEFAULT
, sizeof(ATL_HANDLER_NAME_DEFAULT
));
8599 pRequestInfo
->dwRequestType
= ATLSRV_REQUEST_DLL
;
8601 hcErr
= spExtension
->LoadRequestHandler(szFileName
, szHandlerName
, pRequestInfo
->pServerContext
,
8602 &pRequestInfo
->hInstDll
, &pRequestInfo
->pHandler
);
8605 pRequestInfo
->pServerContext
= NULL
;
8606 spExtension
->FreeRequest(pRequestInfo
);
8612 // unknown extension
8613 pRequestInfo
->pServerContext
= NULL
;
8614 spExtension
->FreeRequest(pRequestInfo
);
8615 return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED
);
8619 hcErr
= pRequestInfo
->pHandler
->GetFlags(&dwStatus
);
8623 pRequestInfo
->pHandler
->UninitializeHandler();
8624 pRequestInfo
->pServerContext
= NULL
;
8625 spExtension
->FreeRequest(pRequestInfo
);
8629 if (dwStatus
& (ATLSRV_INIT_USEASYNC
| ATLSRV_INIT_USEASYNC_EX
))
8632 bAsyncAllowed
= true;
8634 ATLENSURE(pAllocContext
!=NULL
);
8635 hcErr
= pAllocContext
->Alloc(&pRequestInfo
->pServerContext
);
8638 pRequestInfo
->pHandler
->UninitializeHandler();
8639 if (pRequestInfo
->pServerContext
== pServerContextNew
)
8641 pRequestInfo
->pServerContext
= NULL
;
8643 spExtension
->FreeRequest(pRequestInfo
);
8648 hcErr
= pRequestInfo
->pHandler
->InitializeChild(pRequestInfo
, pServiceProvider
, pLookup
);
8651 pRequestInfo
->pHandler
->UninitializeHandler();
8652 if (pRequestInfo
->pServerContext
== pServerContextNew
)
8654 pRequestInfo
->pServerContext
= NULL
;
8656 spExtension
->FreeRequest(pRequestInfo
);
8660 pRequestInfo
->pfnHandleRequest
= &IRequestHandler::HandleRequest
;
8667 ATLASSERT(pRequestInfo
->pfnHandleRequest
!= NULL
);
8668 hcErr
= (pRequestInfo
->pHandler
->*pRequestInfo
->pfnHandleRequest
)(pRequestInfo
, pServiceProvider
);
8671 // must use ATLSRV_INIT_USEASYNC to use ASYNC returns
8672 if (IsAsyncStatus(hcErr
))
8674 ATLASSERT(bAsyncAllowed
);
8678 if (IsAsyncStatus(hcErr
))
8680 ATLASSERT(pState
); // state is required for async
8681 if (IsAsyncContinueStatus(hcErr
))
8683 pState
->pIncludeInfo
= pRequestInfo
;
8684 pRequestInfo
->dwRequestState
= ATLSRV_STATE_CONTINUE
;
8686 else if (IsAsyncDoneStatus(hcErr
))
8688 pRequestInfo
->pHandler
->UninitializeHandler();
8689 if (pRequestInfo
->pServerContext
== pServerContextNew
)
8691 pRequestInfo
->pServerContext
= NULL
;
8693 spExtension
->FreeRequest(pRequestInfo
);
8698 pRequestInfo
->pHandler
->UninitializeHandler();
8699 if (pRequestInfo
->pServerContext
== pServerContextNew
)
8701 pRequestInfo
->pServerContext
= NULL
;
8703 spExtension
->FreeRequest(pRequestInfo
);
8709 hcErr
= AtlsHttpError(500, ISE_SUBERR_UNEXPECTED
);
8715 // CAllocTransferAsyncContext is an unsupported implementation detail, used
8716 // for implementing _AtlTransferRequest.
8717 class CAllocTransferAsyncContext
:
8718 public CAllocContextBase
8721 CAllocTransferAsyncContext(CTransferServerContext
*pInitialContext
):
8722 m_pInitialContext(pInitialContext
)
8726 HTTP_CODE
Alloc(IHttpServerContext
** ppNewContext
)
8729 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED
);
8730 *ppNewContext
= NULL
;
8732 CComObjectNoLock
<CTransferServerContext
>* pServerContext
= NULL
;
8733 ATLTRY(pServerContext
= new CComObjectNoLock
<CTransferServerContext
>);
8734 if (pServerContext
== NULL
)
8735 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM
);
8736 pServerContext
->Initialize(m_pInitialContext
);
8737 pServerContext
->AddRef();
8738 *ppNewContext
= pServerContext
;
8739 return HTTP_SUCCESS
;
8742 CTransferServerContext
*m_pInitialContext
;
8745 inline HTTP_CODE
_AtlTransferRequest(
8746 __in AtlServerRequest
*pRequest
,
8747 __in IServiceProvider
*pServiceProvider
,
8748 __in IWriteStream
*pWriteStream
,
8749 __in_opt IHttpRequestLookup
*pLookup
,
8750 __in LPCSTR szNewUrl
,
8751 __in WORD nCodePage
,
8752 __in
bool bContinueAfterProcess
,
8753 __inout_opt CStencilState
*pState
)
8755 CComObjectStackEx
<CTransferServerContext
> serverContext
;
8756 if (!serverContext
.Initialize(szNewUrl
, pWriteStream
, pRequest
->pServerContext
))
8757 return AtlsHttpError(500, 0);
8758 CAllocTransferAsyncContext
AsyncAllocObj(&serverContext
);
8759 HTTP_CODE hcErr
= _AtlRenderInclude(static_cast<IHttpServerContext
*>(&serverContext
),
8760 serverContext
.m_szFileName
,
8761 serverContext
.m_szQueryString
,
8767 if (hcErr
== HTTP_SUCCESS
&& bContinueAfterProcess
)
8769 return HTTP_SUCCESS_NO_PROCESS
;
8773 // This function is now deprecated. ATL is not using it anymore.
8775 inline ATL_DEPRECATED("Do not use this function.")
8776 void __cdecl
AtlsSecErrHandlerFunc(int /* nCode */, void * /* pv */)
8779 // a buffer overflow has occurred in your code
8784 // terminate process (safest thing to do)
8786 TerminateProcess( GetCurrentProcess(), 1 );
8790 // Class CIsapiExtension
8791 // The main ISAPI Extension implementation.
8792 // Template parameters
8793 // ThreadPoolClass: Specifies the thread pool that will be used by the
8794 // extension to queue incoming requests. CThreadPool is the
8795 // default and is declared and implemented in ATLUTIL.H. This class
8796 // templatizes on a worker thread class. The worker thread class
8797 // represents an abstraction of a thread that will be used to
8798 // process requests as they are dequeued from the pool's work queue.
8799 // You would change this parameter if you wanted to use a completely
8800 // different thread pool, or, more commonly, if you wanted to use
8801 // a different worker thread class. Request processing code can
8802 // access a pointer to the worker thread class, which allows the
8803 // request handling code to easily access per-thread data.
8804 // CRequestStatClass: Specifies the class to be used to track request statistics
8805 // CNoRequestStats is the default which is a noop class.
8806 // You would change this parameter to provide a class that will
8807 // track request statistics for you. ATL provides CStdRequestStats
8808 // and CPerfRequestStatObject but these classes should be used
8809 // with caution because they require interlocked operations to
8810 // keep track of request statistics which can affect server performance.
8811 // HttpUserErrorTextProvider: This class provides error text messages
8812 // and headers, including resource IDs of error messages to the
8813 // isapi extension's error handling functions. You would change this
8814 // parameter if you wanted to provide your own error headers and/or
8815 // messages in response to error encountered during request processing.
8816 // CPageCacheStats, CStencilCacheStats: These two classes are used to keep
8817 // statistics about the page and stencil caches. You could change these
8818 // paramters if you wanted to track statistics for these caches. ATL
8819 // provides CPerfStatClass and CStdStatClass to store the stat data but
8820 // using these classes can affect server performance because they use
8821 // interlocked operations internally to store the data.
8822 template < class ThreadPoolClass
=CThreadPool
<CIsapiWorker
>,
8823 class CRequestStatClass
=CNoRequestStats
,
8824 class HttpUserErrorTextProvider
=CDefaultErrorProvider
,
8825 class WorkerThreadTraits
=DefaultThreadTraits
,
8826 class CPageCacheStats
=CNoStatClass
,
8827 class CStencilCacheStats
=CNoStatClass
>
8828 class CIsapiExtension
:
8829 public IServiceProvider
, public IIsapiExtension
, public IRequestStats
8833 #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
8835 DWORD m_dwCriticalIsapiError
;
8837 #endif // ATL_NO_CRITICAL_ISAPI_ERROR
8842 typedef CWorkerThread
<WorkerThreadTraits
> extWorkerType
;
8844 extWorkerType m_WorkerThread
;
8845 ThreadPoolClass m_ThreadPool
;
8847 CDllCache
<extWorkerType
, CDllCachePeer
> m_DllCache
;
8848 CFileCache
<extWorkerType
, CPageCacheStats
, CPageCachePeer
> m_PageCache
;
8849 CComObjectGlobal
<CStencilCache
<extWorkerType
, CStencilCacheStats
> > m_StencilCache
;
8850 HttpUserErrorTextProvider m_UserErrorProvider
;
8851 HANDLE m_hRequestHeap
;
8852 CComCriticalSection m_critSec
;
8854 // Dynamic services stuff
8862 ServiceNode() throw()
8866 ServiceNode(const ServiceNode
& that
) throw()
8867 :hInst(that
.hInst
), punk(that
.punk
), guidService(that
.guidService
), riid(that
.riid
)
8872 class CServiceEqualHelper
8875 static bool IsEqual(__in
const ServiceNode
& t1
, __in
const ServiceNode
& t2
) throw()
8877 return (InlineIsEqualGUID(t1
.guidService
, t2
.guidService
) != 0 &&
8878 InlineIsEqualGUID(t1
.riid
, t2
.riid
) != 0);
8882 CSimpleArray
<ServiceNode
, CServiceEqualHelper
> m_serviceMap
;
8887 CRequestStatClass m_reqStats
;
8889 AtlServerRequest
*CreateRequest()
8891 // Allocate a fixed block size to avoid fragmentation
8892 AtlServerRequest
*pRequest
= (AtlServerRequest
*) HeapAlloc(m_hRequestHeap
,
8893 HEAP_ZERO_MEMORY
, __max(sizeof(AtlServerRequest
), sizeof(_CComObjectHeapNoLock
<CServerContext
>)));
8896 pRequest
->cbSize
= sizeof(AtlServerRequest
);
8901 void FreeRequest(__inout AtlServerRequest
*pRequest
)
8903 _ReleaseAtlServerRequest(pRequest
);
8904 HeapFree(m_hRequestHeap
, 0, pRequest
);
8907 CIsapiExtension() throw()
8909 m_hRequestHeap
= NULL
;
8914 #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
8916 m_dwCriticalIsapiError
= 0;
8918 #endif // ATL_NO_CRITICAL_ISAPI_ERROR
8921 HTTP_CODE
TransferRequest(
8922 __in AtlServerRequest
*pRequest
,
8923 __in IServiceProvider
*pServiceProvider
,
8924 __in IWriteStream
*pWriteStream
,
8925 __in_opt IHttpRequestLookup
*pLookup
,
8926 __in LPCSTR szNewUrl
,
8927 __in WORD nCodePage
,
8928 __in
bool bContinueAfterProcess
,
8929 __inout_opt CStencilState
*pState
)
8934 hcErr
= _AtlTransferRequest(pRequest
, pServiceProvider
, pWriteStream
,
8935 pLookup
, szNewUrl
, nCodePage
, bContinueAfterProcess
, pState
);
8944 #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
8946 DWORD
ReturnCriticalError(__in EXTENSION_CONTROL_BLOCK
*pECB
)
8953 LPCSTR szHeader
= NULL
;
8960 DWORD dwErr
= GetCriticalIsapiError();
8961 if (!strError
.LoadString(dwErr
))
8963 strError
.Format("Unknown Error %d", dwErr
);
8966 #ifdef ATL_CRITICAL_ISAPI_ERROR_LOGONLY
8967 // we've logged the real error - don't send detailed internal info to the user
8968 m_UserErrorProvider
.GetErrorText(500,
8973 if (!uResId
|| !strBody
.LoadString(uResId
))
8975 strBody
= "<html><body>A server error has occurred.</body></html>";
8978 m_UserErrorProvider
.GetErrorText(500,
8979 ISE_SUBERR_ISAPISTARTUPFAILED
,
8982 if (!uResId
|| !strFormat
.LoadString(uResId
))
8984 strFormat
= "<html><body>A critical error has occurred initializing this ISAPI extension: %s</body></html>";
8986 strBody
.Format(strFormat
, strError
);
8988 strStatus
.Format("500 %s", szHeader
);
8990 HSE_SEND_HEADER_EX_INFO hex
;
8991 hex
.pszStatus
= (LPCSTR
)strStatus
;
8992 hex
.pszHeader
= NULL
;
8993 hex
.cchStatus
= (DWORD
)strStatus
.GetLength();
8995 hex
.fKeepConn
= FALSE
;
8997 pECB
->ServerSupportFunction(pECB
->ConnID
,
8998 HSE_REQ_SEND_RESPONSE_HEADER_EX
,
9003 DWORD dwBodyLen
= strBody
.GetLength();
9004 pECB
->WriteClient(pECB
->ConnID
,
9005 (void *) (LPCSTR
) strBody
,
9011 return HSE_STATUS_ERROR
;
9013 return HSE_STATUS_SUCCESS
;
9016 #endif // ATL_NO_CRITICAL_ISAPI_ERROR
9018 DWORD
HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB
) throw()
9021 #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
9023 if (GetCriticalIsapiError() != 0)
9025 return ReturnCriticalError(lpECB
);
9028 #endif // ATL_NO_CRITICAL_ISAPI_ERROR
9030 AtlServerRequest
*pRequestInfo
= NULL
;
9034 pRequestInfo
= CreateRequest();
9035 if (pRequestInfo
== NULL
)
9036 return HSE_STATUS_ERROR
;
9038 CServerContext
*pServerContext
= NULL
;
9039 ATLTRY(pServerContext
= CreateServerContext(m_hRequestHeap
));
9040 if (pServerContext
== NULL
)
9042 FreeRequest(pRequestInfo
);
9043 return HSE_STATUS_ERROR
;
9046 pServerContext
->Initialize(lpECB
);
9047 pServerContext
->AddRef();
9049 pRequestInfo
->pServerContext
= pServerContext
;
9050 pRequestInfo
->dwRequestType
= ATLSRV_REQUEST_UNKNOWN
;
9051 pRequestInfo
->dwRequestState
= ATLSRV_STATE_BEGIN
;
9052 pRequestInfo
->pExtension
= static_cast<IIsapiExtension
*>(this);
9053 pRequestInfo
->pDllCache
= static_cast<IDllCache
*>(&m_DllCache
);
9054 #ifndef ATL_NO_MMSYS
9055 pRequestInfo
->dwStartTicks
= timeGetTime();
9057 pRequestInfo
->dwStartTicks
= GetTickCount();
9059 pRequestInfo
->pECB
= lpECB
;
9061 m_reqStats
.OnRequestReceived();
9063 if (m_ThreadPool
.QueueRequest(pRequestInfo
))
9064 return HSE_STATUS_PENDING
;
9066 if (pRequestInfo
!= NULL
)
9068 FreeRequest(pRequestInfo
);
9078 return HSE_STATUS_ERROR
;
9082 BOOL
QueueRequest(__in AtlServerRequest
* pRequestInfo
)
9084 return m_ThreadPool
.QueueRequest(pRequestInfo
);
9087 CIsapiWorker
*GetThreadWorker()
9089 return (CIsapiWorker
*) TlsGetValue(m_dwTlsIndex
);
9092 BOOL
SetThreadWorker(__in CIsapiWorker
*pWorker
)
9094 return TlsSetValue(m_dwTlsIndex
, (void*)pWorker
);
9097 // Configuration functions -- override in base class if another value is desired
9098 virtual LPCSTR
GetExtensionDesc() throw() { return "VC Server Classes"; }
9099 virtual int GetNumPoolThreads() throw() { return 0; }
9100 virtual int GetPoolStackSize() throw() { return 0; }
9101 virtual HANDLE
GetIOCompletionHandle() throw() { return INVALID_HANDLE_VALUE
; }
9102 virtual DWORD
GetDllCacheTimeout() throw() { return ATL_DLL_CACHE_TIMEOUT
; }
9103 virtual DWORD
GetStencilCacheTimeout() throw() { return ATL_STENCIL_CACHE_TIMEOUT
; }
9104 virtual LONGLONG
GetStencilLifespan() throw() { return ATL_STENCIL_LIFESPAN
; }
9106 BOOL
OnThreadAttach()
9108 return SUCCEEDED(CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
));
9111 void OnThreadTerminate()
9116 #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
9118 BOOL
SetCriticalIsapiError(__in DWORD dwErr
= 1) throw()
9120 m_dwCriticalIsapiError
= dwErr
;
9122 // send the error to the event log
9129 // format an error message
9130 if (!strError
.LoadString(dwErr
))
9132 strError
.Format("Unknown Error %d", dwErr
);
9135 if (!strFormat
.LoadString(IDS_ATLSRV_CRITICAL_LOGMESSAGE
))
9137 strFormat
= "A critical error has occurred initializing the ISAPI extension: %s";
9139 strBody
.Format(strFormat
, strError
);
9141 // take the base module name as the app name
9144 CStrBuf
buf(path
, MAX_PATH
);
9145 DWORD dwLen
= ::GetModuleFileName(_AtlBaseModule
.GetModuleInstance(), buf
, MAX_PATH
);
9146 if (dwLen
== MAX_PATH
)
9147 buf
.SetLength(MAX_PATH
);
9152 HANDLE h
= RegisterEventSource(NULL
, path
);
9155 LPCSTR szBody
= strBody
;
9156 ReportEventA(h
, EVENTLOG_ERROR_TYPE
, 0, 0, NULL
, 1, 0, &szBody
, NULL
);
9157 DeregisterEventSource(h
);
9167 DWORD
GetCriticalIsapiError() throw()
9169 return m_dwCriticalIsapiError
;
9174 BOOL
SetCriticalIsapiError(__in DWORD dwErr
= 1) throw()
9180 DWORD
GetCriticalIsapiError() throw()
9185 #endif // ATL_NO_CRITICAL_ISAPI_ERROR
9188 BOOL
GetExtensionVersion(__out HSE_VERSION_INFO
* pVer
) throw()
9190 ATLASSERT(pVer
!=NULL
);
9195 // allocate a Tls slot for storing per thread data
9196 m_dwTlsIndex
= TlsAlloc();
9198 // create a private heap for request data
9199 // this heap has to be thread safe to allow for
9200 // async processing of requests
9201 m_hRequestHeap
= HeapCreate(0, 0, 0);
9202 if (!m_hRequestHeap
)
9204 ATLTRACE(atlTraceISAPI
, 0, _T("Failed creating request heap. Using process heap\n"));
9205 m_hRequestHeap
= GetProcessHeap();
9206 if (!m_hRequestHeap
)
9208 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED
);
9213 // create a private heap (synchronized) for
9214 // allocations. This reduces fragmentation overhead
9215 // as opposed to the process heap
9216 HANDLE hHeap
= HeapCreate(0, 0, 0);
9219 ATLTRACE(atlTraceISAPI
, 0, _T("Failed creating extension heap. Using process heap\n"));
9220 hHeap
= GetProcessHeap();
9221 m_heap
.Attach(hHeap
, false);
9225 m_heap
.Attach(hHeap
, true);
9229 if (S_OK
!= m_reqStats
.Initialize())
9231 ATLTRACE(atlTraceISAPI
,
9233 _T("Initialization failed for request statistics perfmon support.\n")
9234 _T("Check request statistics perfmon dll registration\n") );
9237 if (S_OK
!= m_WorkerThread
.Initialize())
9239 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_WORKERINITFAILED
);
9242 if (m_critSec
.Init() != S_OK
)
9244 HRESULT hrIgnore
=m_WorkerThread
.Shutdown();
9246 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_CRITSECINITFAILED
);
9249 if (S_OK
!= m_ThreadPool
.Initialize(static_cast<IIsapiExtension
*>(this), GetNumPoolThreads(), GetPoolStackSize(), GetIOCompletionHandle()))
9251 HRESULT hrIgnore
=m_WorkerThread
.Shutdown();
9254 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_THREADPOOLFAILED
);
9257 if (FAILED(m_DllCache
.Initialize(&m_WorkerThread
, GetDllCacheTimeout())))
9259 HRESULT hrIgnore
=m_WorkerThread
.Shutdown();
9261 m_ThreadPool
.Shutdown();
9263 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_DLLCACHEFAILED
);
9266 if (FAILED(m_PageCache
.Initialize(&m_WorkerThread
)))
9268 HRESULT hrIgnore
=m_WorkerThread
.Shutdown();
9270 m_ThreadPool
.Shutdown();
9271 m_DllCache
.Uninitialize();
9273 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_PAGECACHEFAILED
);
9276 if (S_OK
!= m_StencilCache
.Initialize(static_cast<IServiceProvider
*>(this),
9278 GetStencilCacheTimeout(),
9279 GetStencilLifespan()))
9281 HRESULT hrIgnore
=m_WorkerThread
.Shutdown();
9283 m_ThreadPool
.Shutdown();
9284 m_DllCache
.Uninitialize();
9285 m_PageCache
.Uninitialize();
9287 return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED
);
9290 pVer
->dwExtensionVersion
= HSE_VERSION
;
9291 Checked::strncpy_s(pVer
->lpszExtensionDesc
, HSE_MAX_EXT_DLL_NAME_LEN
, GetExtensionDesc(), _TRUNCATE
);
9292 pVer
->lpszExtensionDesc
[HSE_MAX_EXT_DLL_NAME_LEN
- 1] = '\0';
9297 BOOL
TerminateExtension(DWORD
/*dwFlags*/) throw()
9300 for (int i
=0; i
< m_serviceMap
.GetSize(); i
++)
9302 ATLASSUME(m_serviceMap
[i
].punk
!= NULL
);
9303 if(m_serviceMap
[i
].punk
!= NULL
)
9305 m_serviceMap
[i
].punk
->Release();
9307 m_DllCache
.ReleaseModule(m_serviceMap
[i
].hInst
);
9311 m_ThreadPool
.Shutdown();
9312 m_StencilCache
.Uninitialize();
9313 m_DllCache
.Uninitialize();
9314 m_PageCache
.Uninitialize();
9315 HRESULT hrShutdown
=m_WorkerThread
.Shutdown();
9316 m_reqStats
.Uninitialize();
9319 // free the request heap
9320 if (m_hRequestHeap
!= GetProcessHeap())
9321 HeapDestroy(m_hRequestHeap
);
9323 // free the Tls slot that we allocated
9324 TlsFree(m_dwTlsIndex
);
9326 return SUCCEEDED(hrShutdown
);
9329 static void WINAPI
AsyncCallback(LPEXTENSION_CONTROL_BLOCK
/*lpECB*/,
9330 __in PVOID pContext
,
9332 __in DWORD dwError
) throw(...)
9334 AtlServerRequest
*pRequestInfo
= reinterpret_cast<AtlServerRequest
*>(pContext
);
9335 ATLENSURE(pRequestInfo
);
9336 if (pRequestInfo
->m_hMutex
)
9338 // synchronize in case the previous async_noflush call isn't finished
9339 // setting up state for the next call.
9340 DWORD dwStatus
= WaitForSingleObject(pRequestInfo
->m_hMutex
, ATLS_ASYNC_MUTEX_TIMEOUT
);
9341 if (dwStatus
!= WAIT_OBJECT_0
&& dwStatus
!= WAIT_ABANDONED
)
9345 pRequestInfo
->pExtension
->RequestComplete(pRequestInfo
, 500, ISE_SUBERR_UNEXPECTED
);
9349 ATLTRACE(_T("Warning: Uncaught user exception thrown and caught in AsyncCallback.\n"));
9356 if (pRequestInfo
->pfnAsyncComplete
!= NULL
)
9357 ATLTRY((*pRequestInfo
->pfnAsyncComplete
)(pRequestInfo
, cbIO
, dwError
));
9359 if (pRequestInfo
->dwRequestState
== ATLSRV_STATE_DONE
)
9361 pRequestInfo
->pExtension
->RequestComplete(pRequestInfo
, HTTP_ERROR_CODE(HTTP_SUCCESS
), 0);
9363 else if (pRequestInfo
->dwRequestState
== ATLSRV_STATE_CACHE_DONE
)
9365 CloseHandle(pRequestInfo
->hFile
);
9366 pRequestInfo
->pFileCache
->ReleaseFile(pRequestInfo
->hEntry
);
9367 pRequestInfo
->pExtension
->RequestComplete(pRequestInfo
, HTTP_ERROR_CODE(HTTP_SUCCESS
), 0);
9371 HANDLE hMutex
= pRequestInfo
->m_hMutex
;
9372 pRequestInfo
->pExtension
->QueueRequest(pRequestInfo
);
9374 ReleaseMutex(hMutex
);
9378 void HandleError(__in IHttpServerContext
*pServerContext
, __in DWORD dwStatus
, __in DWORD dwSubStatus
) throw()
9380 RenderError(pServerContext
, dwStatus
, dwSubStatus
, &m_UserErrorProvider
);
9383 void RequestComplete(__inout AtlServerRequest
*pRequestInfo
, __in DWORD dwStatus
, __in DWORD dwSubStatus
)
9385 ATLASSERT(pRequestInfo
);
9387 if (pRequestInfo
->pHandler
!= NULL
)
9388 pRequestInfo
->pHandler
->UninitializeHandler();
9390 DWORD dwReqStatus
= dwStatus
;
9394 if (dwStatus
>= 400)
9396 if (dwSubStatus
!= SUBERR_NO_PROCESS
)
9397 HandleError(pRequestInfo
->pServerContext
, dwStatus
, dwSubStatus
);
9398 m_reqStats
.RequestHandled(pRequestInfo
, FALSE
);
9401 m_reqStats
.RequestHandled(pRequestInfo
, TRUE
);
9403 CComPtr
<IHttpServerContext
> spServerContext
= pRequestInfo
->pServerContext
;
9405 FreeRequest(pRequestInfo
);
9407 spServerContext
->DoneWithSession(dwReqStatus
);
9410 HTTP_CODE
GetHandlerName(__in LPCSTR szFileName
, __out_ecount(MAX_PATH
+ATL_MAX_HANDLER_NAME
+2) LPSTR szHandlerName
) throw()
9412 return _AtlGetHandlerName(szFileName
, szHandlerName
);
9415 HTTP_CODE
LoadDispatchFile(__in LPCSTR szFileName
, __out AtlServerRequest
*pRequestInfo
)
9417 ATLASSERT(szFileName
);
9418 ATLASSERT(pRequestInfo
);
9420 CStencil
*pStencil
= NULL
;
9421 HCACHEITEM hStencil
= NULL
;
9423 // Must have space for the path to the handler + the maximum size
9424 // of the handler, plus the '/' plus the '\0'
9425 CHAR szDllPath
[MAX_PATH
];
9426 CHAR szHandlerName
[ATL_MAX_HANDLER_NAME_LEN
+1];
9428 pRequestInfo
->pHandler
= NULL
;
9429 pRequestInfo
->hInstDll
= NULL
;
9431 m_StencilCache
.LookupStencil(szFileName
, &hStencil
);
9433 // Stencil was found, check to see if it needs to be refreshed
9436 m_StencilCache
.GetStencil(hStencil
, (void **) &pStencil
);
9437 pStencil
->GetHandlerName(szDllPath
, MAX_PATH
, szHandlerName
, ATL_MAX_HANDLER_NAME_LEN
+ 1);
9440 CFileTime cftLastChecked
;
9441 cftCurr
= CFileTime::GetCurrentTime();
9443 pStencil
->GetLastChecked(&cftLastChecked
);
9445 CFileTimeSpan
span(ATL_STENCIL_CHECK_TIMEOUT
* CFileTime::Millisecond
);
9447 if (cftLastChecked
+ span
< cftCurr
)
9449 CComPtr
<IStencilCacheControl
> spCacheCtrl
;
9450 m_StencilCache
.QueryInterface(__uuidof(IStencilCacheControl
), reinterpret_cast<void**>(&spCacheCtrl
));
9453 CFileTime cftLastModified
;
9454 pStencil
->GetLastModified(&cftLastModified
);
9456 // Resource based stencils have a last modified filetime of 0
9457 if (cftLastModified
!= 0)
9459 // for file base stencils, we check whether the file
9460 // has been modified since being put in the cache
9461 WIN32_FILE_ATTRIBUTE_DATA fad
;
9462 pStencil
->SetLastChecked(&cftCurr
);
9463 BOOL bRet
= GetFileAttributesExA(szFileName
, GetFileExInfoStandard
, &fad
);
9465 if ((bRet
&& cftLastModified
< fad
.ftLastWriteTime
) ||
9468 // the file has changed or an error has occurred trying to read the file,
9469 // so remove it from the cache and force a reload
9470 spCacheCtrl
->RemoveStencil(hStencil
);
9482 CHAR szHandlerDllName
[MAX_PATH
+ATL_MAX_HANDLER_NAME_LEN
+1];
9483 *szHandlerDllName
= '\0';
9485 // not in the cache, so open the file
9486 HTTP_CODE hcErr
= GetHandlerName(szFileName
, szHandlerDllName
);
9489 DWORD dwDllPathLen
= MAX_PATH
;
9490 DWORD dwHandlerNameLen
= ATL_MAX_HANDLER_NAME_LEN
+1;
9491 if (!_AtlCrackHandler(szHandlerDllName
, szDllPath
, &dwDllPathLen
, szHandlerName
, &dwHandlerNameLen
))
9493 return AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND
);
9495 ATLASSERT(*szHandlerName
);
9496 ATLASSERT(*szDllPath
);
9497 if (!*szHandlerName
)
9499 return AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND
);
9504 m_StencilCache
.ReleaseStencil(hStencil
);
9508 return LoadRequestHandler(szDllPath
, szHandlerName
, pRequestInfo
->pServerContext
,
9509 &pRequestInfo
->hInstDll
, &pRequestInfo
->pHandler
);
9512 HTTP_CODE
LoadDllHandler(__in LPCSTR szFileName
, __in AtlServerRequest
*pRequestInfo
)
9514 ATLASSERT(szFileName
);
9515 ATLENSURE(pRequestInfo
);
9519 HTTP_CODE hcErr
= HTTP_SUCCESS
;
9520 CHAR szHandler
[ATL_MAX_HANDLER_NAME_LEN
+1] = { 'D', 'e', 'f', 'a', 'u', 'l', 't', '\0' };
9521 LPCSTR szQueryString
= pRequestInfo
->pServerContext
->GetQueryString();
9522 if (szQueryString
!= NULL
)
9524 LPCSTR szHdlr
= strstr(szQueryString
, "Handler=");
9527 if ((szHdlr
== szQueryString
) ||
9528 ((szHdlr
> szQueryString
) && (*(szHdlr
-1) == '&')))
9531 LPSTR pszHandler
= szHandler
;
9532 szHdlr
+= sizeof("Handler=")-1;
9533 while (*szHdlr
&& *szHdlr
!= '&')
9535 if (nCnt
< ATL_MAX_HANDLER_NAME_LEN
)
9537 *pszHandler
++ = *szHdlr
++;
9542 hcErr
= AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND
);
9546 if (hcErr
== HTTP_SUCCESS
)
9554 if (hcErr
== HTTP_SUCCESS
)
9556 CHAR szFile
[MAX_PATH
+ATL_MAX_HANDLER_NAME_LEN
+1];
9557 if (SafeStringCopy(szFile
, szFileName
))
9559 hcErr
= LoadRequestHandler(szFile
, szHandler
, pRequestInfo
->pServerContext
, &pRequestInfo
->hInstDll
, &pRequestInfo
->pHandler
);
9563 hcErr
= AtlsHttpError(500, ISE_SUBERR_UNEXPECTED
);
9571 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED
);
9575 #pragma warning(push)
9576 #pragma warning(disable: 6014)
9577 virtual __success(return) __checkReturn BOOL
GetCacheServerContext(__in AtlServerRequest
*pRequestInfo
, __in IFileCache
*pCache
, __deref_out_opt IHttpServerContext
**pCacheCtx
)
9579 ATLENSURE(pCacheCtx
);
9582 CComObjectNoLock
<CCacheServerContext
> *pCacheServerContext
= NULL
;
9583 ATLTRY(pCacheServerContext
= new CComObjectNoLock
<CCacheServerContext
>);
9584 if (!pCacheServerContext
)
9587 if (!pCacheServerContext
->Initialize(pRequestInfo
->pServerContext
, pCache
))
9589 delete pCacheServerContext
;
9593 pCacheServerContext
->QueryInterface(__uuidof(IHttpServerContext
), (void **) pCacheCtx
);
9598 delete pCacheServerContext
;
9601 #pragma warning(pop)
9603 virtual BOOL
TransmitFromCache(__in AtlServerRequest
* pRequestInfo
, __out BOOL
*pbAllowCaching
)
9605 ATLENSURE(pRequestInfo
);
9606 ATLENSURE(pbAllowCaching
);
9608 *pbAllowCaching
= TRUE
;
9612 if (strcmp(pRequestInfo
->pServerContext
->GetRequestMethod(), "GET"))
9615 char szUrl
[ATL_URL_MAX_URL_LENGTH
+ 1];
9616 LPCSTR szPathInfo
= pRequestInfo
->pServerContext
->GetPathInfo();
9617 LPCSTR szQueryString
= pRequestInfo
->pServerContext
->GetQueryString();
9621 ATLENSURE(szPathInfo
!=NULL
);
9622 while (*szPathInfo
&& nSize
< ATL_URL_MAX_URL_LENGTH
)
9624 *szTo
++ = *szPathInfo
++;
9627 if (nSize
>= ATL_URL_MAX_URL_LENGTH
)
9633 ATLENSURE(szQueryString
!=NULL
);
9634 while (*szQueryString
&& nSize
< ATL_URL_MAX_URL_LENGTH
)
9636 *szTo
++ = *szQueryString
++;
9639 if (nSize
>= ATL_URL_MAX_URL_LENGTH
)
9647 if (S_OK
== m_PageCache
.LookupFile(szUrl
, &hEntry
))
9650 CPageCachePeer::PeerInfo
*pInfo
;
9651 m_PageCache
.GetFile(hEntry
, &szFileName
, (void **)&pInfo
);
9654 HRESULT hr
= E_FAIL
;
9656 CA2CTEX
<MAX_PATH
> strFile(szFileName
);
9657 hr
= file
.Create(strFile
,
9661 FILE_FLAG_SEQUENTIAL_SCAN
| FILE_FLAG_OVERLAPPED
);
9663 if (FAILED(hr
) || GetFileType(file
) != FILE_TYPE_DISK
)
9665 m_PageCache
.ReleaseFile(hEntry
);
9666 *pbAllowCaching
= FALSE
;
9670 pRequestInfo
->pServerContext
->SendResponseHeader(
9671 pInfo
->strHeader
, pInfo
->strStatus
, FALSE
);
9672 HANDLE hFile
= file
.Detach();
9675 pRequestInfo
->dwRequestState
= ATLSRV_STATE_CACHE_DONE
;
9676 pRequestInfo
->hFile
= hFile
;
9677 pRequestInfo
->hEntry
= hEntry
;
9678 pRequestInfo
->pFileCache
= &m_PageCache
;
9680 bRet
= pRequestInfo
->pServerContext
->TransmitFile(
9681 hFile
, // The file to transmit
9682 AsyncCallback
, pRequestInfo
, // The async callback and context
9683 pInfo
->strStatus
, // HTTP status code
9684 0, // Send entire file
9685 0, // Start at the beginning of the file
9686 NULL
, 0, // Head and length
9687 NULL
, 0, // Tail and length
9688 HSE_IO_ASYNC
| HSE_IO_DISCONNECT_AFTER_SEND
| HSE_IO_NODELAY
// Send asynchronously
9693 m_PageCache
.ReleaseFile(hEntry
);
9695 *pbAllowCaching
= FALSE
;
9708 #if defined(_DEBUG) || defined(ATLS_ENABLE_DEBUGGING)
9711 // F5 debugging support for VS7
9712 BOOL
ProcessDebug(__inout AtlServerRequest
*pRequestInfo
)
9714 ATLENSURE(pRequestInfo
);
9715 static GUID clsidDebugger
[] = {
9716 {0x70F65411, 0xFE8C, 0x4248, {0xBC,0xFF,0x70,0x1C,0x8B,0x2F,0x45,0x29}}, // debugger clsid
9717 {0x62A78AC2, 0x7D9A, 0x4377, {0xB9,0x7E,0x69,0x65,0x91,0x9F,0xDD,0x02}}, // reserved
9718 {0xCC23651F, 0x4574, 0x438F, {0xB4,0xAA,0xBC,0xB2,0x8B,0x6B,0x3E,0xCF}}, // reserved
9719 {0xDBFDB1D0, 0x04A4, 0x4315, {0xB1,0x5C,0xF8,0x74,0xF6,0xB6,0xE9,0x0B}}, // reserved
9720 {0xA4FCB474, 0x2687, 0x4924, {0xB0,0xAD,0x7C,0xAF,0x33,0x1D,0xB8,0x26}}, // reserved
9721 {0xBEB261F6, 0xD5F0, 0x43BA, {0xBA,0xF4,0x8B,0x79,0x78,0x5F,0xFF,0xAF}}, // reserved
9722 {0x8E2F5E28, 0xD4E2, 0x44C0, {0xAA,0x02,0xF8,0xC5,0xBE,0xB7,0x0C,0xAC}}, // reserved
9723 {0x08100915, 0x0F41, 0x4CCF, {0x95,0x64,0xEB,0xAA,0x5D,0x49,0x44,0x6C}} // reserved
9727 if (!_stricmp(pRequestInfo
->pServerContext
->GetRequestMethod(), "debug"))
9729 // Debugger must be able to validate the client we are impersonating
9730 // on an NT Domain so the client needs to use either NTLM or Negotiate.
9731 DWORD dwAuthTypeSize
= 64;
9732 char szAuthType
[64] = { 0 };
9734 if ( !pRequestInfo
->pServerContext
->GetServerVariable("AUTH_TYPE", szAuthType
, &dwAuthTypeSize
) )
9736 // error retrieving authentication type
9737 RequestComplete(pRequestInfo
, 501, 0);
9741 // if it's empty or not NTLM or negotiate we fail.
9742 if ( !( *szAuthType
!= '\0 ' &&
9743 ( !_stricmp(szAuthType
, "NTLM") ||
9744 !_stricmp(szAuthType
, "Negotiate"))
9747 // wrong authorization type or not authorized
9748 RequestComplete(pRequestInfo
, 401, 0);
9752 DWORD dwHeadersLen
= 0;
9753 CStringA strHeaders
;
9754 pRequestInfo
->pServerContext
->GetServerVariable("ALL_HTTP", NULL
, &dwHeadersLen
);
9755 BOOL bRet
= pRequestInfo
->pServerContext
->GetServerVariable("ALL_HTTP", strHeaders
.GetBuffer(dwHeadersLen
), &dwHeadersLen
);
9758 RequestComplete(pRequestInfo
, 501, 0);
9761 strHeaders
.ReleaseBuffer(dwHeadersLen
- 1);
9762 LPCSTR szCur
= strHeaders
;
9766 if (!strncmp(szCur
, "HTTP_COMMAND:", 13))
9772 szCur
= strchr(szCur
, '\n');
9775 RequestComplete(pRequestInfo
, 501, 0);
9783 if (!_strnicmp(szCur
, "start-debug", sizeof("start-debug")-sizeof('\0')))
9785 CCritSecLock
Lock(m_critSec
.m_sec
);
9788 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_ALREADY_DEBUGGING
);
9789 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_ALREADY_DEBUGGING
); // Already being debugged by another process
9792 CHttpRequest HttpRequest
;
9793 HttpRequest
.Initialize(pRequestInfo
->pServerContext
);
9794 HttpRequest
.InitFromPost();
9796 szString
= HttpRequest
.FormVars
.Lookup("DebugSessionID");
9797 if (!szString
|| !*szString
)
9799 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_INVALID_SESSION
);
9800 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_INVALID_SESSION
);
9803 CA2W
szSessionID(szString
);
9806 HandleError(pRequestInfo
->pServerContext
, 500, ISE_SUBERR_OUTOFMEM
);
9807 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_OUTOFMEM
);
9810 DWORD dwPid
= GetCurrentProcessId();
9811 LPWSTR szPoint
= szSessionID
;
9812 while (szPoint
&& *szPoint
&& wcsncmp(szPoint
, L
"autoattachclsid=", 16))
9814 szPoint
= wcschr(szPoint
, ';');
9819 if (!szPoint
|| !*szPoint
)
9821 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_BAD_ID
);
9822 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_BAD_ID
);
9826 szPoint
+= (sizeof("autoattachclsid=") - 1);
9829 Checked::wcsncpy_s(szClsid
, _countof(szClsid
), szPoint
, _TRUNCATE
);
9830 if (szClsid
[38] != '\0')
9832 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_BAD_ID
);
9833 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_BAD_ID
);
9838 CLSID clsidDebugAutoAttach
= CLSID_NULL
;
9839 HRESULT hr
= CLSIDFromString(szClsid
, &clsidDebugAutoAttach
);
9843 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_BAD_ID
);
9844 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_BAD_ID
);
9849 nArrSize
= sizeof(clsidDebugger
)/sizeof(GUID
);
9850 for (i
=0; i
<nArrSize
; i
++)
9852 if( InlineIsEqualGUID(clsidDebugAutoAttach
, clsidDebugger
[i
]) )
9857 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_BAD_ID
);
9858 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_BAD_ID
);
9862 CComPtr
<IDebugAutoAttach
> spDebugAutoAttach
;
9863 hr
= CoCreateInstance(clsidDebugAutoAttach
, NULL
, CLSCTX_LOCAL_SERVER
| CLSCTX_INPROC_SERVER
, __uuidof(IDebugAutoAttach
), (void**)&spDebugAutoAttach
);
9866 if (hr
== E_ACCESSDENIED
)
9867 RequestComplete(pRequestInfo
, 401, 0);
9870 HandleError(pRequestInfo
->pServerContext
, 204, DBG_SUBERR_COCREATE
);
9871 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_COCREATE
);
9875 hr
= spDebugAutoAttach
->AutoAttach(GUID_NULL
, dwPid
, AUTOATTACH_PROGRAM_WIN32
, 0, szSessionID
);
9879 int nLen
= sprintf_s(szRetBuf
, _countof(szRetBuf
), "204 HRESULT=0x%.08X;ErrorString=Unable to attach to worker process", hr
);
9883 pRequestInfo
->pServerContext
->SendResponseHeader(NULL
, szRetBuf
, FALSE
);
9884 pRequestInfo
->pServerContext
->WriteClient(szRetBuf
, &dwLen
);
9885 RequestComplete(pRequestInfo
, 204, DBG_SUBERR_ATTACH
);
9890 HandleError(pRequestInfo
->pServerContext
, 200, SUBERR_NONE
);
9891 RequestComplete(pRequestInfo
, 200, SUBERR_NONE
);
9894 else if (!_strnicmp(szCur
, "stop-debug", sizeof("stop-debug")-sizeof('\0')))
9897 HandleError(pRequestInfo
->pServerContext
, 200, SUBERR_NONE
);
9898 RequestComplete(pRequestInfo
, 200, SUBERR_NONE
);
9903 RequestComplete(pRequestInfo
, 501, SUBERR_NONE
); // Not Implemented
9914 #endif // defined(_DEBUG) || defined(ATLS_ENABLE_DEBUGGING)
9916 BOOL
DispatchStencilCall(__inout AtlServerRequest
*pRequestInfo
)
9918 ATLENSURE(pRequestInfo
!=NULL
);
9919 CSetThreadToken sec
;
9921 m_reqStats
.OnRequestDequeued();
9923 if (!sec
.Initialize(pRequestInfo
))
9925 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_IMPERSONATIONFAILED
);
9929 #if defined(_DEBUG) || defined(ATLS_ENABLE_DEBUGGING)
9930 if (!ProcessDebug(pRequestInfo
))
9932 #endif // defined(ATLS_ENABLE_DEBUGGING)
9934 if (pRequestInfo
->m_hMutex
)
9936 // synchronize in case the previous async_noflush call isn't finished
9937 // setting up state for the next call.
9938 DWORD dwStatus
= WaitForSingleObject(pRequestInfo
->m_hMutex
, ATLS_ASYNC_MUTEX_TIMEOUT
);
9939 if (dwStatus
!= WAIT_OBJECT_0
&& dwStatus
!= WAIT_ABANDONED
)
9941 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_UNEXPECTED
);
9947 bool bAsyncAllowed
= false;
9949 HTTP_CODE hcErr
= HTTP_SUCCESS
;
9950 if (pRequestInfo
->dwRequestState
== ATLSRV_STATE_BEGIN
)
9952 BOOL bAllowCaching
= TRUE
;
9953 if (TransmitFromCache(pRequestInfo
, &bAllowCaching
)) // Page is in the cache, send it and bail
9954 { // Async Callback will handle freeing pRequestInfo
9958 // get the srf filename
9959 LPCSTR szFileName
= pRequestInfo
->pServerContext
->GetScriptPathTranslated();
9963 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_UNEXPECTED
);
9967 LPCSTR szDot
= szFileName
+ strlen(szFileName
) - 1;
9970 if (AsciiStricmp(szDot
- ATLS_EXTENSION_LEN
, c_AtlSRFExtension
) == 0)
9972 pRequestInfo
->dwRequestType
= ATLSRV_REQUEST_STENCIL
;
9973 hcErr
= LoadDispatchFile(szFileName
, pRequestInfo
);
9975 else if (AsciiStricmp(szDot
- ATLS_DLL_EXTENSION_LEN
, c_AtlDLLExtension
) == 0)
9977 pRequestInfo
->dwRequestType
= ATLSRV_REQUEST_DLL
;
9978 hcErr
= LoadDllHandler(szFileName
, pRequestInfo
);
9987 RequestComplete(pRequestInfo
, HTTP_ERROR_CODE(hcErr
), HTTP_SUBERROR_CODE(hcErr
));
9991 pRequestInfo
->pfnHandleRequest
= &IRequestHandler::HandleRequest
;
9993 // initialize the handler
9996 hcErr
= pRequestInfo
->pHandler
->GetFlags(&dwStatus
);
9999 RequestComplete(pRequestInfo
, HTTP_ERROR_CODE(hcErr
), HTTP_SUBERROR_CODE(hcErr
));
10003 if (bAllowCaching
&& ((dwStatus
& ATLSRV_INIT_USECACHE
) != 0) &&
10004 !strcmp(pRequestInfo
->pServerContext
->GetRequestMethod(), "GET"))
10006 CComPtr
<IHttpServerContext
> spCacheCtx
;
10007 if (!GetCacheServerContext(pRequestInfo
, &m_PageCache
, &spCacheCtx
) ||
10010 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_OUTOFMEM
);
10014 pRequestInfo
->pServerContext
->Release();
10015 pRequestInfo
->pServerContext
= spCacheCtx
.Detach();
10018 if (dwStatus
& (ATLSRV_INIT_USEASYNC
| ATLSRV_INIT_USEASYNC_EX
))
10021 bAsyncAllowed
= true;
10023 if (!pRequestInfo
->pServerContext
->RequestIOCompletion(AsyncCallback
, (DWORD
*)pRequestInfo
))
10025 RequestComplete(pRequestInfo
, 500, SUBERR_NONE
);
10030 if (dwStatus
& ATLSRV_INIT_USEASYNC_EX
)
10032 pRequestInfo
->m_hMutex
= CreateMutex(NULL
, FALSE
, NULL
);
10033 if (pRequestInfo
->m_hMutex
== NULL
)
10035 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_SYSOBJFAIL
);
10039 DWORD dwMutexStatus
= WaitForSingleObject(pRequestInfo
->m_hMutex
, 10000);
10040 if (dwMutexStatus
!= WAIT_OBJECT_0
&& dwMutexStatus
!= WAIT_ABANDONED
)
10042 RequestComplete(pRequestInfo
, 500, ISE_SUBERR_UNEXPECTED
);
10046 hcErr
= pRequestInfo
->pHandler
->InitializeHandler(pRequestInfo
, static_cast<IServiceProvider
*>(this));
10049 else // pRequestInfo->dwRequestState != ATLSRV_STATE_BEGIN
10051 bAsyncAllowed
= true;
10055 ATLENSURE(pRequestInfo
->pfnHandleRequest
!= NULL
);
10057 if (hcErr
== HTTP_SUCCESS
)
10058 hcErr
= (pRequestInfo
->pHandler
->*pRequestInfo
->pfnHandleRequest
)(pRequestInfo
, static_cast<IServiceProvider
*>(this));
10060 if (hcErr
== HTTP_SUCCESS_NO_CACHE
)
10062 CComPtr
<IPageCacheControl
> spControl
;
10063 HRESULT hr
= pRequestInfo
->pServerContext
->QueryInterface(__uuidof(IPageCacheControl
), reinterpret_cast<void**>(&spControl
));
10065 spControl
->Cache(FALSE
);
10069 // must use ATLSRV_INIT_USEASYNC to use ASYNC returns
10070 if (IsAsyncStatus(hcErr
))
10071 ATLASSERT(bAsyncAllowed
);
10073 // must use ATLSRV_INIT_USEASYNC_EX to use NOFLUSH returns
10074 if (IsAsyncNoFlushStatus(hcErr
))
10075 ATLASSERT(pRequestInfo
->m_hMutex
);
10078 // save hMutex in case pRequestInfo is deleted by AsyncCallback after
10079 // we call StartAsyncFlush but before we check to see if we need to
10080 // call ReleaseMutex
10081 HANDLE hMutex
= pRequestInfo
->m_hMutex
;
10083 if (IsAsyncStatus(hcErr
))
10085 if (IsAsyncDoneStatus(hcErr
))
10086 pRequestInfo
->dwRequestState
= ATLSRV_STATE_DONE
;
10088 pRequestInfo
->dwRequestState
= ATLSRV_STATE_CONTINUE
;
10090 if (IsAsyncFlushStatus(hcErr
) && !StartAsyncFlush(pRequestInfo
))
10092 RequestComplete(pRequestInfo
, 500, SUBERR_NONE
);
10093 pRequestInfo
= NULL
;
10098 RequestComplete(pRequestInfo
, HTTP_ERROR_CODE(hcErr
), HTTP_SUBERROR_CODE(hcErr
));
10099 pRequestInfo
= NULL
;
10103 ReleaseMutex(hMutex
);
10108 BOOL
StartAsyncFlush(__in AtlServerRequest
*pRequestInfo
)
10110 ATLENSURE(pRequestInfo
);
10111 if (pRequestInfo
->pszBuffer
== NULL
|| pRequestInfo
->dwBufferLen
== 0)
10117 ATLENSURE(pRequestInfo
->pServerContext
);
10121 bRet
= pRequestInfo
->pServerContext
->AsyncWriteClient(
10122 LPVOID(pRequestInfo
->pszBuffer
),
10123 &pRequestInfo
->dwBufferLen
);
10133 long GetTotalRequests()
10135 return m_reqStats
.GetTotalRequests();
10138 long GetFailedRequests()
10140 return m_reqStats
.GetFailedRequests();
10143 long GetAvgResponseTime()
10145 return m_reqStats
.GetAvgResponseTime();
10148 long GetCurrWaiting()
10150 return m_reqStats
.GetCurrWaiting();
10153 long GetMaxWaiting()
10155 return m_reqStats
.GetMaxWaiting();
10158 long GetActiveThreads()
10160 return m_reqStats
.GetActiveThreads();
10163 __success(SUCCEEDED(return)) __checkReturn HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid
, __deref_out
void **ppv
)
10167 if (InlineIsEqualGUID(riid
, __uuidof(IRequestStats
)))
10169 *ppv
= static_cast<IRequestStats
*>(this);
10173 if (InlineIsEqualGUID(riid
, __uuidof(IUnknown
)) ||
10174 InlineIsEqualGUID(riid
, __uuidof(IServiceProvider
)))
10176 *ppv
= static_cast<IServiceProvider
*>(this);
10180 if (InlineIsEqualGUID(riid
, __uuidof(IIsapiExtension
)))
10182 *ppv
= static_cast<IIsapiExtension
*>(this);
10186 return E_NOINTERFACE
;
10189 ULONG STDMETHODCALLTYPE
AddRef()
10194 ULONG STDMETHODCALLTYPE
Release()
10199 virtual __checkReturn HRESULT STDMETHODCALLTYPE
QueryService(
10200 __in REFGUID guidService
,
10202 __deref_out
void **ppvObject
)
10207 if (InlineIsEqualGUID(guidService
, __uuidof(IDllCache
)))
10208 return m_DllCache
.QueryInterface(riid
, ppvObject
);
10209 else if (InlineIsEqualGUID(guidService
, __uuidof(IStencilCache
)))
10210 return m_StencilCache
.QueryInterface(riid
, ppvObject
);
10211 else if (InlineIsEqualGUID(guidService
, __uuidof(IThreadPoolConfig
)))
10212 return m_ThreadPool
.QueryInterface(riid
, ppvObject
);
10213 else if (InlineIsEqualGUID(guidService
, __uuidof(IAtlMemMgr
)))
10215 *ppvObject
= static_cast<IAtlMemMgr
*>(&m_heap
);
10218 #ifndef ATL_NO_SOAP
10219 else if (InlineIsEqualGUID(guidService
, __uuidof(ISAXXMLReader
)))
10221 CIsapiWorker
*p
= GetThreadWorker();
10222 ATLENSURE( p
!= NULL
);
10223 return p
->m_spReader
->QueryInterface(riid
, ppvObject
);
10227 // otherwise look it up in the servicemap
10228 return GetService(guidService
, riid
, ppvObject
);
10231 virtual HRESULT
AddService(REFGUID guidService
, REFIID riid
, IUnknown
*punkService
, HINSTANCE hInstance
)
10234 return E_INVALIDARG
;
10236 if (!m_DllCache
.AddRefModule(hInstance
))
10239 ServiceNode srvNode
;
10240 srvNode
.hInst
= hInstance
;
10241 srvNode
.punk
= punkService
;
10242 Checked::memcpy_s(&srvNode
.guidService
, sizeof(GUID
), &guidService
, sizeof(guidService
));
10243 Checked::memcpy_s(&srvNode
.riid
, sizeof(IID
), &riid
, sizeof(riid
));
10245 CComCritSecLock
<CComCriticalSection
> lock(m_critSec
, false);
10246 HRESULT hr
= lock
.Lock();
10252 // if the service is already there, return S_FALSE
10253 int nIndex
= m_serviceMap
.Find(srvNode
);
10257 if (!m_serviceMap
.Add(srvNode
))
10258 return E_OUTOFMEMORY
;
10260 punkService
->AddRef();
10264 virtual HRESULT
RemoveService(__in REFGUID guidService
, __in REFIID riid
)
10266 ServiceNode srvNode
;
10267 Checked::memcpy_s(&srvNode
.guidService
, sizeof(GUID
), &guidService
, sizeof(guidService
));
10268 Checked::memcpy_s(&srvNode
.riid
, sizeof(IID
), &riid
, sizeof(riid
));
10270 CComCritSecLock
<CComCriticalSection
> lock(m_critSec
, false);
10271 HRESULT hr
= lock
.Lock();
10277 int nIndex
= m_serviceMap
.Find(srvNode
);
10281 ATLASSUME(m_serviceMap
[nIndex
].punk
!= NULL
);
10282 m_serviceMap
[nIndex
].punk
->Release();
10284 HINSTANCE hInstRemove
= m_serviceMap
[nIndex
].hInst
;
10286 m_serviceMap
.RemoveAt(nIndex
);
10288 if (!m_DllCache
.ReleaseModule(hInstRemove
))
10294 __success(SUCCEEDED(return)) __checkReturn HRESULT
GetService(__in REFGUID guidService
, __in REFIID riid
, __deref_out
void **ppvObject
) throw()
10300 if (!m_serviceMap
.GetSize())
10301 return E_NOINTERFACE
;
10303 ServiceNode srvNode
;
10304 Checked::memcpy_s(&srvNode
.guidService
, sizeof(GUID
), &guidService
, sizeof(guidService
));
10305 Checked::memcpy_s(&srvNode
.riid
, sizeof(IID
), &riid
, sizeof(riid
));
10307 CComCritSecLock
<CComCriticalSection
> lock(m_critSec
, false);
10308 HRESULT hr
= lock
.Lock();
10314 int nIndex
= m_serviceMap
.Find(srvNode
);
10316 return E_NOINTERFACE
;
10318 ATLASSUME(m_serviceMap
[nIndex
].punk
!= NULL
);
10319 return m_serviceMap
[nIndex
].punk
->QueryInterface(riid
, ppvObject
);
10322 HTTP_CODE
LoadRequestHandler(__in LPCSTR szDllPath
, __in LPCSTR szHandlerName
, __in IHttpServerContext
*pServerContext
,
10323 __out HINSTANCE
*phInstance
, __deref_out IRequestHandler
**ppHandler
)
10325 return _AtlLoadRequestHandler(szDllPath
, szHandlerName
, pServerContext
,
10326 phInstance
, ppHandler
, this, static_cast<IDllCache
*>(&m_DllCache
));
10327 } // LoadRequestHandler
10329 }; // class CIsapiExtension
10332 //===========================================================================================
10333 // IMPORTANT NOTE TO USERS:
10334 // DO NOT ASSUME *ANYTHING* ABOUT THE STRUCTURE OF THESE MAPS/ENTRIES/FUNCTIONS--THEY CAN
10335 // AND *WILL* CHANGE IN THE FUTURE. CORRECT USAGE MANDATES THAT YOU USE THE MACROS PROVIDED.
10336 // ABSOLUTELY NO GUARANTEES ABOUT BACKWARD COMPATABILITY ARE MADE FOR MANUALLY DEFINED
10337 // HANDLERS OR FUNCTIONS.
10338 //===========================================================================================
10340 typedef BOOL (*CREATEHANDLERFUNC
)(IIsapiExtension
*pExtension
, IUnknown
**ppOut
);
10341 typedef BOOL (*INITHANDLERFUNC
)(IHttpServerContext
*, IIsapiExtension
*);
10342 typedef void (*UNINITHANDLERFUNC
)();
10344 struct _HANDLER_ENTRY
10347 CREATEHANDLERFUNC pfnCreate
;
10348 INITHANDLERFUNC pfnInit
;
10349 UNINITHANDLERFUNC pfnUninit
;
10351 // definitions of data segments and _HANDLER_ENTRY delimiters
10352 #pragma section("ATLS$A", read, shared)
10353 #pragma section("ATLS$Z", read, shared)
10354 #pragma section("ATLS$C", read, shared)
10357 __declspec(selectany
) __declspec(allocate("ATLS$A")) ATL::_HANDLER_ENTRY
* __phdlrA
= NULL
;
10358 __declspec(selectany
) __declspec(allocate("ATLS$Z")) ATL::_HANDLER_ENTRY
* __phdlrZ
= NULL
;
10361 #if !defined(_M_IA64)
10362 #pragma comment(linker, "/merge:ATLS=.rdata")
10365 #ifndef HANDLER_ENTRY_PRAGMA
10367 #if defined(_M_IX86)
10368 #define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:___phdlrEntry_" #class "_" #line));
10369 #elif defined(_M_IA64)
10370 #define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:__phdlrEntry_" #class "_" #line));
10371 #elif defined(_M_AMD64)
10372 #define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:__phdlrEntry_" #class "_" #line));
10374 #error Unknown Platform. define HANDLER_ENTRY_PRAGMA
10377 #endif // HANDLER_ENTRY_PRAGMA
10379 // DECLARE_REQUEST_HANDLER macro
10380 #define __DECLARE_REQUEST_HANDLER_INTERNAL(handlerName, className, classQName, lineNum) \
10381 __declspec(selectany) ATL::_HANDLER_ENTRY __hdlrEntry_ ## className ## _ ## lineNum = { handlerName, classQName::CreateRequestHandler, classQName::InitRequestHandlerClass, classQName::UninitRequestHandlerClass }; \
10382 extern "C" __declspec(allocate("ATLS$C")) __declspec(selectany) \
10383 ATL::_HANDLER_ENTRY * const __phdlrEntry_ ## className ## _ ## lineNum = &__hdlrEntry_ ## className ## _ ## lineNum; \
10384 HANDLER_ENTRY_PRAGMA(className, lineNum) \
10385 __if_not_exists(GetAtlHandlerByName) \
10387 extern "C" ATL_NOINLINE inline BOOL __declspec(dllexport) __stdcall GetAtlHandlerByName(LPCSTR szHandlerName, IIsapiExtension *pExtension, IUnknown **ppHandler) throw() \
10389 *ppHandler = NULL; \
10390 ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
10391 while (pEntry != &__phdlrZ) \
10393 if (*pEntry && (*pEntry)->szName) \
10395 if (strcmp((*pEntry)->szName, szHandlerName)==0) \
10397 return (*(*pEntry)->pfnCreate)(pExtension, ppHandler); \
10404 extern "C" ATL_NOINLINE inline BOOL __declspec(dllexport) __stdcall InitializeAtlHandlers(IHttpServerContext *pContext, IIsapiExtension *pExt) throw() \
10406 ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
10407 BOOL bRet = TRUE; \
10408 while (pEntry != &__phdlrZ) \
10410 if (*pEntry && (*pEntry)->szName && (*pEntry)->pfnInit) \
10412 bRet = (*(*pEntry)->pfnInit)(pContext, pExt); \
10420 if (pEntry == &__phdlrA) \
10426 (*(*pEntry)->pfnUninit)(); \
10428 while (pEntry != &__phdlrA); \
10432 extern "C" ATL_NOINLINE inline void __declspec(dllexport) __stdcall UninitializeAtlHandlers() throw() \
10434 ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
10435 while (pEntry != &__phdlrZ) \
10437 if (*pEntry && (*pEntry)->szName && (*pEntry)->pfnUninit) \
10439 (*(*pEntry)->pfnUninit)(); \
10446 #define __DECLARE_REQUEST_HANDLER(handlerName, className, classQName, lineNum) __DECLARE_REQUEST_HANDLER_INTERNAL(handlerName, className, classQName, lineNum)
10447 #define DECLARE_REQUEST_HANDLER(handlerName, className, classQName) __DECLARE_REQUEST_HANDLER(handlerName, className, classQName, __COUNTER__)
10449 #define BEGIN_HANDLER_MAP()
10450 #define HANDLER_ENTRY(handlerName, className) DECLARE_REQUEST_HANDLER(handlerName, className, className)
10451 #define HANDLER_ENTRY_EX(handlerName, className, classQName) DECLARE_REQUEST_HANDLER(handlerName, className, classQName)
10452 #define END_HANDLER_MAP()
10454 #define __HANDLER_ENTRY_SDL_INTERNAL(handlerString, handlerClass, handlerQClass, sdlClassName, lineNum)\
10455 _ATLSOAP_DECLARE_WSDL_SRF() \
10456 extern __declspec(selectany) const char const s_szClassName##sdlClassName##lineNum[]=handlerString;\
10457 typedef ATL::CSDLGenerator<handlerQClass, s_szClassName##sdlClassName##lineNum> sdlClassName; \
10458 HANDLER_ENTRY_EX(handlerString, handlerClass, handlerQClass)\
10459 HANDLER_ENTRY(#sdlClassName, sdlClassName)
10461 #define __HANDLER_ENTRY_SDL(handlerString, handlerClass, handlerQClass, sdlClassName, lineNum) __HANDLER_ENTRY_SDL_INTERNAL(handlerString, handlerClass, handlerQClass, sdlClassName, lineNum)
10462 #define HANDLER_ENTRY_SDL(handlerString, handlerClass, handlerQClass, sdlClassName) __HANDLER_ENTRY_SDL(handlerString, handlerClass, handlerQClass, sdlClassName, __COUNTER__)
10464 // Use this class to check the authorization level of a client who is making
10465 // a request to this application. This class checks for the stronger authentication
10466 // levels (NTLM and Negotiate). You can call it directly from an implementation
10467 // of HandleRequest to check authorization before handling a request.
10468 #define MAX_AUTH_TYPE 50
10469 #define MAX_NAME_LEN 255
10475 HTTP_CODE
IsAuthorized(__in AtlServerRequest
*pInfo
, __in_opt
const SID
* psidAuthGroup
)
10478 ATLENSURE(pInfo
->pServerContext
);
10479 ATLASSERT(psidAuthGroup
);
10480 ATLASSERT(::IsValidSid((PSID
) psidAuthGroup
));
10482 HTTP_CODE hcErr
= HTTP_UNAUTHORIZED
;
10483 char szAuthType
[MAX_AUTH_TYPE
];
10484 DWORD dwSize
= MAX_AUTH_TYPE
;
10487 if (pInfo
->pServerContext
->GetServerVariable("AUTH_TYPE",
10488 szAuthType
, &dwSize
))
10490 if (szAuthType
[0] && (!_stricmp(szAuthType
, "NTLM")
10491 || !_stricmp(szAuthType
, "Negotiate")))
10493 // if we were passed a group name
10494 // we check to see that the logged on user is part
10495 // of that group, else we just return success.
10498 T
* pT
= static_cast<T
*>(this);
10499 if (pT
->CheckAccount(pInfo
->pServerContext
, psidAuthGroup
))
10500 hcErr
= HTTP_SUCCESS
;
10502 hcErr
= pT
->HandleError(pInfo
);
10505 hcErr
= HTTP_SUCCESS
;
10517 virtual bool CheckAccount(IHttpServerContext
*pContext
, const SID
*psidAuthGroup
) throw()
10519 (pContext
); // unused
10520 (psidAuthGroup
); // unused
10524 HTTP_CODE
HandleError(AtlServerRequest
*pRequestInfo
) throw()
10526 pRequestInfo
; // unused
10530 __checkReturn
bool CheckAuthAccount(__in IHttpServerContext
*pContext
, __in
const SID
* psidAuthGroup
) throw()
10532 ATLASSERT(pContext
);
10533 ATLASSERT(psidAuthGroup
);
10534 if (!pContext
|| !psidAuthGroup
)
10537 HANDLE hToken
= INVALID_HANDLE_VALUE
;
10541 if (!pContext
->GetImpersonationToken(&hToken
) ||
10542 hToken
== INVALID_HANDLE_VALUE
)
10546 tok
.Attach(hToken
);
10547 bool bRet
= tok
.CheckTokenMembership(CSid(psidAuthGroup
), &bIsMember
);
10562 // Checks that the user that is logging on is in the required group
10563 class CDefaultAuth
:
10564 public CVerifyAuth
<CDefaultAuth
>
10567 virtual bool CheckAccount(__in IHttpServerContext
*pContext
, __in
const SID
* psidAuthGroup
) throw()
10569 return CheckAuthAccount(pContext
, psidAuthGroup
);
10572 HTTP_CODE
HandleError(__in AtlServerRequest
*pRequestInfo
) throw()
10574 ATLASSERT(pRequestInfo
); // should always be valid
10582 CHttpResponse
response(pRequestInfo
->pServerContext
);
10583 response
.Write(GetErrorResponse());
10591 return HTTP_SUCCESS_NO_PROCESS
;
10594 virtual LPCSTR
GetErrorResponse()
10596 static const char *szResponse
= "<html><body>"
10597 "<H1 align=center>NOT AUTHORIZED</H1><p>"
10607 #pragma warning(pop)
10609 #endif // __ATLISAPI_H__