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
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;
|
|
}
|
|
|