488 lines
14 KiB
C
488 lines
14 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
compression.h
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Do Http compression
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Anil Ruia (AnilR) 10-Apr-2000
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#ifndef _COMPRESSION_H_
|
||
|
#define _COMPRESSION_H_
|
||
|
|
||
|
#define COMPRESSION_MIN_IO_BUFFER_SIZE 256
|
||
|
#define COMPRESSION_MAX_IO_BUFFER_SIZE 100000
|
||
|
#define COMPRESSION_MIN_COMP_BUFFER_SIZE 1024
|
||
|
#define COMPRESSION_MAX_COMP_BUFFER_SIZE 100000
|
||
|
#define COMPRESSION_MAX_QUEUE_LENGTH 10000
|
||
|
#define COMPRESSION_MIN_FILES_DELETED_PER_DISK_FREE 1
|
||
|
#define COMPRESSION_MAX_FILES_DELETED_PER_DISK_FREE 1024
|
||
|
#define COMPRESSION_MAX_COMPRESSION_LEVEL 10
|
||
|
|
||
|
#define COMPRESSION_DEFAULT_DISK_SPACE_USAGE 100000000
|
||
|
#define COMPRESSION_DEFAULT_BUFFER_SIZE 8192
|
||
|
#define COMPRESSION_DEFAULT_QUEUE_LENGTH 1000
|
||
|
#define COMPRESSION_DEFAULT_FILES_DELETED_PER_DISK_FREE 256
|
||
|
#define COMPRESSION_DEFAULT_FILE_SIZE_FOR_COMPRESSION 1
|
||
|
|
||
|
|
||
|
class COMPRESSION_SCHEME
|
||
|
{
|
||
|
public:
|
||
|
COMPRESSION_SCHEME()
|
||
|
: m_hCompressionDll (NULL),
|
||
|
m_pCompressionContext (NULL),
|
||
|
m_dwPriority (1),
|
||
|
m_dwDynamicCompressionLevel (0),
|
||
|
m_dwOnDemandCompressionLevel (COMPRESSION_MAX_COMPRESSION_LEVEL),
|
||
|
m_dwCreateFlags (0),
|
||
|
m_fDoStaticCompression (TRUE),
|
||
|
m_fDoOnDemandCompression (TRUE),
|
||
|
m_fDoDynamicCompression (TRUE),
|
||
|
m_pfnInitCompression (NULL),
|
||
|
m_pfnDeInitCompression (NULL),
|
||
|
m_pfnCreateCompression (NULL),
|
||
|
m_pfnCompress (NULL),
|
||
|
m_pfnDestroyCompression (NULL),
|
||
|
m_pfnResetCompression (NULL)
|
||
|
{}
|
||
|
|
||
|
HRESULT Initialize(MB *pmb, LPWSTR schemeName);
|
||
|
|
||
|
~COMPRESSION_SCHEME()
|
||
|
{
|
||
|
if (m_pfnDestroyCompression && m_pCompressionContext)
|
||
|
{
|
||
|
m_pfnDestroyCompression(m_pCompressionContext);
|
||
|
m_pCompressionContext = NULL;
|
||
|
}
|
||
|
|
||
|
if (m_pfnDeInitCompression)
|
||
|
{
|
||
|
m_pfnDeInitCompression();
|
||
|
}
|
||
|
|
||
|
if (m_hCompressionDll)
|
||
|
{
|
||
|
FreeLibrary(m_hCompressionDll);
|
||
|
m_hCompressionDll = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STRU m_strCompressionSchemeName;
|
||
|
STRA m_straCompressionSchemeName;
|
||
|
STRU m_strFilePrefix;
|
||
|
MULTISZ m_mszFileExtensions;
|
||
|
MULTISZ m_mszScriptFileExtensions;
|
||
|
|
||
|
DWORD m_dwPriority;
|
||
|
|
||
|
HMODULE m_hCompressionDll;
|
||
|
PFNCODEC_INIT_COMPRESSION m_pfnInitCompression;
|
||
|
PFNCODEC_DEINIT_COMPRESSION m_pfnDeInitCompression;
|
||
|
PFNCODEC_CREATE_COMPRESSION m_pfnCreateCompression;
|
||
|
PFNCODEC_COMPRESS m_pfnCompress;
|
||
|
PFNCODEC_DESTROY_COMPRESSION m_pfnDestroyCompression;
|
||
|
PFNCODEC_RESET_COMPRESSION m_pfnResetCompression;
|
||
|
|
||
|
// The compression context used for static compression
|
||
|
PVOID m_pCompressionContext;
|
||
|
|
||
|
DWORD m_dwDynamicCompressionLevel;
|
||
|
DWORD m_dwOnDemandCompressionLevel;
|
||
|
DWORD m_dwCreateFlags;
|
||
|
BOOL m_fDoDynamicCompression;
|
||
|
BOOL m_fDoStaticCompression;
|
||
|
BOOL m_fDoOnDemandCompression;
|
||
|
};
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
COMPRESSION_WORK_ITEM_COMPRESS,
|
||
|
COMPRESSION_WORK_ITEM_DELETE,
|
||
|
COMPRESSION_WORK_ITEM_TERMINATE
|
||
|
} COMPRESSION_WORK_ITEM_TYPE;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
LIST_ENTRY ListEntry;
|
||
|
COMPRESSION_WORK_ITEM_TYPE WorkItemType;
|
||
|
COMPRESSION_SCHEME *scheme;
|
||
|
STRU strPhysicalPath;
|
||
|
} COMPRESSION_WORK_ITEM;
|
||
|
|
||
|
#define MAX_SERVER_SCHEMES 100
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
DO_STATIC_COMPRESSION,
|
||
|
DO_DYNAMIC_COMPRESSION
|
||
|
} COMPRESSION_TO_PERFORM;
|
||
|
|
||
|
#define DYNAMIC_COMPRESSION_BUFFER_SIZE 4096
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
IN_CHUNK_LENGTH,
|
||
|
IN_CHUNK_EXTENSION,
|
||
|
IN_CHUNK_HEADER_NEW_LINE,
|
||
|
AT_CHUNK_DATA_NEW_LINE,
|
||
|
IN_CHUNK_DATA_NEW_LINE,
|
||
|
IN_CHUNK_DATA
|
||
|
} COMPRESS_CHUNK_STATE;
|
||
|
|
||
|
class COMPRESSION_BUFFER
|
||
|
{
|
||
|
public:
|
||
|
static HRESULT Initialize()
|
||
|
{
|
||
|
ALLOC_CACHE_CONFIGURATION acConfig;
|
||
|
|
||
|
acConfig.nConcurrency = 1;
|
||
|
acConfig.nThreshold = 100;
|
||
|
acConfig.cbSize = sizeof COMPRESSION_BUFFER;
|
||
|
|
||
|
allocHandler = new ALLOC_CACHE_HANDLER("COMPRESSION_BUFFER",
|
||
|
&acConfig);
|
||
|
if (allocHandler == NULL)
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static void Terminate()
|
||
|
{
|
||
|
if (allocHandler != NULL)
|
||
|
{
|
||
|
delete allocHandler;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void *operator new(size_t size)
|
||
|
{
|
||
|
DBG_ASSERT(size == sizeof COMPRESSION_BUFFER);
|
||
|
DBG_ASSERT(allocHandler != NULL);
|
||
|
return allocHandler->Alloc();
|
||
|
}
|
||
|
|
||
|
void operator delete(void *pCompressionBuffer)
|
||
|
{
|
||
|
DBG_ASSERT(pCompressionBuffer != NULL);
|
||
|
DBG_ASSERT(allocHandler != NULL);
|
||
|
DBG_REQUIRE(allocHandler->Free(pCompressionBuffer));
|
||
|
}
|
||
|
|
||
|
BYTE buffer[6 + DYNAMIC_COMPRESSION_BUFFER_SIZE + 7];
|
||
|
LIST_ENTRY listEntry;
|
||
|
|
||
|
static ALLOC_CACHE_HANDLER *allocHandler;
|
||
|
};
|
||
|
|
||
|
class COMPRESSION_CONTEXT
|
||
|
{
|
||
|
public:
|
||
|
static HRESULT Initialize()
|
||
|
{
|
||
|
ALLOC_CACHE_CONFIGURATION acConfig;
|
||
|
|
||
|
acConfig.nConcurrency = 1;
|
||
|
acConfig.nThreshold = 100;
|
||
|
acConfig.cbSize = sizeof COMPRESSION_CONTEXT;
|
||
|
|
||
|
allocHandler = new ALLOC_CACHE_HANDLER("COMPRESSION_CONTEXT",
|
||
|
&acConfig);
|
||
|
if (allocHandler == NULL)
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static void Terminate()
|
||
|
{
|
||
|
if (allocHandler != NULL)
|
||
|
{
|
||
|
delete allocHandler;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void *operator new(size_t size)
|
||
|
{
|
||
|
DBG_ASSERT(size == sizeof COMPRESSION_CONTEXT);
|
||
|
DBG_ASSERT(allocHandler != NULL);
|
||
|
return allocHandler->Alloc();
|
||
|
}
|
||
|
|
||
|
void operator delete(void *pCompressionContext)
|
||
|
{
|
||
|
DBG_ASSERT(pCompressionContext != NULL);
|
||
|
DBG_ASSERT(allocHandler != NULL);
|
||
|
DBG_REQUIRE(allocHandler->Free(pCompressionContext));
|
||
|
}
|
||
|
|
||
|
COMPRESSION_CONTEXT()
|
||
|
: m_pScheme (NULL),
|
||
|
m_fTransferChunkEncoded (FALSE),
|
||
|
m_pCompressionContext (NULL),
|
||
|
m_dwBytesInCurrentEncodedChunk (0),
|
||
|
m_encodedChunkState (IN_CHUNK_LENGTH),
|
||
|
m_pIoBuffer (NULL),
|
||
|
m_fRequestIsHead (FALSE),
|
||
|
m_fOriginalBodyEmpty (TRUE)
|
||
|
{
|
||
|
InitializeListHead(&m_BufferListHead);
|
||
|
}
|
||
|
|
||
|
~COMPRESSION_CONTEXT()
|
||
|
{
|
||
|
FreeBuffers();
|
||
|
|
||
|
if (m_pIoBuffer != NULL)
|
||
|
{
|
||
|
delete m_pIoBuffer;
|
||
|
}
|
||
|
|
||
|
if (m_pCompressionContext)
|
||
|
{
|
||
|
m_pScheme->m_pfnDestroyCompression(m_pCompressionContext);
|
||
|
m_pCompressionContext = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FreeBuffers()
|
||
|
{
|
||
|
while (!IsListEmpty(&m_BufferListHead))
|
||
|
{
|
||
|
LIST_ENTRY *pEntry = RemoveHeadList(&m_BufferListHead);
|
||
|
COMPRESSION_BUFFER *pBuffer = CONTAINING_RECORD(pEntry,
|
||
|
COMPRESSION_BUFFER,
|
||
|
listEntry);
|
||
|
delete pBuffer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BYTE *GetNewBuffer()
|
||
|
{
|
||
|
COMPRESSION_BUFFER *pBuffer = new COMPRESSION_BUFFER;
|
||
|
if (pBuffer == NULL)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
InitializeListHead(&pBuffer->listEntry);
|
||
|
InsertHeadList(&m_BufferListHead, &pBuffer->listEntry);
|
||
|
|
||
|
return pBuffer->buffer;
|
||
|
}
|
||
|
|
||
|
HRESULT SetupCurrentULChunk();
|
||
|
|
||
|
DWORD QueryBytesAvailable()
|
||
|
{
|
||
|
if (m_fCurrentULChunkFromMemory)
|
||
|
{
|
||
|
return m_pCurrentULChunk->FromMemory.BufferLength;
|
||
|
}
|
||
|
|
||
|
// We don't handle (nor do we plan to) FileName chunks
|
||
|
DBG_ASSERT(m_pCurrentULChunk->DataChunkType == HttpDataChunkFromFileHandle);
|
||
|
return m_bytesInIoBuffer - m_currentLocationInIoBuffer;
|
||
|
}
|
||
|
|
||
|
BYTE *QueryBytePtr()
|
||
|
{
|
||
|
if (m_fCurrentULChunkFromMemory)
|
||
|
{
|
||
|
return (PBYTE)m_pCurrentULChunk->FromMemory.pBuffer;
|
||
|
}
|
||
|
|
||
|
// We don't handle (nor do we plan to) FileName chunks
|
||
|
DBG_ASSERT(m_pCurrentULChunk->DataChunkType == HttpDataChunkFromFileHandle);
|
||
|
return m_pIoBuffer + m_currentLocationInIoBuffer;
|
||
|
}
|
||
|
|
||
|
HRESULT ProcessEncodedChunkHeader();
|
||
|
|
||
|
HRESULT CalculateEncodedChunkByteCount();
|
||
|
|
||
|
HRESULT DeleteEncodedChunkExtension();
|
||
|
|
||
|
HRESULT IncrementPointerInULChunk(IN DWORD dwIncr = 1);
|
||
|
|
||
|
COMPRESSION_SCHEME *m_pScheme;
|
||
|
|
||
|
//
|
||
|
// Is the original response chunk encoded?
|
||
|
//
|
||
|
BOOL m_fTransferChunkEncoded;
|
||
|
|
||
|
//
|
||
|
// If the original response is Chunk encoded, information about the
|
||
|
// current chunk in the response
|
||
|
//
|
||
|
DWORD m_dwBytesInCurrentEncodedChunk;
|
||
|
COMPRESS_CHUNK_STATE m_encodedChunkState;
|
||
|
|
||
|
//
|
||
|
// The context used by the compression routines
|
||
|
//
|
||
|
PVOID m_pCompressionContext;
|
||
|
|
||
|
//
|
||
|
// Storage for the original response
|
||
|
//
|
||
|
BUFFER m_ULChunkBuffer;
|
||
|
DWORD m_cULChunks;
|
||
|
|
||
|
//
|
||
|
// position in the original response
|
||
|
//
|
||
|
DWORD m_cCurrentULChunk;
|
||
|
HTTP_DATA_CHUNK *m_pCurrentULChunk;
|
||
|
BOOL m_fCurrentULChunkFromMemory;
|
||
|
|
||
|
//
|
||
|
// buffer for reading data for FileHandle chunks
|
||
|
//
|
||
|
BYTE *m_pIoBuffer;
|
||
|
DWORD m_currentLocationInIoBuffer;
|
||
|
DWORD m_bytesInIoBuffer;
|
||
|
|
||
|
static ALLOC_CACHE_HANDLER *allocHandler;
|
||
|
|
||
|
LIST_ENTRY m_BufferListHead;
|
||
|
|
||
|
//
|
||
|
// Some members to keep track of HEAD request body suppression
|
||
|
//
|
||
|
BOOL m_fRequestIsHead;
|
||
|
BOOL m_fOriginalBodyEmpty;
|
||
|
};
|
||
|
|
||
|
class HTTP_COMPRESSION
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
static HRESULT Initialize();
|
||
|
|
||
|
static VOID Terminate();
|
||
|
|
||
|
static HRESULT DoStaticFileCompression(IN W3_CONTEXT *pW3Context,
|
||
|
IN OUT W3_FILE_INFO **ppFileInfo);
|
||
|
|
||
|
static HRESULT OnSendResponse(
|
||
|
IN W3_CONTEXT *pW3Context,
|
||
|
IN BOOL fMoreData);
|
||
|
|
||
|
static HRESULT DoDynamicCompression(
|
||
|
IN W3_CONTEXT *pW3Context,
|
||
|
IN BOOL fMoreData);
|
||
|
|
||
|
static BOOL QueryDoStaticCompression()
|
||
|
{
|
||
|
return sm_fDoStaticCompression;
|
||
|
}
|
||
|
|
||
|
static BOOL QueryDoDynamicCompression()
|
||
|
{
|
||
|
return sm_fDoDynamicCompression;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
static COMPRESSION_SCHEME *sm_pCompressionSchemes[MAX_SERVER_SCHEMES];
|
||
|
static DWORD sm_dwNumberOfSchemes;
|
||
|
static STRU *sm_pstrCompressionDirectory;
|
||
|
static STRA *sm_pstrCacheControlHeader;
|
||
|
static STRA *sm_pstrExpiresHeader;
|
||
|
static BOOL sm_fDoStaticCompression;
|
||
|
static BOOL sm_fDoDynamicCompression;
|
||
|
static BOOL sm_fDoOnDemandCompression;
|
||
|
static BOOL sm_fDoDiskSpaceLimiting;
|
||
|
static BOOL sm_fNoCompressionForHttp10;
|
||
|
static BOOL sm_fNoCompressionForProxies;
|
||
|
static BOOL sm_fNoCompressionForRange;
|
||
|
static BOOL sm_fSendCacheHeaders;
|
||
|
static DWORD sm_dwMaxDiskSpaceUsage;
|
||
|
static DWORD sm_dwIoBufferSize;
|
||
|
static DWORD sm_dwCompressionBufferSize;
|
||
|
static DWORD sm_dwMaxQueueLength;
|
||
|
static DWORD sm_dwFilesDeletedPerDiskFree;
|
||
|
static DWORD sm_dwMinFileSizeForCompression;
|
||
|
static PBYTE sm_pIoBuffer;
|
||
|
static PBYTE sm_pCompressionBuffer;
|
||
|
static CRITICAL_SECTION sm_CompressionDirectoryLock;
|
||
|
static DWORD sm_dwCurrentDiskSpaceUsage;
|
||
|
static BOOL sm_fCompressionVolumeIsFat;
|
||
|
static LIST_ENTRY sm_CompressionThreadWorkQueue;
|
||
|
static CRITICAL_SECTION sm_CompressionThreadLock;
|
||
|
static HANDLE sm_hThreadEvent;
|
||
|
static HANDLE sm_hCompressionThreadHandle;
|
||
|
static DWORD sm_dwCurrentQueueLength;
|
||
|
static BOOL sm_fHttpCompressionInitialized;
|
||
|
static BOOL sm_fIsTerminating;
|
||
|
|
||
|
static HRESULT ReadMetadata(MB *pmb);
|
||
|
|
||
|
static HRESULT InitializeCompressionSchemes(MB *pmb);
|
||
|
|
||
|
static HRESULT InitializeCompressionDirectory();
|
||
|
|
||
|
static HRESULT InitializeCompressionThread();
|
||
|
|
||
|
static DWORD WINAPI CompressionThread(LPVOID);
|
||
|
|
||
|
static BOOL QueueWorkItem(
|
||
|
IN COMPRESSION_WORK_ITEM *WorkItem,
|
||
|
IN BOOL fOverrideMaxQueueLength,
|
||
|
IN BOOL fQueueAtHead);
|
||
|
|
||
|
static VOID FindMatchingSchemes(
|
||
|
IN CHAR * pszAcceptEncoding,
|
||
|
IN LPWSTR pszExtension,
|
||
|
IN COMPRESSION_TO_PERFORM performCompr,
|
||
|
OUT DWORD matchingSchemes[],
|
||
|
OUT DWORD *pdwClientCompressionCount);
|
||
|
|
||
|
static HRESULT ConvertPhysicalPathToCompressedPath(
|
||
|
IN COMPRESSION_SCHEME *scheme,
|
||
|
IN STRU *pstrPhysicalPath,
|
||
|
OUT STRU *pstrCompressedFileName);
|
||
|
|
||
|
static BOOL CheckForExistenceOfCompressedFile(
|
||
|
IN W3_FILE_INFO *pOrigFile,
|
||
|
IN STRU *pstrCompressedFileName,
|
||
|
OUT W3_FILE_INFO **ppCompFile,
|
||
|
IN BOOL fDeleteAllowed = TRUE);
|
||
|
|
||
|
static BOOL QueueCompressFile(
|
||
|
IN COMPRESSION_SCHEME *scheme,
|
||
|
IN STRU &strPhysicalPath);
|
||
|
|
||
|
static VOID CompressFile(IN COMPRESSION_SCHEME *scheme,
|
||
|
IN STRU &strPhysicalPath);
|
||
|
|
||
|
static VOID FreeDiskSpace();
|
||
|
|
||
|
static BOOL CompressAndWriteData(
|
||
|
IN COMPRESSION_SCHEME *scheme,
|
||
|
IN PBYTE InputBuffer,
|
||
|
IN DWORD BytesToCompress,
|
||
|
OUT PDWORD BytesWritten,
|
||
|
IN HANDLE hCompressedFile);
|
||
|
};
|
||
|
|
||
|
#endif _COMPRESSION_H_
|