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.
4903 lines
118 KiB
4903 lines
118 KiB
/*********************************************************************
|
|
* *
|
|
* File: specweb99-CAD.cxx *
|
|
* ---- *
|
|
* *
|
|
* *
|
|
* Overview: *
|
|
* -------- *
|
|
* *
|
|
* Implementation of the dynamic GET with custom ad rotation *
|
|
* operation in the SPECweb99 benchmark. *
|
|
* *
|
|
* *
|
|
* Revision History: *
|
|
* ---------------- *
|
|
* *
|
|
* Date Author Reason *
|
|
* ---- ------ ------ *
|
|
* *
|
|
* 07/28/02 Ankur Upadhyaya Initial Creation. *
|
|
* *
|
|
*********************************************************************/
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
//
|
|
// Includes.
|
|
//
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include "httpextp.h"
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
//
|
|
// Defines.
|
|
//
|
|
|
|
|
|
#define USE_SYNC_IO 1
|
|
|
|
#define USE_ASYNC_IO 2
|
|
|
|
#define USE_ADAPTABLE_IO 3
|
|
|
|
#define INIT_ISAPI_RESPONSE_STACK_SIZE 1024
|
|
|
|
#define INIT_CLASS1_DATA_BUFFER_STACK_SIZE 1024
|
|
|
|
#define INIT_CLASS2_DATA_BUFFER_STACK_SIZE 1024
|
|
|
|
#define NUM_CUSTOM_ADS 360
|
|
|
|
#define CUSTOM_AD_SIZE 39
|
|
|
|
#define USER_PROFILE_SIZE 15
|
|
|
|
#define FREE 0
|
|
|
|
#define LOCKED -1
|
|
|
|
#define FILE_DATA_BUFFER_SIZE 8192
|
|
|
|
#define MAX_COOKIE_STRING_LENGTH 128
|
|
|
|
#define MAX_FRAGMENT_CACHE_KEY_LENGTH 1024
|
|
|
|
#define WEIGHTING_MASK 0x0000000f
|
|
|
|
#define GENDER_MASK 0x30000000
|
|
|
|
#define AGE_GROUP_MASK 0x0f000000
|
|
|
|
#define REGION_MASK 0x00f00000
|
|
|
|
#define INTEREST1_MASK 0x000ffc00
|
|
|
|
#define INTEREST2_MASK 0x000003ff
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
//
|
|
// Type definitions.
|
|
//
|
|
|
|
|
|
typedef struct class1_data_buffer
|
|
{
|
|
|
|
SINGLE_LIST_ENTRY item_entry;
|
|
|
|
CHAR buffer[ 10 * 1024 ];
|
|
|
|
} CLASS1_DATA_BUFFER;
|
|
|
|
|
|
typedef struct class2_data_buffer
|
|
{
|
|
|
|
SINGLE_LIST_ENTRY item_entry;
|
|
|
|
CHAR buffer[ 100 * 1024 ];
|
|
|
|
} CLASS2_DATA_BUFFER;
|
|
|
|
|
|
typedef struct isapi_response
|
|
{
|
|
|
|
SINGLE_LIST_ENTRY item_entry;
|
|
|
|
OVERLAPPED overlapped;
|
|
|
|
HANDLE hFile;
|
|
|
|
HSE_VECTOR_ELEMENT vector_element;
|
|
|
|
DWORD ad_id;
|
|
|
|
DWORD class_number;
|
|
|
|
CLASS1_DATA_BUFFER *class1_data_buffer_ptr;
|
|
|
|
CLASS2_DATA_BUFFER *class2_data_buffer_ptr;
|
|
|
|
EXTENSION_CONTROL_BLOCK *pECB;
|
|
|
|
HSE_VECTOR_ELEMENT vector_element_array[ 7 ];
|
|
|
|
BOOL use_async_vector_send;
|
|
|
|
HSE_RESPONSE_VECTOR response_vector;
|
|
|
|
CHAR remote_addr[ 16 ];
|
|
|
|
CHAR pszHeaders[ 67 + MAX_COOKIE_STRING_LENGTH ];
|
|
|
|
CHAR set_cookie_string[ MAX_COOKIE_STRING_LENGTH ];
|
|
|
|
WCHAR unicode_fragment_cache_key[ MAX_FRAGMENT_CACHE_KEY_LENGTH ];
|
|
|
|
} ISAPI_RESPONSE;
|
|
|
|
|
|
typedef struct custom_ad
|
|
{
|
|
|
|
DWORD ad_id;
|
|
|
|
DWORD ad_demographics;
|
|
|
|
DWORD gender_wt;
|
|
|
|
DWORD age_group_wt;
|
|
|
|
DWORD region_wt;
|
|
|
|
DWORD interest1_wt;
|
|
|
|
DWORD interest2_wt;
|
|
|
|
DWORD minimum_match_value;
|
|
|
|
DWORD expiration_time;
|
|
|
|
} CUSTOM_AD;
|
|
|
|
|
|
typedef struct lock
|
|
{
|
|
|
|
volatile LONG current_state;
|
|
|
|
volatile LONG num_writers;
|
|
|
|
} LOCK;
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
//
|
|
// Global Variables.
|
|
//
|
|
|
|
|
|
DWORD g_vector_send_io_mode_config = USE_SYNC_IO;
|
|
|
|
DWORD g_vector_send_async_range_start = 0;
|
|
|
|
DWORD g_read_file_io_mode_config = USE_SYNC_IO;
|
|
|
|
DWORD g_read_file_async_range_start = 0;
|
|
|
|
CHAR g_root_dir[ MAX_PATH ];
|
|
|
|
DWORD g_root_dir_length;
|
|
|
|
CHAR g_app_pool_name[ 1024 ];
|
|
|
|
DWORD g_app_pool_name_length = 1024;
|
|
|
|
SLIST_HEADER g_isapi_response_stack;
|
|
|
|
SLIST_HEADER g_class1_data_buffer_stack;
|
|
|
|
SLIST_HEADER g_class2_data_buffer_stack;
|
|
|
|
CUSTOM_AD g_custom_ads_buffer[ NUM_CUSTOM_ADS ];
|
|
|
|
DWORD *g_user_personalities_buffer = NULL;
|
|
|
|
LOCK g_user_personalities_buffer_lock;
|
|
|
|
LOCK g_custom_ads_buffer_lock;
|
|
|
|
HANDLE g_update_buffers_thread;
|
|
|
|
CHAR g_fragment_cache_key_base[ MAX_FRAGMENT_CACHE_KEY_LENGTH ];
|
|
|
|
DWORD g_fragment_cache_key_base_length;
|
|
|
|
volatile LONG g_fragment_cache_key_base_not_initialized = 1;
|
|
|
|
DWORD g_num_users;
|
|
|
|
CHAR *g_pszStatus_200 = "200 OK";
|
|
|
|
CHAR *g_pszStatus_404 = "404 File Inaccessible";
|
|
|
|
HSE_VECTOR_ELEMENT g_vector_element_0 = { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER,
|
|
"<html>\n"
|
|
"<head><title>SPECweb99 Dynamic GET & POST Test</title></head>\n"
|
|
"<body>\n"
|
|
"<p>SERVER_SOFTWARE = Microsoft-IIS/6.0\n"
|
|
"<p>REMOTE_ADDR = ",
|
|
0,
|
|
132 };
|
|
|
|
HSE_VECTOR_ELEMENT g_vector_element_2 = { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER,
|
|
"\n<p>SCRIPT_NAME = /specweb99-CAD.dll\n"
|
|
"<p>QUERY_STRING = ",
|
|
0,
|
|
55 };
|
|
|
|
HSE_VECTOR_ELEMENT g_vector_element_4 = { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER,
|
|
"\n<pre>\n",
|
|
0,
|
|
7 };
|
|
|
|
HSE_VECTOR_ELEMENT g_vector_element_6 = { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER,
|
|
"</pre>\n"
|
|
"</body>\n</html>\n",
|
|
0,
|
|
23 };
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
//
|
|
// Function prototypes.
|
|
//
|
|
|
|
|
|
VOID init_lock( LOCK *lock );
|
|
|
|
VOID acquire_exclusive_lock( LOCK *lock );
|
|
|
|
VOID release_exclusive_lock( LOCK *lock );
|
|
|
|
VOID acquire_shared_lock( LOCK *lock );
|
|
|
|
VOID release_shared_lock( LOCK *lock );
|
|
|
|
VOID initialize_isapi_response_stack( VOID );
|
|
|
|
ISAPI_RESPONSE *allocate_isapi_response( VOID );
|
|
|
|
VOID free_isapi_response( ISAPI_RESPONSE * isapi_response_ptr );
|
|
|
|
VOID clear_isapi_response_stack( VOID );
|
|
|
|
VOID initialize_class1_data_buffer_stack( VOID );
|
|
|
|
CLASS1_DATA_BUFFER *allocate_class1_data_buffer( VOID );
|
|
|
|
VOID free_class1_data_buffer( CLASS1_DATA_BUFFER *class1_data_buffer_ptr );
|
|
|
|
VOID clear_class1_data_buffer_stack( VOID );
|
|
|
|
VOID initialize_class2_data_buffer_stack( VOID );
|
|
|
|
CLASS2_DATA_BUFFER *allocate_class2_data_buffer( VOID );
|
|
|
|
VOID free_class2_data_buffer( CLASS2_DATA_BUFFER *class2_data_buffer_ptr );
|
|
|
|
VOID clear_class2_data_buffer_stack( VOID );
|
|
|
|
VOID initialize_isapi_response_stack( VOID );
|
|
|
|
ISAPI_RESPONSE *allocate_isapi_response( VOID );
|
|
|
|
VOID free_isapi_response( ISAPI_RESPONSE * isapi_response_ptr );
|
|
|
|
VOID clear_isapi_response_stack( VOID );
|
|
|
|
BOOL initialize_isapi_response( ISAPI_RESPONSE *isapi_response_ptr,
|
|
EXTENSION_CONTROL_BLOCK *pECB,
|
|
CHAR *filename,
|
|
DWORD filesize,
|
|
CHAR *set_cookie_string,
|
|
BOOL use_async_vector_send,
|
|
DWORD query_string_length );
|
|
|
|
BOOL load_user_personality_file( VOID );
|
|
|
|
BOOL load_custom_ads_file( VOID );
|
|
|
|
DWORD WINAPI update_user_and_ad_buffers( VOID *dummy_parameter );
|
|
|
|
DWORD send_error_page( EXTENSION_CONTROL_BLOCK *pECB,
|
|
CHAR *error_message,
|
|
CHAR *status,
|
|
DWORD query_string_length );
|
|
|
|
VOID dword_to_string( DWORD dword,
|
|
CHAR *string );
|
|
|
|
VOID WINAPI vector_send_completion_callback( EXTENSION_CONTROL_BLOCK *pECB,
|
|
VOID *pvContext,
|
|
DWORD cbIO,
|
|
DWORD dwError );
|
|
|
|
VOID CALLBACK read_file_completion_callback( DWORD dwErrorCode,
|
|
DWORD dwNumberOfBytesTransferred,
|
|
OVERLAPPED *overlapped );
|
|
|
|
DWORD determine_set_cookie_string( INT64 user_index,
|
|
DWORD last_ad,
|
|
CHAR *set_cookie_string );
|
|
|
|
VOID insert_custom_ad_information( CHAR *buffer, DWORD ad_id );
|
|
|
|
DWORD handle_class1_or_class2_request( EXTENSION_CONTROL_BLOCK *pECB,
|
|
ISAPI_RESPONSE *isapi_response_ptr,
|
|
CHAR *filename,
|
|
DWORD filesize,
|
|
BOOL use_async_vector_send,
|
|
DWORD ad_id,
|
|
DWORD query_string_length );
|
|
|
|
BOOL load_registry_data( VOID );
|
|
|
|
BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer );
|
|
|
|
DWORD WINAPI HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB );
|
|
|
|
BOOL WINAPI TerminateExtension( DWORD dwFlags );
|
|
|
|
#define LOG_ERROR( text, error ) _LogError(__FUNCTION__, __FILE__, __LINE__, text, error )
|
|
|
|
void _LogError(const char * function, const char *file, int line, char * text, DWORD error)
|
|
{
|
|
char buf[1024];
|
|
sprintf(buf, "%s(%d) %s - error=0x%x (%d) - %s\n", file, line, function, error, error, text );
|
|
OutputDebugStringA(buf);
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID initialize_isapi_response_stack( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the stack of free ISAPI_RESPONSE structures on
|
|
the heap.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Initialize the SLIST_HEADER head that will be used to implement
|
|
// the stack of free ISAPI_RESPONSE structures on the heap.
|
|
//
|
|
|
|
InitializeSListHead( &g_isapi_response_stack );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
ISAPI_RESPONSE *allocate_isapi_response( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pops an element off of the stack of free ISAPI_RESPONSE structures
|
|
on the heap.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to a free ISAPI_RESPONSE structure on the heap.
|
|
|
|
--*/
|
|
{
|
|
|
|
ISAPI_RESPONSE *isapi_response_ptr;
|
|
|
|
|
|
//
|
|
// Attempt to pop a free ISAPI_RESPONSE
|
|
// structure off of g_isapi_response_stack.
|
|
//
|
|
|
|
if ( !( isapi_response_ptr =
|
|
( ISAPI_RESPONSE * )InterlockedPopEntrySList( &g_isapi_response_stack ) ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If g_isapi_response_stack was empty
|
|
// allocate a new ISAPI_RESPONSE structure
|
|
// off of the heap.
|
|
//
|
|
|
|
isapi_response_ptr = ( ISAPI_RESPONSE * )malloc( sizeof( ISAPI_RESPONSE ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Return a pointer to the ISAPI_RESPONSE
|
|
// structure allocated.
|
|
//
|
|
|
|
return( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID free_isapi_response( ISAPI_RESPONSE *isapi_response_ptr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes an element onto the stack of free ISAPI_RESPONSE structures
|
|
on the heap.
|
|
|
|
Arguments:
|
|
|
|
isapi_response_ptr - A pointer to a free ISAPI_RESPONSE structure
|
|
on the heap.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Pop the input pointer to a free ISAPI_RESPONSE
|
|
// structure on the heap onto g_isapi_response_stack.
|
|
//
|
|
|
|
InterlockedPushEntrySList( &g_isapi_response_stack,
|
|
( SINGLE_LIST_ENTRY * )isapi_response_ptr );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID clear_isapi_response_stack( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the stack of free ISAPI_RESPONSE structures on the heap,
|
|
freeing all system resources allocated for this data structure.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Flush g_isapi_response_stack of all of
|
|
// its elements and set 'curr_struct_ptr'
|
|
// to point to a linked list containing
|
|
// all of these elements.
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY *curr_struct_ptr = InterlockedFlushSList( &g_isapi_response_stack );
|
|
|
|
SINGLE_LIST_ENTRY *kill_struct_ptr;
|
|
|
|
|
|
//
|
|
// Destroy all elements in the linked
|
|
// list initially pointed to by
|
|
// 'curr_struct_ptr'.
|
|
//
|
|
|
|
while( curr_struct_ptr )
|
|
{
|
|
|
|
kill_struct_ptr = curr_struct_ptr;
|
|
|
|
curr_struct_ptr = curr_struct_ptr->Next;
|
|
|
|
free( ( ISAPI_RESPONSE * )kill_struct_ptr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID initialize_class1_data_buffer_stack( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the stack of free CLASS1_DATA_BUFFER structures on
|
|
the heap.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Initialize the SLIST_HEADER head that will be used to implement
|
|
// the stack of free CLASS1_DATA_BUFFER structures on the heap.
|
|
//
|
|
|
|
InitializeSListHead( &g_class1_data_buffer_stack );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
CLASS1_DATA_BUFFER *allocate_class1_data_buffer( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pops an element off of the stack of free CLASS1_DATA_BUFFER
|
|
structures on the heap.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to a free CLASS1_DATA_BUFFER structure on
|
|
the heap.
|
|
|
|
--*/
|
|
{
|
|
|
|
CLASS1_DATA_BUFFER *class1_data_buffer_ptr;
|
|
|
|
|
|
//
|
|
// Attempt to pop a free CLASS1_DATA_BUFFER
|
|
// structure off of g_class1_data_buffer_stack.
|
|
//
|
|
|
|
if ( !( class1_data_buffer_ptr =
|
|
( CLASS1_DATA_BUFFER * )InterlockedPopEntrySList( &g_class1_data_buffer_stack ) ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If g_class1_data_buffer_stack was empty
|
|
// allocate a new CLASS1_DATA_BUFFER structure
|
|
// off of the heap.
|
|
//
|
|
|
|
class1_data_buffer_ptr = ( CLASS1_DATA_BUFFER * )malloc( sizeof( CLASS1_DATA_BUFFER ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Return a pointer to the CLASS1_DATA_BUFFER
|
|
// structure allocated.
|
|
//
|
|
|
|
return( class1_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID free_class1_data_buffer( CLASS1_DATA_BUFFER *class1_data_buffer_ptr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes an element onto the stack of free CLASS1_DATA_BUFFER
|
|
structures on the heap.
|
|
|
|
Arguments:
|
|
|
|
A pointer to a free CLASS1_DATA_BUFFER structure on the heap.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Pop the input pointer to a free CLASS1_DATA_BUFFER
|
|
// structure on the heap onto g_class1_data_buffer_stack.
|
|
//
|
|
|
|
InterlockedPushEntrySList( &g_class1_data_buffer_stack,
|
|
( SINGLE_LIST_ENTRY * )class1_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID clear_class1_data_buffer_stack( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the stack of free CLASS1_DATA_BUFFER structures on the heap,
|
|
freeing all system resources allocated for this data structure.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Flush g_class1_data_buffer_stack of all
|
|
// of its elements and set 'curr_struct_ptr'
|
|
// to point to a linked list containing
|
|
// all of these elements.
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY *curr_struct_ptr = InterlockedFlushSList( &g_class1_data_buffer_stack );
|
|
|
|
SINGLE_LIST_ENTRY *kill_struct_ptr;
|
|
|
|
|
|
//
|
|
// Destroy all elements in the linked
|
|
// list initially pointed to by
|
|
// 'curr_struct_ptr'
|
|
//
|
|
|
|
while( curr_struct_ptr )
|
|
{
|
|
|
|
kill_struct_ptr = curr_struct_ptr;
|
|
|
|
curr_struct_ptr = curr_struct_ptr->Next;
|
|
|
|
free( ( CLASS1_DATA_BUFFER * )kill_struct_ptr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID initialize_class2_data_buffer_stack( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the stack of free CLASS2_DATA_BUFFER structures on
|
|
the heap.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Initialize the SLIST_HEADER head that will be used to implement
|
|
// the stack of free CLASS2_DATA_BUFFER structures on the heap.
|
|
//
|
|
|
|
InitializeSListHead( &g_class2_data_buffer_stack );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
CLASS2_DATA_BUFFER *allocate_class2_data_buffer( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pops an element off of the stack of free CLASS2_DATA_BUFFER
|
|
structures on the heap.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to a free CLASS2_DATA_BUFFER structure on
|
|
the heap.
|
|
|
|
--*/
|
|
{
|
|
|
|
CLASS2_DATA_BUFFER *class2_data_buffer_ptr;
|
|
|
|
|
|
//
|
|
// Attempt to pop a free CLASS2_DATA_BUFFER
|
|
// structure off of g_class2_data_buffer_stack.
|
|
//
|
|
|
|
if ( !( class2_data_buffer_ptr =
|
|
( CLASS2_DATA_BUFFER * )InterlockedPopEntrySList( &g_class2_data_buffer_stack ) ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If g_class2_data_buffer_stack was empty
|
|
// allocate a new CLASS2_DATA_BUFFER structure
|
|
// off of the heap.
|
|
//
|
|
|
|
class2_data_buffer_ptr = ( CLASS2_DATA_BUFFER * )malloc( sizeof( CLASS2_DATA_BUFFER ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Return a pointer to the CLASS2_DATA_BUFFER
|
|
// structure allocated.
|
|
//
|
|
|
|
return( class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID free_class2_data_buffer( CLASS2_DATA_BUFFER *class2_data_buffer_ptr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pushes an element onto the stack of free CLASS2_DATA_BUFFER
|
|
structures on the heap.
|
|
|
|
Arguments:
|
|
|
|
A pointer to a free CLASS2_DATA_BUFFER structure on the heap.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Push the input pointer to a free CLASS2_DATA_BUFFER
|
|
// structure on the heap onto g_class2_data_buffer_stack.
|
|
//
|
|
|
|
InterlockedPushEntrySList( &g_class2_data_buffer_stack,
|
|
( SINGLE_LIST_ENTRY * )class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID clear_class2_data_buffer_stack( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the stack of free CLASS2_DATA_BUFFER structures on the heap,
|
|
freeing all system resources allocated for this data structure.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Flush g_class2_data_buffer_stack of all
|
|
// of its elements and set 'curr_struct_ptr'
|
|
// to point to a linked list containing
|
|
// all of these elements.
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY *curr_struct_ptr = InterlockedFlushSList( &g_class2_data_buffer_stack );
|
|
|
|
SINGLE_LIST_ENTRY *kill_struct_ptr;
|
|
|
|
|
|
//
|
|
// Destroy all elements in the linked
|
|
// list initially pointed to by
|
|
// 'curr_struct_ptr'
|
|
//
|
|
|
|
while( curr_struct_ptr )
|
|
{
|
|
|
|
kill_struct_ptr = curr_struct_ptr;
|
|
|
|
curr_struct_ptr = curr_struct_ptr->Next;
|
|
|
|
free( ( CLASS2_DATA_BUFFER * )kill_struct_ptr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
BOOL initialize_isapi_response( ISAPI_RESPONSE *isapi_response_ptr,
|
|
EXTENSION_CONTROL_BLOCK *pECB,
|
|
CHAR *filename,
|
|
DWORD filesize,
|
|
CHAR *set_cookie_string,
|
|
BOOL use_async_vector_send,
|
|
DWORD query_string_length )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an ISAPI response structure by populating it with the
|
|
appropriate data.
|
|
|
|
Arguments:
|
|
|
|
isapi_response_ptr - Pointer to the ISAPI response structure to be
|
|
initialized.
|
|
|
|
pECB - Pointer to the relevant extension control block.
|
|
|
|
filename - Optional field indicating the absolute name of the file
|
|
requested.
|
|
|
|
filesize - Optional field indicating the size of the file requested.
|
|
|
|
set_cookie_string - String to use for Set-Cookie HTTP header field.
|
|
|
|
use_async_vector_send - Flag indicating whether VectorSend is to be
|
|
used in asynchronous or synchronous mode.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE on success and FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD remote_addr_size = 16; // 16 == sizeof( isapi_response_ptr->remote_addr )
|
|
|
|
CHAR fragment_cache_key[ MAX_FRAGMENT_CACHE_KEY_LENGTH ];
|
|
|
|
CHAR content_length_string[ 16 ];
|
|
|
|
DWORD content_length_string_length = 16; // 16 == sizeof( content_length_string )
|
|
|
|
DWORD set_cookie_string_length;
|
|
|
|
DWORD ii;
|
|
|
|
|
|
//
|
|
// Set the fragment cache key.
|
|
//
|
|
|
|
strcpy( fragment_cache_key, g_fragment_cache_key_base );
|
|
|
|
memcpy( fragment_cache_key + g_fragment_cache_key_base_length,
|
|
pECB->lpszQueryString,
|
|
query_string_length + 1 );
|
|
|
|
for ( ii = 0;
|
|
isapi_response_ptr->unicode_fragment_cache_key[ ii ] = ( WCHAR )fragment_cache_key[ ii ];
|
|
ii++ );
|
|
|
|
|
|
//
|
|
// Obtain the IP address of the client.
|
|
//
|
|
|
|
if ( !pECB->GetServerVariable( pECB->ConnID,
|
|
"REMOTE_ADDR",
|
|
isapi_response_ptr->remote_addr,
|
|
&remote_addr_size ) )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Obtain the cookie string.
|
|
//
|
|
|
|
strncpy( isapi_response_ptr->set_cookie_string, set_cookie_string, MAX_COOKIE_STRING_LENGTH );
|
|
|
|
|
|
//
|
|
// Populate the vector_element_array data structure.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 0 ] = g_vector_element_0;
|
|
|
|
|
|
isapi_response_ptr->vector_element_array[ 1 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
|
|
isapi_response_ptr->vector_element_array[ 1 ].pvContext = isapi_response_ptr->remote_addr;
|
|
|
|
isapi_response_ptr->vector_element_array[ 1 ].cbSize = remote_addr_size - 1;
|
|
|
|
|
|
isapi_response_ptr->vector_element_array[ 2 ] = g_vector_element_2;
|
|
|
|
|
|
isapi_response_ptr->vector_element_array[ 3 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
|
|
isapi_response_ptr->vector_element_array[ 3 ].pvContext = pECB->lpszQueryString;
|
|
|
|
isapi_response_ptr->vector_element_array[ 3 ].cbSize = query_string_length;
|
|
|
|
|
|
isapi_response_ptr->vector_element_array[ 4 ] = g_vector_element_4;
|
|
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_FRAGMENT;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].pvContext = ( DWORD * )isapi_response_ptr->unicode_fragment_cache_key;
|
|
|
|
|
|
isapi_response_ptr->vector_element_array[ 6 ] = g_vector_element_6;
|
|
|
|
|
|
//
|
|
// Populate the HSE_RESPONSE_VECTOR structure.
|
|
// To do this, use the following steps...
|
|
//
|
|
|
|
|
|
//
|
|
// First, compute the string of HTTP headers to be sent with the
|
|
// response. To avoid a costly sprintf call, we will use memcpy
|
|
// instead.
|
|
//
|
|
|
|
|
|
//
|
|
// Copy the initial "hardcoded" component of the HTTP header string.
|
|
//
|
|
|
|
strcpy( isapi_response_ptr->pszHeaders,
|
|
|
|
"Content-type: text/html\r\n"
|
|
"Content-Length: " );
|
|
|
|
|
|
//
|
|
// Construct a string representing the value of the HTTP Content-Length
|
|
// header field. Determine and store the length of this string. Note
|
|
// that 16 is the size of the buffer to which the content_length_string
|
|
// is written.
|
|
//
|
|
|
|
dword_to_string( ( DWORD )( filesize +
|
|
isapi_response_ptr->vector_element_array[ 0 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 1 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 2 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 3 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 4 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 6 ].cbSize ),
|
|
|
|
content_length_string );
|
|
|
|
|
|
//
|
|
// Concatenate the content length string to the header
|
|
// string. Note that prior to this operation the length
|
|
// of the header string is 41.
|
|
//
|
|
|
|
memcpy( isapi_response_ptr->pszHeaders + 41,
|
|
content_length_string,
|
|
content_length_string_length = strlen( content_length_string ) );
|
|
|
|
|
|
//
|
|
// Concatenate the 14 character hardcoded string "\r\nSet-Cookie: "
|
|
// to the header string. Note that just prior to this operation
|
|
// the length of the header string is given by the variable 'ii'.
|
|
//
|
|
|
|
ii = 41 + content_length_string_length;
|
|
|
|
memcpy( isapi_response_ptr->pszHeaders + ii,
|
|
"\r\nSet-Cookie: ",
|
|
14 );
|
|
|
|
|
|
//
|
|
// Concatenate the Set-Cookie string to the header string. Note
|
|
// that just prior to this operation the length of the header
|
|
// string is given by the variable ii.
|
|
//
|
|
|
|
ii += 14;
|
|
|
|
memcpy( isapi_response_ptr->pszHeaders + ii,
|
|
set_cookie_string,
|
|
set_cookie_string_length = strlen( set_cookie_string ) );
|
|
|
|
|
|
//
|
|
// Concatenate the 5 character hardcoded string "\r\n\r\n\0"
|
|
// to the header string, indicating its termination.
|
|
//
|
|
|
|
ii += set_cookie_string_length;
|
|
|
|
memcpy( isapi_response_ptr->pszHeaders + ii, "\r\n\r\n\0", 5 );
|
|
|
|
|
|
//
|
|
// Set the 'pszHeaders' field of the HSE_RESPONSE_VECTOR structure
|
|
// to point to the HTTP header string just computed.
|
|
//
|
|
|
|
isapi_response_ptr->response_vector.pszHeaders = isapi_response_ptr->pszHeaders;
|
|
|
|
|
|
//
|
|
// Set the 'lpElementArray' field of the HSE_RESPONSE_VECTOR structure
|
|
// to point to the array of HSE_VECTOR_ELEMENT structures populated
|
|
// above.
|
|
//
|
|
|
|
isapi_response_ptr->response_vector.lpElementArray = isapi_response_ptr->vector_element_array;
|
|
|
|
|
|
//
|
|
// Indicate that the HSE_RESPONSE_VECTOR structure
|
|
// has seven entries.
|
|
//
|
|
|
|
isapi_response_ptr->response_vector.nElementCount = 7;
|
|
|
|
|
|
//
|
|
// Set the HTTP status to "200 OK".
|
|
//
|
|
|
|
isapi_response_ptr->response_vector.pszStatus = g_pszStatus_200;
|
|
|
|
|
|
//
|
|
// Set the 'dwFlags' field of the HSE_RESPONSE_VECTOR structure
|
|
// based on whether or not asynchronous VectorSend is to be
|
|
// used.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
isapi_response_ptr->response_vector.dwFlags = //HSE_IO_FINAL_SEND |
|
|
HSE_IO_SEND_HEADERS |
|
|
HSE_IO_ASYNC;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
isapi_response_ptr->response_vector.dwFlags = //HSE_IO_FINAL_SEND |
|
|
HSE_IO_SEND_HEADERS |
|
|
HSE_IO_SYNC;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Set the following additional fields if the ISAPI
|
|
// response is to be sent to the client using
|
|
// asynchronous VectorSend.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
|
|
//
|
|
// By setting an 'hFile' HANDLE field we can check, in the
|
|
// VectorSend completion callback routine, whether the
|
|
// asynchronous VectorSend operation just completed made
|
|
// use of a file handle. If so, we must close the handle
|
|
// in this callback.
|
|
//
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If you have not yet returned indicating failure, return
|
|
// indicating success.
|
|
//
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID init_lock( LOCK *lock )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes a LOCK object.
|
|
|
|
NOTE: This locking mechanism is directly based on the dual spin
|
|
lock implemented by Neel Jain (njain) for use in the original
|
|
SPECweb99 ISAPI. Its reliability remains to be determined.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the LOCK object to be initialized.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Indicate that shared ownership is not currently held.
|
|
//
|
|
|
|
lock->current_state = FREE;
|
|
|
|
|
|
//
|
|
// Indicate that exclusive ownership is not currently held or being
|
|
// waited for.
|
|
//
|
|
|
|
lock->num_writers = 0;
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID acquire_exclusive_lock( LOCK *lock )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to acquire exclusive ownership of a LOCK object.
|
|
|
|
NOTE: This locking mechanism is directly based on the dual spin
|
|
lock implemented by Neel Jain (njain) for use in the original
|
|
SPECweb99 ISAPI. Its reliability remains to be determined.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the LOCK object to be acquired.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Indicate that exclusive ownership has been requested.
|
|
//
|
|
|
|
InterlockedIncrement( &( lock->num_writers ) );
|
|
|
|
while( TRUE )
|
|
{
|
|
|
|
|
|
//
|
|
// If no other shared or exclusive ownership is held, do
|
|
// the following...
|
|
//
|
|
|
|
if ( lock->current_state == FREE )
|
|
{
|
|
|
|
//
|
|
// If no other shared or exclusive ownership is held,
|
|
// attempt to gain exclusive ownership.
|
|
//
|
|
|
|
if ( FREE == InterlockedCompareExchange( &( lock->current_state ), LOCKED, FREE ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If exclusive ownership was gained, return.
|
|
//
|
|
|
|
return;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
//
|
|
// If exclusive ownership could not be gained, go to the
|
|
// top of the loop.
|
|
//
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, if shared or exclusive ownership is held,
|
|
// essentially block this thread and switch to another that
|
|
// is ready.
|
|
//
|
|
|
|
else
|
|
{
|
|
|
|
SwitchToThread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID release_exclusive_lock( LOCK *lock )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Relinquishes exclusive ownership of a LOCK object.
|
|
|
|
NOTE: This locking mechanism is directly based on the dual spin
|
|
lock implemented by Neel Jain (njain) for use in the original
|
|
SPECweb99 ISAPI. Its reliability remains to be determined.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the LOCK object to be released.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Indicate that neither shared nor exclusive ownership is now
|
|
// held.
|
|
//
|
|
|
|
lock->current_state = FREE;
|
|
|
|
|
|
//
|
|
// Indicate that no exclusive ownership is now held or being
|
|
// waited for.
|
|
//
|
|
|
|
InterlockedDecrement( &( lock->num_writers ) );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID acquire_shared_lock( LOCK *lock )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to acquire shared ownership of a LOCK object.
|
|
|
|
NOTE: This locking mechanism is directly based on the dual spin
|
|
lock implemented by Neel Jain (njain) for use in the original
|
|
SPECweb99 ISAPI. Its reliability remains to be determined.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the LOCK object to be acquired.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
LONG current_state;
|
|
|
|
LONG writers_waiting;
|
|
|
|
while( TRUE )
|
|
{
|
|
|
|
current_state = lock->current_state;
|
|
|
|
writers_waiting = lock->num_writers;
|
|
|
|
|
|
//
|
|
// If no exclusive ownership of the lock is currently
|
|
// held or being waited for, do the following...
|
|
//
|
|
|
|
if ( ( current_state != LOCKED ) && ( !writers_waiting ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Attempt to acquire a new instance of shared ownership
|
|
// of the lock.
|
|
//
|
|
|
|
if ( current_state == InterlockedCompareExchange( &( lock->current_state ),
|
|
current_state + 1,
|
|
current_state ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If shared ownership was gained, return.
|
|
//
|
|
|
|
return;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
//
|
|
// If shared ownership could not be gained, go to the
|
|
// top of the loop.
|
|
//
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, if shared or exclusive ownership is held,
|
|
// essentially block this thread and switch to another that
|
|
// is ready.
|
|
//
|
|
|
|
else
|
|
{
|
|
|
|
SwitchToThread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID release_shared_lock( LOCK *lock )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Relinquishes shared ownership of a LOCK object.
|
|
|
|
NOTE: This locking mechanism is directly based on the dual spin
|
|
lock implemented by Neel Jain (njain) for use in the original
|
|
SPECweb99 ISAPI. Its reliability remains to be determined.
|
|
|
|
Arguments:
|
|
|
|
A pointer to the LOCK object to be released.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Indicate that one fewer instance of shared ownership is
|
|
// now held.
|
|
//
|
|
|
|
InterlockedDecrement( &( lock->current_state ) );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
BOOL load_user_personality_file( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the User.Personality file data into an easily accessible
|
|
in-memory data structure.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the User.Personality file data is successfully
|
|
loaded into the in-memory data structure and FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
CHAR user_personality_filename[ MAX_PATH ];
|
|
|
|
HANDLE hFile;
|
|
|
|
DWORD user_personality_filesize;
|
|
|
|
CHAR *user_personality_file_data_buffer = NULL;
|
|
|
|
DWORD bytes_read;
|
|
|
|
DWORD user_id;
|
|
|
|
DWORD ii;
|
|
|
|
|
|
//
|
|
// Read the User.Personality file into memory. To do this, first
|
|
// obtain a handle on this file.
|
|
//
|
|
|
|
strcpy( user_personality_filename, g_root_dir );
|
|
|
|
strcat( user_personality_filename, "/User.Personality" );
|
|
|
|
if ( ( hFile = CreateFile( user_personality_filename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL ) ) == INVALID_HANDLE_VALUE )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Determine the size of the User.Personality file.
|
|
//
|
|
|
|
user_personality_filesize = GetFileSize( hFile, NULL );
|
|
|
|
|
|
//
|
|
// Allocate a new memory buffer to hold the raw data of the
|
|
// latest version of the User.Personality file.
|
|
//
|
|
|
|
if ( !( user_personality_file_data_buffer = ( CHAR * )malloc( user_personality_filesize ) ) )
|
|
{
|
|
|
|
CloseHandle( hFile );
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Load the raw User.Personality file data into the allocated
|
|
// buffer and close the handle to this file.
|
|
//
|
|
|
|
if ( !ReadFile( hFile,
|
|
user_personality_file_data_buffer,
|
|
user_personality_filesize,
|
|
&bytes_read,
|
|
NULL ) )
|
|
{
|
|
|
|
CloseHandle( hFile );
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
CloseHandle( hFile );
|
|
|
|
|
|
//
|
|
// Store the User.Personality file data in a DWORD array that is
|
|
// indexed by user ID and whose entries consist of user demographics
|
|
// data. This structure makes it easier to access this data than
|
|
// if it were read from the raw User.Personality file data.
|
|
//
|
|
|
|
|
|
//
|
|
// If a DWORD array containing the data for a previous version of the
|
|
// User.Personality file exists, free it.
|
|
//
|
|
|
|
if ( g_user_personalities_buffer )
|
|
{
|
|
|
|
free( g_user_personalities_buffer );
|
|
|
|
g_user_personalities_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a new DWORD array.
|
|
//
|
|
|
|
g_num_users = user_personality_filesize / USER_PROFILE_SIZE;
|
|
|
|
if ( !( g_user_personalities_buffer = ( DWORD * )malloc( g_num_users * sizeof( DWORD ) ) ) )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Fill the DWORD array using the raw User.Personality file data.
|
|
//
|
|
|
|
for ( ii = 0; ii < g_num_users; ii++ )
|
|
{
|
|
|
|
sscanf( user_personality_file_data_buffer + ( ii * USER_PROFILE_SIZE ),
|
|
|
|
"%5d %8X",
|
|
|
|
&user_id,
|
|
&( g_user_personalities_buffer[ ii ] )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Free the buffer containing the raw User.Personality file data.
|
|
//
|
|
|
|
free( user_personality_file_data_buffer );
|
|
|
|
|
|
//
|
|
// If we have not yet returned indicating failure, return indicating
|
|
// success.
|
|
//
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
BOOL load_custom_ads_file( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the Custom.Ads file data into an easily accessible in-memory
|
|
data structure.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE is the Custom.Ads file data is successfully
|
|
loaded into the in-memory data structure and false otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
CHAR custom_ads_filename[ MAX_PATH ];
|
|
|
|
DWORD custom_ad_filesize = NUM_CUSTOM_ADS * CUSTOM_AD_SIZE;
|
|
|
|
CHAR custom_ads_file_data_buffer[ NUM_CUSTOM_ADS * CUSTOM_AD_SIZE ];
|
|
|
|
HANDLE hFile;
|
|
|
|
DWORD bytes_read;
|
|
|
|
DWORD weightings;
|
|
|
|
DWORD ii;
|
|
|
|
|
|
//
|
|
// Read the Custom.Ads file into memory. To do this, first
|
|
// obtain a handle on this file.
|
|
//
|
|
|
|
strcpy( custom_ads_filename, g_root_dir );
|
|
|
|
strcat( custom_ads_filename, "/Custom.Ads" );
|
|
|
|
if ( ( hFile = CreateFile( custom_ads_filename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL ) ) == INVALID_HANDLE_VALUE )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Load the raw Custom.Ads file data into an in-memory buffer
|
|
// and close the handle to this file.
|
|
//
|
|
|
|
if ( !ReadFile( hFile,
|
|
custom_ads_file_data_buffer,
|
|
custom_ad_filesize,
|
|
&bytes_read,
|
|
NULL ) )
|
|
{
|
|
|
|
CloseHandle( hFile );
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
CloseHandle( hFile );
|
|
|
|
|
|
//
|
|
// Store the Custom.Ads file data in an array (indexed by ad ID)
|
|
// of structs whose fields represent the individual data items that
|
|
// comprise a custom ad. This structure makes it easier to access
|
|
// this data than if it were read from the raw Custom.Ads file.
|
|
//
|
|
|
|
for ( ii = 0; ii < NUM_CUSTOM_ADS; ii++ )
|
|
{
|
|
|
|
sscanf( custom_ads_file_data_buffer + ( ii * CUSTOM_AD_SIZE ),
|
|
|
|
"%5d %8X %8X %3d %10d",
|
|
|
|
&( g_custom_ads_buffer[ ii ].ad_id ),
|
|
&( g_custom_ads_buffer[ ii ].ad_demographics ),
|
|
&weightings,
|
|
&( g_custom_ads_buffer[ ii ].minimum_match_value ),
|
|
&( g_custom_ads_buffer[ ii ].expiration_time ) );
|
|
|
|
g_custom_ads_buffer[ ii ].gender_wt = ( weightings >> 16 ) & WEIGHTING_MASK;
|
|
|
|
g_custom_ads_buffer[ ii ].age_group_wt = ( weightings >> 12 ) & WEIGHTING_MASK;
|
|
|
|
g_custom_ads_buffer[ ii ].region_wt = ( weightings >> 8 ) & WEIGHTING_MASK;
|
|
|
|
g_custom_ads_buffer[ ii ].interest1_wt = ( weightings >> 4 ) & WEIGHTING_MASK;
|
|
|
|
g_custom_ads_buffer[ ii ].interest2_wt = weightings & WEIGHTING_MASK;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If we have made it this far without returning indicating failure,
|
|
// return indicating success.
|
|
//
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
DWORD WINAPI update_user_and_ad_buffers( VOID *dummy_parameter )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function defines a persistent thread that waits on a change
|
|
notification event that is signaled whenever a file in the webserver
|
|
document root is changed (i.e. User.Personality or Custom.Ads) and
|
|
refreshes the in-memory data structures holding the User.Personality
|
|
and Custom.Ads file data with data from the latest versions of these
|
|
files on disk.
|
|
|
|
Arguments:
|
|
|
|
dummy_parameter - An unused parameter passed through the call to the
|
|
Win32 CreateThread function that invokes the routine
|
|
in a new thread. This parameter added for compatibility
|
|
the CreateThread interface.
|
|
|
|
Return Values:
|
|
|
|
None. This routine implements a persistent thread that cannot fail
|
|
and so, never returns.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Create a change notification event that is put in its signalled
|
|
// state whenever a file in the webserver document root directory is
|
|
// modified. Note that the only such files that could possible be
|
|
// changes are the User.Personality and Custom.Ads files.
|
|
//
|
|
|
|
HANDLE hEvent = FindFirstChangeNotification( g_root_dir,
|
|
FALSE,
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE );
|
|
|
|
|
|
//
|
|
// Repeat the following steps indefinitely.
|
|
//
|
|
|
|
while( TRUE )
|
|
{
|
|
|
|
FindNextChangeNotification( hEvent );
|
|
|
|
//
|
|
// Wait for a file under the webserver document root to be
|
|
// written to.
|
|
//
|
|
|
|
WaitForSingleObject( hEvent, INFINITE );
|
|
|
|
|
|
//
|
|
// Refresh the User.Preferences file data in memory.
|
|
//
|
|
|
|
acquire_exclusive_lock( &g_user_personalities_buffer_lock );
|
|
|
|
load_user_personality_file();
|
|
|
|
release_exclusive_lock( &g_user_personalities_buffer_lock );
|
|
|
|
|
|
//
|
|
// Refresh the Custom.Ads file data in memory.
|
|
//
|
|
|
|
acquire_exclusive_lock( &g_custom_ads_buffer_lock );
|
|
|
|
load_custom_ads_file();
|
|
|
|
release_exclusive_lock( &g_custom_ads_buffer_lock );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
DWORD send_error_page( EXTENSION_CONTROL_BLOCK *pECB,
|
|
CHAR *error_message,
|
|
CHAR *status,
|
|
DWORD query_string_length )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends an HTML error page with a message and status specified by the
|
|
caller.
|
|
|
|
Arguments:
|
|
|
|
pECB - Pointer to the relevant extension control block.
|
|
|
|
error_msg - Error message to send.
|
|
|
|
status - Status to send (e.g. "200 OK").
|
|
|
|
Return Value:
|
|
|
|
Returns HSE_STATUS_SUCCESS if the error page was successfully
|
|
written to the client and HSE_STATUS_ERROR otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
ISAPI_RESPONSE local_isapi_response;
|
|
|
|
ISAPI_RESPONSE *isapi_response_ptr = &local_isapi_response;
|
|
|
|
CHAR content_length_string[ 16 ];
|
|
|
|
DWORD content_length_string_length;
|
|
|
|
DWORD ii;
|
|
|
|
|
|
//
|
|
// Initialize the ISAPI response structure to be associated
|
|
// with the error page.
|
|
//
|
|
|
|
if ( !initialize_isapi_response( isapi_response_ptr,
|
|
pECB,
|
|
"",
|
|
0,
|
|
"",
|
|
FALSE,
|
|
query_string_length ) )
|
|
{
|
|
LOG_ERROR("initialize_isapi_response failed", 0 );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Change the response_vector and vector_element_array in the
|
|
// ISAPI response structure to specify and error message (to be
|
|
// transmitted using synchronous I/O). To do this use the
|
|
// following steps...
|
|
//
|
|
|
|
|
|
//
|
|
// Change the fifth element of the vector_element_array in the
|
|
// ISAPI_RESPONSE structure (i.e. the "meat" of the message) to contain
|
|
// the error message.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].pvContext = error_message;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbSize = strlen( error_message );
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbOffset = 0;
|
|
|
|
|
|
//
|
|
// Construct the headers of the HTTP response. Instead of using
|
|
// a slow sprintf call, do the following...
|
|
//
|
|
|
|
|
|
//
|
|
// Construct the first part of the header string.
|
|
//
|
|
|
|
strcat( isapi_response_ptr->pszHeaders,
|
|
|
|
"Content-Type: text/html\r\n"
|
|
"Content-Length: " );
|
|
|
|
|
|
//
|
|
// Construct a string containing the value of the 'Content-Length'
|
|
// header field and use memcpy to concatenate it onto the existing
|
|
// header string (which, incidentally is 41 characters long at this
|
|
// point).
|
|
//
|
|
|
|
dword_to_string( ( DWORD )( isapi_response_ptr->vector_element_array[ 0 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 1 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 2 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 3 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 4 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbSize +
|
|
isapi_response_ptr->vector_element_array[ 6 ].cbSize ),
|
|
|
|
content_length_string );
|
|
|
|
memcpy( isapi_response_ptr->pszHeaders + 41,
|
|
content_length_string,
|
|
content_length_string_length = strlen( content_length_string ) );
|
|
|
|
|
|
//
|
|
// Now, use memcpy to concatenate the trailing new-line, carriage
|
|
// return and null terminator characters that denote the end of
|
|
// HTTP headers (incidentally, the header string is 41 + content_
|
|
// length_string_length characters long before this concatenation.
|
|
//
|
|
|
|
ii = 41 + content_length_string_length;
|
|
|
|
memcpy( isapi_response_ptr->pszHeaders + ii,
|
|
"\r\n\r\n\0",
|
|
5 );
|
|
|
|
isapi_response_ptr->response_vector.pszStatus = status;
|
|
|
|
|
|
//
|
|
// Finally, send out the error page.
|
|
//
|
|
|
|
if ( !pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
LOG_ERROR("VectorSend() for error page failed", GetLastError() );
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If you have made it this far without returning indicating an
|
|
// error, return indicating success.
|
|
//
|
|
|
|
return( HSE_STATUS_SUCCESS );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID dword_to_string( DWORD dword, CHAR *string )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a character string that captures the decimal representation
|
|
of a DWORD.
|
|
|
|
Arguments:
|
|
|
|
dword - The DWORD whose decimal representation is to be captured in
|
|
a character string.
|
|
|
|
string - A pointer to the buffer in which the character string generated
|
|
is to be stored.
|
|
|
|
string_buffer_size - A pointer to a DWORD which contains the size of
|
|
the output buffer provided. On completion, the
|
|
size of the string generated is written to the
|
|
DWORD pointed to by this parameter.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
DWORD num_digits;
|
|
|
|
INT ii;
|
|
|
|
|
|
//
|
|
// Determine the number of digits in the decimal
|
|
// representation of the input DWORD by taking the
|
|
// integer part of its base 10 representation.
|
|
//
|
|
|
|
num_digits = ( DWORD )log10( dword ) + 1;
|
|
|
|
|
|
//
|
|
// Scroll through and fill the output buffer from the
|
|
// lowest order to the highest order digit. On each
|
|
// iteration the lowest order decimal digit is obtained
|
|
// by using a modulo 10 (i.e. % 10) operation and then
|
|
// dropped by performing an integer division by 10.
|
|
//
|
|
|
|
for ( ii = num_digits - 1; ii >= 0; ii-- )
|
|
{
|
|
|
|
string[ ii ] = '0' + ( CHAR )( dword % 10 );
|
|
|
|
dword /= 10;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Set the '\0' terminating character of the
|
|
// output string after the lowest order digit.
|
|
//
|
|
|
|
string[ num_digits ] = '\0';
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID WINAPI vector_send_completion_callback( EXTENSION_CONTROL_BLOCK *pECB,
|
|
VOID *pvContext,
|
|
DWORD cbIO,
|
|
DWORD dwError )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback invoked after completion of an asynchronous VectorSend.
|
|
|
|
Arguments:
|
|
|
|
pECB - Pointer to the relevant extension control block.
|
|
|
|
pContext - Pointer to the relevant ISAPI response structure.
|
|
|
|
cbIO - Number of bytes sent.
|
|
|
|
dwError - Error code for the VectorSend.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
ISAPI_RESPONSE *isapi_response_ptr = ( ISAPI_RESPONSE * )pvContext;
|
|
|
|
DWORD status = HSE_STATUS_SUCCESS;
|
|
|
|
|
|
//
|
|
// If the file was sent using a file handle, close that
|
|
// handle.
|
|
//
|
|
|
|
if ( isapi_response_ptr->hFile != INVALID_HANDLE_VALUE )
|
|
{
|
|
|
|
CloseHandle( isapi_response_ptr->hFile );
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Free any class 1 or class 2 data buffer structs used
|
|
// by pushing them back onto the stack of free CLASS1_
|
|
// DATA_BUFFER or the stack of free CLASS2_DATA_BUFFER
|
|
// structures on the heap, respectively.
|
|
//
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else if ( isapi_response_ptr->class_number == 2 )
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Free the ISAPI response structure used by pushing
|
|
// it back on the stack of free ISAPI_RESPONSE
|
|
// structures.
|
|
//
|
|
|
|
free_isapi_response( ( ISAPI_RESPONSE * )pvContext );
|
|
|
|
|
|
//
|
|
// Indicate successful completion or failed completion of
|
|
// client request servicing by calling the HSE_REQ_DONE_
|
|
// WITH_SESSION ServerSupportFunction with the appropriate
|
|
// status code.
|
|
//
|
|
|
|
if ( dwError != ERROR_SUCCESS )
|
|
{
|
|
LOG_ERROR("Completion returned error", dwError );
|
|
status = HSE_STATUS_ERROR;
|
|
}
|
|
|
|
pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&status,
|
|
NULL,
|
|
NULL );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID CALLBACK read_file_completion_callback( DWORD dwErrorCode,
|
|
DWORD dwNumberOfBytesTransferred,
|
|
OVERLAPPED *overlapped )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback routine invoked after completion of the asynchronous call
|
|
to ReadFile in handle_class1_or_class2_request. Continues handling
|
|
of a SPECweb99 dynamic GET with custom ad rotation operation in
|
|
which a class 1 or class 2 file is requested.
|
|
|
|
Arguments:
|
|
|
|
dwErrorCode - Error code returned for the asynchronous ReadFile
|
|
operation.
|
|
|
|
dwNumberOfBytesTransferred - Number of bytes transferred in the
|
|
asynchronous ReadFile operation.
|
|
|
|
overlapped - A pointer to the OVERLAPPED structure used in the
|
|
asynchronous ReadFile operation. Note that this
|
|
OVERLAPPED structure is the second field of the
|
|
relevant ISAPI_RESPONSE structure, coming immediately
|
|
after a SINGLE_LIST_ENTRY field.
|
|
|
|
Return Values:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
ISAPI_RESPONSE *isapi_response_ptr =
|
|
( ISAPI_RESPONSE * )( ( ( BYTE * )overlapped ) - sizeof( SINGLE_LIST_ENTRY ) );
|
|
|
|
CHAR *buffer;
|
|
|
|
DWORD status = HSE_STATUS_SUCCESS;
|
|
|
|
HCONN connection_id;
|
|
|
|
|
|
//
|
|
// Now that the asynchronous ReadFile has been is past us,
|
|
// we are done with the copy of the file requested on disk and
|
|
// so, can close the handle to it.
|
|
//
|
|
|
|
CloseHandle( isapi_response_ptr->hFile );
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
//
|
|
// Check if the asynchronous ReadFile operation completed
|
|
// successfully. If not, bomb out.
|
|
//
|
|
|
|
if ( dwErrorCode != ERROR_SUCCESS )
|
|
{
|
|
|
|
|
|
//
|
|
// Cache away the connection ID stored "in" the ISAPI_RESPONSE
|
|
// structure used as we will need it to call the HSE_REQ_DONE_
|
|
// WITH_SESSION ServerSupportFunction even after we have
|
|
// deallocated the ISAPI_RESPONSE structure.
|
|
//
|
|
|
|
connection_id = isapi_response_ptr->pECB->ConnID;
|
|
|
|
|
|
//
|
|
// In the error case free any ISAPI_RESPONSE
|
|
// or CLASS?_DATA_BUFFER structures allocated.
|
|
//
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
|
|
//
|
|
// Indicate to IIS that you are finished servicing
|
|
// the client request due to an error, by calling
|
|
// the HSE_REQ_DONE_WITH_SESSION ServerSupportFunction
|
|
// with an HSE_STATUS_ERROR status code.
|
|
//
|
|
|
|
status = HSE_STATUS_ERROR;
|
|
|
|
isapi_response_ptr->pECB->ServerSupportFunction( connection_id,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&status,
|
|
NULL,
|
|
NULL );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
buffer = isapi_response_ptr->class1_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
buffer = isapi_response_ptr->class2_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Process the raw file data read into memory
|
|
// to ensure that the appropriate custom ad
|
|
// information is inserted.
|
|
//
|
|
|
|
insert_custom_ad_information( buffer, isapi_response_ptr->ad_id );
|
|
|
|
|
|
//
|
|
// Now that the processing of the file data in the buffer
|
|
// is complete, we are ready to send it off to the client.
|
|
// To do this, set the sixth item in the response vector
|
|
// (i.e. the meat of the ISAPI response) to be the data
|
|
// buffer containing the processed file data.
|
|
//
|
|
// Note that the size of the file data to be sent was
|
|
// not changed in the processing applied and so, the
|
|
// HTTP headers or other fields dependent on content
|
|
// length do not need to be changed.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].pvContext = buffer;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbSize = dwNumberOfBytesTransferred;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbOffset = 0;
|
|
|
|
|
|
//
|
|
// Perform the synchronous or asynchronous VectorSend
|
|
// operation.
|
|
//
|
|
|
|
if ( !isapi_response_ptr->pECB->ServerSupportFunction( isapi_response_ptr->pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Cache away the connection ID stored "in" the ISAPI_RESPONSE
|
|
// structure used as we will need it to call the HSE_REQ_DONE_
|
|
// WITH_SESSION ServerSupportFunction even after we have
|
|
// deallocated the ISAPI_RESPONSE structure.
|
|
//
|
|
|
|
connection_id = isapi_response_ptr->pECB->ConnID;
|
|
|
|
|
|
//
|
|
// In the error case, free any ISAPI_RESPONSE
|
|
// or CLASS?_DATA_BUFFER structures allocated.
|
|
//
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
|
|
//
|
|
// Indicate to IIS that you are finished servicing
|
|
// the client request due to an error, by calling
|
|
// the HSE_REQ_DONE_WITH_SESSION ServerSupportFunction
|
|
// with an HSE_STATUS_ERROR status code.
|
|
//
|
|
|
|
status = HSE_STATUS_ERROR;
|
|
|
|
isapi_response_ptr->pECB->ServerSupportFunction( connection_id,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&status,
|
|
NULL,
|
|
NULL );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If you requested a synchronous VectorSend, you are
|
|
// done at this point, so call the HSE_REQ_DONE_WITH_SESSION
|
|
// ServerSupportFunction with an HSE_STATUS_SUCCESS status
|
|
// code and decallocate any ISAPI_RESPONSE or CLASS?_DATA_BUFFER
|
|
// structures used.
|
|
//
|
|
// If you requested an asynchronous VectorSend, just exit;
|
|
// the status code is still the HSE_STATUS_PENDING returned
|
|
// in handle_class1_or_class2_request.
|
|
//
|
|
|
|
if ( !isapi_response_ptr->use_async_vector_send )
|
|
{
|
|
|
|
connection_id = isapi_response_ptr->pECB->ConnID;
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
isapi_response_ptr->pECB->ServerSupportFunction( connection_id,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&status,
|
|
NULL,
|
|
NULL );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
DWORD determine_set_cookie_string( INT64 user_index,
|
|
DWORD last_ad,
|
|
CHAR *set_cookie_string )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Computes the Set-Cookie string and the ID of the ad to be sent in a
|
|
based on the index (i.e. ID - 10000) of the user who made the request
|
|
being serviced and the ID of the last ad seen by that user.
|
|
|
|
Arguments:
|
|
|
|
user_index - The user index of the user whose request is being
|
|
serviced. Note that a user index is defined as the
|
|
pertinent user ID minus 10000. These indices are
|
|
used as user ID's start from 10000.
|
|
|
|
last_ad - The ID of the last ad seen by the user whose request is
|
|
being serviced. last_ad is -1 if the user has not
|
|
previously seen an ad.
|
|
|
|
set_cookie_string - A pointer to a buffer, of size MAX_COOKIE_STRING_
|
|
LENGTH, that will be used to store the Set-Cookie
|
|
string computed.
|
|
|
|
Return Values:
|
|
|
|
Returns the ID of the ad to be sent or -1 if an out of range user index
|
|
is provided. Computes the Set-Cookie string and stores it in the buffer
|
|
indicated by the parameter 'set_cookie_string'.
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD current_time;
|
|
|
|
DWORD ad_index;
|
|
|
|
DWORD combined_demographics;
|
|
|
|
DWORD ad_weight;
|
|
|
|
DWORD expired;
|
|
|
|
CHAR ad_index_string[ 4 ];
|
|
|
|
CHAR ad_weight_string[ 11 ];
|
|
|
|
DWORD ad_index_string_size;
|
|
|
|
DWORD ad_weight_string_size;
|
|
|
|
DWORD ad_index_string_length;
|
|
|
|
DWORD ad_weight_string_length;
|
|
|
|
DWORD ii;
|
|
|
|
DWORD user_demographics;
|
|
|
|
|
|
//
|
|
// Check if the user_index is out of range. If it is, use the
|
|
// default Set-Cookie string and return the invalid ad ID of -1.
|
|
//
|
|
|
|
if ( user_index < 0 || user_index >= g_num_users )
|
|
{
|
|
|
|
strcpy( set_cookie_string, "found_cookie=Ad_id=-1&Ad_weight=00&Expired=1" );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Obtain the current time.
|
|
//
|
|
|
|
time( ( time_t * )&( current_time ) );
|
|
|
|
|
|
//
|
|
// Obtain the demographic data for the specified user.
|
|
//
|
|
|
|
acquire_shared_lock( &g_user_personalities_buffer_lock );
|
|
|
|
user_demographics = g_user_personalities_buffer[ user_index ];
|
|
|
|
release_shared_lock( &g_user_personalities_buffer_lock );
|
|
|
|
|
|
//
|
|
// Scroll through custom ads in the in-memory data structure holding
|
|
// the Custom.Ads file data and select an appropriate ad. Note that
|
|
// as this in-memory buffer is a shared resourse, shared ownership of
|
|
// the lock protecting it, 'g_custom_ads_buffer_lock', must first
|
|
// be obtained.
|
|
//
|
|
|
|
ad_index = last_ad;
|
|
|
|
acquire_shared_lock( &g_custom_ads_buffer_lock );
|
|
|
|
do
|
|
{
|
|
|
|
|
|
//
|
|
// Increment ad_index (initially set to the ad ID of
|
|
// the last ad seen by the user). If you go past the
|
|
// maximum ad ID of 359, go back to the ad with ID 0.
|
|
//
|
|
|
|
ad_index = ( ( ad_index + 1 ) % 360 );
|
|
|
|
|
|
//
|
|
// Determine the combined demographics information of the
|
|
// user and the current ad that you are on.
|
|
//
|
|
|
|
combined_demographics = g_custom_ads_buffer[ ad_index ].ad_demographics &
|
|
user_demographics;
|
|
|
|
|
|
//
|
|
// Using the combined demographics information, calculate
|
|
// the weight of the current ad using the following steps.
|
|
//
|
|
|
|
ad_weight = 0;
|
|
|
|
if ( combined_demographics & GENDER_MASK )
|
|
{
|
|
|
|
ad_weight += g_custom_ads_buffer[ ad_index ].gender_wt;
|
|
|
|
}
|
|
|
|
if ( combined_demographics & AGE_GROUP_MASK )
|
|
{
|
|
|
|
ad_weight += g_custom_ads_buffer[ ad_index ].age_group_wt;
|
|
|
|
}
|
|
|
|
if ( combined_demographics & REGION_MASK )
|
|
{
|
|
|
|
ad_weight += g_custom_ads_buffer[ ad_index ].region_wt;
|
|
|
|
}
|
|
|
|
if ( combined_demographics & INTEREST1_MASK )
|
|
{
|
|
|
|
ad_weight += g_custom_ads_buffer[ ad_index ].interest1_wt;
|
|
|
|
}
|
|
|
|
if ( combined_demographics & INTEREST2_MASK )
|
|
{
|
|
|
|
ad_weight += g_custom_ads_buffer[ ad_index ].interest2_wt;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If the weight of the current ad meets a certain minimum
|
|
// threshold, we have a match. In this case, determine whether
|
|
// or not the selected ad has expired and break from the loop.
|
|
//
|
|
|
|
if ( ad_weight >= g_custom_ads_buffer[ ad_index ].minimum_match_value )
|
|
{
|
|
|
|
if ( current_time >= g_custom_ads_buffer[ ad_index ].expiration_time )
|
|
{
|
|
|
|
expired = 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
expired = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If the current ad was not acceptable, move on to the next.
|
|
// Continue looping in this fashion until an acceptable ad is
|
|
// found (which may turn out to be the last ad seen). Note that
|
|
// SPECweb99 is clearly ensuring that the custom ads buffer
|
|
// will contain at least one matching record.
|
|
//
|
|
|
|
} while( ad_index != last_ad );
|
|
|
|
|
|
//
|
|
// Once we have found a matching ad, we are done with the
|
|
// custom ads buffer. Release the shared lock held on it.
|
|
//
|
|
|
|
release_shared_lock( &g_custom_ads_buffer_lock );
|
|
|
|
|
|
//
|
|
// Construct the Set-Cookie string to write in the buffer
|
|
// 'set_cookie_string'. To avoid making a costly sprintf
|
|
// call or effectively repeating strlen calls, memcpy will
|
|
// be used.
|
|
//
|
|
// Note that the Set-Cookie string returned will have the format:
|
|
// "found_cookie=Ad_id=<ad_id>&Ad_weight=<ad_weight>&Expired=<expired>"
|
|
//
|
|
|
|
|
|
//
|
|
// Copy the "hardcoded" base of the Set-Cookie string.
|
|
//
|
|
|
|
strcpy( set_cookie_string, "found_cookie=Ad_id=" );
|
|
|
|
|
|
//
|
|
// Construct a string representing the ID of the ad
|
|
// to be returned. Determine and store the length
|
|
// of this string.
|
|
//
|
|
|
|
dword_to_string( ad_index, ad_index_string );
|
|
|
|
ad_index_string_length = strlen( ad_index_string );
|
|
|
|
|
|
//
|
|
// Construct a string representing the weight of the
|
|
// ad selected. Determine and store the length of this
|
|
// string.
|
|
//
|
|
// Note that we do not check to failure of dword_to_string
|
|
// as the buffer 'ad_weight_string' is sufficiently large
|
|
// and so this function is guaranteed to succeed.
|
|
//
|
|
|
|
dword_to_string( ad_weight, ad_weight_string );
|
|
|
|
ad_weight_string_length = strlen( ad_weight_string );
|
|
|
|
|
|
//
|
|
// Concatenate the ad ID string onto the Set-Cookie string.
|
|
// Note that 19 is the length of the Set-Cookie string before
|
|
// this operation.
|
|
//
|
|
|
|
memcpy( set_cookie_string + 19, ad_index_string, ad_index_string_length );
|
|
|
|
|
|
//
|
|
// Concatenate the hardcoded, 11 character string "&Ad_weight",
|
|
// onto the Set-Cookie string. Note that ii contains the length
|
|
// of the Set-Cookie string just prior to this operation.
|
|
//
|
|
|
|
ii = 19 + ad_index_string_length;
|
|
|
|
memcpy( set_cookie_string + ii, "&Ad_weight=", 11 );
|
|
|
|
|
|
//
|
|
// Concatenate the ad weigth string onto the Set-Cookie string.
|
|
// Note that ii contains the length of the Set-Cookie string
|
|
// just prior to this operation.
|
|
//
|
|
|
|
ii += 11;
|
|
|
|
memcpy( set_cookie_string + ii, ad_weight_string, ad_weight_string_length );
|
|
|
|
|
|
//
|
|
// Concatenate either of the hardcoded, 11 character strings
|
|
// "&Expired=1\0" or "&Expired=0\0" onto the Set-Cookie string,
|
|
// depending on the value of the variable 'expired'. Note that
|
|
// ii contains the length of the Set-Cookie string just prior
|
|
// to this operation.
|
|
//
|
|
|
|
ii += ad_weight_string_length;
|
|
|
|
memcpy( set_cookie_string + ii,
|
|
( expired ) ? "&Expired=1\0" : "&Expired=0\0",
|
|
11 );
|
|
|
|
|
|
//
|
|
// Return the ID of the ad selected.
|
|
//
|
|
|
|
return( ad_index );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
VOID insert_custom_ad_information( CHAR *buffer, DWORD ad_id )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
buffer - Pointer to a buffer, containing the raw data of a class
|
|
1 or class 2 file, into which custom ad information is to
|
|
be inserted.
|
|
|
|
ad_id - The ID of the custom ad to be inserted into the raw file
|
|
data.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
CHAR substitute_directory_string[ 5 ];
|
|
|
|
CHAR substitute_class_char;
|
|
|
|
CHAR substitute_file_char;
|
|
|
|
CHAR *tag_start_ptr;
|
|
|
|
DWORD ii;
|
|
|
|
DWORD jj;
|
|
|
|
|
|
//
|
|
// We must now go through the file data buffer process it in
|
|
// the following manner.
|
|
//
|
|
// (1) Find each tag of the form "<!WEB99CAD><IMG SRC=/file_set/
|
|
// dirNNNNN/classX_Y><!/WEB99CAD>" using the minimum required
|
|
// search string "<!WEB99CAD><IMG SRC=/file_set/dir".
|
|
//
|
|
// (2) For each tag found in (1), in turn, do the following:
|
|
//
|
|
// (a) Replace NNNNN with ( ad_id / 36 ) padded to five
|
|
// digits.
|
|
//
|
|
// (b) Replace X with ( ( ad_id / 36 ) / 9 ).
|
|
//
|
|
// (c) Replace Y with ( ad_id % 9 ).
|
|
//
|
|
|
|
|
|
//
|
|
// Compute the directory number characters. That is, compute the
|
|
// charasters that will be used to replace NNNNN in the tags
|
|
// described.
|
|
//
|
|
|
|
substitute_directory_string[ 0 ] = '0';
|
|
|
|
substitute_directory_string[ 1 ] = '0';
|
|
|
|
substitute_directory_string[ 2 ] = '0';
|
|
|
|
substitute_directory_string[ 3 ] = '0' + ( CHAR )( ( ad_id / 36 ) / 10 );
|
|
|
|
substitute_directory_string[ 4 ] = '0' + ( CHAR )( ( ad_id / 36 ) % 10 );
|
|
|
|
|
|
//
|
|
// Compute the class number. That is, compute the character
|
|
// that will be used to replace X in the tags described.
|
|
//
|
|
|
|
substitute_class_char = '0' + ( CHAR )( ( ad_id % 36 ) / 9 );
|
|
|
|
|
|
//
|
|
// Compute the file number. That is, compute the character
|
|
// that will be used to replace Y in the tags described.
|
|
//
|
|
|
|
substitute_file_char = '0' + ( CHAR )( ad_id % 9 );
|
|
|
|
|
|
//
|
|
// Now, actually scroll through the file data buffer, find the
|
|
// tags described and make the specified subsitutions for each.
|
|
//
|
|
|
|
for ( tag_start_ptr = strstr( buffer, "<!WEB99CAD><IMG SRC=\"/file_set/dir" );
|
|
tag_start_ptr;
|
|
tag_start_ptr = strstr( tag_start_ptr + 1, "<!WEB99CAD><IMG SRC=\"/file_set/dir" ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Note that characters 34 through 39 in the tags
|
|
// specified (where the first character is numbered 0)
|
|
// correspond to the NNNNN to be replaced with the
|
|
// substitute directory number.
|
|
//
|
|
|
|
for ( ii = 0, jj = 34; ii < 5; ii++, jj++ )
|
|
{
|
|
|
|
tag_start_ptr[ jj ] = substitute_directory_string[ ii ];
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Note that character 45 in the tags specified
|
|
// (where the first character is numbered 0) corresponds
|
|
// to the X to replace with the substitute class character.
|
|
//
|
|
|
|
tag_start_ptr[ 45 ] = substitute_class_char;
|
|
|
|
|
|
//
|
|
// Note that character 47 in the tags specified
|
|
// (where the first character is numbered 0) corresponsds
|
|
// to the Y to replace with the substitute file character.
|
|
//
|
|
|
|
tag_start_ptr[ 47 ] = substitute_file_char;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
DWORD handle_class1_or_class2_request( EXTENSION_CONTROL_BLOCK *pECB,
|
|
ISAPI_RESPONSE *isapi_response_ptr,
|
|
CHAR *filename,
|
|
DWORD filesize,
|
|
BOOL use_async_vector_send,
|
|
DWORD ad_id,
|
|
DWORD query_string_length )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine, called from HttpExtensionProc completes the handling
|
|
of a SPECweb99 dynamic GET with custom ad rotation operation in
|
|
which a class 1 or class 2 file is requested.
|
|
|
|
Arguments:
|
|
|
|
pECB - Pointer to the relevant extension control block.
|
|
|
|
isapi_response_ptr - Pointer to the relevant ISAPI_RESPONSE structure.
|
|
|
|
filename - Pointer to a string specifying the absolute name of
|
|
the file requested.
|
|
|
|
filesize - Size of the file requested.
|
|
|
|
use_async_vector_send - Flag indicating whether VectorSend is to be
|
|
used in asynchronous or synchronous mode.
|
|
|
|
ad_id - The ID of the custom ad selected.
|
|
|
|
query_string_length - The length of the query string provided.
|
|
|
|
Return Values:
|
|
|
|
Returns HSE_STATUS_ERROR if any failures occur in the routine.
|
|
|
|
Otherwise, if either asynchronous VectorSend or ReadFile is
|
|
used, HSE_STATUS_PENDING is returned. Alternatively, if neither
|
|
asynchronous VectorSend nor ReadFile is used, HSE_STATUS_SUCCESS
|
|
is returned.
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL use_async_read_file;
|
|
|
|
ISAPI_RESPONSE *heap_isapi_response_ptr;
|
|
|
|
DWORD bytes_read;
|
|
|
|
CHAR *buffer;
|
|
|
|
CLASS2_DATA_BUFFER stack_class2_data_buffer;
|
|
|
|
DWORD size = sizeof( stack_class2_data_buffer.buffer );
|
|
|
|
DWORD ii;
|
|
|
|
OVERLAPPED *overlapped_ptr;
|
|
|
|
DWORD dwFlagsAndAttributes;
|
|
|
|
|
|
//
|
|
// We need to obtain the raw file data of the class1 or
|
|
// class 2 file requested. Before we do this, however,
|
|
// we need a buffer in which to hold this data. Allocate
|
|
// such a buffer from the heap if we know, at this point,
|
|
// that will be carrying out an asynchronous I/O operation
|
|
// (i.e. is use_async_vector_send true) and from the stack
|
|
// otherwise.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
if ( !( isapi_response_ptr->class1_data_buffer_ptr = allocate_class1_data_buffer() ) )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
LOG_ERROR( "allocation failed", 0 );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
buffer = isapi_response_ptr->class1_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( !( isapi_response_ptr->class2_data_buffer_ptr = allocate_class2_data_buffer() ) )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
LOG_ERROR("Allocation failed",0 );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
buffer = isapi_response_ptr->class2_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
|
|
//
|
|
// Note that if we are not using asynchronous VectorSend
|
|
// it is safe, for now at least, to use a buffer on the stack
|
|
// to store our file data. This may change if we find that
|
|
// we will have to use asynchronous ReadFile later on./
|
|
//
|
|
// Also, we are simply using a CLASS2_DATA_BUFFER buffer
|
|
// field here as it is sufficiently large to hold both
|
|
// class 1 and 2 files and thus, to store an additional
|
|
// CLASS1_DATA_BUFFER on the stack would have been wasteful.
|
|
//
|
|
|
|
isapi_response_ptr->class2_data_buffer_ptr = &( stack_class2_data_buffer );
|
|
|
|
buffer = isapi_response_ptr->class2_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now, attempt to read the data of the file requested
|
|
// into the allocated buffer from the HTTP.SYS fragment
|
|
// cache.
|
|
//
|
|
|
|
if ( pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_READ_FRAGMENT_FROM_CACHE,
|
|
buffer,
|
|
&size,
|
|
( DWORD * )isapi_response_ptr->unicode_fragment_cache_key ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Now, if you had a cache hit, apply the necessary
|
|
// processing to the file data.
|
|
//
|
|
|
|
insert_custom_ad_information( buffer, ad_id );
|
|
|
|
|
|
//
|
|
// Now that the processing of the file data in the buffer
|
|
// is complete, we are ready to send it off to the client.
|
|
// To do this, set the sixth item in the response vector
|
|
// (i.e. the meat of the ISAPI response) to be the data
|
|
// buffer containing the processed file data.
|
|
//
|
|
// Note that the size of the file data to be sent was
|
|
// not changed in the processing applied and so, the
|
|
// HTTP headers or other fields dependent on content
|
|
// length do not need to be changed.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].pvContext = buffer;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbSize = filesize;
|
|
|
|
|
|
//
|
|
// Carry out the actual synchronous or
|
|
// asynchronous VectorSend operation.
|
|
//
|
|
|
|
if ( pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If the VectorSend operation returned
|
|
// indicating success return HSE_STATUS_PENDING
|
|
// or HSE_STATUS_SUCCESS depending on whether
|
|
// or not asynchronous or synchronous I/O was
|
|
// used, respectively.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
return( HSE_STATUS_PENDING );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
return( HSE_STATUS_SUCCESS );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
DWORD dwError = GetLastError();
|
|
LOG_ERROR(" VectorSend() failed", GetLastError() );
|
|
|
|
//
|
|
// Otherwise, if the VectorSend operation
|
|
// returned indicating failure return
|
|
// HSE_STATUS_ERROR.
|
|
//
|
|
// Note that if it was the initiation of an
|
|
// asynchronous VectorSend that failed,
|
|
// the ISAPI_RESPONSE and CLASS1_DATA_BUFFER
|
|
// or CLASS2_DATA_BUFFER structures used
|
|
// must be deallocated before returning.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Alternatively, if you had a cache miss, you must
|
|
// read the data of the file requested into memory
|
|
// using either a synchronous or asynchronous ReadFile
|
|
// operation.
|
|
//
|
|
|
|
|
|
//
|
|
// First, determine the I/O mode to use with ReadFile.
|
|
//
|
|
|
|
switch( g_read_file_io_mode_config )
|
|
{
|
|
|
|
case USE_ASYNC_IO:
|
|
{
|
|
|
|
use_async_read_file = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case USE_SYNC_IO:
|
|
{
|
|
|
|
use_async_read_file = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case USE_ADAPTABLE_IO:
|
|
{
|
|
|
|
use_async_read_file = ( filesize >= g_read_file_async_range_start );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//
|
|
// Now, if we are using an ISAPI_RESPONSE structure or file data
|
|
// buffer on the stack and find that we must do an asynchronous
|
|
// ReadFile, we must switch to using an ISAPI_RESPONSE struct and
|
|
// file data buffer allocated from the heap.
|
|
//
|
|
// The condition ( !use_async_vector_send && use_async_read_file )
|
|
// can be explained as follows:
|
|
//
|
|
// (1) We only need to worry about switching to a file data
|
|
// buffer and ISAPI_RESPONSE structure on the heap at this
|
|
// point if we are using synchronous VectorSend, as if we are
|
|
// using asynchronous VectorSend we would have known this earlier
|
|
// and already the heap.
|
|
//
|
|
// (2) Also, if we are using synchronous VectorSend we only need
|
|
// to worry about using an ISAPI_RESPONSE structure and file
|
|
// data buffer on the heap if we are going to use asynchronous
|
|
// ReadFile. Otherwise, all of our I/O is synchronous and stack
|
|
// memory will suffice.
|
|
//
|
|
|
|
if ( !use_async_vector_send && use_async_read_file )
|
|
{
|
|
|
|
|
|
//
|
|
// First, switch from using an ISAPI_RESPONSE structure
|
|
// on the stack to using an ISAPI_RESPONSE structure on
|
|
// the heap.
|
|
//
|
|
|
|
|
|
//
|
|
// Try to allocate a free ISAPI_RESPONSE
|
|
// structure on the heap.
|
|
//
|
|
|
|
if ( !( heap_isapi_response_ptr = allocate_isapi_response() ) )
|
|
{
|
|
LOG_ERROR("Allocation failed", 0 );
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now copy all relevant data in the ISAPI_RESPONSE
|
|
// structure on the stack to the newly allocated one
|
|
// on the heap.
|
|
//
|
|
// Note that the vector_element_array member of
|
|
// the ISAPI_RESPONSE structure has seven elements.
|
|
//
|
|
|
|
memcpy( heap_isapi_response_ptr, isapi_response_ptr, sizeof( ISAPI_RESPONSE ) );
|
|
|
|
heap_isapi_response_ptr->response_vector.lpElementArray = heap_isapi_response_ptr->vector_element_array;
|
|
|
|
heap_isapi_response_ptr->response_vector.pszHeaders = heap_isapi_response_ptr->pszHeaders;
|
|
|
|
|
|
//
|
|
// After copying all of the data from the stack
|
|
// to the heap ISAPI_RESPONSE struct, set
|
|
// 'isapi_response_ptr' to point to the latter.
|
|
//
|
|
|
|
isapi_response_ptr = heap_isapi_response_ptr;
|
|
|
|
|
|
//
|
|
// Now, switch to using a file data buffer on
|
|
// the heap instead of one on the stack.
|
|
//
|
|
|
|
|
|
//
|
|
// If the file class number is 1, do the following...
|
|
//
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
|
|
//
|
|
// Attempt to allocate a CLASS1_DATA_BUFFER structure
|
|
// off of the stack of free CLASS1_DATA_BUFFER
|
|
// structures on the heap.
|
|
//
|
|
|
|
if ( !( isapi_response_ptr->class1_data_buffer_ptr = allocate_class1_data_buffer() ) )
|
|
{
|
|
|
|
|
|
//
|
|
// In the error case, free the ISAPI_RESPONSE
|
|
// structure allocated from the heap.
|
|
//
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
LOG_ERROR( "Allocation failed", 0 );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
buffer = isapi_response_ptr->class1_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, if the file class number is 2, do the following...
|
|
// Note that this is essentially a mirror of the case for
|
|
// class 1 directly above.
|
|
//
|
|
|
|
else
|
|
{
|
|
|
|
|
|
//
|
|
// Attempt to allocate a CLASS2_DATA_BUFFER
|
|
// structure off of the stack of free CLASS2_
|
|
// DATA_BUFFER structures on the heap.
|
|
//
|
|
|
|
if ( !( isapi_response_ptr->class2_data_buffer_ptr = allocate_class2_data_buffer() ) )
|
|
{
|
|
|
|
|
|
//
|
|
// In the error case, free the ISAPI_RESPONSE
|
|
// structure allocated from the heap.
|
|
//
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
LOG_ERROR("Allocation failed", 0 );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
buffer = isapi_response_ptr->class2_data_buffer_ptr->buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now, we must read the contents of the requested file into
|
|
// memory before we can perform the necessary processing on
|
|
// its data. To do this we must first open a handle to the
|
|
// file using CreateFile.
|
|
//
|
|
// Note that we specify an OVERLAPPED structure as we may
|
|
// want to use asynchronous ReadFile.
|
|
//
|
|
|
|
if ( use_async_read_file )
|
|
{
|
|
|
|
dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
|
|
|
|
isapi_response_ptr->overlapped.Offset = 0;
|
|
|
|
isapi_response_ptr->overlapped.OffsetHigh = 0;
|
|
|
|
isapi_response_ptr->overlapped.hEvent = 0;
|
|
|
|
overlapped_ptr = &( isapi_response_ptr->overlapped );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
overlapped_ptr = NULL;
|
|
|
|
}
|
|
|
|
if ( ( isapi_response_ptr->hFile = CreateFile( filename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
dwFlagsAndAttributes,
|
|
NULL ) ) == INVALID_HANDLE_VALUE )
|
|
{
|
|
|
|
|
|
//
|
|
// In the failure case, free any ISAPI_RESPONSE
|
|
// and CLASS1_DATA_BUFFER or CLASS2_DATA_BUFFER
|
|
// structures allocated and send an HTML error
|
|
// page to the client.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( send_error_page( pECB,
|
|
"File inaccessible.",
|
|
g_pszStatus_404,
|
|
query_string_length ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If asynchonous ReadFile is to be used, the following
|
|
// steps must be carried out...
|
|
//
|
|
|
|
if ( use_async_read_file )
|
|
{
|
|
|
|
|
|
//
|
|
// Specify the completion callback routine to invoke
|
|
// after the asynchronous ReadFile operation has
|
|
// completed.
|
|
//
|
|
|
|
BindIoCompletionCallback( isapi_response_ptr->hFile,
|
|
read_file_completion_callback,
|
|
0 );
|
|
|
|
|
|
//
|
|
// Copy the following additional data items, that will be
|
|
// needed in the asynchronous ReadFile completion callback
|
|
// routine, to the ISAPI_RESPONSE struct
|
|
//
|
|
|
|
isapi_response_ptr->ad_id = ad_id;
|
|
|
|
isapi_response_ptr->pECB = pECB;
|
|
|
|
|
|
//
|
|
// Initiate the asynchronous ReadFile operation
|
|
// and return HSE_STATUS_PENDING.
|
|
//
|
|
|
|
if ( !ReadFile( isapi_response_ptr->hFile,
|
|
buffer,
|
|
filesize,
|
|
&bytes_read,
|
|
overlapped_ptr ) )
|
|
{
|
|
|
|
LOG_ERROR( "ReadFile failed()", GetLastError() );
|
|
|
|
//
|
|
// In the error case, first close the file
|
|
// handle that has been opened.
|
|
//
|
|
|
|
CloseHandle( isapi_response_ptr->hFile );
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
//
|
|
// Also, free any ISAPI_RESPONSE or CLASS1_DATA_
|
|
// BUFFER structures allocated.
|
|
//
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Free the current thread by returning
|
|
// HSE_STATUS_PENDING.
|
|
//
|
|
|
|
return( HSE_STATUS_PENDING );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Perform the appropriate synchronous ReadFile
|
|
// operation depending on the class number of the
|
|
// file requested.
|
|
//
|
|
|
|
if ( !ReadFile( isapi_response_ptr->hFile,
|
|
buffer,
|
|
filesize,
|
|
&bytes_read,
|
|
NULL ) )
|
|
{
|
|
|
|
LOG_ERROR( "ReadFile failed()", GetLastError() );
|
|
|
|
//
|
|
// In the error case, first free the file
|
|
// handle opened.
|
|
//
|
|
|
|
CloseHandle( isapi_response_ptr->hFile );
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
//
|
|
// Also, free any ISAPI_RESPONSE or CLASS1_
|
|
// DATA_BUFFER structures allocated.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// We are now done with the copy of the requested file on disk
|
|
// and so, the handle to it can be closed.
|
|
//
|
|
|
|
CloseHandle( isapi_response_ptr->hFile );
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
//
|
|
// Process the raw file data read into memory
|
|
// to ensure that the appropriate custom ad
|
|
// information is inserted.
|
|
//
|
|
|
|
insert_custom_ad_information( buffer, ad_id );
|
|
|
|
|
|
//
|
|
// Now that the processing of the file data in the buffer
|
|
// is complete, we are ready to send it off to the client.
|
|
// To do this, set the sixth item in the response vector
|
|
// (i.e. the meat of the ISAPI response) to be the data
|
|
// buffer containing the processed file data.
|
|
//
|
|
// Note that the size of the file data to be sent was
|
|
// not changed in the processing applied and so, the
|
|
// HTTP headers or other fields dependent on content
|
|
// length do not need to be changed.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].pvContext = buffer;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbSize = filesize;
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ].cbOffset = 0;
|
|
|
|
|
|
//
|
|
// Carry out the actual synchronous or
|
|
// asynchronous VectorSend operation.
|
|
//
|
|
|
|
if ( pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If the VectorSend operation returned
|
|
// indicating success return HSE_STATUS_PENDING
|
|
// or HSE_STATUS_SUCCESS depending on whether
|
|
// or not asynchronous or synchronous I/O was
|
|
// used, respectively.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
return( HSE_STATUS_PENDING );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
return( HSE_STATUS_SUCCESS );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
LOG_ERROR("VectorSend() failed",GetLastError());
|
|
|
|
//
|
|
// Otherwise, if the VectorSend operation
|
|
// returned indicating failure return
|
|
// HSE_STATUS_ERROR.
|
|
//
|
|
// Note that if it was the initiation of an
|
|
// asynchronous VectorSend that failed,
|
|
// the ISAPI_RESPONSE and CLASS1_DATA_BUFFER
|
|
// or CLASS2_DATA_BUFFER structures used
|
|
// must be deallocated before returning.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
free_class1_data_buffer( isapi_response_ptr->class1_data_buffer_ptr );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
free_class2_data_buffer( isapi_response_ptr->class2_data_buffer_ptr );
|
|
|
|
}
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
BOOL load_registry_data( VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads registry values used for configuration of specweb99-CAD.dll.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if all registry values are successfully loaded and
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
HKEY hKey;
|
|
|
|
DWORD value_type;
|
|
|
|
DWORD value_size;
|
|
|
|
DWORD sizeof_value;
|
|
|
|
BYTE value[ 4 ];
|
|
|
|
|
|
//
|
|
// Open the registry key containing all values used for
|
|
// specweb99-CAD.dll configuration.
|
|
//
|
|
|
|
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
"Software\\Microsoft\\SPECweb99 ISAPI",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey ) )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Load the value VECTOR_SEND_IO_MODE_CONFIG into the variable
|
|
// g_vector_send_io_mode_config.
|
|
//
|
|
|
|
value_size = sizeof_value = sizeof( value );
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
"VECTOR_SEND_IO_MODE_CONFIG",
|
|
NULL,
|
|
&value_type,
|
|
value,
|
|
&value_size ) == NO_ERROR )
|
|
{
|
|
g_vector_send_io_mode_config = *( ( DWORD* )value );
|
|
}
|
|
|
|
|
|
//
|
|
// Load the value VECTOR_SEND_ASYNC_RANGE_START into the
|
|
// variable g_vector_send_async_range_start.
|
|
//
|
|
|
|
value_size = sizeof_value;
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
"VECTOR_SEND_ASYNC_RANGE_START",
|
|
NULL,
|
|
&value_type,
|
|
value,
|
|
&value_size ) == NO_ERROR )
|
|
{
|
|
g_vector_send_async_range_start = *( ( DWORD * )value );
|
|
}
|
|
|
|
|
|
//
|
|
// Load the value READ_FILE_IO_MODE_CONFIG into the variable
|
|
// g_read_file_io_mode_config.
|
|
//
|
|
|
|
value_size = sizeof_value;
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
"READ_FILE_IO_MODE_CONFIG",
|
|
NULL,
|
|
&value_type,
|
|
value,
|
|
&value_size ) == NO_ERROR )
|
|
{
|
|
g_read_file_io_mode_config = *( ( DWORD * )value );
|
|
}
|
|
|
|
|
|
//
|
|
// Load the value READ_FILE_ASYNC_RANGE_START into the variable
|
|
// g_read_file_async_range_start.
|
|
//
|
|
|
|
value_size = sizeof_value;
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
"READ_FILE_ASYNC_RANGE_START",
|
|
NULL,
|
|
&value_type,
|
|
value,
|
|
&value_size ) == NO_ERROR )
|
|
{
|
|
g_read_file_async_range_start = *( ( DWORD * )value );
|
|
}
|
|
|
|
|
|
//
|
|
// Load the value ROOT_DIR into the variable g_root_dir.
|
|
// Store the length of this string in g_root_dir_length.
|
|
//
|
|
|
|
value_size = sizeof( g_root_dir );
|
|
|
|
if ( RegQueryValueEx( hKey,
|
|
"ROOT_DIR",
|
|
NULL,
|
|
&value_type,
|
|
( BYTE * )g_root_dir,
|
|
&value_size ) == NO_ERROR )
|
|
{
|
|
g_root_dir_length = value_size - 1;
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// If you have made it this far without returning indicating
|
|
// failure, return indicating success.
|
|
//
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implementation of the GetExtensionVersion ISAPI entry point.
|
|
Carries out general initialization tasks and provides IIS with
|
|
version information.
|
|
|
|
Arguments:
|
|
|
|
pVer - Pointer to the version information structure to be
|
|
populated.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if all initialization steps are successfully completed
|
|
and FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Load configuration data from the registry.
|
|
//
|
|
|
|
if ( !load_registry_data() )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Load the User.Personality file data from disk into an
|
|
// easy to access in-memory data structure.
|
|
//
|
|
|
|
if ( !load_user_personality_file() )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Load the Custom.Ads file data from disk into an
|
|
// easy to access in-memory data structure.
|
|
//
|
|
|
|
if ( !load_custom_ads_file() )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize the shared/exclusive mode locks used to guary
|
|
// the in memory data structures containing data from the
|
|
// User.Personality and Custom.Ads files.
|
|
//
|
|
|
|
init_lock( &g_user_personalities_buffer_lock );
|
|
|
|
init_lock( &g_custom_ads_buffer_lock );
|
|
|
|
|
|
//
|
|
// Create a persistent thread that will ensure that the
|
|
// data in the User.Personality and Custom.Ads in-memory
|
|
// structures always reflects the most recent versions of
|
|
// these files on disk.
|
|
//
|
|
|
|
if ( !( g_update_buffers_thread = CreateThread( NULL,
|
|
0,
|
|
update_user_and_ad_buffers,
|
|
NULL,
|
|
0,
|
|
NULL ) ) )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize all stacks from which particular structures
|
|
// (e.g. ISAPI_RESPONSE, CLASS1_DATA_BUFFER and CLASS2_DATA_BUFFER
|
|
// structs) on the heap are to be allocated from.
|
|
//
|
|
|
|
initialize_isapi_response_stack();
|
|
|
|
initialize_class1_data_buffer_stack();
|
|
|
|
initialize_class2_data_buffer_stack();
|
|
|
|
|
|
//
|
|
// Populate the ISAPI version information structure to
|
|
// be used by IIS.
|
|
//
|
|
|
|
pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR,
|
|
HSE_VERSION_MAJOR );
|
|
|
|
strcpy( pVer->lpszExtensionDesc,
|
|
"SPECweb99-CAD ISAPI Extension");
|
|
|
|
|
|
//
|
|
// If you have made it this far without returning indicating
|
|
// failure, return indicating success.
|
|
//
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
DWORD WINAPI HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implementation of the HttpExtensionProc ISAPI entry point.
|
|
Handles a SPECweb99 dynamic GET with custom ad rotation request.
|
|
|
|
Arguments:
|
|
|
|
pECB - Pointer to the relevant extension control block.
|
|
|
|
Return Value:
|
|
|
|
Returns HSE_STATUS_ERROR if any failures occur in the routine.
|
|
|
|
Otherwise, if either asynchronous VectorSend or ReadFile is
|
|
used, HSE_STATUS_PENDING is returned. Alternatively, if neither
|
|
asynchronous VectorSend nor ReadFile is used, HSE_STATUS_SUCCESS
|
|
is returned.
|
|
|
|
--*/
|
|
{
|
|
|
|
CHAR server_name[ 32 ];
|
|
|
|
CHAR server_port[ 32 ];
|
|
|
|
DWORD server_name_size = 32;
|
|
|
|
DWORD server_port_size = 32;
|
|
|
|
DWORD server_name_length;
|
|
|
|
DWORD server_port_length;
|
|
|
|
DWORD app_pool_name_size = 1024; // 1024 == sizeof( g_app_pool_name )
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fileinfo;
|
|
|
|
ISAPI_RESPONSE local_isapi_response;
|
|
|
|
ISAPI_RESPONSE *isapi_response_ptr = &local_isapi_response;
|
|
|
|
CHAR filename[ MAX_PATH ];
|
|
|
|
CHAR cookie_string[ MAX_COOKIE_STRING_LENGTH ];
|
|
|
|
DWORD cookie_string_size = MAX_COOKIE_STRING_LENGTH;
|
|
|
|
CHAR user_id_string[ 10 ];
|
|
|
|
CHAR last_ad_string[ 10 ];
|
|
|
|
DWORD ii;
|
|
|
|
DWORD jj;
|
|
|
|
INT64 user_index;
|
|
|
|
DWORD last_ad;
|
|
|
|
DWORD ad_index;
|
|
|
|
BOOL return_value = HSE_STATUS_SUCCESS;
|
|
|
|
DWORD combined_demographics;
|
|
|
|
DWORD bytes_read;
|
|
|
|
BOOL use_async_vector_send;
|
|
|
|
HANDLE hFile;
|
|
|
|
CHAR file_data_buffer[ FILE_DATA_BUFFER_SIZE ];
|
|
|
|
DWORD query_string_length;
|
|
|
|
|
|
//
|
|
// Initialize the common "prefix" of the HTTP.SYS
|
|
// fragment cache strings to be used.
|
|
//
|
|
|
|
if ( InterlockedExchange( &g_fragment_cache_key_base_not_initialized, 0 ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Obtain the server name.
|
|
//
|
|
|
|
if ( !pECB->GetServerVariable( pECB->ConnID,
|
|
"SERVER_NAME",
|
|
server_name,
|
|
&server_name_size ) )
|
|
{
|
|
LOG_ERROR( "GetServerVariable(SERVER_NAME) failed", GetLastError() );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
server_name_length = server_name_size - 1;
|
|
|
|
|
|
//
|
|
// Obtain the server port number used.
|
|
//
|
|
|
|
if ( !pECB->GetServerVariable( pECB->ConnID,
|
|
"SERVER_PORT",
|
|
server_port,
|
|
&server_port_size ) )
|
|
{
|
|
LOG_ERROR( "GetServerVariable(SERVER_PORT) failed", GetLastError() );
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
server_port_length = server_port_size - 1;
|
|
|
|
|
|
//
|
|
// Obtain the name of the application pool used.
|
|
//
|
|
|
|
if ( !pECB->GetServerVariable( pECB->ConnID,
|
|
"APP_POOL_ID",
|
|
g_app_pool_name,
|
|
&app_pool_name_size ) )
|
|
{
|
|
LOG_ERROR( "GetServerVariable(APP_POOL_ID) failed", GetLastError() );
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
g_app_pool_name_length = app_pool_name_size - 1;
|
|
|
|
|
|
//
|
|
// Construct the base part of the fragment cache key in the
|
|
// buffer g_fragment_cache_key_base; this will have the form
|
|
// "<app_pool_name>/http://<server_name>:<port_number>".
|
|
//
|
|
// Rather than using unnecessary strcat calls, make use of
|
|
// memcpy to do this.
|
|
//
|
|
|
|
|
|
//
|
|
// Copy the application pool name to the beginning of the
|
|
// key prefix.
|
|
//
|
|
|
|
strcpy( g_fragment_cache_key_base, g_app_pool_name );
|
|
|
|
|
|
//
|
|
// Concatenate the eight character string literal "/http://'
|
|
// onto the key prefix, which has a length of g_app_pool_
|
|
// name_length before the concatenation.
|
|
//
|
|
|
|
memcpy( g_fragment_cache_key_base + g_app_pool_name_length, "/http://", 8 );
|
|
|
|
|
|
//
|
|
// Concatenate the server name onto the key prefix, which has
|
|
// a length of g_app_pool_name_length + 8 before the
|
|
// concatenation.
|
|
//
|
|
|
|
ii = g_app_pool_name_length + 8;
|
|
|
|
memcpy( g_fragment_cache_key_base + ii, server_name, server_name_length );
|
|
|
|
|
|
//
|
|
// Concatenate a ':' character, to separate the server name
|
|
// and port number, onto the key prefix. In the memcpy call
|
|
// that does this, the variable 'ii' contains the length of
|
|
// the key prefix before the concatenation.
|
|
//
|
|
|
|
ii += server_name_length;
|
|
|
|
g_fragment_cache_key_base[ ii ] = ':';
|
|
|
|
|
|
//
|
|
// Concatenate the server port onto the key prefix. In the
|
|
// memcpy call that does this, the variable 'ii' contains
|
|
// the length of the key prefix before the concatenation.
|
|
//
|
|
|
|
ii += 1;
|
|
|
|
memcpy( g_fragment_cache_key_base + ii, server_port, server_port_length );
|
|
|
|
ii += server_port_length;
|
|
|
|
|
|
//
|
|
// Terminate the key prefix string.
|
|
//
|
|
|
|
g_fragment_cache_key_base[ ii ] = '\0';
|
|
|
|
|
|
//
|
|
// Determine and cache in g_fragment_cache_key_base_length,
|
|
// the length of the key prefix.
|
|
//
|
|
|
|
g_fragment_cache_key_base_length = strlen( g_fragment_cache_key_base );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Construct the absolute name of the file requested.
|
|
// Take the following steps to do this.
|
|
//
|
|
|
|
|
|
//
|
|
// First copy the webserver root directory path (e.g.
|
|
// "D:/inetpub/wwwroot" to the start of the filename
|
|
// buffer.
|
|
//
|
|
|
|
strcpy( filename, g_root_dir );
|
|
|
|
|
|
//
|
|
// Next concatenate the query string (e.g. "/file_set/
|
|
// dir00001/class0_1") onto the filename string. Here
|
|
// we use memcpy as we need to store the length of the
|
|
// query string for later use and want to avoid repeated
|
|
// strlen operations.
|
|
//
|
|
|
|
query_string_length = strlen( pECB->lpszQueryString );
|
|
|
|
memcpy( filename + g_root_dir_length,
|
|
pECB->lpszQueryString,
|
|
query_string_length + 1 );
|
|
|
|
|
|
//
|
|
// Determine the size of the file requested.
|
|
//
|
|
|
|
if ( !GetFileAttributesEx( filename,
|
|
GetFileExInfoStandard,
|
|
&fileinfo ) )
|
|
{
|
|
|
|
return( send_error_page( pECB,
|
|
"File inaccessible.",
|
|
g_pszStatus_404,
|
|
query_string_length ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Determine the I/O mode to use with VectorSend.
|
|
//
|
|
|
|
switch( g_vector_send_io_mode_config )
|
|
{
|
|
|
|
case USE_ASYNC_IO:
|
|
{
|
|
|
|
use_async_vector_send = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case USE_SYNC_IO:
|
|
{
|
|
|
|
use_async_vector_send = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case USE_ADAPTABLE_IO:
|
|
{
|
|
|
|
use_async_vector_send = ( fileinfo.nFileSizeLow >= g_vector_send_async_range_start );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
//
|
|
// Set isapi_response_ptr to point to an ISAPI response
|
|
// structure on the heap if using asynchronous VectorSend.
|
|
// Also set the VectorSend completion callback and return
|
|
// value.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
|
|
//
|
|
// Try to allocate a free ISAPI_RESPONSE
|
|
// structure on the heap.
|
|
//
|
|
|
|
if ( !( isapi_response_ptr = allocate_isapi_response() ) )
|
|
{
|
|
LOG_ERROR( "Allocation failed", 0 );
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Set the completion callback routine to invoke after completion
|
|
// of the asynchronous VectorSend operation.
|
|
//
|
|
|
|
if ( !pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_IO_COMPLETION,
|
|
vector_send_completion_callback,
|
|
NULL,
|
|
( DWORD * )isapi_response_ptr ) )
|
|
{
|
|
|
|
|
|
//
|
|
// In the error case, free any ISAPI_RESPONSE
|
|
// structure allocated.
|
|
//
|
|
LOG_ERROR( " REQ_IO_COMPLETION failed", GetLastError() );
|
|
free_isapi_response( isapi_response_ptr );
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Set the return value to use after the asynchronous
|
|
// VectorSend is initiated to HSE_STATUS_PENDING.
|
|
//
|
|
|
|
return_value = HSE_STATUS_PENDING;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Parse the cookie string provided by the client.
|
|
//
|
|
|
|
if ( !pECB->GetServerVariable( pECB->ConnID,
|
|
"HTTP_COOKIE",
|
|
cookie_string,
|
|
&cookie_string_size ) )
|
|
{
|
|
|
|
|
|
LOG_ERROR( "GetServerVariable(HTTP_COOKIE) failed", GetLastError() );
|
|
//
|
|
// If there was an error in retrieving the cookie string, free
|
|
// the ISAPI_RESPONSE structure allocated in the asynchronous
|
|
// VectorSend case by placing it on the stack of free
|
|
// ISAPI_RESPONSE structures on the heap.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// The cookie string provided is of the form "my_cookie=user_id=<user_id>&
|
|
// last_ad=<last_ad>". Here we copy <user_id> and <last_ad> into
|
|
// user_id_string and last_ad_string.
|
|
//
|
|
// Note that the index at which <user_id> starts in the buffer 'cookie_string'
|
|
// is 18 and the index at which <last_id> starts in the buffer 'cookie_string'
|
|
// is 9 greater than index of the '&' character that terminates <user_id>.
|
|
//
|
|
|
|
for ( ii = 0, jj = 18; ( user_id_string[ ii ] = cookie_string[ jj ] ) != '&'; ii++, jj++ );
|
|
|
|
user_id_string[ ii ] = '\0';
|
|
|
|
for ( ii = 0, jj += 9; ( last_ad_string[ ii ] = cookie_string[ jj ] ); ii++, jj++ );
|
|
|
|
|
|
//
|
|
// Compute the cookie string and the ID of the ad to send back in
|
|
// the ISAPI response. Store the cookie string in 'cookie_string'
|
|
// and the ad ID in 'ad_index'.
|
|
//
|
|
// Note that the user_index = user_id - 10000, as user_id numbers
|
|
// start at 10000.
|
|
//
|
|
|
|
ad_index = determine_set_cookie_string( atoi( user_id_string ) - 10000,
|
|
atoi( last_ad_string ),
|
|
cookie_string );
|
|
|
|
|
|
//
|
|
// Initialize the ISAPI response using the cookie string computed.
|
|
//
|
|
|
|
if ( !initialize_isapi_response( isapi_response_ptr,
|
|
pECB,
|
|
filename,
|
|
fileinfo.nFileSizeLow,
|
|
cookie_string,
|
|
use_async_vector_send,
|
|
query_string_length ) )
|
|
{
|
|
|
|
LOG_ERROR("initialize_isapi_response failed", 0);
|
|
//
|
|
// In the error case, free any ISAPI_RESPONSE
|
|
// structure allocated.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Handle the special cases in which a class 1 or 2 file is requested.
|
|
//
|
|
|
|
|
|
//
|
|
// If the class number of the file requested is either 1 or 2,
|
|
// then the request must be handled differently (i.e. specific
|
|
// substitutions have to be made in tags contained in the file
|
|
// data). In this case, simply return the value resulting from
|
|
// the appropriate call to handle_class1_or_class2_request; this
|
|
// routine handles these special cases.
|
|
//
|
|
// To determine the class number of the file, note that as the
|
|
// query string contains the path of the filename relative to the
|
|
// webserver document root directory (e.g. /file_set/dir00001/class0_1)
|
|
// and class numbers are always one digit, the third last character
|
|
// in the query string will always denote the class number (this
|
|
// explains the use of "query_string_length - 3" below).
|
|
//
|
|
|
|
isapi_response_ptr->class_number = pECB->lpszQueryString[ query_string_length - 3 ] - '0';
|
|
|
|
if ( isapi_response_ptr->class_number == 1 )
|
|
{
|
|
|
|
return( handle_class1_or_class2_request( pECB,
|
|
isapi_response_ptr,
|
|
filename,
|
|
fileinfo.nFileSizeLow,
|
|
use_async_vector_send,
|
|
ad_index,
|
|
query_string_length ) );
|
|
|
|
}
|
|
else if ( isapi_response_ptr->class_number == 2 )
|
|
{
|
|
|
|
return( handle_class1_or_class2_request( pECB,
|
|
isapi_response_ptr,
|
|
filename,
|
|
fileinfo.nFileSizeLow,
|
|
use_async_vector_send,
|
|
ad_index,
|
|
query_string_length ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Execute the VectorSend operation. If ServerSupportFunction
|
|
// returns TRUE, handle the cache hit case. Otherwise, assume
|
|
// a cache miss and handle it.
|
|
//
|
|
|
|
if ( pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
return( return_value );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now that a cache miss occurred, we will try to add the requested
|
|
// file to the HTTP.SYS fragment cache using a handle to it and then
|
|
// reattempt the VectorSend. To do this, first obtain a handle to
|
|
// the file using CreateFile.
|
|
//
|
|
|
|
if ( ( hFile = CreateFile( filename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL ) ) == INVALID_HANDLE_VALUE )
|
|
{
|
|
|
|
|
|
//
|
|
// Again, in the error case free the ISAPI_RESPONSE struct
|
|
// allocated, if any.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( send_error_page( pECB,
|
|
"File inaccessible.",
|
|
g_pszStatus_404,
|
|
query_string_length ) );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Add the file to the HTTP.SYS fragment cache using the file
|
|
// handle.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element.ElementType = HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE;
|
|
|
|
isapi_response_ptr->vector_element.pvContext = hFile;
|
|
|
|
isapi_response_ptr->vector_element.cbSize = fileinfo.nFileSizeLow;
|
|
|
|
isapi_response_ptr->vector_element.cbOffset = 0;
|
|
|
|
if ( !pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_ADD_FRAGMENT_TO_CACHE,
|
|
&isapi_response_ptr->vector_element,
|
|
( DWORD * )isapi_response_ptr->unicode_fragment_cache_key,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If the add HSE_REQ_ADD_FRAGMENT_TO_CACHE operation failed,
|
|
// (e.g. more than one thread attempted this at once), simply
|
|
// try the VectorSend using the file handle.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ] = isapi_response_ptr->vector_element;
|
|
|
|
|
|
//
|
|
// You need to store the file handle on the
|
|
// heap to be able to close it in the VectorSend
|
|
// completion callback, if necessary.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
isapi_response_ptr->hFile = hFile;
|
|
|
|
}
|
|
|
|
if ( !pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Clearly, you will not be going to the
|
|
// VectorSend completion callback now.
|
|
//
|
|
LOG_ERROR( "VectorSend() failed", GetLastError() );
|
|
CloseHandle( hFile );
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Again, in the failure case, free any ISAPI_RESPONSE
|
|
// structure allocated.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
if ( !use_async_vector_send )
|
|
{
|
|
CloseHandle( hFile );
|
|
}
|
|
|
|
return( return_value );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Retry the VectorSend after successfully adding the requested
|
|
// file to the HTTP.SYS fragment cache.
|
|
//
|
|
|
|
if ( !pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// If the VectorSend operation fails now (e.g. it was flushed
|
|
// from the cache after it was added) simply try the VectorSend
|
|
// using the file handle.
|
|
//
|
|
|
|
isapi_response_ptr->vector_element_array[ 5 ] = isapi_response_ptr->vector_element;
|
|
|
|
|
|
//
|
|
// You need to store the file handle on the
|
|
// heap to be able to close it in the VectorSend
|
|
// completion callback, if necessary.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
isapi_response_ptr->hFile = hFile;
|
|
|
|
}
|
|
|
|
if ( !pECB->ServerSupportFunction( pECB->ConnID,
|
|
HSE_REQ_VECTOR_SEND,
|
|
&( isapi_response_ptr->response_vector ),
|
|
NULL,
|
|
NULL ) )
|
|
{
|
|
|
|
|
|
//
|
|
// Clearly, you will not be going to the
|
|
// VectorSend completion callback now.
|
|
//
|
|
|
|
CloseHandle( hFile );
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
isapi_response_ptr->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Again, in the failure case, free the ISAPI_RESPONSE
|
|
// structure allocated, if any.
|
|
//
|
|
|
|
if ( use_async_vector_send )
|
|
{
|
|
|
|
free_isapi_response( isapi_response_ptr );
|
|
|
|
}
|
|
|
|
return( HSE_STATUS_ERROR );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( !use_async_vector_send )
|
|
{
|
|
CloseHandle( hFile );
|
|
}
|
|
|
|
return( return_value );
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
BOOL WINAPI TerminateExtension( DWORD dwFlags )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Implementation of the TerminateExtension ISAPI entry point.
|
|
Carries out all cleanup tasks needed when SPECweb99-CAD.dll
|
|
is unloaded.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - A DWORD that specifies whether IIS should shut down
|
|
the extension.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if all cleanup tasks are successfully completed
|
|
and FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// Destroy all entries in the stack of free
|
|
// ISAPI_RESPONSE structures on the heap.
|
|
//
|
|
|
|
clear_isapi_response_stack();
|
|
|
|
|
|
//
|
|
// Destroy all entries in the stack of free
|
|
// CLASS1_DATA_BUFFER structures on the heap.
|
|
//
|
|
|
|
clear_class1_data_buffer_stack();
|
|
|
|
|
|
//
|
|
// Destroy all elements in the stack of free
|
|
// CLASS2_DATA_BUFFER structures on the heap.
|
|
//
|
|
|
|
clear_class2_data_buffer_stack();
|
|
|
|
|
|
//
|
|
// Terminate the thread responsible for updating the in-memory
|
|
// data structures holding the User.Personality and Custom.Ads
|
|
// file data and close the handle to it.
|
|
//
|
|
|
|
if ( !TerminateThread( g_update_buffers_thread, STILL_ACTIVE ) )
|
|
{
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
CloseHandle( g_update_buffers_thread );
|
|
|
|
|
|
//
|
|
// Destroy the in-heap memory data structure holding
|
|
// the data from the User.Personality file.
|
|
//
|
|
|
|
free( g_user_personalities_buffer );
|
|
|
|
|
|
//
|
|
// If you have made it this far without returning indicating
|
|
// failure, return indicating success.
|
|
//
|
|
|
|
return( TRUE );
|
|
|
|
}
|