OTSimpleDownloadHTTPTest.c

/*
    File:       OTSimpleDownloadHTTPTest.c
 
    Contains:   A test program for the simple HTTP download sample.
 
    Written by: Quinn "The Eskimo!" 
 
    Copyright:  Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                3/02/2001   Chad Jones      Updated to Metroworks Codewarrior Pro IDE 4.1.  Also carbonized for OSX.
                7/23/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
 
/////////////////////////////////////////////////////////////////////
// Pick up our own prototype.
 
#include "OTSimpleDownloadHTTP.h"
 
/////////////////////////////////////////////////////////////////////
// OTDebugStr is not defined in any OT header files, but it is
// exported by the libraries, so we define the prototype here.
 
extern pascal void OTDebugStr(const char* str);
 
 
/////////////////////////////////////////////////////////////////////
#if defined(__MWERKS__)
#   include <Threads.h>
 
#   include <string.h>
#   include <stdio.h>
#   include <ctype.h>
#endif
 
enum {
    kNotHTTPURLErr = -666,
    kWhatImplementationErr = -667
};
 
enum {
    kDownloadSimple,
    kDownloadFaster
};
 
//These are my versions of PStrCat and PStrCopy.
 
StringPtr PStrCat(StringPtr dst, ConstStr255Param   src);
StringPtr PStrCopy(StringPtr dst, ConstStr255Param   src);
 
 
StringPtr PStrCat(StringPtr dst, ConstStr255Param   src)
{
    SInt16 size = src[0];
    
    if (0xff - dst[0] < size)
        size = 0xff - dst[0];
    
    BlockMoveData(&src[1], &dst[dst[0]], size);
    dst[0] = dst[0] + size;
    
    return dst;
}
 
StringPtr PStrCopy(StringPtr dst, ConstStr255Param   src)
{
dst[0] = src[0]; BlockMoveData(&src[1], &dst[1], src[0]); return dst;
}
 
 
static OSStatus DownloadURL(const char *urlString)
{
    OSStatus err;
    size_t hostCharCount;
    char hostName[256];
    Str255 tickCountString;
    Str255 destFileName;
    FSSpec destFSSpec;
    short destFileRefNum;
    char httpGetCommand[256];
    OTTimeStamp startTime;
    UInt32 timeToDownload;
    long fileSize;
    
    err = noErr;
    // First check that the urlString begins with "http://"
    if ( strspn(urlString, "http://") != strlen("http://") ) {
        err = kNotHTTPURLErr;
    }
 
    // Now skip over the "http://" and extract the host name.
    if (err == noErr) {
        // Skip over the "http://".
        urlString += strlen("http://");
        
        // Count the characters before the next slash.
        hostCharCount = strcspn(urlString, "/");
        
        // Extract those characters from the URL into hostName
        //  and then make sure it's null terminated.
        (void) strncpy(hostName, urlString, hostCharCount);
        hostName[hostCharCount] = 0;
        urlString += hostCharCount;
        
        // Add a ":80" to the host name if necessary.
        if ( strchr( hostName, ':' ) == nil ) {
            strcat( hostName, ":80" );
        }
 
        if (err == noErr) {
            NumToString(TickCount(), tickCountString);
            PStrCopy(destFileName, "\pSimpleDownload#");
            PStrCat(destFileName, tickCountString);
            (void) FSMakeFSSpec(0, 0, destFileName, &destFSSpec);
                        
            (void) FSpCreate(&destFSSpec, 'R*ch', 'TEXT', 0);
            err = FSpOpenDF(&destFSSpec, fsRdWrPerm, &destFileRefNum);
            if (err != noErr) {
                destFileRefNum = 0;
            }
        }
        
        if (err == noErr) {
            // Now place the URL into the HTTP command that we send to DownloadHTTPSimple.
            if ( *urlString == 0 ) {
                urlString = "/";
            }
            (void) sprintf(httpGetCommand, "GET %s HTTP/1.0%c%c%c%c", urlString, 13, 10, 13, 10);
            printf("Calling DownloadHTTPXxxx(Ò%sÓ, Ò%sÓ, %04x).\n", hostName, urlString, destFileRefNum);
                        fflush(stdout);
            
            OTGetTimeStamp(&startTime);
            err = DownloadHTTPSimple(hostName, httpGetCommand, destFileRefNum);
            timeToDownload = OTElapsedMicroseconds(&startTime);
        }
 
        if (err == noErr) {
            MoreAssert(GetFPos(destFileRefNum, &fileSize) == noErr); //Assertion Fails if: GetFPos failed
            printf("Bytes downloaded: %ld.\n", fileSize);
            printf("Time to download: %ldus.\n", timeToDownload);
            printf("Bytes per second: %f.\n", (float) fileSize / (((float) timeToDownload) / 1000000.0) );  
                        fflush(stdout);
                        
        }
 
        // Clean up.
        if (destFileRefNum != 0) {
            (void) FSClose(destFileRefNum);
        }
        if (err != noErr) {
            //(void) FSpDelete(&destFSSpec);
        }
 
    }
 
    return (err);
}
 
static UInt32 gLastPrinted = 0;
 
static pascal OSStatus ProgressThread(void *junkParam)
{
    #pragma unused(junkParam)
    OSStatus junk;
 
    while (true) {
        if ( TickCount() > (gLastPrinted + 60) ) {
            printf(".");
            fflush(stdout);
            gLastPrinted = TickCount();
        }
        junk = YieldToAnyThread();
                    MoreAssert(junk == noErr); //Assertion fails if:  YieldToAnyThread failed
    }
 
    return (noErr); 
}
 
static const char *defaultURLs[10] = {
"http://www.apple.com",
"http://www.apple.com/developer/",
"http://developer.apple.com/dev/opentransport/",
"http://developer.apple.com/technotes/index.html",
"",
"",
"",
"",
"",
""
};
 
int main(void)
{
    OSStatus err;
    OSStatus junk;
    char urlString[256];
    UInt32 i;
    ThreadID progressThread;
    
#if defined(__MWERKS__)
    SIOUXSettings.autocloseonquit = FALSE;  // don't close the SIOUX window on program termination
    SIOUXSettings.asktosaveonclose = FALSE; // don't offer to save on a close
#endif
 
    printf("OTDownloadHTTP!\n");
    printf("-- Downloads a URL using OT's thread support\n");
    printf("-- or using notifiers.\n");
    printf("\n");
        fflush(stdout);
 
    err = InitOpenTransportInContext(kInitOTForApplicationMask, nil);
    
    if (err == noErr) {
        
        for (i = 0; i < 10; i++) {
            if ( *defaultURLs[i] != 0 ) {
                printf("%ld> %s\n", i, defaultURLs[i]);
                                fflush(stdout);
            }
        }
        printf("\n");
        printf("Enter a URL (or type a number from the above list):\n");
                fflush(stdout);
        scanf("%s", (char *)&urlString);
        if ( strlen(urlString) == 1 && isdigit(urlString[0])) {
            strcpy( urlString, defaultURLs[ urlString[0] - '0' ] );
        }
        if ( urlString[0] == 0 ) {
            strcpy( urlString, defaultURLs[0] );
        }
        err = NewThread(kCooperativeThread,
                            NewThreadEntryUPP((void *) &ProgressThread), nil,
                            0, kCreateIfNeeded,
                            nil,
                            &progressThread);
        if (err == noErr) {
            err = DownloadURL(urlString);
            junk = DisposeThread(progressThread, nil, true);
            MoreAssert(junk == noErr); //Assertion fails if:  DisposeThread failed
        }
        
        CloseOpenTransportInContext(nil);
    }
    
    if (err == noErr) {
        printf("Success.\n");
    } else {
        printf("Failed with error %ld.\n", err);
    }   
    printf("Done.\n");
        fflush(stdout);
        return(0);
}