1 // This is a part of the Active Template Library.
\r
2 // Copyright (C) Microsoft Corporation
\r
3 // All rights reserved.
\r
5 // This source code is only intended as a supplement to the
\r
6 // Active Template Library Reference and related
\r
7 // electronic documentation provided with the library.
\r
8 // See these sources for detailed information regarding the
\r
9 // Active Template Library product.
\r
11 #ifndef __ATLHTTP_INL__
\r
12 #define __ATLHTTP_INL__
\r
16 #pragma warning(push)
\r
17 #pragma warning(disable: 4061) // enumerate 'enum value' in switch of enum 'enum type' is not explicitly handled by a case label
\r
18 #pragma warning(disable: 4062) // enumerate 'enum value' in switch of enum 'enum type' is not handled
\r
23 /////////////////////////////////////////////////////////////////////////////////
\r
26 // Implementation of CAtlHttpClient member functions
\r
28 /////////////////////////////////////////////////////////////////////////////////
\r
29 template <class TSocketClass>
\r
30 inline CAtlHttpClientT<TSocketClass>::CAtlHttpClientT() throw()
\r
35 // Sets this object to a known state.
\r
36 template <class TSocketClass>
\r
37 inline void CAtlHttpClientT<TSocketClass>::InitializeObject() throw()
\r
39 Close(); // will close the socket if it's already open
\r
41 SetSilentLogonOk(FALSE);
\r
44 template <class TSocketClass>
\r
45 inline void CAtlHttpClientT<TSocketClass>::ResetRequest() throw()
\r
47 // reset all data that has to do with the current request
\r
48 m_HeaderMap.RemoveAll();
\r
50 m_urlCurrent.Clear();
\r
51 m_strMethod.Empty();
\r
52 m_nStatus = ATL_INVALID_STATUS;
\r
55 m_dwHeaderStart = 0;
\r
58 m_LastResponseParseError = RR_NOT_READ;
\r
64 // Use this function to retrieve an entity from a server via an HTTP
\r
65 // request. This function will either request a connection from the
\r
66 // server specified in the szURL parameter or request a connection from
\r
67 // the proxy server. If a proxy server is to be used, you must call
\r
68 // SetProxy prior to calling this function to specify the proxy server
\r
69 // being used. Once the connection is established, an HTTP request
\r
70 // is built and sent to the HTTP server. An attempt to read the HTTP
\r
71 // response is then made. If the response is successfully read, the
\r
72 // response will be parsed and stored in this class instance. The
\r
73 // headers can be parsed via the LookupHeader function and the body
\r
74 // of the response can be retrieved using the GetBody function. You
\r
75 // can also retrieve the contents of the entire response by calling
\r
77 template <class TSocketClass>
\r
78 inline bool CAtlHttpClientT<TSocketClass>::Navigate(
\r
81 ATL_NAVIGATE_DATA *pNavData
\r
84 if (!szUrl || *szUrl == _T('\0'))
\r
88 TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
\r
89 if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
\r
92 if(!url.CrackUrl(szTmp))
\r
96 return Navigate(&url, pNavData);
\r
99 template <class TSocketClass>
\r
100 inline bool CAtlHttpClientT<TSocketClass>::Navigate(
\r
103 ATL_NAVIGATE_DATA *pNavData
\r
107 if (!szServer || *szServer == _T('\0'))
\r
109 if (!szPath || *szPath == _T('\0'))
\r
112 url.SetScheme(ATL_URL_SCHEME_HTTP);
\r
113 url.SetHostName(szServer);
\r
114 url.SetUrlPath(szPath);
\r
116 url.SetPortNumber(pNavData->nPort);
\r
118 url.SetPortNumber(ATL_URL_DEFAULT_HTTP_PORT);
\r
120 TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
\r
121 DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
\r
122 if (!url.CreateUrl(szUrl, &dwMaxLen))
\r
126 return Navigate(szUrl, pNavData);
\r
129 template <class TSocketClass>
\r
130 inline bool CAtlHttpClientT<TSocketClass>::Navigate(
\r
132 ATL_NAVIGATE_DATA *pData
\r
141 CAtlNavigateData default_nav_data;
\r
143 m_pNavData = &default_nav_data;
\r
145 m_pNavData = pData;
\r
147 ATLASSUME(m_pNavData);
\r
151 m_strMethod = m_pNavData->szMethod;
\r
158 SetSocketTimeout(m_pNavData->dwTimeout);
\r
160 // set m_urlCurrent
\r
161 if (!SetDefaultUrl(pUrl, m_pNavData->nPort))
\r
164 CString strRequest;
\r
165 CString strExtraInfo;
\r
167 if (!BuildRequest(&strRequest,
\r
168 m_pNavData->szMethod,
\r
169 m_pNavData->szExtraHeaders))
\r
175 if (!ConnectSocket())
\r
178 LPCTSTR szTRequest = strRequest;
\r
179 CT2CA strARequest(szTRequest);
\r
180 DWORD dwRequestLen = (DWORD)strlen(strARequest);
\r
181 DWORD dwAvailable = dwRequestLen + m_pNavData->dwDataLen;
\r
183 if (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_CALLBACK)
\r
185 dwSent = WriteWithCallback(strARequest, dwRequestLen);
\r
187 else if (!m_pNavData->pData)
\r
188 dwSent = WriteWithNoData(strARequest, dwRequestLen);
\r
189 else if (m_pNavData->pData && (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_BLOCKS))
\r
191 dwSent = WriteWithChunks(strARequest, dwRequestLen);
\r
193 else if(m_pNavData->pData)
\r
195 dwSent = WriteWithData(strARequest, dwRequestLen);
\r
199 // make sure everything was sent
\r
200 if (dwSent == dwAvailable)
\r
202 // Read the response
\r
203 if (RR_OK == ReadHttpResponse())
\r
205 // if navigation isn't complete, try to complete
\r
206 // it based on the status code and flags
\r
207 if ((m_pNavData->dwFlags & ATL_HTTP_FLAG_PROCESS_RESULT)&&
\r
208 !ProcessStatus(m_pNavData->dwFlags))
\r
220 Close(); // some kind of failure happened, close the socket.
\r
226 template <class TSocketClass>
\r
227 inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithNoData(LPCSTR pRequest, DWORD dwRequestLen)
\r
229 ATLASSUME(m_pNavData);
\r
231 Buffer.buf = (char*)pRequest;
\r
232 Buffer.len = (int)dwRequestLen;
\r
233 DWORD dwWritten = 0;
\r
234 Write(&Buffer, 1, &dwWritten);
\r
235 if (m_pNavData->pfnSendStatusCallback)
\r
236 m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend);
\r
240 // The entity body will be retrieved from the client by calling their
\r
241 // callback function.
\r
242 template <class TSocketClass>
\r
243 inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithCallback(LPCSTR pRequest, DWORD dwRequestLen)
\r
245 ATLASSUME(m_pNavData);
\r
246 if (!(m_pNavData->pfnChunkCallback &&
\r
247 (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_CALLBACK)))
\r
248 return 0; // error, must have flag set and callback function
\r
250 // write the request
\r
251 DWORD dwTotalWritten = 0;
\r
253 Buffer.buf = (char*)pRequest;
\r
254 Buffer.len = (int)dwRequestLen;
\r
255 DWORD dwWritten = 0;
\r
256 Write(&Buffer, 1, &dwWritten);
\r
257 if (m_pNavData->pfnSendStatusCallback)
\r
258 if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
\r
261 return 0; // failure
\r
262 dwTotalWritten += dwWritten;
\r
264 // start writing data;
\r
265 while (m_pNavData->pfnChunkCallback((BYTE**)&Buffer.buf, (DWORD*)&Buffer.len, m_pNavData->m_lParamChunkCB) &&
\r
267 Buffer.buf != NULL)
\r
269 Write(&Buffer, 1, &dwWritten);
\r
270 if (dwWritten != Buffer.len)
\r
272 if (m_pNavData->pfnSendStatusCallback)
\r
273 if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
\r
275 dwTotalWritten += dwWritten;
\r
277 return dwTotalWritten;
\r
280 template <class TSocketClass>
\r
281 inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithChunks(LPCSTR pRequest, DWORD dwRequestLen)
\r
283 ATLASSUME(m_pNavData);
\r
284 if (!(m_pNavData->dwSendBlockSize > 0 && (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_BLOCKS)))
\r
285 return 0; // error, must have flag set and callback function
\r
287 // write the request
\r
288 DWORD dwTotalWritten = 0;
\r
290 Buffer.buf = (char*)pRequest;
\r
291 Buffer.len = (int)dwRequestLen;
\r
292 DWORD dwWritten = 0;
\r
293 Write(&Buffer, 1, &dwWritten);
\r
294 if (m_pNavData->pfnSendStatusCallback)
\r
295 if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
\r
298 return 0; // failure
\r
299 dwTotalWritten += dwWritten;
\r
301 // start writing data;
\r
302 DWORD dwDataWritten = 0;
\r
303 DWORD dwDataLeft = m_pNavData->dwDataLen;
\r
306 Buffer.buf = (char*)(m_pNavData->pData + dwDataWritten);
\r
307 Buffer.len = __min(dwDataLeft, m_pNavData->dwSendBlockSize);
\r
308 Write(&Buffer, 1, &dwWritten);
\r
309 if (dwWritten != Buffer.len)
\r
311 if (m_pNavData->pfnSendStatusCallback)
\r
312 if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
\r
314 dwTotalWritten += dwWritten;
\r
315 dwDataWritten += dwWritten;
\r
316 dwDataLeft -= dwWritten;
\r
318 return dwTotalWritten;
\r
321 template <class TSocketClass>
\r
322 inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithData(LPCSTR pRequest, DWORD dwRequestLen)
\r
325 Buffers[0].buf = (char*)pRequest;
\r
326 Buffers[0].len = dwRequestLen;
\r
327 Buffers[1].buf = (char*)m_pNavData->pData;
\r
328 Buffers[1].len = m_pNavData->dwDataLen;
\r
330 DWORD dwWritten = 0;
\r
331 Write(Buffers, 2, &dwWritten);
\r
332 if (m_pNavData->pfnSendStatusCallback)
\r
333 m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend);
\r
338 template <class TSocketClass>
\r
339 bool CAtlHttpClientT<TSocketClass>::NavigateChunked(
\r
342 ATL_NAVIGATE_DATA *pNavData
\r
346 if (!szServer || *szServer == _T('\0'))
\r
348 if (!szPath || *szPath == _T('\0'))
\r
353 // To do chunked navigation you must specify an
\r
354 // ATL_NAVIGATE_DATA structure that has the pfnChunkCallback
\r
355 // member filled out.
\r
360 url.SetScheme(ATL_URL_SCHEME_HTTP);
\r
361 url.SetHostName(szServer);
\r
362 url.SetUrlPath(szPath);
\r
364 url.SetPortNumber(pNavData->nPort);
\r
366 url.SetPortNumber(ATL_URL_DEFAULT_HTTP_PORT);
\r
368 TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
\r
369 DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
\r
370 if (!url.CreateUrl(szUrl, &dwMaxLen))
\r
374 return NavigateChunked(szUrl, pNavData);
\r
377 template <class TSocketClass>
\r
378 bool CAtlHttpClientT<TSocketClass>::NavigateChunked(
\r
380 ATL_NAVIGATE_DATA *pNavData
\r
383 if (!szURL || *szURL == _T('\0'))
\r
388 ATLASSERT(pNavData);
\r
391 if (!url.CrackUrl(szURL))
\r
395 return NavigateChunked(&url, pNavData);
\r
398 template <class TSocketClass>
\r
399 inline bool CAtlHttpClientT<TSocketClass>::NavigateChunked(
\r
401 ATL_NAVIGATE_DATA *pNavData
\r
409 // To do chunked navigation you must specify an
\r
410 // ATL_NAVIGATE_DATA structure that has the pfnChunkCallback
\r
411 // member filled out.
\r
416 m_pNavData = pNavData;
\r
417 if (!pNavData->pfnChunkCallback)
\r
424 m_strMethod = m_pNavData->szMethod;
\r
431 SetSocketTimeout(m_pNavData->dwTimeout);
\r
433 // set m_urlCurrent
\r
434 if (!SetDefaultUrl(pUrl, m_pNavData->nPort))
\r
439 CString strRequest;
\r
440 CString strExtraInfo;
\r
442 if (!BuildRequest(&strRequest,
\r
443 m_pNavData->szMethod,
\r
444 m_pNavData->szExtraHeaders // extra headers
\r
450 if (!ConnectSocket())
\r
457 CT2A pRequest(strRequest);
\r
459 Buffers[0].buf = (char*)pRequest;
\r
460 Buffers[0].len = strRequest.GetLength();
\r
462 // send the first buffer which is the request
\r
463 if (!Write(Buffers, 1, &dwSent))
\r
474 Buffers[0].buf = NULL;
\r
475 Buffers[0].len = 0;
\r
477 CStringA strChunkSize;
\r
479 Buffers[2].buf = "\r\n";
\r
480 Buffers[2].len = 2;
\r
483 // start sending the chunks
\r
487 Buffers[1].buf = NULL;
\r
488 Buffers[1].len = 0;
\r
489 if (m_pNavData->pfnChunkCallback((BYTE**)&Buffers[1].buf, &Buffers[1].len,
\r
490 m_pNavData->m_lParamChunkCB))
\r
494 if (Buffers[1].len > 0)
\r
497 strChunkSize.Format("%x\r\n", Buffers[1].len);
\r
498 Buffers[0].buf = (char*)(LPCSTR)strChunkSize;
\r
499 Buffers[0].len = strChunkSize.GetLength();
\r
500 if (!Write(Buffers, 3, &dwSent))
\r
506 else if (Buffers[1].len == 0)
\r
508 strChunkSize = "0\r\n\r\n\r\n";
\r
509 Buffers[0].buf = (char*)(LPCSTR)strChunkSize;
\r
510 Buffers[0].len = strChunkSize.GetLength();
\r
511 if (!Write(Buffers, 1, &dwSent))
\r
528 break; // something went wrong in callback
\r
530 }while (Buffers[1].len > 0);
\r
532 strRequest.ReleaseBuffer();
\r
536 // Read the response
\r
537 if (RR_OK == ReadHttpResponse())
\r
539 // if navigation isn't complete, try to complete
\r
540 // it based on the status code and flags
\r
541 if ((m_pNavData->dwFlags & ATL_HTTP_FLAG_PROCESS_RESULT)
\r
542 && !ProcessStatus(m_pNavData->dwFlags))
\r
558 template <class TSocketClass>
\r
559 inline bool CAtlHttpClientT<TSocketClass>::ConnectSocket() throw()
\r
562 // connect to the correct server
\r
565 //if we're using a proxy connect to the proxy
\r
566 bRet=Connect(m_strProxy, m_nProxyPort);
\r
570 bRet=Connect(m_urlCurrent.GetHostName(),m_urlCurrent.GetPortNumber()); // connect to the server
\r
576 template <class TSocketClass>
\r
577 inline bool CAtlHttpClientT<TSocketClass>::BuildRequest(/*out*/CString *pstrRequest,
\r
579 LPCTSTR szExtraHeaders) throw()
\r
585 // build up the request
\r
586 CString strRequest = szMethod;
\r
587 strRequest += _T(" ");
\r
590 TCHAR buffURL[ATL_URL_MAX_URL_LENGTH];
\r
591 DWORD dwSize = ATL_URL_MAX_URL_LENGTH;
\r
592 m_urlCurrent.CreateUrl(buffURL, &dwSize);
\r
593 strRequest += buffURL;
\r
595 strRequest += ATL_HTTP_HEADER_PROXY;
\r
597 if (m_urlCurrent.GetPortNumber() != ATL_URL_DEFAULT_HTTP_PORT)
\r
598 strHost.Format(_T("Host: %s:%d\r\n"), m_urlCurrent.GetHostName(), m_urlCurrent.GetPortNumber());
\r
600 strHost.Format(_T("Host: %s\r\n"), m_urlCurrent.GetHostName());
\r
601 strRequest += strHost;
\r
603 if (m_pNavData->dwDataLen>0)
\r
606 strCL.Format(_T("Content-Length: %d\r\n"), m_pNavData->dwDataLen);
\r
607 strRequest += strCL;
\r
610 if (m_pNavData->szDataType)
\r
612 strRequest += _T("Content-Type: ");
\r
613 strRequest += m_pNavData->szDataType;
\r
614 strRequest += _T("\r\n");
\r
617 if (m_pNavData->szExtraHeaders)
\r
618 strRequest += szExtraHeaders;
\r
619 strRequest += ATL_HTTP_USERAGENT;
\r
623 strRequest += m_urlCurrent.GetUrlPath();
\r
624 strRequest += m_urlCurrent.GetExtraInfo();
\r
625 strRequest += ATL_HTTP_HEADER;
\r
627 if (m_pNavData->dwDataLen > 0)
\r
630 strCL.Format(_T("Content-Length: %d\r\n"), m_pNavData->dwDataLen);
\r
631 strRequest += strCL;
\r
634 if (m_pNavData->szDataType &&
\r
635 *m_pNavData->szDataType)
\r
637 strRequest += _T("Content-Type: ");
\r
638 strRequest += m_pNavData->szDataType;
\r
639 strRequest += _T("\r\n");
\r
642 if (szExtraHeaders)
\r
643 strRequest += szExtraHeaders;
\r
647 strHost.Format(_T("Host: %s\r\n"), m_urlCurrent.GetHostName());
\r
648 strRequest += strHost;
\r
649 strRequest += ATL_HTTP_USERAGENT;
\r
651 strRequest += _T("\r\n");
\r
654 *pstrRequest = strRequest;
\r
663 template <class TSocketClass>
\r
664 inline bool CAtlHttpClientT<TSocketClass>::ReadSocket() throw()
\r
667 unsigned char read_buff[ATL_READ_BUFF_SIZE];
\r
668 int dwSize = ATL_READ_BUFF_SIZE;
\r
671 for (int i = 0; i < ATL_HTTP_CLIENT_EMPTY_READ_RETRIES; ++i)
\r
673 bRet = Read(read_buff, (DWORD*)&dwSize);
\r
680 if (m_pNavData->pfnReadStatusCallback)
\r
681 bRet = m_pNavData->pfnReadStatusCallback(dwSize, m_pNavData->m_lParamRead);
\r
688 // append the data to our internal buffer
\r
689 // m_current holds bytes (not UNICODE!)
\r
691 if (!m_current.Append((LPCSTR)read_buff, dwSize))
\r
694 m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
\r
695 m_pCurrent = (BYTE*)(LPCSTR)m_current;
\r
698 bRet = false; // nothing was read
\r
704 // Starts searching for a complete header set at
\r
705 // m_pCurrent. This function will only move m_pCurrent
\r
706 // if a complete set is found. Returns the header beginning
\r
708 template <class TSocketClass>
\r
709 inline unsigned char* CAtlHttpClientT<TSocketClass>::FindHeaderEnd(unsigned char** ppBegin) throw()
\r
714 BYTE *pCurr = m_pCurrent;
\r
715 BYTE *pBegin = m_pCurrent;
\r
716 int nLen = m_current.GetLength();
\r
718 if (pCurr >= (BYTE*)(LPCSTR)m_current + m_current.GetLength())
\r
719 return NULL; // no more chars in buffer
\r
720 // look for the end of the header (the \r\n\r\n)
\r
721 while (pCurr <= (pBegin + nLen - ATL_HEADER_END_LEN))
\r
724 if (* ((UNALIGNED DWORD*)pCurr)==ATL_DW_HEADER_END)
\r
726 // set m_pCurrent pointer to the end of the header
\r
727 m_pCurrent = pCurr + ATL_HEADER_END_LEN;
\r
737 // Call this function after sending an HTTP request over the socket. The complete
\r
738 // HTTP response will be read. This function will also parse
\r
739 // response headers into the response header map.
\r
740 template <class TSocketClass>
\r
741 inline typename CAtlHttpClientT<TSocketClass>::HTTP_RESPONSE_READ_STATUS CAtlHttpClientT<TSocketClass>::ReadHttpResponse()
\r
743 // Read until we at least have the response headers
\r
744 HTTP_RESPONSE_READ_STATUS result = RR_OK;
\r
745 readstate state = rs_init;
\r
746 unsigned char *pBodyBegin = NULL;
\r
747 unsigned char *pHeaderBegin = NULL;
\r
750 m_LastResponseParseError = RR_OK;
\r
752 while (state != rs_complete)
\r
757 m_HeaderMap.RemoveAll();
\r
758 m_nStatus = ATL_INVALID_STATUS;
\r
761 state = rs_readheader;
\r
764 case rs_readheader:
\r
766 // read from the socket until we have a complete set of headers.
\r
767 pBodyBegin = FindHeaderEnd(&pHeaderBegin);
\r
772 // Either reading from the socket failed, or there
\r
773 // was not data to read. Set the nav status to error
\r
774 // and change the state to complete.
\r
775 state = rs_complete;
\r
776 result = RR_READSOCKET_FAILED;
\r
780 break; // loop back and FindHeaderEnd again.
\r
782 // we have a complete set of headers
\r
783 m_dwHeaderLen = (DWORD)(pBodyBegin-pHeaderBegin);
\r
784 m_dwHeaderStart = (DWORD)(pHeaderBegin - (BYTE*)(LPCSTR)m_current);
\r
786 state = rs_scanheader;
\r
788 case rs_scanheader:
\r
789 // set m_nStatus and check for valid status
\r
790 ParseStatusLine(pHeaderBegin);
\r
791 // failed to set m_nStatus;
\r
792 if (m_nStatus == ATL_INVALID_STATUS)
\r
794 state = rs_complete;
\r
795 result = RR_STATUS_INVALID;
\r
799 else if (m_nStatus == 100) // continue
\r
805 // crack all the headers and put them into a header map. We've already
\r
806 // done the check to make sure we have a complete set of headers in
\r
807 // rs_readheader above
\r
808 if (ATL_HEADER_PARSE_COMPLETE != CrackResponseHeader((LPCSTR)pHeaderBegin,
\r
809 (LPCSTR*)&pBodyBegin))
\r
811 // something bad happened while parsing the headers!
\r
812 state = rs_complete;
\r
813 result = RR_PARSEHEADERS_FAILED;
\r
816 state = rs_readbody;
\r
820 // headers are parsed and cracked, we're ready to read the rest
\r
821 // of the response.
\r
822 if (IsMsgBodyChunked())
\r
824 if (!ReadChunkedBody())
\r
826 result = RR_READCHUNKEDBODY_FAILED;
\r
827 state = rs_complete;
\r
832 if (!ReadBody(GetContentLength(), m_current.GetLength()-(m_dwHeaderStart+m_dwHeaderLen)))
\r
833 result = RR_READBODY_FAILED;
\r
834 state = rs_complete;
\r
838 // clean up the connection if the server requested a close;
\r
839 DisconnectIfRequired();
\r
843 m_LastResponseParseError = result;
\r
847 template <class TSocketClass>
\r
848 inline typename CAtlHttpClientT<TSocketClass>::HTTP_RESPONSE_READ_STATUS CAtlHttpClientT<TSocketClass>::GetResponseStatus()
\r
850 return m_LastResponseParseError;
\r
853 // Checks to see if the server has closed the connection.
\r
854 // If it has, we create a new socket and reconnect it to
\r
855 // the current server. This also clears the contents of the
\r
856 // current response buffer.
\r
857 template <class TSocketClass>
\r
858 inline void CAtlHttpClientT<TSocketClass>::ResetConnection() throw()
\r
860 ReconnectIfRequired();
\r
861 m_HeaderMap.RemoveAll();
\r
863 m_nStatus = ATL_INVALID_STATUS;
\r
864 m_AuthTypes.RemoveAll(); // the server will keep sending back www-authenticate
\r
865 // headers until the connection is authorized
\r
868 // Takes action based on the flags passed and the current
\r
869 // status for this object.
\r
870 template <class TSocketClass>
\r
871 inline bool CAtlHttpClientT<TSocketClass>::ProcessStatus(DWORD dwFlags) throw()
\r
875 case 200: // In all these cases there is no further action
\r
876 case 201: // to take. Any additional informaion is returned
\r
877 case 202: // in the entity body.
\r
889 if (dwFlags & ATL_HTTP_FLAG_AUTO_REDIRECT)
\r
890 return ProcessObjectMoved();
\r
892 case 401: // auth required
\r
893 return NegotiateAuth(false);
\r
895 case 407: // proxy auth required
\r
896 return NegotiateAuth(true);
\r
903 // Looks up the value of a response header in the header map. Call with
\r
904 // NULL szBuffer to have length of the required buffer placed in
\r
905 // pdwLen on output.
\r
907 // szName is the name of the header to look up.
\r
908 // szBuffer is the buffer that will contain the looked up string.
\r
909 // pdwLen contains the length of szBuffer in characters on input and the length
\r
910 // of the string including NULL terminator in characters on output.
\r
911 template<class TSocketClass>
\r
912 inline bool CAtlHttpClientT<TSocketClass>::GetHeaderValue(LPCTSTR szName, CString& strValue) const throw()
\r
916 return m_HeaderMap.Lookup(szName, strValue);
\r
924 template<class TSocketClass>
\r
925 inline bool CAtlHttpClientT<TSocketClass>::GetHeaderValue(__in_z LPCTSTR szName, __out_ecount_part_z_opt(*pdwLen, *pdwLen) LPTSTR szBuffer, __inout DWORD *pdwLen) const throw()
\r
928 bool bRet = GetHeaderValue(szName, strValue);
\r
929 DWORD nLen = strValue.GetLength();
\r
933 if ((pdwLen && *pdwLen < nLen+1) ||
\r
934 (!szBuffer && pdwLen) )
\r
943 Checked::tcsncpy_s(szBuffer, nLen+1, (LPCTSTR)strValue, _TRUNCATE);
\r
949 // Adds an authorization object to use for a particular scheme.
\r
950 // This will overwrite an existing entry if an object for the
\r
951 // same scheme has already been set.
\r
952 template<class TSocketClass>
\r
953 inline bool CAtlHttpClientT<TSocketClass>::AddAuthObj(LPCTSTR szScheme,
\r
954 CAtlBaseAuthObject *pObject, IAuthInfo *pInfo/*=NULL*/) throw()
\r
959 pObject->Init(this, pInfo);
\r
963 POSITION pos = m_AuthMap.SetAt(szScheme, pObject);
\r
975 // Tries to find an authorization object to use for a particular
\r
977 template<class TSocketClass>
\r
978 inline const CAtlBaseAuthObject* CAtlHttpClientT<TSocketClass>::FindAuthObject(LPCTSTR szScheme) throw()
\r
980 CAtlBaseAuthObject *pObject = NULL;
\r
981 if (m_AuthMap.Lookup(szScheme, pObject))
\r
983 return const_cast<const CAtlBaseAuthObject*>(pObject);
\r
988 // Removes an existing authorization object from the map.
\r
989 template<class TSocketClass>
\r
990 inline bool CAtlHttpClientT<TSocketClass>::RemoveAuthObject(LPCTSTR szScheme) throw()
\r
992 return m_AuthMap.RemoveKey(szScheme);
\r
995 // Sets the current proxy server and port
\r
996 template<class TSocketClass>
\r
997 inline bool CAtlHttpClientT<TSocketClass>::SetProxy(LPCTSTR szProxy, short nProxyPort) throw()
\r
1001 if (!LookupRegProxy())
\r
1008 m_strProxy = szProxy;
\r
1009 m_nProxyPort = nProxyPort;
\r
1019 // Removes the current proxy settings.
\r
1020 template<class TSocketClass>
\r
1021 inline void CAtlHttpClientT<TSocketClass>::RemoveProxy() throw()
\r
1023 m_strProxy.Empty();
\r
1024 m_nProxyPort = ATL_URL_INVALID_PORT_NUMBER;
\r
1027 // retrieves the current proxy
\r
1028 template<class TSocketClass>
\r
1029 inline LPCTSTR CAtlHttpClientT<TSocketClass>::GetProxy() const throw()
\r
1031 if (m_strProxy.GetLength())
\r
1032 return (LPCTSTR)m_strProxy;
\r
1036 template<class TSocketClass>
\r
1037 inline short CAtlHttpClientT<TSocketClass>::GetProxyPort() const throw()
\r
1039 return m_nProxyPort;
\r
1042 // Gets the contents of the entire response buffer.
\r
1043 template<class TSocketClass>
\r
1044 inline const BYTE* CAtlHttpClientT<TSocketClass>::GetResponse() throw()
\r
1046 return (const BYTE*)(LPCSTR)m_current;
\r
1049 template<class TSocketClass>
\r
1050 inline DWORD CAtlHttpClientT<TSocketClass>::GetResponseLength() throw()
\r
1052 return m_current.GetLength();
\r
1055 // Gets the length in bytes of the body of the
\r
1056 // current response
\r
1057 template<class TSocketClass>
\r
1058 inline DWORD CAtlHttpClientT<TSocketClass>::GetBodyLength() const throw()
\r
1060 return m_dwBodyLen;
\r
1063 // Gets the contents of the body of the current response. This
\r
1064 // is the response without the headers.
\r
1065 template<class TSocketClass>
\r
1066 inline const BYTE* CAtlHttpClientT<TSocketClass>::GetBody() throw()
\r
1068 return (BYTE*)((LPCSTR)m_current + m_dwHeaderLen + m_dwHeaderStart);
\r
1071 // Get the length of the header part of the response in bytes.
\r
1072 template<class TSocketClass>
\r
1073 inline DWORD CAtlHttpClientT<TSocketClass>::GetRawResponseHeaderLength() throw()
\r
1075 return m_dwHeaderLen >= 2 ? m_dwHeaderLen-2 : 0; // m_dwHeaderLen includes the final \r\n
\r
1078 // buffer must include space for null terminator.
\r
1079 // on input, pdwLen specifies the size of szBuffer,
\r
1080 // on output, pdwLen holds the number of bytes copied
\r
1081 // to szBuffer, or the required size of szBuffer if
\r
1082 // szBuffer wasn't big enough
\r
1083 template<class TSocketClass>
\r
1084 inline bool CAtlHttpClientT<TSocketClass>::GetRawResponseHeader(LPBYTE szBuffer, DWORD *pdwLen) throw()
\r
1089 DWORD header_len = GetRawResponseHeaderLength();
\r
1090 if (header_len == 0)
\r
1093 if (!szBuffer || *pdwLen < header_len+1)
\r
1095 *pdwLen = header_len+1;
\r
1099 Checked::memcpy_s(szBuffer, *pdwLen, (BYTE*)(LPCSTR)m_current, header_len);
\r
1100 szBuffer[header_len]='\0';
\r
1102 *pdwLen = header_len+1;
\r
1106 // Gets the current URL object.
\r
1107 template<class TSocketClass>
\r
1108 inline LPCURL CAtlHttpClientT<TSocketClass>::GetCurrentUrl() const throw()
\r
1110 return (LPCURL)&m_urlCurrent;
\r
1113 template<class TSocketClass>
\r
1114 inline bool CAtlHttpClientT<TSocketClass>::SetDefaultUrl( LPCTSTR szUrl,
\r
1115 short nPortNumber) throw()
\r
1117 return _SetDefaultUrl(szUrl,nPortNumber);
\r
1120 template<class TSocketClass>
\r
1121 inline bool CAtlHttpClientT<TSocketClass>::SetDefaultUrl( LPCURL pUrl,
\r
1122 short nPortNumber) throw()
\r
1124 m_urlCurrent = *pUrl;
\r
1125 return _SetDefaultUrl(NULL, nPortNumber);
\r
1128 template<class TSocketClass>
\r
1129 inline bool CAtlHttpClientT<TSocketClass>::SetDefaultMethod(LPCTSTR szMethod) throw()
\r
1134 m_strMethod = szMethod;
\r
1143 template<class TSocketClass>
\r
1144 inline DWORD CAtlHttpClientT<TSocketClass>::GetFlags() const throw()
\r
1147 return m_pNavData->dwFlags;
\r
1149 return ATL_HTTP_FLAG_INVALID_FLAGS;
\r
1152 template<class TSocketClass>
\r
1153 inline bool CAtlHttpClientT<TSocketClass>::LookupRegProxy() throw()
\r
1155 // attempt to look it up from the registry
\r
1157 ULONG nChars = ATL_URL_MAX_URL_LENGTH+1;
\r
1158 TCHAR szUrl[ATL_URL_MAX_URL_LENGTH+1] = { 0 };
\r
1160 DWORD dwErr = rkProxy.Open(HKEY_CURRENT_USER,
\r
1161 _T("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"), KEY_READ);
\r
1162 if (dwErr == ERROR_SUCCESS)
\r
1164 dwErr = rkProxy.QueryStringValue(_T("ProxyServer"), szUrl, &nChars);
\r
1166 if (dwErr == ERROR_SUCCESS)
\r
1169 if (url.CrackUrl(szUrl))
\r
1171 if (url.GetScheme()==ATL_URL_SCHEME_UNKNOWN)
\r
1173 // without the scheme name (e.g. proxy:80)
\r
1174 m_strProxy = url.GetSchemeName();
\r
1175 m_nProxyPort = (short)_ttoi(url.GetHostName());
\r
1178 else if (url.GetHostName())
\r
1180 // with the scheme (e.g. http://proxy:80)
\r
1181 m_strProxy = url.GetHostName();
\r
1182 m_nProxyPort = url.GetPortNumber();
\r
1190 template<class TSocketClass>
\r
1191 inline bool CAtlHttpClientT<TSocketClass>::DisconnectIfRequired() throw()
\r
1194 if (GetHeaderValue(_T("Connection"), strValue) && !strValue.CompareNoCase(_T("close")))
\r
1202 class CInitializeCOMThread
\r
1205 CInitializeCOMThread()
\r
1206 : m_bCoInit(FALSE),m_bShouldUninit(FALSE)
\r
1208 //At this point the Thread can be uninit, init to STA or init to MTA.
\r
1209 //CoInitialize can always fail unexpectedly.
\r
1210 HRESULT hr = ::CoInitialize(NULL);
\r
1211 if (SUCCEEDED(hr))
\r
1214 m_bShouldUninit=TRUE;
\r
1215 } else if (hr == RPC_E_CHANGED_MODE)
\r
1220 ~CInitializeCOMThread()
\r
1222 if (m_bShouldUninit)
\r
1224 ::CoUninitialize();
\r
1227 BOOL IsInitialized() { return m_bCoInit; }
\r
1230 BOOL m_bShouldUninit;
\r
1232 // Tries to find an authorization object that meets
\r
1233 template<class TSocketClass>
\r
1234 inline bool CAtlHttpClientT<TSocketClass>::NegotiateAuth(bool bProxy) throw()
\r
1236 //Test if can silently pass user credentials to server.
\r
1237 if (!m_bSilentLogonOk)
\r
1239 //Call CoInit, because ATL Http code cannot assume it has already been called by the user.
\r
1240 CInitializeCOMThread initThread;
\r
1241 if (initThread.IsInitialized())
\r
1243 HRESULT hr = S_OK;
\r
1244 CComPtr<IInternetSecurityManager> spSecurityMgr;
\r
1246 hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
\r
1247 IID_IInternetSecurityManager, (void**)&spSecurityMgr);
\r
1248 if (SUCCEEDED(hr))
\r
1250 TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
\r
1251 DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
\r
1252 if (!m_urlCurrent.CreateUrl(szUrl, &dwMaxLen))
\r
1257 CStringW strUrlW(szUrl);
\r
1258 DWORD dwPolicy=0xFFFF;
\r
1259 hr=spSecurityMgr->ProcessUrlAction(strUrlW.GetString(),
\r
1260 URLACTION_CREDENTIALS_USE,
\r
1261 reinterpret_cast<BYTE*>(&dwPolicy),
\r
1268 if (FAILED(hr) || dwPolicy != URLPOLICY_CREDENTIALS_SILENT_LOGON_OK)
\r
1275 // CoCreateInstance failed, return false
\r
1281 // CoInit failed, return false
\r
1286 // szAuthHeaderValue should contain a comma separated list
\r
1287 // of authentication types
\r
1288 CAtlBaseAuthObject *pAuthObj = NULL;
\r
1289 bool bRet = false;
\r
1290 for (size_t i = 0; i<m_AuthTypes.GetCount(); i++)
\r
1294 CString strName = m_AuthTypes[i];
\r
1295 int nSpace = strName.Find(_T(' '));
\r
1297 strName.SetAt(nSpace,0);
\r
1299 if (m_AuthMap.Lookup(strName, pAuthObj) &&
\r
1300 !pAuthObj->m_bFailed)
\r
1301 bRet = pAuthObj->Authenticate(m_AuthTypes[i], bProxy);
\r
1314 template<class TSocketClass>
\r
1315 inline long CAtlHttpClientT<TSocketClass>::GetContentLength() throw()
\r
1318 if (GetHeaderValue(_T("Content-Length"), strValue))
\r
1320 TCHAR *pStop = NULL;
\r
1321 return _tcstol(strValue, &pStop, 10);
\r
1327 template<class TSocketClass>
\r
1328 inline LPCSTR CAtlHttpClientT<TSocketClass>::NextLine(BYTE* pCurr) throw()
\r
1333 while ( pCurr < m_pEnd && *pCurr && !(*pCurr == '\r' && *(pCurr+1) == '\n'))
\r
1336 if (pCurr >= m_pEnd)
\r
1339 // if (pCurr < m_pEnd-4)
\r
1340 // if (!memcmp(pCurr, ATL_HEADER_END, 4))
\r
1343 return (LPCSTR)(pCurr+2);
\r
1346 template<class TSocketClass>
\r
1347 inline bool CAtlHttpClientT<TSocketClass>::IsMsgBodyChunked() throw()
\r
1351 GetHeaderValue(_T("Transfer-Encoding"), strValue) &&
\r
1352 !strValue.CompareNoCase(_T("chunked"))
\r
1357 // finds the end of an individual header field pointed to by
\r
1358 // pszStart. Header fields can be multi-line with multi-line
\r
1359 // header fields being a line that starts with some kind of
\r
1361 template<class TSocketClass>
\r
1362 inline LPCSTR CAtlHttpClientT<TSocketClass>::FindEndOfHeader(LPCSTR pszStart) throw()
\r
1364 // move through all the lines until we come to one
\r
1365 // that doesn't start with white space
\r
1366 LPCSTR pLineStart = pszStart;
\r
1367 LPCSTR pHeaderEnd = NULL;
\r
1371 pLineStart = NextLine((BYTE*)pLineStart);
\r
1372 }while (pLineStart && isspace(static_cast<unsigned char>(*pLineStart)) && strncmp(pLineStart-2, ATL_HEADER_END, ATL_HEADER_END_LEN));
\r
1374 if (pLineStart > (LPCSTR)m_pEnd)
\r
1375 return NULL; // ran out of data in the buffer without finding the end of a line
\r
1376 // or the end of the headers.
\r
1379 pHeaderEnd = pLineStart-2;
\r
1381 pHeaderEnd = NULL;
\r
1383 return pHeaderEnd;
\r
1386 template<class TSocketClass>
\r
1387 inline bool CAtlHttpClientT<TSocketClass>::DecodeHeader(LPCSTR pHeaderStart, LPCSTR pHeaderEnd) throw()
\r
1391 if (!pHeaderStart || !pHeaderEnd)
\r
1393 LPCSTR pTemp = pHeaderStart;
\r
1394 while (*pTemp != ATL_FIELDNAME_DELIMITER && pTemp < pHeaderEnd)
\r
1396 if (*pTemp == ATL_FIELDNAME_DELIMITER)
\r
1398 char szName[ATL_MAX_FIELDNAME_LEN];
\r
1399 char szValue[ATL_MAX_VALUE_LEN];
\r
1400 int nLen = (int)(pTemp-pHeaderStart) ;
\r
1401 ATLASSERT(nLen < ATL_MAX_FIELDNAME_LEN);
\r
1402 if (nLen >= ATL_MAX_FIELDNAME_LEN)
\r
1403 return false; // won't fit in the buffer.
\r
1404 Checked::memcpy_s(szName, ATL_MAX_FIELDNAME_LEN, pHeaderStart, nLen);
\r
1407 pTemp++; // move past delimiter;
\r
1408 while (isspace(static_cast<unsigned char>(*pTemp)) && pTemp < pHeaderEnd)
\r
1411 nLen = (int)(pHeaderEnd-pTemp);
\r
1412 ATLASSERT(nLen < ATL_MAX_VALUE_LEN);
\r
1413 if (nLen >= ATL_MAX_VALUE_LEN)
\r
1414 return false; // won't fit in the buffer
\r
1415 Checked::memcpy_s(szValue, ATL_MAX_VALUE_LEN, pTemp, nLen);
\r
1419 CA2T pszName(szName);
\r
1420 CA2T pszValue(szValue);
\r
1422 if (!_tcsicmp(pszName, _T("www-authenticate")) ||
\r
1423 !_tcsicmp(pszName, _T("proxy-authenticate")))
\r
1425 m_AuthTypes.Add(pszValue);
\r
1428 if (!m_HeaderMap.Lookup(pszName, strExist))
\r
1429 m_HeaderMap.SetAt(pszName, pszValue);
\r
1432 // field-values for headers with the same name can be appended
\r
1433 // per rfc2068 4.2, we do the appending so we don't have to
\r
1434 // store/lookup duplicate keys.
\r
1436 strExist += pszValue;
\r
1437 m_HeaderMap.SetAt(pszName, (LPCTSTR)strExist);
\r
1440 // if it's a set-cookie header notify users so they can do
\r
1441 // somthing with it.
\r
1442 if (!_tcsicmp(pszName, _T("set-cookie")))
\r
1443 OnSetCookie(pszValue);
\r
1454 template<class TSocketClass>
\r
1455 inline void CAtlHttpClientT<TSocketClass>::OnSetCookie(LPCTSTR) throw()
\r
1460 template<class TSocketClass>
\r
1461 inline LPCSTR CAtlHttpClientT<TSocketClass>::ParseStatusLine(BYTE* pBuffer) throw()
\r
1465 if (m_pEnd <= pBuffer)
\r
1468 // find the first space'
\r
1469 while (pBuffer < m_pEnd && !isspace(static_cast<unsigned char>(*pBuffer)))
\r
1472 if (pBuffer >= m_pEnd)
\r
1475 // move past the space
\r
1476 while (pBuffer < m_pEnd && isspace(static_cast<unsigned char>(*pBuffer)))
\r
1479 if (pBuffer >= m_pEnd)
\r
1482 // pBuffer better be pointing at the status code now
\r
1483 LPCSTR pEnd = NULL;
\r
1484 if (*pBuffer >= '0' && *pBuffer <= '9')
\r
1486 // probably a good status code
\r
1487 errno_t errnoValue = AtlStrToNum(&m_nStatus, (LPSTR)pBuffer, (LPSTR*)&pEnd, 10);
\r
1488 if (errnoValue == ERANGE)
\r
1489 return NULL; // bad status code
\r
1492 return FALSE; // bad status code;
\r
1495 return FALSE; // bad status code;
\r
1497 pBuffer = (BYTE*)pEnd;
\r
1498 // move to end of line
\r
1499 while (pBuffer < m_pEnd && *pBuffer != '\n')
\r
1502 if (pBuffer >= m_pEnd)
\r
1505 // set the return pointing to the first
\r
1506 // character after our status line.
\r
1507 return (LPCSTR)++pBuffer;
\r
1511 // pBuffer should start at the first character
\r
1512 // after the status line.
\r
1513 template<class TSocketClass>
\r
1514 inline int CAtlHttpClientT<TSocketClass>::CrackResponseHeader(LPCSTR pBuffer, /*out*/ LPCSTR *pEnd) throw()
\r
1516 // read up to the double /r/n
\r
1517 LPCSTR pszStartSearch = pBuffer;
\r
1519 return ATL_HEADER_PARSE_HEADERERROR;
\r
1522 if (pszStartSearch == NULL)
\r
1523 return ATL_HEADER_PARSE_HEADERERROR;
\r
1525 // start parsing headers
\r
1526 LPCSTR pHeaderStart = ParseStatusLine((BYTE*)pBuffer);
\r
1527 if (!pHeaderStart)
\r
1528 return ATL_HEADER_PARSE_HEADERERROR;
\r
1529 LPCSTR pHeaderEnd = NULL;
\r
1531 while (pHeaderStart && *pHeaderStart && pHeaderStart < (LPCSTR)m_pEnd)
\r
1533 pHeaderEnd = FindEndOfHeader(pHeaderStart);
\r
1537 DecodeHeader(pHeaderStart, pHeaderEnd);
\r
1539 if (!strncmp(pHeaderEnd, ATL_HEADER_END, strlen(ATL_HEADER_END)))
\r
1541 *pEnd = pHeaderEnd + strlen(ATL_HEADER_END);
\r
1542 break; // we're done
\r
1545 pHeaderStart = pHeaderEnd+2;
\r
1548 return ATL_HEADER_PARSE_COMPLETE;
\r
1551 // Reads the body if the encoding is not chunked.
\r
1552 template<class TSocketClass>
\r
1553 inline bool CAtlHttpClientT<TSocketClass>::ReadBody(int nContentLen, int nCurrentBodyLen) throw()
\r
1555 // nCurrentBodyLen is the length of the body that has already been read
\r
1556 // nContentLen is the value of Content-Length
\r
1557 // current is the buffer that will contain the entire response
\r
1559 ATLASSUME(m_pNavData);
\r
1563 CTempBuffer<BYTE, 512> readbuff;
\r
1564 DWORD dwReadBuffSize = 0;
\r
1566 if (m_pNavData->dwReadBlockSize)
\r
1568 ATLTRY(readbuff.Allocate(m_pNavData->dwReadBlockSize));
\r
1569 dwReadBuffSize = m_pNavData->dwReadBlockSize;
\r
1573 ATLTRY(readbuff.Allocate(ATL_READ_BUFF_SIZE));
\r
1574 dwReadBuffSize = ATL_READ_BUFF_SIZE;
\r
1577 if (readbuff.operator BYTE*() == NULL)
\r
1580 if (nContentLen != -1) // We know the content length.
\r
1582 // read the rest of the body.
\r
1583 while (nCurrentBodyLen < nContentLen)
\r
1585 dwRead = dwReadBuffSize;
\r
1586 // loop while dwRead == 0
\r
1587 for (int nRetry = 0; nRetry < ATL_HTTP_CLIENT_EMPTY_READ_RETRIES; ++nRetry)
\r
1589 if (!Read(readbuff, &dwRead))
\r
1595 if (m_pNavData->pfnReadStatusCallback)
\r
1596 if (!m_pNavData->pfnReadStatusCallback(dwRead, m_pNavData->m_lParamRead))
\r
1602 nCurrentBodyLen += dwRead;
\r
1603 if (!m_current.Append((LPCSTR)(BYTE*)readbuff, dwRead))
\r
1606 return false; // error!
\r
1608 m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
\r
1614 m_dwBodyLen = nCurrentBodyLen;
\r
1616 else // We don't know content length. All we can do is
\r
1617 { // read until there is nothing else to read.
\r
1621 dwRead = dwReadBuffSize;
\r
1622 if (Read((BYTE*)readbuff, (DWORD*)&dwRead))
\r
1627 if (m_pNavData->pfnReadStatusCallback)
\r
1628 bRet = m_pNavData->pfnReadStatusCallback(dwRead, m_pNavData->m_lParamRead);
\r
1635 if (nRetries++ < ATL_HTTP_CLIENT_EMPTY_READ_RETRIES)
\r
1641 nCurrentBodyLen += dwRead;
\r
1642 if (!m_current.Append((LPCSTR)(BYTE*)readbuff, dwRead))
\r
1644 m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
\r
1651 if (m_pNavData->pfnReadStatusCallback)
\r
1652 bRet = m_pNavData->pfnReadStatusCallback(dwRead, m_pNavData->m_lParamRead);
\r
1661 m_dwBodyLen = nCurrentBodyLen;
\r
1667 // This function moves pBuffStart only on success. On success, pBuffStart is moved
\r
1668 // to the element past the last element we consumed.
\r
1669 template<class TSocketClass>
\r
1670 inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::get_chunked_size(char *&pBuffStart, char *&pBuffEnd, long* pnChunkSize) throw()
\r
1672 CHUNK_LEX_RESULT result = LEX_ERROR;
\r
1673 char *pStop = NULL;
\r
1675 if (pBuffStart >= pBuffEnd)
\r
1676 result = LEX_OUTOFDATA;
\r
1680 errno_t errnoValue = AtlStrToNum(&nResult, pBuffStart, &pStop, 16);
\r
1681 if (errnoValue != ERANGE &&
\r
1683 nResult < 0xFFFFFFFF &&
\r
1684 pStop <= pBuffEnd &&
\r
1687 // move pBuffStart
\r
1688 // return chunk size
\r
1689 *pnChunkSize = nResult;
\r
1690 pBuffStart = pStop;
\r
1693 if (*pStop != '\r')
\r
1695 result = LEX_OUTOFDATA; // not enough data in the buffer
\r
1701 template<class TSocketClass>
\r
1702 inline bool CAtlHttpClientT<TSocketClass>::move_leftover_bytes( __in_ecount(nLen) char *pBufferStart,
\r
1704 __deref_inout_ecount(nLen) char *&pBuffStart,
\r
1705 __deref_inout char *& pBuffEnd) throw()
\r
1708 Checked::memcpy_s(pBufferStart, (pBuffEnd-pBuffStart), pBuffStart, nLen);
\r
1712 template<class TSocketClass>
\r
1713 inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::get_chunked_data(char *&pBufferStart,
\r
1714 char *&pBufferEnd,
\r
1716 char **ppDataStart,
\r
1717 long *pnDataLen) throw()
\r
1719 CHUNK_LEX_RESULT result = LEX_ERROR;
\r
1720 if (pBufferStart + nChunkSize - 1 < pBufferEnd)
\r
1722 *ppDataStart = pBufferStart;
\r
1723 *pnDataLen = nChunkSize;
\r
1724 pBufferStart = pBufferStart + nChunkSize;
\r
1727 else if (pBufferStart + nChunkSize - 1 >= pBufferEnd)
\r
1728 result = LEX_OUTOFDATA;
\r
1733 template<class TSocketClass>
\r
1734 inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::consume_chunk_trailer(char *&pBufferStart, char *pBufferEnd)
\r
1736 CHUNK_LEX_RESULT result = LEX_ERROR;
\r
1737 if (pBufferStart >= pBufferEnd)
\r
1740 char *pHeaderEnd = NULL;
\r
1741 char *pTemp = pBufferStart;
\r
1742 // check for empty trailer, this means there are no more trailers
\r
1743 if ( (pTemp < pBufferEnd && *pTemp == '\r') &&
\r
1744 (pTemp+1 < pBufferEnd && *(pTemp+1) == '\n'))
\r
1746 pBufferStart += 2;
\r
1747 return LEX_TRAILER_COMPLETE;
\r
1750 while (pTemp <= pBufferEnd)
\r
1752 if ( (pTemp < pBufferEnd && *pTemp == '\r') &&
\r
1753 (pTemp+1 < pBufferEnd && *(pTemp+1) == '\n'))
\r
1755 pHeaderEnd = pTemp; // success case
\r
1762 if (result == LEX_OK)
\r
1764 DecodeHeader(pBufferStart, pHeaderEnd);
\r
1765 pBufferStart = pHeaderEnd + 2;
\r
1767 else if (result != LEX_OK &&
\r
1768 pTemp > pBufferEnd)
\r
1769 result = LEX_OUTOFDATA;
\r
1773 template<class TSocketClass>
\r
1774 inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::consume_chunk_footer(char *&pBufferStart, char *&pBufferEnd)
\r
1776 CHUNK_LEX_RESULT result = LEX_ERROR;
\r
1777 if (pBufferStart < pBufferEnd &&
\r
1778 (pBufferStart+1) <= pBufferEnd)
\r
1780 if ( *pBufferStart == '\r' &&
\r
1781 *(pBufferStart+1) == '\n')
\r
1783 pBufferStart += 2;
\r
1788 result = LEX_OUTOFDATA;
\r
1792 #define CHUNK_BUFF_SIZE 2048
\r
1794 template<class TSocketClass>
\r
1795 inline bool CAtlHttpClientT<TSocketClass>::ReadChunkedBody() throw()
\r
1797 // At this point, m_current contains the headers, up to and including the \r\n\r\n,
\r
1798 // plus any additional data that might have been read off the socket. So, we need
\r
1799 // to copy off the additional data into our read buffer before we start parsing the
\r
1802 // nReadCount, keeps track of how many socket reads we do.
\r
1803 int nReadCount = 0;
\r
1806 // nChunkBuffCarryOver
\r
1807 // When we run out of data in the input buffer, this is the
\r
1808 // count of bytes that are left in the input that could not
\r
1809 // be lexed into anything useful. We copy this many bytes to
\r
1810 // the top of the input buffer before we fill the input buffer
\r
1811 // with more bytes from the socket
\r
1812 long nChunkBuffCarryOver = 0;
\r
1815 // The size of the next chunk to be read from the input buffer.
\r
1816 long nChunkSize = 0;
\r
1819 // The heap allocated buffer that we holds data
\r
1820 // read from the socket. We will increase the size
\r
1821 // of this buffer to 2 times the max chunk size we
\r
1822 // need to read if we have to.
\r
1823 CHeapPtr<char> t_chunk_buffer;
\r
1825 // nTChunkBuffSize
\r
1826 // Keeps track of the allocated size of t_chunk_buffer.
\r
1827 // This size will change if we need to read chunks bigger
\r
1828 // than the currently allocated size of t_chunk_buffer.
\r
1829 long nTChunkBuffSize = CHUNK_BUFF_SIZE;
\r
1831 // chunk_buffer & chunk_buffer_end
\r
1832 // Keeps track of the current location
\r
1833 // in t_chunk_buffer that we are lexing input from.
\r
1834 // chunk_buffer_end is the end of the input buffer we
\r
1835 // are lexing from. chunk_buffer_end is used as a marker
\r
1836 // to make sure we don't read past the end of our input buffer
\r
1837 char *chunk_buffer, *chunk_buffer_end;
\r
1840 // The current state of the chunk parsing state machine. We
\r
1841 // start out reading the size of the first chunk.
\r
1842 CHUNK_STATE cstate = READ_CHUNK_SIZE;
\r
1845 // Holds the value of the result of a lexing operation performed
\r
1846 // on the input buffer.
\r
1847 CHUNK_LEX_RESULT cresult = LEX_OK;
\r
1849 CAtlIsapiBuffer<> result_buffer;
\r
1851 // Initialize pointers and allocate the chunk buffer.
\r
1852 chunk_buffer = chunk_buffer_end = NULL;
\r
1853 if( !t_chunk_buffer.Allocate(nTChunkBuffSize) )
\r
1856 // copy the headers into a temporary buffer.
\r
1857 result_buffer.Append(m_current + m_dwHeaderStart, m_dwHeaderLen);
\r
1859 // calculate number of bytes left in m_current past the headers
\r
1860 long leftover_in_m_current = m_current.GetLength() - (m_dwHeaderStart + m_dwHeaderLen);
\r
1862 // copy the extra bytes that might have been read into m_current into the chunk buffer
\r
1863 if (leftover_in_m_current > 0)
\r
1865 if (leftover_in_m_current > nTChunkBuffSize)
\r
1867 if( ! t_chunk_buffer.Reallocate(leftover_in_m_current) )
\r
1871 chunk_buffer = (char*)t_chunk_buffer;
\r
1872 Checked::memcpy_s(chunk_buffer, leftover_in_m_current, ((LPCSTR)m_current)+ m_dwHeaderStart + m_dwHeaderLen, leftover_in_m_current);
\r
1873 chunk_buffer_end = chunk_buffer + leftover_in_m_current;
\r
1876 m_current.Empty();
\r
1878 m_dwHeaderStart = 0;
\r
1880 // as we start the state machine, we should be either pointing at the first
\r
1881 // byte of chunked response or nothing, in which case we will need to get
\r
1882 // more data from the socket.
\r
1885 bool bDone = false;
\r
1889 // if we run out of data during processing, chunk_buffer
\r
1890 // get set to null
\r
1891 if (!chunk_buffer ||
\r
1892 chunk_buffer >= chunk_buffer_end)
\r
1894 // we ran out of data in our input buffer, we need
\r
1895 // to read more from the socket.
\r
1896 DWORD dwReadBuffSize = nTChunkBuffSize - nChunkBuffCarryOver;
\r
1897 chunk_buffer = t_chunk_buffer;
\r
1898 if (!Read((const unsigned char*)(chunk_buffer+nChunkBuffCarryOver), &dwReadBuffSize))
\r
1900 ATLTRACE("ReadChunkedBody: Error reading from socket (%d)\n", GetLastError());
\r
1903 else if(dwReadBuffSize == 0)
\r
1905 ATLTRACE("ReadChunkedBody: The socket read timed out and no bytes were read from the socket.\n");
\r
1909 ATLTRACE("ReadChunkedBody read %d bytes from socket. Reads %d \n", dwReadBuffSize, ++nReadCount);
\r
1911 chunk_buffer_end = chunk_buffer + nChunkBuffCarryOver + dwReadBuffSize;
\r
1912 nChunkBuffCarryOver = 0;
\r
1917 case READ_CHUNK_SIZE:
\r
1919 cresult = get_chunked_size(chunk_buffer, chunk_buffer_end, &nChunkSize);
\r
1923 ATLTRACE("ReadChunkedBody Failed retrieving chunk size\n");
\r
1926 case LEX_OUTOFDATA:
\r
1927 nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
\r
1928 if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
\r
1929 chunk_buffer, chunk_buffer_end))
\r
1931 ATLTRACE("failed to move leftover chunk data to head of buffer\n");
\r
1934 chunk_buffer = chunk_buffer_end = NULL;
\r
1937 if (nChunkSize == 0)
\r
1939 cstate = CHUNK_READ_DATA_COMPLETE;
\r
1941 else if (nChunkSize + 2 > nTChunkBuffSize)
\r
1943 char *pBuffStart = (char*)t_chunk_buffer;
\r
1944 int nReadSoFar = (int)(chunk_buffer - pBuffStart);
\r
1945 int nTotal = (int)(chunk_buffer_end - pBuffStart);
\r
1946 if( FAILED(::ATL::AtlMultiply(&nTChunkBuffSize, nChunkSize, 2L)))
\r
1950 t_chunk_buffer.Reallocate(nTChunkBuffSize);
\r
1951 pBuffStart = (char*)t_chunk_buffer;
\r
1952 chunk_buffer = pBuffStart + nReadSoFar;
\r
1953 chunk_buffer_end = pBuffStart + nTotal;
\r
1954 cstate = READ_CHUNK_SIZE_FOOTER;
\r
1955 m_dwBodyLen += nChunkSize;
\r
1959 // everything is OK. move to next state
\r
1960 cstate = READ_CHUNK_SIZE_FOOTER;
\r
1961 m_dwBodyLen += nChunkSize;
\r
1971 case READ_CHUNK_DATA:
\r
1973 char *pDataStart = NULL;
\r
1974 long nDataLen = 0;
\r
1976 cresult = get_chunked_data(chunk_buffer, chunk_buffer_end,
\r
1977 nChunkSize, &pDataStart, &nDataLen);
\r
1981 ATLTRACE("ReadChunkedBody failed to retrieve chunk data\n");
\r
1984 case LEX_OUTOFDATA:
\r
1985 nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
\r
1986 if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
\r
1987 chunk_buffer, chunk_buffer_end))
\r
1989 ATLTRACE("failed to move leftover chunk data to head of buffer\n");
\r
1992 chunk_buffer = chunk_buffer_end = NULL;
\r
1995 result_buffer.Append(pDataStart, nDataLen);
\r
1996 cstate = READ_CHUNK_DATA_FOOTER;
\r
2004 case READ_CHUNK_SIZE_FOOTER:
\r
2005 case READ_CHUNK_DATA_FOOTER:
\r
2007 cresult = consume_chunk_footer(chunk_buffer, chunk_buffer_end);
\r
2011 cstate = (cstate == READ_CHUNK_SIZE_FOOTER) ? READ_CHUNK_DATA : READ_CHUNK_SIZE;
\r
2014 ATLTRACE("Error consuming chunk footer!\n");
\r
2017 case LEX_OUTOFDATA:
\r
2018 nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
\r
2019 if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
\r
2020 chunk_buffer, chunk_buffer_end))
\r
2022 ATLTRACE("failed to move leftover chunk data to head of buffer\n");
\r
2025 chunk_buffer = chunk_buffer_end = NULL;
\r
2034 case CHUNK_READ_DATA_COMPLETE:
\r
2036 // We read the chunk of size 0
\r
2037 // consume the chunk footer.
\r
2039 cresult = consume_chunk_footer(chunk_buffer, chunk_buffer_end);
\r
2040 if (GetHeaderValue((_T("Trailer")), NULL, &dwLen))
\r
2042 cstate = READ_CHUNK_TRAILER; // start reading trailer headers
\r
2049 case READ_CHUNK_TRAILER:
\r
2050 cresult = consume_chunk_trailer(chunk_buffer, chunk_buffer_end);
\r
2054 cstate = READ_CHUNK_TRAILER; // keep reading
\r
2057 ATLTRACE("Error consuming chunk trailers!\n");
\r
2060 case LEX_OUTOFDATA:
\r
2061 nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
\r
2062 if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
\r
2063 chunk_buffer, chunk_buffer_end))
\r
2065 ATLTRACE("failed to move leftover chunk data to head of buffer\n");
\r
2068 chunk_buffer = chunk_buffer_end = NULL;
\r
2070 case LEX_TRAILER_COMPLETE:
\r
2084 if (!m_current.Append((LPCSTR)result_buffer))
\r
2087 m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
\r
2092 template<class TSocketClass>
\r
2093 inline bool CAtlHttpClientT<TSocketClass>::ReconnectIfRequired() throw()
\r
2096 // if we have a keep-alive header then return true
\r
2097 // else we have to close and re-open the connection
\r
2098 if (GetHeaderValue(_T("Connection"), strValue))
\r
2100 if (!strValue.CompareNoCase(_T("keep-alive")))
\r
2101 return true; // server said keep connection open.
\r
2105 return true; // there was no 'Connection' header
\r
2108 if (!strValue.CompareNoCase(_T("close")))
\r
2116 // Complete relative URLs and URLs
\r
2117 // that have a missing path. These are common with redirect headers.
\r
2118 // http://www.microsoft.com becomes http://www.microsoft.com/
\r
2119 // localstart.asp becomes whatever our current (m_urlCurrent)
\r
2120 // path is plus localstart.asp
\r
2121 template<class TSocketClass>
\r
2122 inline bool CAtlHttpClientT<TSocketClass>::CompleteURL(CString& strURL) throw()
\r
2126 CString strUrlTemp = strURL;
\r
2127 strUrlTemp.Trim();
\r
2129 bool bErr = false;
\r
2130 if (url.CrackUrl(strUrlTemp))
\r
2132 return true; // URL is already valid
\r
2136 // if we have a scheme and a host name but no
\r
2137 // path, then add the path of '/'
\r
2138 if (url.GetScheme() == ATL_URL_SCHEME_HTTP &&
\r
2139 url.GetHostNameLength() > 0 &&
\r
2140 !url.GetUrlPathLength() )
\r
2142 url.SetUrlPath(_T("/"));
\r
2145 // if we have leading / (absolute path) (ex: /Test/bbb.asp) we can concatinate it
\r
2146 // to it to our current URL (m_urlCurrent) scheme and host
\r
2147 else if (strUrlTemp[0] == _T('/'))
\r
2149 url = m_urlCurrent;
\r
2150 url.SetUrlPath(strUrlTemp);
\r
2153 // relative path (ex: bbb.asp) - we don't have a valid url
\r
2154 // and the first char is not /
\r
2155 // Get the url from our current URL (m_urlCurrent) and add
\r
2156 // our relative paths
\r
2160 url = m_urlCurrent;
\r
2162 if (!url.GetUrlPathLength())
\r
2164 szPath = _T('/'); // current URL has no path!
\r
2168 szPath = url.GetUrlPath();
\r
2171 // back up to the first / and insert our current url
\r
2172 int pos = szPath.ReverseFind(_T('/'));
\r
2178 szPath.GetBufferSetLength(pos+1);
\r
2179 szPath.ReleaseBuffer();
\r
2182 url.SetUrlPath(szPath);
\r
2189 DWORD dwLen = ATL_URL_MAX_PATH_LENGTH;
\r
2191 return url.CreateUrl(strURL.GetBuffer(ATL_URL_MAX_PATH_LENGTH),
\r
2192 &dwLen) ? true : false;
\r
2200 template<class TSocketClass>
\r
2201 inline bool CAtlHttpClientT<TSocketClass>::ProcessObjectMoved() throw()
\r
2205 // look for a location header
\r
2207 CString strURLNew;
\r
2208 if (GetHeaderValue(_T("Location"), strValue))
\r
2210 CString strRedirectReqHeaders=m_pNavData->szExtraHeaders;
\r
2211 ReconnectIfRequired();
\r
2212 m_HeaderMap.RemoveAll();
\r
2213 m_current.Empty();
\r
2216 // create a new URL based on what is in the
\r
2217 // Location header and set it as this object's
\r
2219 strURLNew = strValue;
\r
2220 CompleteURL(strURLNew);
\r
2221 CString strCurrHostName = m_urlCurrent.GetHostName();
\r
2222 ATL_URL_PORT nCurrPort=m_urlCurrent.GetPortNumber();
\r
2224 SetDefaultUrl((LPCTSTR)strURLNew, m_urlCurrent.GetPortNumber());
\r
2225 //If redirected (new url in strURLNew) to different host (server) or port, need a new socket.
\r
2226 if (m_urlCurrent.GetHostName()!=strCurrHostName || m_urlCurrent.GetPortNumber()!=nCurrPort)
\r
2231 // build up a request.
\r
2232 CString strRequest;
\r
2233 BuildRequest(&strRequest,
\r
2235 strRedirectReqHeaders.GetString());
\r
2237 // send the request
\r
2238 DWORD dwSent = strRequest.GetLength();
\r
2239 DWORD dwAvailable = dwSent;
\r
2240 if (!Write((BYTE*)((LPCSTR)CT2A(strRequest.GetBuffer(dwAvailable))), &dwSent))
\r
2242 strRequest.ReleaseBuffer();
\r
2244 if (dwSent != dwAvailable)
\r
2247 // read the response
\r
2248 if (RR_OK == ReadHttpResponse())
\r
2251 ProcessStatus(m_pNavData->dwFlags);
\r
2262 template<class TSocketClass>
\r
2263 inline bool CAtlHttpClientT<TSocketClass>::_SetDefaultUrl(LPCTSTR szURL, short nPort) throw()
\r
2267 if (!m_urlCurrent.CrackUrl(szURL)) // re-inits the field of the CUrl first
\r
2270 ATL_URL_SCHEME currScheme = m_urlCurrent.GetScheme();
\r
2271 if ( currScheme != ATL_URL_SCHEME_HTTP &&
\r
2272 !TSocketClass::SupportsScheme(currScheme) )
\r
2273 return false; // only support HTTP
\r
2275 if (!m_urlCurrent.GetUrlPathLength())
\r
2277 // no path, default to /
\r
2278 m_urlCurrent.SetUrlPath(_T("/"));
\r
2281 if (!m_urlCurrent.GetHostNameLength())
\r
2287 if (m_urlCurrent.GetPortNumber() == ATL_URL_INVALID_PORT_NUMBER)
\r
2288 m_urlCurrent.SetPortNumber(nPort);
\r
2292 template<class TSocketClass>
\r
2293 inline int CAtlHttpClientT<TSocketClass>::GetStatus() throw()
\r
2298 template<class TSocketClass>
\r
2299 inline LPCTSTR CAtlHttpClientT<TSocketClass>::GetMethod() throw()
\r
2301 return m_strMethod;
\r
2304 template<class TSocketClass>
\r
2305 inline BYTE* CAtlHttpClientT<TSocketClass>::GetPostData() throw()
\r
2308 return m_pNavData->pData;
\r
2312 template<class TSocketClass>
\r
2313 inline DWORD CAtlHttpClientT<TSocketClass>::GetPostDataLen() throw()
\r
2316 return m_pNavData->dwDataLen;
\r
2320 template<class TSocketClass>
\r
2321 inline LPCTSTR CAtlHttpClientT<TSocketClass>::GetPostDataType() throw()
\r
2324 return m_pNavData->szDataType;
\r
2328 template<class TSocketClass>
\r
2329 inline DWORD CAtlHttpClientT<TSocketClass>::GetLastError() throw()
\r
2331 return m_dwLastError;
\r
2334 template<class TSocketClass>
\r
2335 inline const SOCKET& CAtlHttpClientT<TSocketClass>::GetSocket() throw()
\r
2337 return const_cast<const SOCKET&>(m_socket);
\r
2340 template<class TSocketClass>
\r
2341 inline void CAtlHttpClientT<TSocketClass>::Close() throw()
\r
2343 TSocketClass::Close();
\r
2346 template<class TSocketClass>
\r
2347 inline DWORD CAtlHttpClientT<TSocketClass>::SetSocketTimeout(DWORD dwNewTimeout) throw()
\r
2349 return TSocketClass::SetSocketTimeout(dwNewTimeout);
\r
2352 template<class TSocketClass>
\r
2353 inline DWORD CAtlHttpClientT<TSocketClass>::GetSocketTimeout() throw()
\r
2355 return TSocketClass::GetSocketTimeout();
\r
2358 template<class TSocketClass>
\r
2359 inline void CAtlHttpClientT<TSocketClass>::AuthProtocolFailed(LPCTSTR szProto) throw()
\r
2361 CAtlBaseAuthObject *pAuthObj = NULL;
\r
2364 if (m_AuthMap.Lookup(szProto, pAuthObj) && pAuthObj)
\r
2366 pAuthObj->m_bFailed = true;
\r
2374 template<class TSocketClass>
\r
2375 inline const ATL_NAVIGATE_DATA* CAtlHttpClientT<TSocketClass>::GetCurrentNavdata()
\r
2377 return m_pNavData;
\r
2381 /////////////////////////////////////////////////////////////////////////////////
\r
2383 // CNTLMAuthObject
\r
2384 // NTLM Security Authorization functions
\r
2386 /////////////////////////////////////////////////////////////////////////////////
\r
2387 inline CNTLMAuthObject::CNTLMAuthObject() throw() :
\r
2389 m_nMaxTokenSize(0),
\r
2390 m_pAuthInfo(NULL),
\r
2393 SecInvalidateHandle(&m_hCredentials)
\r
2396 inline CNTLMAuthObject::CNTLMAuthObject(IAuthInfo *pAuthInfo) throw() :
\r
2398 m_nMaxTokenSize(0),
\r
2399 m_pAuthInfo(pAuthInfo)
\r
2401 SecInvalidateHandle(&m_hCredentials)
\r
2404 inline CNTLMAuthObject::~CNTLMAuthObject() throw()
\r
2406 if (!ATL_IS_INVALIDCREDHANDLE(m_hCredentials))
\r
2407 FreeCredentialsHandle(&m_hCredentials);
\r
2410 inline void CNTLMAuthObject::Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo) throw()
\r
2412 m_pSocket = pSocket;
\r
2413 SetAuthInfo(pAuthInfo);
\r
2416 inline void CNTLMAuthObject::SetAuthInfo(IAuthInfo *pAuthInfo) throw()
\r
2418 m_pAuthInfo = pAuthInfo;
\r
2421 inline bool CNTLMAuthObject::Authenticate(LPCTSTR /*szAuthTypes*/, bool bProxy) throw()
\r
2423 m_bProxy = bProxy;
\r
2424 if (AcquireCredHandle())
\r
2425 return DoNTLMAuthenticate();
\r
2429 inline bool CNTLMAuthObject::AcquireCredHandle() throw()
\r
2431 PSecPkgInfo pPackageInfo = NULL;
\r
2432 SECURITY_STATUS SecurityStatus = SEC_E_OK;
\r
2434 // Acquire a credentials handle on the NTLM security package
\r
2435 SecurityStatus = QuerySecurityPackageInfo(ATL_HTTP_AUTHTYPE_NTLM,
\r
2438 if (SecurityStatus != SEC_E_OK)
\r
2441 void *pAuthData = NULL;
\r
2442 CSecAuthIdentity CA;
\r
2445 // if m_pAuthInfo has been set then the caller wants us
\r
2446 // to get credentials from them.
\r
2447 if (CA.Init(m_pAuthInfo))
\r
2448 pAuthData = static_cast<void*>(&CA);
\r
2451 SecurityStatus = AcquireCredentialsHandle(
\r
2453 pPackageInfo->Name,
\r
2454 SECPKG_CRED_OUTBOUND,
\r
2463 m_nMaxTokenSize = pPackageInfo->cbMaxToken;
\r
2464 FreeContextBuffer(pPackageInfo);
\r
2465 return SecurityStatus == SEC_E_OK ? true : false;
\r
2468 inline bool CNTLMAuthObject::DoNTLMAuthenticate() throw()
\r
2470 bool bRet = false;
\r
2472 m_CurrentRequestData = (*(const_cast<const ATL_NAVIGATE_DATA*>(m_pSocket->GetCurrentNavdata())));
\r
2473 // make sure we have a good credentials handle
\r
2474 ATLASSERT(!ATL_IS_INVALIDCREDHANDLE(m_hCredentials));
\r
2475 if (ATL_IS_INVALIDCREDHANDLE(m_hCredentials))
\r
2478 SECURITY_STATUS SecurityStatus = SEC_E_OK;
\r
2480 unsigned long ContextAttributes = 0;
\r
2481 CSecBufferDesc OutBufferDesc;
\r
2482 CtxtHandle SecurityContext;
\r
2483 SecInvalidateHandle(&SecurityContext);
\r
2485 // Create a SecBufferDesc with one buffer of m_nMaxTokenSize
\r
2486 if (!OutBufferDesc.AddBuffers(1, m_nMaxTokenSize))
\r
2489 SecurityStatus = InitializeSecurityContext(
\r
2493 ISC_REQ_CONNECTION,
\r
2500 &ContextAttributes,
\r
2504 if ( (SecurityStatus == SEC_I_COMPLETE_NEEDED) ||
\r
2505 (SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE) )
\r
2507 SecurityStatus = CompleteAuthToken( &SecurityContext, (PSecBufferDesc)OutBufferDesc);
\r
2510 if (IS_ERROR(SecurityStatus))
\r
2513 // create an Authentication header with the contents of the
\r
2514 // security buffer and send it to the HTTP server. The output
\r
2515 // buffer will be pointing to a buffer that contains the
\r
2516 // response from the HTTP server on return.
\r
2517 LPSTR pszbuff = NULL;
\r
2518 if (!SendSecurityInfo(OutBufferDesc.Buffers(0), &pszbuff) || !pszbuff)
\r
2522 if (!m_pSocket->GetHeaderValue(m_bProxy ? g_pszProxyAuthenticate : g_pszWWWAuthenticate, strVal))
\r
2523 return false; // wrong authentication type
\r
2525 LPCTSTR szResponsecode = strVal;
\r
2526 TCHAR pszcode[ATL_AUTH_HDR_SIZE];
\r
2527 if (szResponsecode)
\r
2529 // first four characters better be 'NTLM'
\r
2530 if (_tcsncicmp(szResponsecode, _T("NTLM"), 4) != 0)
\r
2534 szResponsecode += 4;
\r
2537 while (*szResponsecode && _AtlIsHttpSpace(*szResponsecode))
\r
2540 // find end of header
\r
2541 LPCTSTR pszend = szResponsecode;
\r
2542 while (*pszend && *pszend != _T('\r'))
\r
2547 // copy authentication data to our buffer
\r
2548 // and base64decode it.
\r
2549 int nlen = (int)(pszend-szResponsecode);
\r
2550 Checked::memcpy_s(pszcode, ATL_AUTH_HDR_SIZE, szResponsecode, nlen*sizeof(TCHAR));
\r
2551 pszcode[pszend-szResponsecode]=0;
\r
2553 // re-use OutBufferDesc here since we'll need to need
\r
2554 // a SecBufferDesc to pass to the next call to InitializeSecurityContext
\r
2556 if(!OutBufferDesc.Buffers(0)->ClearBuffer(m_nMaxTokenSize))
\r
2561 CT2A pszcode_a(pszcode);
\r
2562 bRet = Base64Decode(pszcode_a,
\r
2563 (int) strlen(pszcode_a),
\r
2564 (BYTE*)OutBufferDesc.Buffers(0)->pvBuffer,
\r
2565 (int*) &OutBufferDesc.Buffers(0)->cbBuffer) != FALSE;
\r
2576 // Create buffers for the challenge data
\r
2577 CSecBufferDesc *InBufferDesc = &OutBufferDesc;
\r
2578 CSecBufferDesc OutBufferDesc2;
\r
2579 if (!OutBufferDesc2.AddBuffers(1, m_nMaxTokenSize))
\r
2582 // Process the challenge response from the server
\r
2583 SecurityStatus = InitializeSecurityContext(
\r
2594 &ContextAttributes,
\r
2598 if (IS_ERROR(SecurityStatus))
\r
2602 if (SendSecurityInfo(OutBufferDesc2.Buffers(0), &pszbuff))
\r
2604 // at this point we should be authenticated and either have the page
\r
2605 // we requested or be getting re-directed to another page under our
\r
2606 // authorization. Either way, we don't want to go through authorization
\r
2607 // code again if we are not authorized to prevent recursive authorization
\r
2608 // so we tell the client not to try this protocol again.
\r
2609 if (m_pSocket->GetStatus() == 401 ||
\r
2610 m_pSocket->GetStatus() == 407)
\r
2612 // Authorization with this protocol failed.
\r
2613 // don't try it again.
\r
2614 m_pSocket->AuthProtocolFailed(_T("NTLM"));
\r
2616 bRet = m_pSocket->ProcessStatus(m_pSocket->GetFlags());
\r
2622 inline bool CNTLMAuthObject::GetCredentialNames(CString& theName)
\r
2624 if (ATL_IS_INVALIDCREDHANDLE(m_hCredentials))
\r
2627 SecPkgCredentials_Names spcn;
\r
2628 if(!IS_ERROR(QueryCredentialsAttributes(&m_hCredentials,
\r
2629 SECPKG_CRED_ATTR_NAMES, (void*)&spcn)))
\r
2631 theName = spcn.sUserName;
\r
2637 inline bool CNTLMAuthObject::SendSecurityInfo(SecBuffer *pSecBuffer, LPSTR *pszBuffer) throw()
\r
2639 ATLASSERT(pSecBuffer);
\r
2640 ATLASSUME(m_pSocket);
\r
2641 ATLASSERT(pszBuffer);
\r
2643 int nDest = ATL_AUTH_HDR_SIZE;
\r
2644 char auth_b64encoded[ATL_AUTH_HDR_SIZE];
\r
2645 char auth_header[ATL_AUTH_HDR_SIZE];
\r
2646 const char *pszFmtStr = m_bProxy ? m_pszFmtProxy : m_pszFmtWWW;
\r
2648 if (!pSecBuffer || !pSecBuffer->pvBuffer || !pszBuffer)
\r
2652 // Base64Encode will fail gracefully if buffer not big enough
\r
2653 if (Base64Encode((BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer,
\r
2654 auth_b64encoded, &nDest, ATL_BASE64_FLAG_NOCRLF))
\r
2656 if (nDest < ATL_AUTH_HDR_SIZE)
\r
2658 auth_b64encoded[nDest]=0;
\r
2659 // make sure we have enough room in our header buffer
\r
2660 if ( (strlen(pszFmtStr)-2 + nDest) < ATL_AUTH_HDR_SIZE)
\r
2661 sprintf_s(auth_header, ATL_AUTH_HDR_SIZE, pszFmtStr, auth_b64encoded);
\r
2671 // reset the connection if required
\r
2672 m_pSocket->ResetConnection();
\r
2674 // Resend the request with the authorization information
\r
2675 LPCURL pUrl = m_pSocket->GetCurrentUrl();
\r
2676 bool bRet = false;
\r
2678 TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
\r
2679 DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
\r
2680 if( ! pUrl->CreateUrl(szUrl, &dwMaxLen) )
\r
2685 CA2CT hdr(auth_header);
\r
2686 CAtlNavigateData navigate_data(m_CurrentRequestData);
\r
2687 // append authorization header to extra headers
\r
2688 CString strHeaders = navigate_data.GetExtraHeaders();
\r
2689 strHeaders += hdr;
\r
2690 navigate_data.SetExtraHeaders(strHeaders);
\r
2691 navigate_data.RemoveFlags(ATL_HTTP_FLAG_PROCESS_RESULT);
\r
2693 bRet = m_pSocket->Navigate( szUrl, &navigate_data);
\r
2700 *pszBuffer = (LPSTR)m_pSocket->GetResponse();
\r
2704 /////////////////////////////////////////////////////////////////////////////////
\r
2706 // CBasicAuthObject
\r
2707 // BASIC Security Authorization functions
\r
2709 /////////////////////////////////////////////////////////////////////////////////
\r
2710 inline bool CBasicAuthObject::DoBasicAuthenticate() throw()
\r
2712 bool bRet = false;
\r
2713 ATLASSUME(m_pClient);
\r
2714 ATLASSUME(m_pAuthInfo);
\r
2715 // Create an authentication string
\r
2716 CTempBuffer<TCHAR, (_ATL_MAX_AUTH_BUFF*2)+2> auth_string;
\r
2717 CAuthInfoBuffType buffUID;
\r
2718 CAuthInfoBuffType buffPWD;
\r
2720 DWORD dwUID=0,dwPWD=0;
\r
2721 if (!_AtlGetAuthInfoHelper(m_pAuthInfo, &IAuthInfo::GetPassword, buffPWD, &dwPWD) ||
\r
2722 !_AtlGetAuthInfoHelper(m_pAuthInfo, &IAuthInfo::GetUsername, buffUID, &dwUID))
\r
2727 if (!auth_string.Allocate((_ATL_MAX_AUTH_BUFF*2)+2))
\r
2730 Checked::tcscpy_s(auth_string, _ATL_MAX_AUTH_BUFF, buffUID);
\r
2731 Checked::tcscat_s(auth_string, _ATL_MAX_AUTH_BUFF, _T(":"));
\r
2732 Checked::tcscat_s(auth_string, _ATL_MAX_AUTH_BUFF, buffPWD);
\r
2734 // Base64 encode the auth string
\r
2735 char *auth_string_enc = NULL;
\r
2736 CTempBuffer<char, 512> auth_string_buff;
\r
2737 CT2A auth_string_a(auth_string);
\r
2739 int nLen = Base64EncodeGetRequiredLength((int)strlen((LPSTR)auth_string_a));
\r
2740 auth_string_buff.Allocate(nLen+1);
\r
2741 if (!((char*)auth_string_buff))
\r
2744 auth_string_enc = (char*)auth_string_buff;
\r
2745 if (!Base64Encode((const BYTE*)(LPSTR)auth_string_a, (int)strlen((LPSTR)auth_string_a),
\r
2746 auth_string_enc, &nLen, ATL_BASE64_FLAG_NOCRLF))
\r
2748 auth_string_buff[nLen]=0;
\r
2750 // Format the Authentication header
\r
2751 int nLenFmt = (m_bProxy ? (int)strlen(m_pszFmtProxy) : (int)strlen(m_pszFmtWWW)) + 2;
\r
2753 ++nLen; // Space for '\0'
\r
2755 CTempBuffer<char, 512> auth_header_buff;
\r
2756 ATLTRY(auth_header_buff.Allocate(nLen));
\r
2757 if (!((char*)auth_header_buff))
\r
2760 char *auth_header = (char*)auth_header_buff;
\r
2761 Checked::strcpy_s(auth_header, nLen, m_bProxy ? m_pszFmtProxy : m_pszFmtWWW);
\r
2762 Checked::strcat_s(auth_header, nLen, auth_string_enc);
\r
2763 Checked::strcat_s(auth_header, nLen, "\r\n");
\r
2765 // Resend the request with the authorization information
\r
2766 LPCURL pUrl = m_pClient->GetCurrentUrl();
\r
2767 TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
\r
2768 DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
\r
2769 pUrl->CreateUrl(szUrl, &dwMaxLen);
\r
2771 // reset the connection if required
\r
2772 m_pClient->ResetConnection();
\r
2774 CA2T hdr(auth_header);
\r
2775 CAtlNavigateData navigate_data(*(const_cast<const ATL_NAVIGATE_DATA*>(m_pClient->GetCurrentNavdata())));
\r
2776 // append authorization header to extra headers
\r
2777 CString strHeaders = navigate_data.GetExtraHeaders();
\r
2778 strHeaders += hdr;
\r
2779 navigate_data.SetExtraHeaders(strHeaders);
\r
2780 navigate_data.RemoveFlags(ATL_HTTP_FLAG_PROCESS_RESULT);
\r
2781 bRet = m_pClient->Navigate( szUrl,
\r
2791 // Request was successfully sent. Process the result.
\r
2792 if (m_pClient->GetStatus() == 401 ||
\r
2793 m_pClient->GetStatus() == 407)
\r
2795 // Authorization with this protocol failed.
\r
2796 // don't try it again.
\r
2797 m_pClient->AuthProtocolFailed(_T("basic"));
\r
2799 bRet = m_pClient->ProcessStatus(m_pClient->GetFlags());
\r
2804 inline CBasicAuthObject::CBasicAuthObject() throw()
\r
2807 m_pAuthInfo = NULL;
\r
2812 inline CBasicAuthObject::CBasicAuthObject(IAuthInfo *pAuthInfo) throw()
\r
2814 m_pAuthInfo = pAuthInfo;
\r
2818 inline void CBasicAuthObject::SetAuthInfo(IAuthInfo *pAuthInfo) throw()
\r
2820 m_pAuthInfo = pAuthInfo;
\r
2823 // Called by the CAtlHttpClient class to
\r
2824 // authenticate a user.
\r
2825 inline bool CBasicAuthObject::Authenticate(LPCTSTR szAuthTypes, bool bProxy) throw()
\r
2827 if (lstrlen(szAuthTypes) > ATL_AUTH_HDR_SIZE)
\r
2830 m_bProxy = bProxy;
\r
2832 if (!CrackRealm(szAuthTypes))
\r
2834 return DoBasicAuthenticate();
\r
2837 inline LPCTSTR CBasicAuthObject::GetRealm() throw()
\r
2839 return const_cast<LPCTSTR>(m_szRealm);
\r
2842 // Called by the CAtlHttpClient class to initialize
\r
2843 // this authentication object.
\r
2844 inline void CBasicAuthObject::Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo) throw()
\r
2846 ATLASSERT(pSocket);
\r
2847 m_pClient = pSocket;
\r
2849 SetAuthInfo(pAuthInfo);
\r
2852 inline bool CBasicAuthObject::CrackRealm(LPCTSTR szHeader) throw()
\r
2854 // szHeader is pointing at the
\r
2855 // "basic" in the header
\r
2856 // see if realm is available
\r
2857 const TCHAR *pStart = szHeader;
\r
2863 while (*pStart && _AtlIsHttpSpace(*pStart))
\r
2866 // are we pointing at 'realm'?
\r
2867 if ((*pStart == 'r' || *pStart == 'R') &&
\r
2868 (*(pStart+1) == 'e' || *(pStart+1) == 'E') &&
\r
2869 (*(pStart+2) == 'a' || *(pStart+2) == 'A') &&
\r
2870 (*(pStart+3) == 'l' || *(pStart+3) == 'L') &&
\r
2871 (*(pStart+4) == 'm' || *(pStart+4) == 'M'))
\r
2877 while (*pStart && _AtlIsHttpSpace(*pStart))
\r
2881 if (*pStart && *pStart == _T('='))
\r
2884 return false; // invalid realm
\r
2887 while (*pStart && _AtlIsHttpSpace(*pStart))
\r
2890 // skip quotes if they are there
\r
2891 if (*pStart == '\"')
\r
2894 const TCHAR *pEnd = pStart;
\r
2895 while (*pEnd && *pEnd != '\"')
\r
2897 if (*pEnd == '\\' && *(pEnd + 1)) // escaped character, skip it
\r
2903 if (*pEnd == '\"' && *(pEnd+1) != '\0')
\r
2904 return false; //trailing junk after the quoted realm
\r
2906 if (*pEnd=='\0' || *pEnd =='\"')
\r
2908 int nLen = (int)(pEnd-pStart);
\r
2909 if (nLen < MAX_REALM_LEN)
\r
2911 Checked::tcsncpy_s(m_szRealm, _countof(m_szRealm), pStart, nLen);
\r
2912 m_szRealm[nLen]=0;
\r
2913 if (!AtlUnescapeUrl(m_szRealm, m_szRealm, NULL, MAX_REALM_LEN))
\r
2914 return false; // error unescaping the string
\r
2923 inline CAtlBaseAuthObject::CAtlBaseAuthObject()
\r
2925 m_bFailed = false;
\r
2929 inline CAtlNavigateData::CAtlNavigateData() throw()
\r
2931 dwFlags = ATL_HTTP_FLAG_AUTO_REDIRECT|
\r
2932 ATL_HTTP_FLAG_PROCESS_RESULT|
\r
2933 ATL_HTTP_FLAG_SEND_BLOCKS;
\r
2934 szExtraHeaders = NULL;
\r
2935 szMethod = ATL_HTTP_METHOD_GET;
\r
2936 nPort = ATL_URL_DEFAULT_HTTP_PORT;
\r
2939 szDataType = NULL;
\r
2940 dwTimeout = ATL_SOCK_TIMEOUT;
\r
2941 dwSendBlockSize = ATL_HTTP_DEFAULT_BLOCK_SIZE;
\r
2942 dwReadBlockSize = ATL_HTTP_DEFAULT_BLOCK_SIZE;
\r
2943 pfnChunkCallback = NULL;
\r
2944 pfnSendStatusCallback = NULL;
\r
2945 pfnReadStatusCallback = NULL;
\r
2950 inline CAtlNavigateData::CAtlNavigateData(const CAtlNavigateData &rhs)
\r
2952 this->operator=(rhs);
\r
2955 inline CAtlNavigateData::CAtlNavigateData(const ATL_NAVIGATE_DATA &rhs)
\r
2957 this->operator=(rhs);
\r
2960 inline CAtlNavigateData& CAtlNavigateData::operator=(const CAtlNavigateData &rhs)
\r
2962 return this->operator=(static_cast<const ATL_NAVIGATE_DATA&>(rhs));
\r
2965 inline CAtlNavigateData& CAtlNavigateData::operator=(const ATL_NAVIGATE_DATA &rhs)
\r
2967 dwFlags = rhs.dwFlags;
\r
2968 szExtraHeaders = rhs.szExtraHeaders;
\r
2969 szMethod = rhs.szMethod;
\r
2970 nPort = rhs.nPort;
\r
2971 pData = rhs.pData;
\r
2972 dwDataLen = rhs.dwDataLen;
\r
2973 szDataType = rhs.szDataType;
\r
2974 dwTimeout = rhs.dwTimeout;
\r
2975 dwSendBlockSize = rhs.dwSendBlockSize;
\r
2976 dwReadBlockSize = rhs.dwReadBlockSize;
\r
2977 pfnChunkCallback = rhs.pfnChunkCallback;
\r
2978 pfnSendStatusCallback = rhs.pfnSendStatusCallback;
\r
2979 pfnReadStatusCallback = rhs.pfnReadStatusCallback;
\r
2980 m_lParamSend = rhs.m_lParamSend;
\r
2981 m_lParamRead = rhs.m_lParamRead;
\r
2985 inline DWORD CAtlNavigateData::SetFlags(DWORD dwNewFlags) throw()
\r
2987 // check for mutually exclusive flags
\r
2988 if ((dwNewFlags & ATL_HTTP_FLAG_SEND_CALLBACK) &&
\r
2989 (dwNewFlags & ATL_HTTP_FLAG_SEND_BLOCKS))
\r
2992 return ATL_HTTP_FLAG_INVALID_FLAGS;
\r
2995 DWORD dwOldFlags = dwFlags;
\r
2996 dwFlags = dwNewFlags;
\r
2997 return dwOldFlags;
\r
3000 inline DWORD CAtlNavigateData::GetFlags() throw()
\r
3005 inline DWORD CAtlNavigateData::AddFlags(DWORD dwFlagsToAdd) throw()
\r
3007 // check for mutually exclusive flags
\r
3009 ((dwFlagsToAdd & ATL_HTTP_FLAG_SEND_CALLBACK) &&
\r
3010 (dwFlags & ATL_HTTP_FLAG_SEND_BLOCKS)) ||
\r
3011 ((dwFlagsToAdd & ATL_HTTP_FLAG_SEND_BLOCKS) &&
\r
3012 (dwFlags & ATL_HTTP_FLAG_SEND_CALLBACK))
\r
3016 return ATL_HTTP_FLAG_INVALID_FLAGS;
\r
3019 DWORD dwOldFlags = dwFlags;
\r
3020 dwFlags |= dwFlagsToAdd;
\r
3021 return dwOldFlags;
\r
3024 inline DWORD CAtlNavigateData::RemoveFlags(DWORD dwFlagsToRemove) throw()
\r
3026 DWORD dwOldFlags = dwFlags;
\r
3027 dwFlags &= ~dwFlagsToRemove;
\r
3028 return dwOldFlags;
\r
3031 inline LPCTSTR CAtlNavigateData::SetExtraHeaders(LPCTSTR szNewHeaders) throw()
\r
3033 LPCTSTR szold = szExtraHeaders;
\r
3034 szExtraHeaders = szNewHeaders;
\r
3038 inline LPCTSTR CAtlNavigateData::GetExtraHeaders() throw()
\r
3040 return szExtraHeaders;
\r
3042 inline LPCTSTR CAtlNavigateData::SetMethod(LPCTSTR szNewMethod) throw()
\r
3044 LPCTSTR szold = szMethod;
\r
3045 szMethod = szNewMethod;
\r
3048 inline LPCTSTR CAtlNavigateData::GetMethod() throw()
\r
3052 inline short CAtlNavigateData::SetPort(short newPort) throw()
\r
3054 short oldport = nPort;
\r
3058 inline short CAtlNavigateData::GetPort() throw()
\r
3062 inline void CAtlNavigateData::SetPostData(BYTE *pd, DWORD len, LPCTSTR type) throw()
\r
3066 szDataType = type;
\r
3069 inline DWORD CAtlNavigateData::SetSocketTimeout(DWORD dwNewTimeout) throw()
\r
3071 DWORD dwold = dwTimeout;
\r
3072 dwTimeout = dwNewTimeout;
\r
3075 inline DWORD CAtlNavigateData::GetSocketTimeout() throw()
\r
3079 inline DWORD CAtlNavigateData::SetSendBlockSize(DWORD dwNewBlockSize) throw()
\r
3081 DWORD dwold = dwSendBlockSize;
\r
3082 dwSendBlockSize = dwNewBlockSize;
\r
3085 inline DWORD CAtlNavigateData::GetSendBlockSize() throw()
\r
3087 return dwSendBlockSize;
\r
3090 inline DWORD CAtlNavigateData::SetReadBlockSize(DWORD dwNewBlockSize) throw()
\r
3092 DWORD dwold = dwReadBlockSize;
\r
3093 dwReadBlockSize = dwNewBlockSize;
\r
3097 inline DWORD CAtlNavigateData::GetReadBlockSize() throw()
\r
3099 return dwReadBlockSize;
\r
3102 inline PFNATLCHUNKEDCB CAtlNavigateData::SetChunkCallback(PFNATLCHUNKEDCB pfn, DWORD_PTR dwParam) throw()
\r
3104 PFNATLCHUNKEDCB pold = pfnChunkCallback;
\r
3105 pfnChunkCallback = pfn;
\r
3106 m_lParamChunkCB = dwParam;
\r
3109 inline PFNATLCHUNKEDCB CAtlNavigateData::GetChunkCallback() throw()
\r
3111 return pfnChunkCallback;
\r
3114 inline PFNATLSTATUSCALLBACK CAtlNavigateData::SetSendStatusCallback(PFNATLSTATUSCALLBACK pfn, DWORD_PTR dwData) throw()
\r
3116 PFNATLSTATUSCALLBACK pold = pfnSendStatusCallback;
\r
3117 pfnSendStatusCallback = pfn;
\r
3118 m_lParamSend = dwData;
\r
3122 inline PFNATLSTATUSCALLBACK CAtlNavigateData::GetSendStatusCallback() throw()
\r
3124 return pfnSendStatusCallback;
\r
3127 inline PFNATLSTATUSCALLBACK CAtlNavigateData::SetReadStatusCallback(PFNATLSTATUSCALLBACK pfn, DWORD_PTR dwData) throw()
\r
3129 PFNATLSTATUSCALLBACK pOld = pfnReadStatusCallback;
\r
3130 pfnReadStatusCallback = pfn;
\r
3131 m_lParamRead = dwData;
\r
3135 inline PFNATLSTATUSCALLBACK CAtlNavigateData::GetReadStatusCallback() throw()
\r
3137 return pfnReadStatusCallback;
\r
3140 } // namespace ATL
\r
3142 #pragma warning(pop)
\r
3144 #endif // __ATLHTTP_INL__
\r