Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

481 lines
13 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
prefetch.cxx
Abstract:
Functions and methods to support http random read access.
Contents:
AttemptReadFromFile
SetStreamPointer
SetLengthFromCache
ReadLoop_Fsm
WriteResponseBufferToCache
WriteQueryBufferToCache
Author:
Rajeev Dujari (rajeevd) March 1996
Revision History:
Ahsan Kabir (akabir) November 1997
--*/
#include <wininetp.h>
#define READLOOP_BUFSIZE 2048
BOOL
HTTP_REQUEST_HANDLE_OBJECT::AttemptReadFromFile(
LPVOID lpBuf,
DWORD cbToRead,
DWORD* pcbRead
)
{
DEBUG_ENTER((DBG_CACHE,
Bool,
"INTERNET_CONNECT_HANDLE_OBJECT::AttemptReadFromFile",
"%#x, %d, %#x",
lpBuf,
cbToRead,
pcbRead
));
BOOL fSuccess;
DWORD dwBytesToCopy = 0;
if (!cbToRead)
{
*pcbRead = 0;
DEBUG_LEAVE(TRUE);
return TRUE;
}
if (IsCacheReadInProgress())
{
INET_ASSERT(_VirtualCacheFileSize == _RealCacheFileSize);
// Entire read should be satisfied from cache.
*pcbRead = cbToRead;
if (ReadUrlCacheEntryStream
(_hCacheStream, _dwCurrentStreamPosition, lpBuf, pcbRead, 0))
{
AdvanceReadPosition (*pcbRead);
DEBUG_LEAVE(TRUE);
return TRUE;
}
else
{
*pcbRead = 0;
DEBUG_LEAVE(FALSE);
return FALSE;
}
}
else if (IsCacheWriteInProgress())
{
// See if the read is completely within the file.
if (!IsEndOfFile() && _dwCurrentStreamPosition + cbToRead > _VirtualCacheFileSize)
{
DEBUG_PRINT(HTTP, ERROR, ("AttemptRead Failed streampos=%d cbToRead=%d, _VitrualCacheFileSize=%d\n",
_dwCurrentStreamPosition, cbToRead, _VirtualCacheFileSize));
DEBUG_LEAVE(FALSE);
return FALSE;
}
HANDLE hfRead;
hfRead = GetDownloadFileReadHandle();
if (hfRead == INVALID_HANDLE_VALUE)
{
DEBUG_LEAVE(FALSE);
return FALSE;
}
// Read the data from the file.
SetFilePointer (hfRead, _dwCurrentStreamPosition, NULL, FILE_BEGIN);
fSuccess = ReadFile (hfRead, lpBuf, cbToRead, pcbRead, NULL);
if (!fSuccess)
{
DEBUG_LEAVE(FALSE);
return FALSE;
}
AdvanceReadPosition (*pcbRead);
DEBUG_LEAVE(TRUE);
return TRUE;
}
else
{
DEBUG_LEAVE(FALSE);
return FALSE;
}
}
// Called from InternetSetFilePointer
DWORD
HTTP_REQUEST_HANDLE_OBJECT::SetStreamPointer(
LONG lDistanceToMove,
DWORD dwMoveMethod
)
{
DWORD dwErr = ERROR_SUCCESS;
// Fail if data is not from cache or going to cache.
if (!IsCacheReadInProgress() && !IsCacheWriteInProgress())
{
dwErr = ERROR_INTERNET_INVALID_OPERATION;
goto done;
}
// BUGBUG: we don't handle chunked transfer, new with http 1.1
if (IsChunkEncoding())
{
dwErr = ERROR_INTERNET_INVALID_OPERATION;
goto done;
}
switch (dwMoveMethod)
{
case FILE_BEGIN:
_dwCurrentStreamPosition = (DWORD) lDistanceToMove;
break;
case FILE_CURRENT:
if (lDistanceToMove < 0
&& ((DWORD) -lDistanceToMove) > _dwCurrentStreamPosition)
{
dwErr = ERROR_NEGATIVE_SEEK;
}
else
{
_dwCurrentStreamPosition += lDistanceToMove;
}
break;
case FILE_END:
if (!IsContentLength())
dwErr = ERROR_INTERNET_INVALID_OPERATION;
else if (lDistanceToMove < 0 && ((DWORD) -lDistanceToMove) > _ContentLength)
dwErr = ERROR_NEGATIVE_SEEK;
else
_dwCurrentStreamPosition = _ContentLength + lDistanceToMove;
break;
default:
dwErr = ERROR_INVALID_PARAMETER;
break;
}
done:
if (dwErr == ERROR_SUCCESS)
{
if (IsKeepAlive() && IsContentLength())
_BytesRemaining = _ContentLength - _dwCurrentStreamPosition;
if (_VirtualCacheFileSize > _dwCurrentStreamPosition)
SetAvailableDataLength (_VirtualCacheFileSize - _dwCurrentStreamPosition);
else
SetAvailableDataLength (0);
return _dwCurrentStreamPosition;
}
else
{
SetLastError (dwErr);
return (DWORD) -1L;
}
}
DWORD
CFsm_ReadLoop::RunSM(
IN CFsm * Fsm
)
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"CFsm_ReadLoop::RunSM",
"%#x",
Fsm
));
DWORD error;
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
CFsm_ReadLoop * stateMachine = (CFsm_ReadLoop *)Fsm;
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
switch (Fsm->GetState()) {
case FSM_STATE_INIT:
case FSM_STATE_CONTINUE:
error = pRequest->ReadLoop_Fsm(stateMachine);
break;
default:
error = ERROR_INTERNET_INTERNAL_ERROR;
Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR);
INET_ASSERT(FALSE);
break;
}
DEBUG_LEAVE(error);
return error;
}
DWORD
HTTP_REQUEST_HANDLE_OBJECT::ReadLoop_Fsm(
IN CFsm_ReadLoop * Fsm
)
{
DEBUG_ENTER((DBG_HTTP,
Dword,
"HTTP_REQUEST_HANDLE_OBJECT::ReadLoop_Fsm",
"%#x",
Fsm
));
CFsm_ReadLoop & fsm = *Fsm;
DWORD dwErr = fsm.GetError();
if (fsm.GetState() == FSM_STATE_CONTINUE)
{
goto receive_continue;
}
// Set the goal for reading from the socket.
fsm.m_dwReadEnd = _dwCurrentStreamPosition +
((fsm.m_dwSocketFlags & SF_NO_WAIT)? 1 : fsm.m_cbReadIn);
// If app is trying to read beyond eof, limit it.
if (fsm.m_dwReadEnd > _ContentLength)
{
fsm.m_dwReadEnd = _ContentLength;
fsm.m_cbReadIn = fsm.m_dwReadEnd - _dwCurrentStreamPosition;
}
// Flush any data in response buffer to download file.
dwErr = WriteResponseBufferToCache();
if (dwErr != ERROR_SUCCESS)
goto done;
// Flush any data in query buffer to download file.
dwErr = WriteQueryBufferToCache();
if (dwErr != ERROR_SUCCESS)
goto done;
// BUGBUG: what if HaveReadFileExData?
// Allocate receive buffer. We could optimize this to use
// the client buffer or query buffer when available to avoid
// reading from the file data that was just written.
fsm.m_cbBuf = READLOOP_BUFSIZE;
if (!(fsm.m_pBuf = (PVOID) new BYTE [fsm.m_cbBuf]))
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
goto done;
}
HANDLE hfRead;
hfRead = GetDownloadFileReadHandle();
if (hfRead == INVALID_HANDLE_VALUE)
{
dwErr = GetLastError();
DEBUG_PRINT(CACHE, ERROR, ("Cache: createfile failed error=%ld\n", dwErr));
goto done;
}
while (1)
{
// Calculate amount of data available for next read.
if (_VirtualCacheFileSize > _dwCurrentStreamPosition)
SetAvailableDataLength (_VirtualCacheFileSize - _dwCurrentStreamPosition);
else
SetAvailableDataLength (0);
// Check if pending request can be satisfied from file.
if (_EndOfFile || _VirtualCacheFileSize >= fsm.m_dwReadEnd)
{
INET_ASSERT (AvailableDataLength());
if (fsm.m_pRead)
{
// The client wants us to fill the buffer.
if (fsm.m_dwSocketFlags & SF_NO_WAIT)
{
// If the app is greedy, give it all we've got.
if (fsm.m_cbReadIn > AvailableDataLength())
fsm.m_cbReadIn = AvailableDataLength();
}
else
{
INET_ASSERT (fsm.m_cbReadIn <= AvailableDataLength());
}
}
// Read the data from the file.
LONG lRet;
lRet = SetFilePointer
(hfRead, _dwCurrentStreamPosition, NULL, FILE_BEGIN);
INET_ASSERT ((DWORD) lRet == _dwCurrentStreamPosition);
if (lRet == -1)
{
dwErr = GetLastError();
goto done;
}
BOOL fRet;
fRet = ReadFile
(hfRead, fsm.m_pRead, fsm.m_cbReadIn, fsm.m_pcbReadOut, NULL);
if (!fRet)
{
dwErr = GetLastError();
goto done;
}
AdvanceReadPosition (*fsm.m_pcbReadOut);
INET_ASSERT (dwErr == ERROR_SUCCESS);
goto done;
}
INET_ASSERT (!_EndOfFile);
INET_ASSERT (_Socket);
fsm.m_cbRead = fsm.m_cbBuf;
if (IsKeepAlive() && fsm.m_cbRead > _BytesInSocket)
fsm.m_cbRead = _BytesInSocket;
fsm.m_cbRecv = 0;
// Get some more data from the socket.
dwErr = _Socket->Receive
(
&fsm.m_pBuf, // IN OUT LPVOID* lplpBuffer,
&fsm.m_cbBuf, // IN OUT LPDWORD lpdwBufferLength,
&fsm.m_cbRead, // IN OUT LPDWORD lpdwBufferRemaining,
&fsm.m_cbRecv, // IN OUT LPDWORD lpdwBytesReceived,
0, // IN DWORD dwExtraSpace,
fsm.m_dwSocketFlags, // IN DWORD dwFlags,
&_EndOfFile // OUT LPBOOL lpbEof
);
if (dwErr == ERROR_IO_PENDING)
{
if (fsm.m_dwSocketFlags & SF_NO_WAIT)
{
// InternetReadFileEx can set IRF_NO_WAIT. If we must go async
// in this case, morph the request into a QueryDataAvailable
// and app will call again to get the data after notification.
fsm.m_pRead = NULL;
fsm.m_cbReadIn = 1;
fsm.m_pcbReadOut = NULL;
}
goto done;
}
receive_continue:
if (dwErr != ERROR_SUCCESS)
{
DEBUG_PRINT(HTTP,
ERROR,
("error %d on socket %#x\n",
dwErr,
_Socket->GetSocket()));
goto done;
}
// Append data to download file.
dwErr = WriteCache ((PBYTE) fsm.m_pBuf, fsm.m_cbRecv);
if (dwErr != ERROR_SUCCESS)
goto done;
// If content length is known, check for EOF.
if (IsKeepAlive())
{
_BytesInSocket -= fsm.m_cbRecv;
if (!_BytesInSocket)
_EndOfFile = TRUE;
}
if (_EndOfFile)
{
// Set handle state.
SetState(HttpRequestStateReopen);
if (!IsKeepAlive())
CloseConnection(FALSE);
SetData(FALSE);
}
} // end while (1)
done:
if (dwErr != ERROR_IO_PENDING)
{
delete [] fsm.m_pBuf;
fsm.SetDone();
if (dwErr != ERROR_SUCCESS)
{
DEBUG_PRINT(HTTP, ERROR, ("Readloop: Error = %d\n", dwErr));
SetState(HttpRequestStateError);
LocalEndCacheWrite(FALSE);
}
}
DEBUG_LEAVE(dwErr);
return dwErr;
}
// Called from ReadLoop to write data in response buffer to download file.
DWORD HTTP_REQUEST_HANDLE_OBJECT::WriteResponseBufferToCache (VOID)
{
DWORD cbData = _BytesReceived - _DataOffset;
if (!cbData)
return ERROR_SUCCESS;
DWORD dwErr = WriteCache
(((LPBYTE) _ResponseBuffer) + _DataOffset, cbData);
_DataOffset += cbData;
DEBUG_PRINT(HTTP, ERROR, ("WriteResponseBufferToCache: Error = %d", dwErr));
return dwErr;
}
// Called from ReadLoop to write data in query buffer to download file.
DWORD HTTP_REQUEST_HANDLE_OBJECT::WriteQueryBufferToCache (VOID)
{
if (!_QueryBytesAvailable)
return ERROR_SUCCESS;
DWORD dwErr = WriteCache
(((LPBYTE) _QueryBuffer) + _QueryOffset, _QueryBytesAvailable);
_QueryOffset += _QueryBytesAvailable;
_QueryBytesAvailable = 0;
DEBUG_PRINT(HTTP, ERROR, ("WriteQueryBufferToCache: Error = %d", dwErr));
return dwErr;
}