windows-nt/Source/XPSP1/NT/inetsrv/iis/utils/thttp/thttp.c

1398 lines
35 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
thttp.c
Abstract:
Simple test program for the HTTP API.
Author:
Keith Moore (keithmo) 16-Nov-1994
Revision History:
--*/
#include <windows.h>
#include <wininet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
//
// macros
//
#define IS_ARG(c) ((c) == '-')
//
// Private constants.
//
#define DEFAULT_CONTEXT 1
#define OPEN_CONTEXT 2
#define CONNECT_CONTEXT 3
#define REQUEST_CONTEXT 4
#define LOAD_ENTRY( hMod, Name ) \
(p##Name = (pfn##Name) GetProcAddress( (hMod), #Name ))
//
// Private types.
//
typedef struct _QUERY_LEVEL
{
DWORD QueryType;
CHAR * QueryName;
} QUERY_LEVEL;
#define MK_QUERY(x) { HTTP_QUERY_ ## x, #x }
typedef
INTERNETAPI
HINTERNET
(WINAPI *
pfnInternetOpenA)(
IN LPCSTR lpszAgent,
IN DWORD dwAccessType,
IN LPCSTR lpszProxy OPTIONAL,
IN LPCSTR lpszProxyBypass OPTIONAL,
IN DWORD dwFlags
);
typedef
INTERNETAPI
INTERNET_STATUS_CALLBACK
(WINAPI *
pfnInternetSetStatusCallback)(
IN HINTERNET hInternet,
IN INTERNET_STATUS_CALLBACK lpfnInternetCallback
);
typedef
INTERNETAPI
HINTERNET
(WINAPI *
pfnInternetConnectA)(
IN HINTERNET hInternet,
IN LPCSTR lpszServerName,
IN INTERNET_PORT nServerPort,
IN LPCSTR lpszUserName OPTIONAL,
IN LPCSTR lpszPassword OPTIONAL,
IN DWORD dwService,
IN DWORD dwFlags,
IN DWORD dwContext
);
typedef
INTERNETAPI
HINTERNET
(WINAPI *
pfnHttpOpenRequestA)(
IN HINTERNET hConnect,
IN LPCSTR lpszVerb,
IN LPCSTR lpszObjectName,
IN LPCSTR lpszVersion,
IN LPCSTR lpszReferrer OPTIONAL,
IN LPCSTR FAR * lplpszAcceptTypes OPTIONAL,
IN DWORD dwFlags,
IN DWORD dwContext
);
typedef
INTERNETAPI
BOOL
(WINAPI *
pfnHttpAddRequestHeadersA)(
IN HINTERNET hRequest,
IN LPCSTR lpszHeaders,
IN DWORD dwHeadersLength,
IN DWORD dwModifiers
);
typedef
INTERNETAPI
BOOL
(WINAPI *
pfnHttpSendRequestA)(
IN HINTERNET hRequest,
IN LPCSTR lpszHeaders OPTIONAL,
IN DWORD dwHeadersLength,
IN LPVOID lpOptional OPTIONAL,
IN DWORD dwOptionalLength
);
typedef
INTERNETAPI
BOOL
(WINAPI *
pfnHttpQueryInfoA)(
IN HINTERNET hRequest,
IN DWORD dwInfoLevel,
IN OUT LPVOID lpBuffer OPTIONAL,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPDWORD lpdwIndex OPTIONAL
);
typedef
INTERNETAPI
BOOL
(WINAPI *
pfnInternetCloseHandle)(
IN HINTERNET hInternet
);
typedef
INTERNETAPI
BOOL
(WINAPI *
pfnInternetReadFile)(
IN HINTERNET hFile,
IN LPVOID lpBuffer,
IN DWORD dwNumberOfBytesToRead,
OUT LPDWORD lpdwNumberOfBytesRead
);
//
// Private globals.
//
CHAR MoreHeaders[] = "Pragma: This is garbage!\r\n";
HMODULE hWininet;
LPTSTR AcceptTypes[] =
{
"*/*",
NULL
};
QUERY_LEVEL QueryLevels[] =
{
MK_QUERY( STATUS_CODE ),
MK_QUERY( STATUS_TEXT ),
MK_QUERY( VERSION ),
MK_QUERY( MIME_VERSION ),
MK_QUERY( CONTENT_TYPE ),
MK_QUERY( CONTENT_TRANSFER_ENCODING ),
MK_QUERY( CONTENT_ID ),
MK_QUERY( CONTENT_DESCRIPTION ),
MK_QUERY( CONTENT_LENGTH ),
MK_QUERY( CONTENT_LANGUAGE ),
MK_QUERY( ALLOW ),
MK_QUERY( PUBLIC ),
MK_QUERY( DATE ),
MK_QUERY( EXPIRES ),
MK_QUERY( LAST_MODIFIED ),
MK_QUERY( MESSAGE_ID ),
MK_QUERY( URI ),
MK_QUERY( DERIVED_FROM ),
MK_QUERY( COST ),
MK_QUERY( LINK ),
MK_QUERY( PRAGMA ),
MK_QUERY( CONNECTION ),
MK_QUERY( RAW_HEADERS_CRLF )
};
#define NUM_LEVELS (sizeof(QueryLevels) / sizeof(QueryLevels[0]))
BOOL Verbose = FALSE;
BOOL Quiet = FALSE; // Don't print failed headers and content
BOOL Recurse = FALSE; // Follow links
BOOL Cache = FALSE; // Don't allow caching (i.e., force reload)
BOOL Stats = FALSE; // Print stats
BOOL Logs = FALSE; // Print log
BOOL LargeBuf= TRUE; // Use 8k reads rather then 512 byte
BOOL KeepAlive = FALSE;
DWORD AccessType = PRE_CONFIG_INTERNET_ACCESS;
BOOL EnableCallbacks = FALSE;
BOOL UserSuppliedContext = FALSE;
INTERNET_STATUS_CALLBACK PreviousCallback;
DWORD cLevel = 0; // Current recurse level
DWORD cMaxLevel = 10; // Max Recurse level
DWORD cbReceived = 0;
DWORD cmsecStart = 0;
DWORD cFiles = 0;
DWORD cIterations= 1; // Total iterations to perform request
LPSTR GatewayServer = NULL;
INTERNET_PORT nServerPort = 0;
DWORD LogError = ERROR_SUCCESS;
HANDLE AsyncEvent = NULL;
BOOL AsyncMode = FALSE;
DWORD AsyncResult;
DWORD AsyncError;
DWORD Context = 0;
pfnInternetOpenA pInternetOpenA;
pfnInternetSetStatusCallback pInternetSetStatusCallback;
pfnInternetConnectA pInternetConnectA;
pfnHttpOpenRequestA pHttpOpenRequestA;
pfnHttpAddRequestHeadersA pHttpAddRequestHeadersA;
pfnHttpSendRequestA pHttpSendRequestA;
pfnHttpQueryInfoA pHttpQueryInfoA;
pfnInternetCloseHandle pInternetCloseHandle;
pfnInternetReadFile pInternetReadFile;
//
// Private prototypes.
//
void usage(void);
DWORD
DoTest(
LPSTR Host,
LPSTR Verb,
LPSTR Object
);
BOOL
add_headers(
HINTERNET hHttpRequest,
LPSTR lpszHeaders,
DWORD dwHeadersLength
);
void my_callback(HINTERNET, DWORD, DWORD, LPVOID, DWORD);
VOID
FindLink(
LPSTR Host,
LPSTR Verb,
CHAR * buf,
DWORD len,
CHAR * pchLink,
BOOL * pfCopyingLink,
CHAR * pchReferer
);
DWORD ReadHtml(HINTERNET hInternet, LPVOID buf, DWORD len, LPDWORD pRead);
BOOL
LoadWininet(
VOID
);
//
// Public functions.
//
int
__cdecl
main(
int argc,
char * argv[]
)
{
LPSTR host = NULL;
LPSTR verb = NULL;
LPSTR object = NULL;
if ( !LoadWininet() )
{
printf(" Unable to load wininet.dll, error %d\n", GetLastError() );
return GetLastError();
}
for (--argc, ++argv; argc; --argc, ++argv) {
if (IS_ARG(**argv)) {
switch (*++*argv) {
case '?':
usage();
break;
case 'c':
EnableCallbacks = TRUE;
break;
case 'C':
Cache = TRUE;
break;
case 'G':
printf("'G' flag is not supported at this time\n");
GatewayServer = ++*argv;
//AccessType = GATEWAY_INTERNET_ACCESS;
break;
case 'i':
if ( isdigit( argv[0][1] ))
{
cIterations = atoi( ++*argv );
while ( isdigit( *(*argv)++ ))
;
}
break;
case 'k':
KeepAlive = TRUE;
break;
case 'l':
LargeBuf = TRUE;
break;
case 'L':
AccessType = LOCAL_INTERNET_ACCESS;
break;
case 'p':
object = ++*argv;
break;
case 'P':
if ( isdigit( argv[0][1] ))
{
nServerPort = (INTERNET_PORT)atoi( ++*argv );
while ( isdigit( *(*argv)++ ))
;
}
break;
case 'q':
Quiet = TRUE;
break;
case 'r':
Recurse = TRUE;
if ( isdigit( argv[0][1] ))
{
cMaxLevel = atoi( ++*argv );
while ( isdigit( *(*argv)++ ))
;
}
break;
case 's':
Stats = TRUE;
cmsecStart = GetTickCount();
break;
case 'v':
Verbose = TRUE;
break;
case 'x':
++*argv;
if (!**argv) {
Context = DEFAULT_CONTEXT;
} else {
Context = (DWORD)strtoul(*argv, NULL, 0);
UserSuppliedContext = TRUE;
}
break;
case 'y':
AsyncMode = TRUE;
break;
case 'z':
Logs = TRUE;
cmsecStart = GetTickCount();
break;
default:
printf("error: unrecognized command line flag: '%c'\n", **argv);
usage();
}
} else if (!host) {
host = *argv;
} else if (!verb) {
verb = *argv;
} else if (!object) {
object = *argv;
} else {
printf("error: unrecognized command line argument: \"%s\"\n", *argv);
usage();
}
}
if (!verb) {
verb = "GET";
}
if (!object) {
object = "\r\n";
}
if (!(host && verb && object)) {
printf("error: missing command-line argument\n");
usage();
}
if (AsyncMode) {
//
// create an auto-reset event
//
AsyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
//
// Make stdout "binary" so we can retrieve GIFs, JPEGs, etc.
//
_setmode( _fileno( stdout ), _O_BINARY );
//
// Perform some tests.
//
while ( cIterations-- )
{
DWORD Error;
Error = DoTest(host, verb, object );
if( Error != ERROR_SUCCESS ) {
LogError = Error;
}
}
if ( Stats )
{
DWORD csecTotal = (GetTickCount() - cmsecStart) / 1000;
DWORD cMin = csecTotal / 60;
DWORD cSec = csecTotal % 60;
fprintf( stderr,
"=====================================\n"
"Total data bytes received: %ld\n"
"Total files retrieved: %ld\n"
"Total time: %d:%d\n"
"=====================================\n",
cbReceived,
cFiles,
cMin,
cSec );
}
if ( Logs )
{
DWORD csecTotal = (GetTickCount() - cmsecStart) ;
SYSTEMTIME SystemTime;
GetLocalTime( &SystemTime );
fprintf( stderr,
"LOG: [%02u/%02u %02u:%02u:%02u] "
"%-10s %-32s %4s %8d %8d\n",
SystemTime.wMonth,
SystemTime.wDay,
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond,
GatewayServer,
host,
object,
LogError,
csecTotal );
}
return 0;
} // main
void usage() {
printf("usage: thttp [-c] [-C] [-l] [-L] [-k] [-p<path>] [-q] [-r] [-s] [-v] [-P]\n"
" [-x$] [-y] [-z] [-G<servername>] <host> [<verb>] [<object>]\n"
"\n"
"where: -c = Enable call-backs\n"
" -C = Enable caching\n"
" -i[n] = Iterate n times\n"
" -l = Large network buffer\n"
" -L = Force local access (i.e., do not use gateway)\n"
" -k = Use Keep-Alive\n"
" -p = path (e.g. if path starts with '/')\n"
" -q = Quiet mode, no failed headers, no content\n"
" -r[n] = Recurse into links, n = max recurse level\n"
" -s = Print network statistics\n"
" -v = Verbose mode\n"
" -G = specific gateway server\n"
" -P[n] = Use port n; default = 80\n"
" -x = Context value. $ is number string (binary, hex, decimal)\n"
" -y = Async mode\n"
" -z = print log\n"
"Verb defaults to \"GET\"\n"
"Object defaults to \"\\r\\n\"\n"
);
exit(1);
}
BOOL
LoadWininet(
VOID
)
{
if ( !(hWininet = LoadLibrary( "wininet.dll" )) )
{
printf("Failed to load wininet.dll\n" );
return FALSE;
}
if ( !LOAD_ENTRY( hWininet, InternetOpenA ) ||
!LOAD_ENTRY( hWininet, InternetSetStatusCallback ) ||
!LOAD_ENTRY( hWininet, InternetConnectA ) ||
!LOAD_ENTRY( hWininet, HttpOpenRequestA ) ||
!LOAD_ENTRY( hWininet, HttpAddRequestHeadersA ) ||
!LOAD_ENTRY( hWininet, HttpSendRequestA ) ||
!LOAD_ENTRY( hWininet, HttpQueryInfoA ) ||
!LOAD_ENTRY( hWininet, InternetCloseHandle ) ||
!LOAD_ENTRY( hWininet, InternetReadFile ) )
{
return FALSE;
}
return TRUE;
}
DWORD
DoTest(
LPSTR Host,
LPSTR Verb,
LPSTR Object
)
{
DWORD Error = ERROR_SUCCESS;
HINTERNET InternetHandle = NULL;
HINTERNET InternetConnectHandle = NULL;
HINTERNET hhttp = NULL;
DWORD len;
int i;
CHAR buf[8192];
CHAR bufLink[512];
BOOL fCopyingLink = FALSE;
*bufLink = '\0';
//
// open internet.
//
if (Verbose) {
printf("calling InternetOpen()...\n");
}
InternetHandle = pInternetOpenA(
"THTTP: HTTP API Test Application", // lpszCallerName
AccessType, // dwAccessType
GatewayServer, // lpszProxyName
INTERNET_INVALID_PORT_NUMBER, // nProxyPort
AsyncMode ? INTERNET_FLAG_ASYNC : 0 // dwFlags (async)
);
if (InternetHandle == NULL) {
if (AsyncMode) {
Error = GetLastError();
if (Error == ERROR_IO_PENDING) {
if (Verbose) {
fprintf(stderr, "error: InternetOpen() is async (spanish inquisition mode)\n");
printf("waiting for async InternetOpen()...\n");
}
WaitForSingleObject(AsyncEvent, INFINITE);
if (AsyncResult == 0) {
fprintf(stderr, "error: async InternetOpen() returns %d\n",
AsyncError);
goto Cleanup;
} else {
InternetHandle = (HINTERNET)AsyncResult;
}
} else {
fprintf(stderr, "error: async InternetOpen() returns %d\n", Error);
goto Cleanup;
}
} else {
fprintf( stderr,
"InternetOpen() failed, error %d\n",
Error = GetLastError() );
goto Cleanup;
}
}
if (Verbose) {
printf("InternetOpen() returns %x\n", InternetHandle);
}
if (EnableCallbacks) {
//
// let's have a status callback
//
// Note that call-backs can be set even before we have opened a handle
// to the internet/gateway
//
PreviousCallback = pInternetSetStatusCallback(InternetHandle, my_callback);
if (Verbose) {
printf("previous Internet callback = %x\n", PreviousCallback);
}
}
//
// Call internet connect to connect to the http server.
//
if (Verbose) {
printf("calling InternetConnect()...\n");
}
InternetConnectHandle = pInternetConnectA(
InternetHandle, // hInternetSession
Host, // lpszServerName
nServerPort, // nServerPort
NULL, // lpszUserName
NULL, // lpszPassword
INTERNET_SERVICE_HTTP, // dwService
0, // dwFlags
UserSuppliedContext ? Context : CONNECT_CONTEXT
);
if( InternetConnectHandle == NULL )
{
if (AsyncMode) {
Error = GetLastError();
if (Error == ERROR_IO_PENDING) {
if (Verbose) {
fprintf(stderr, "error: InternetConnect() is async (spanish inquisition mode)\n");
printf("waiting for async InternetConnect()...\n");
}
WaitForSingleObject(AsyncEvent, INFINITE);
if (AsyncResult == 0) {
fprintf(stderr, "error: async InternetConnect() returns %d\n",
AsyncError);
goto Cleanup;
} else {
InternetConnectHandle = (HINTERNET)AsyncResult;
}
} else {
fprintf(stderr, "error: async InternetConnect() returns %d\n", Error);
goto Cleanup;
}
} else {
fprintf( stderr,
"InternetConnect() failed, error %d\n",
Error = GetLastError() );
goto Cleanup;
}
}
if (Verbose) {
printf("InternetConnect() returns %x\n", InternetConnectHandle);
}
//
// Open a request handle.
//
if (Verbose) {
printf("calling HttpOpenRequest()...\n");
}
hhttp = pHttpOpenRequestA(
InternetConnectHandle, // hHttpSession
Verb, // lpszVerb
Object, // lpszObjectName
NULL, // lpszVersion
NULL, // lpszReferer
AcceptTypes, // lplpszAcceptTypes
(Cache ? 0 :
INTERNET_FLAG_RELOAD),
UserSuppliedContext ? Context : REQUEST_CONTEXT
);
if( hhttp == NULL )
{
if (AsyncMode) {
Error = GetLastError();
if (Error == ERROR_IO_PENDING) {
if (Verbose) {
fprintf(stderr, "error: HttpOpenRequest() is async (spanish inquisition mode)\n");
printf("waiting for async HttpOpenRequest()...\n");
}
WaitForSingleObject(AsyncEvent, INFINITE);
if (AsyncResult == 0) {
fprintf(stderr, "error: async HttpOpenRequest() returns %d\n",
AsyncError);
goto Cleanup;
} else {
hhttp = (HINTERNET)AsyncResult;
}
} else {
fprintf(stderr, "error: async HttpOpenRequest() returns %d\n", Error);
goto Cleanup;
}
} else {
fprintf( stderr,
"HttpOpenRequest() failed, error %d\n",
Error = GetLastError() );
goto Cleanup;
}
}
if (Verbose) {
printf("HttpOpenRequest() returns %x\n", hhttp);
}
//
// add keep-alive header if requested
//
if (KeepAlive) {
if (!add_headers(hhttp, "Connection: Keep-Alive\r\n", (DWORD)-1)) {
fprintf(stderr, "HttpAddRequestHeaders() returns %d\n", GetLastError());
}
}
//
// Add additional request headers.
//
if( !add_headers(
hhttp,
"Pragma: bite-me\r\n",
(DWORD)-1L ) )
{
fprintf( stderr,
"HttpAddRequestHeaders() failed, error %d\n",
GetLastError() );
}
if( !add_headers(
hhttp,
"Pragma: bite-me-again\r\n",
(DWORD)-1L ) )
{
fprintf( stderr,
"HttpAddRequestHeaders() failed, error %d\n",
GetLastError() );
}
if( !add_headers(
hhttp,
"Pragma: bite-me-a-third-time\r\n",
(DWORD)-1L ) )
{
fprintf( stderr,
"HttpAddRequestHeaders() failed, error %d\n",
GetLastError() );
}
//
// Send the request.
//
if (Verbose) {
printf("calling HttpSendRequest()...\n");
}
if( !pHttpSendRequestA(
hhttp, // hHttpRequest
MoreHeaders, // lpszHeaders
(DWORD)-1L, // dwHeadersLength
NULL, // lpOptional
0 ) ) // dwOptionalLength
{
if (AsyncMode) {
Error = GetLastError();
if (Error == ERROR_IO_PENDING) {
if (Verbose) {
printf("HttpSendRequest() waiting for async completion\n");
}
WaitForSingleObject(AsyncEvent, INFINITE);
Error = AsyncError;
if (!AsyncResult) {
printf("error: ASYNC HttpSendRequest() returns FALSE\n");
if (Error == ERROR_SUCCESS) {
printf("error: ASYNC HttpSendRequest() (FALSE) returns ERROR_SUCCESS!\n");
} else {
printf("ASYNC HttpSendRequest() returns %d\n", Error);
}
} else if (Verbose) {
printf("ASYNC HttpSendRequest() success\n");
}
} else {
printf("error: ASYNC HttpSendRequest() returns %d\n", Error);
}
} else {
fprintf( stderr,
"HttpSendRequest() failed, error %d\n",
Error = GetLastError() );
}
} else if (AsyncMode) {
//
// we expect async HttpSendRequest() to always return FALSE w/ error
// or ERROR_IO_PENDING
//
printf("ASYNC HttpSendRequest() returns TRUE\n");
// } else {
//
// Error is still ERROR_SUCCESS from initialization
//
}
if (Error == ERROR_SUCCESS) {
//
// Process the queries.
//
if ( Quiet )
{
len = sizeof(buf);
//
// Only look for failures to retrieve if we're in quiet mode
//
if ( !pHttpQueryInfoA(
hhttp,
HTTP_QUERY_STATUS_CODE,
buf,
&len,
NULL ))
{
fprintf( stderr,
"HttpQueryInfo( HTTP_QUERY_STATUS_CODE ) failed, error %d\n",
GetLastError() );
}
if ( *buf != '2' )
{
Error = atoi(buf);
goto PrintAllHeaders;
}
cFiles++;
}
else
{
PrintAllHeaders:
if( !Logs ) {
for( i = 0 ; i < NUM_LEVELS ; i++ )
{
len = sizeof(buf);
if( !pHttpQueryInfoA(
hhttp,
QueryLevels[i].QueryType,
buf,
&len,
NULL ) )
{
if ( QueryLevels[i].QueryType == HTTP_QUERY_STATUS_CODE &&
*buf == '2' )
{
cFiles++;
}
if ( !Quiet && GetLastError() != ERROR_HTTP_HEADER_NOT_FOUND )
{
fprintf( stderr,
"HttpQueryInfo( %s ) failed, error %d\n",
QueryLevels[i].QueryName,
GetLastError() );
}
}
else
{
fprintf( stderr,
"%s = %s\n",
QueryLevels[i].QueryName,
buf );
}
}
}
}
//
// Read the data.
//
for( ; ; )
{
len = LargeBuf ? sizeof(buf) : 512;
Error = ReadHtml(hhttp, buf, len, &len);
if (Error != ERROR_SUCCESS) {
fprintf( stderr,
"InternetReadFile() failed, error %d\n",
Error = GetLastError() );
break;
}
cbReceived += len;
if( len == 0 )
{
if ( !Quiet )
{
fprintf( stderr,
"EOF\n" );
}
break;
}
if ( !Quiet )
{
fwrite( buf, 1, (size_t)len, stdout );
}
if ( Recurse && cLevel < cMaxLevel )
{
CHAR ContentType[50];
DWORD cbContentType = sizeof( ContentType );
//
// Only look for links if the content type is text/html
//
if( pHttpQueryInfoA(
hhttp,
HTTP_QUERY_CONTENT_TYPE,
ContentType,
&cbContentType,
NULL ) &&
!_stricmp( ContentType,
"text/html" ))
{
FindLink( Host,
Verb,
buf,
len,
bufLink,
&fCopyingLink,
Object );
}
}
}
//
// Perform an extraneous read.
//
len = sizeof(buf);
Error = ReadHtml(hhttp, buf, len, &len);
if (Error != ERROR_SUCCESS) {
fprintf( stderr,
"InternetReadFile() failed, error %d\n",
Error = GetLastError() );
}
else
if( len != 0 )
{
fprintf( stderr,
"BOGUS EXTRANEOUS READ: %d\n",
len );
}
}
Cleanup:
//
// Close handles.
//
if( hhttp != NULL )
{
if( !pInternetCloseHandle( hhttp ) )
{
fprintf( stderr,
"InternetCloseHandle() failed, error %d\n",
GetLastError() );
}
}
if( InternetConnectHandle != NULL )
{
if( !pInternetCloseHandle( InternetConnectHandle ) )
{
fprintf( stderr,
"InternetCloseHandle() failed, error %d\n",
GetLastError() );
}
}
if( InternetHandle != NULL )
{
if( !pInternetCloseHandle( InternetHandle ) )
{
fprintf( stderr,
"InternetCloseHandle() failed, error %d\n",
GetLastError() );
}
}
cLevel--;
return( Error );
} // DoTest
BOOL
add_headers(
HINTERNET hHttpRequest,
LPSTR lpszHeaders,
DWORD dwHeadersLength
)
{
BOOL ok;
ok = pHttpAddRequestHeadersA(hHttpRequest, lpszHeaders, dwHeadersLength, 0);
if (AsyncMode) {
if (!ok) {
DWORD err;
err = GetLastError();
if (err == ERROR_IO_PENDING) {
if (Verbose) {
printf("warning: HttpAddRequestHeaders() is async - unexpected\n");
printf("waiting for async HttpAddRequestHeaders()...\n");
}
WaitForSingleObject(AsyncEvent, INFINITE);
ok = (BOOL)AsyncResult;
if (!ok) {
printf("error: async HttpAddRequestHeaders() returns %d\n",
AsyncError);
}
} else {
printf("error: async HttpAddRequestHeaders() returns %d\n", err);
}
}
}
return ok;
}
VOID
my_callback(
HINTERNET hInternet,
DWORD Context,
DWORD Status,
LPVOID Info,
DWORD Length
)
{
char* type$;
BOOL unknown = FALSE;
switch (Status) {
case INTERNET_STATUS_RESOLVING_NAME:
type$ = "RESOLVING NAME";
break;
case INTERNET_STATUS_NAME_RESOLVED:
type$ = "NAME RESOLVED";
break;
case INTERNET_STATUS_CONNECTING_TO_SERVER:
type$ = "CONNECTING TO SERVER";
break;
case INTERNET_STATUS_CONNECTED_TO_SERVER:
type$ = "CONNECTED TO SERVER";
break;
case INTERNET_STATUS_SENDING_REQUEST:
type$ = "SENDING REQUEST";
break;
case INTERNET_STATUS_REQUEST_SENT:
type$ = "REQUEST SENT";
break;
case INTERNET_STATUS_RECEIVING_RESPONSE:
type$ = "RECEIVING RESPONSE";
break;
case INTERNET_STATUS_RESPONSE_RECEIVED:
type$ = "RESPONSE RECEIVED";
break;
case INTERNET_STATUS_CLOSING_CONNECTION:
type$ = "CLOSING CONNECTION";
break;
case INTERNET_STATUS_CONNECTION_CLOSED:
type$ = "CONNECTION CLOSED";
break;
case INTERNET_STATUS_REQUEST_COMPLETE:
type$ = "REQUEST COMPLETE";
if (AsyncMode) {
AsyncResult = ((LPINTERNET_ASYNC_RESULT)Info)->dwResult;
AsyncError = ((LPINTERNET_ASYNC_RESULT)Info)->dwError;
SetEvent(AsyncEvent);
} else {
printf("error: REQUEST_COMPLETE not expected - not async\n");
}
break;
default:
type$ = "???";
unknown = TRUE;
break;
}
if (Verbose) {
printf("callback: handle %x [context %x [%s]] %s ",
hInternet,
Context,
UserSuppliedContext ? "User"
: (Context == DEFAULT_CONTEXT) ? "Default"
: (Context == OPEN_CONTEXT) ? "Open"
: (Context == CONNECT_CONTEXT) ? "Connect"
: (Context == REQUEST_CONTEXT) ? "Request"
: "???",
type$
);
if (Info && !unknown) {
if (Status == INTERNET_STATUS_REQUEST_COMPLETE) {
if (Verbose) {
printf("dwResult = %x, dwError = %d\n",
((LPINTERNET_ASYNC_RESULT)Info)->dwResult,
((LPINTERNET_ASYNC_RESULT)Info)->dwError
);
}
} else {
printf(Info);
}
}
putchar('\n');
}
}
VOID
FindLink(
LPSTR Host,
LPSTR Verb,
CHAR * buf,
DWORD len,
CHAR * pchLink,
BOOL * pfCopyingLink,
CHAR * pchReferer
)
{
DWORD Error;
CHAR * pchEnd = buf + len;
CHAR * pch = buf;
DWORD cchLink = strlen( pchLink );
while ( TRUE )
{
if ( *pfCopyingLink )
{
FindEOT:
//
// Look for end of href
//
while ( pch < pchEnd )
{
if ( *pch == '"' )
goto FoundEOT;
pchLink[cchLink++] = *pch;
pch++;
}
//
// Used up all of the buffer and we didn't find the end of the tag,
// get some more data
//
pchLink[cchLink] = '\0';
return;
FoundEOT:
pchLink[cchLink] = '\0';
*pfCopyingLink = FALSE;
//
// We only traverse URLs of the form '/dir/bar/doc.htm'
//
if ( pchLink[0] != '/' )
{
CHAR * pchLastSlash;
CHAR achTemp[512];
//
// If it's relative, use the referer to make it absolute
//
// Note we don't process /dir/bar/doc.htm#GoHere tags
//
if ( (pchLastSlash = strrchr( pchReferer, '/' )) &&
strncmp( pchLink, "ftp:", 4 ) &&
strncmp( pchLink, "http:", 5 ) &&
strncmp( pchLink, "gopher:", 7 ) &&
strncmp( pchLink, "mailto:", 7 ) &&
!strchr( pchLink, '#' ))
{
*(pchLastSlash + 1) = '\0';
strcpy( achTemp, pchReferer );
strcat( achTemp, pchLink );
strcpy( pchLink, achTemp );
}
else
{
fprintf( stderr,
"Ignoring %s\n",
pchLink );
return;
}
}
fprintf( stderr,
"Traversing %s\n",
pchLink );
cLevel++;
Error = DoTest(
Host,
Verb,
pchLink );
if( Error != ERROR_SUCCESS ) {
LogError = Error;
}
}
else
{
*pchLink = '\0';
//
// Scan for the beginning of an href tag
//
while ( pch < pchEnd )
{
if ( *pch == '<' )
{
//
// Look for "<A HREF="", note we aren't flexible about spacing
//
if ( !_strnicmp( pch, "<A HREF=\"", 9 ) ||
!_strnicmp( pch, "<IMG SRC=\"", 10 ))
{
pch += (toupper(pch[1]) == 'A' ? 9 : 10);
*pfCopyingLink = TRUE;
cchLink = 0;
goto FindEOT;
}
}
pch++;
}
//
// No tag found, return
//
return;
}
}
}
DWORD ReadHtml(HINTERNET hInternet, LPVOID buf, DWORD len, LPDWORD pRead) {
DWORD error = ERROR_SUCCESS;
if (!pInternetReadFile(hInternet, buf, len, pRead)) {
if (AsyncMode) {
error = GetLastError();
if (error == ERROR_IO_PENDING) {
if (Verbose) {
printf("ASYNC InternetReadFile() waiting for async completion\n");
}
WaitForSingleObject(AsyncEvent, INFINITE);
error = AsyncError;
if (!AsyncResult) {
printf("error: ASYNC InternetReadFile() returns FALSE\n");
if (error == ERROR_SUCCESS) {
printf("error: ASYNC InternetReadFile() (FALSE) returns ERROR_SUCCESS!\n");
} else {
printf("ASYNC InternetReadFile() returns %d\n", error);
}
} else if (Verbose) {
printf("ASYNC InternetReadFile() success\n");
//
// error should be ERROR_SUCCESS from callback
//
if (error != ERROR_SUCCESS) {
printf("error: async error = %d. Expected ERROR_SUCCESS\n", error);
}
}
} else {
printf("error: ASYNC InternetReadFile() returns %d\n", error);
}
} else {
error = GetLastError();
printf("error: SYNC InternetReadFile() returns %d\n", error);
}
} else if (AsyncMode) {
//
// we expect async InternetReadFile() to always return FALSE w/ error
// or ERROR_IO_PENDING
//
if (Verbose) {
printf("ASYNC InternetReadFile() returns TRUE\n");
}
} else {
//
// error is still ERROR_SUCCESS from initialization
//
if (Verbose) {
printf("SYNC InternetReadFile() returns TRUE\n");
}
}
return error;
}