1398 lines
35 KiB
C
1398 lines
35 KiB
C
/*++
|
||
|
||
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;
|
||
}
|