Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
OTSimpleDownloadHTTP.c
/* |
File: OTSimpleDownloadHTTP.c |
Contains: Implementation of 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); |
///////////////////////////////////////////////////////////////////// |
enum { |
kTransferBufferSize = 4096 |
}; |
///////////////////////////////////////////////////////////////////// |
static pascal void YieldingNotifier(void* contextPtr, OTEventCode code, |
OTResult result, void* cookie) |
// This simple notifier checks for kOTSyncIdleEvent and |
// when it gets one calls the Thread Manager routine |
// YieldToAnyThread. Open Transport sends kOTSyncIdleEvent |
// whenever it's waiting for something, eg data to arrive |
// inside a sync/blocking OTRcv call. In such cases, we |
// yield the processor to some other thread that might |
// be doing useful work. |
{ |
#pragma unused(contextPtr) |
#pragma unused(result) |
#pragma unused(cookie) |
OSStatus junk; |
switch (code) { |
case kOTSyncIdleEvent: |
junk = YieldToAnyThread(); |
MoreAssert(junk == noErr); //Assertion fails if: YieldToAnyThread failed |
break; |
default: |
// do nothing |
break; |
} |
} |
///////////////////////////////////////////////////////////////////// |
OSStatus DownloadHTTPSimple(const char *hostName, |
const char *httpCommand, |
const short destFileRefNum) |
// Download a URL from the a web server. hostName is a pointer |
// to a string that contains the DNS address of the web server. |
// The DNS address must be suffixed by ":<port>", where <port> |
// is the port number the web server is operating on. |
// httpCommand contains the HTTP command to send. Typically this |
// is of the form: |
// |
// GET <x> HTTP/1.0\0x0d\0x0a\0x0d\0x0a |
// |
// where <x> is the URL path. destFileRefNum is the file |
// reference number to which the results of the HTTP command |
// are written. This routine does not parse the returned HTTP |
// header in any way. The entire incoming stream goes into |
// the file verbatim. |
// |
// For example, if you were asked to download a URL like: |
// |
// http://devworld.apple.com/dev/technotes.shtml |
// |
// you would set: |
// |
// o hostName to "devworld.apple.com:80" (80 is the |
// default port for HTTP. |
// o httpCommand to "GET /dev/technotes.shtml HTTP/1.0\0x0d\0x0a\0x0d\0x0a" |
{ |
OSStatus err; |
OSStatus junk; |
Ptr transferBuffer = nil; |
EndpointRef ep = kOTInvalidEndpointRef; |
TCall sndCall; |
DNSAddress hostDNSAddress; |
OTFlags junkFlags; |
OTResult bytesSent; |
OTResult bytesReceived; |
OTResult lookResult; |
Boolean bound = false; |
// First allocate a buffer for storing the data as we read it. |
err = noErr; |
transferBuffer = OTAllocMemInContext(kTransferBufferSize,nil); |
if ( transferBuffer == nil ) { |
err = kENOMEMErr; |
} |
// Now open a TCP endpoint. |
if (err == noErr) { |
ep = OTOpenEndpointInContext(OTCreateConfiguration(kTCPName), 0, nil, &err,nil); |
} |
// If the endpoint opens successfully... |
if (err == noErr) { |
// Establish the modes of operation. This sample uses |
// sync/blocking mode, with sync idle events that yield |
// time using the Thread Manager. |
junk = OTSetSynchronous(ep); |
MoreAssert(junk == noErr); //Assertion Fails if: OTSetSynchronous failed |
junk = OTSetBlocking(ep); |
MoreAssert(junk == noErr); //Assertion Fails if: OTSetBlocking failed |
junk = OTInstallNotifier(ep, NewOTNotifyUPP(YieldingNotifier), nil); |
MoreAssert(junk == noErr); //Assertion Fails if: OTInstallNotifier failed |
junk = OTUseSyncIdleEvents(ep, true); |
MoreAssert(junk == noErr); //Assertion Fails if: OTUseSyncIdleEvents failed |
// Bind the endpoint. Because we're an outgoing connection, |
// we don't have to bind it to a specific address. |
err = OTBind(ep, nil, nil); |
bound = (err == noErr); |
} |
// Initialise the sndCall structure and call OTConnect. We nil |
// out most of the fields in the sndCall structure because |
// we don't want any special options or connection data. |
// The important field of the sndCall is the addr TNetBuf, |
// which we initialise to the |
if (err == noErr) { |
sndCall.addr.buf = (UInt8 *) &hostDNSAddress; |
sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostName); |
sndCall.opt.buf = nil; // no connection options |
sndCall.opt.len = 0; |
sndCall.udata.buf = nil; // no connection data |
sndCall.udata.len = 0; |
sndCall.sequence = 0; // ignored by OTConnect |
err = OTConnect(ep, &sndCall, nil); |
} |
// Send the HTTP command to the server. |
if (err == noErr) { |
bytesSent = OTSnd(ep, (void *) httpCommand, OTStrLength(httpCommand), 0); |
// OTSnd returns the number of bytes sent. Because we're in |
// synchronous mode, it won't return until it's sent all the |
// bytes, or it gets an error. |
if (bytesSent > 0) { |
err = noErr; |
} else { |
err = bytesSent; |
} |
} |
// Now that we have sent the HTTP command, we turn around and |
// receive the data comming back from the server. |
if (err == noErr) { |
do { |
bytesReceived = OTRcv(ep, (void *) transferBuffer, kTransferBufferSize, &junkFlags); |
// OTRcv returns the number of bytes received. Because we're in |
// synchronous mode, it won't return until it's sent all the |
// bytes, or it gets an error. |
if (bytesReceived > 0) { |
err = FSWrite(destFileRefNum, &bytesReceived, transferBuffer); |
} else { |
err = bytesReceived; |
} |
// We keep running this loop until we get an error. |
} while (err == noErr); |
} |
// Now we handle the various forms of error we can get. The |
// most common in kOTLookErr. This means that some event |
// has happened that we need to look at. We call OTLook |
// to get the event code and then handle the various types |
// of event appropriately. |
if (err == kOTLookErr) { |
lookResult = OTLook(ep); |
switch (lookResult) { |
case T_DISCONNECT: |
// If we get a T_DISCONNECT event, the remote peer |
// has disconnected the stream in a dis-orderly |
// fashion. HTTP servers will often just disconnect |
// a connection like this to indicate the end of the |
// data, so all we need do is clear the T_DISCONNECT |
// event on the endpoint. |
err = OTRcvDisconnect(ep, nil); |
break; |
case T_ORDREL: |
// If we get a T_ORDREL event, the remote peer |
// has disconnected the stream in an orderly |
// fashion. This orderly disconnect indicates that |
// the end of the data. We respond by clearing |
// the T_ORDREL, and then calling OTSndOrderlyDisconnect |
// to acknowledge the orderly disconnect at |
// the remote peer. |
err = OTRcvOrderlyDisconnect(ep); |
if (err == noErr) { |
err = OTSndOrderlyDisconnect(ep); |
} |
break; |
default: |
// Leave err as kOTLookErr. |
break; |
} |
} |
if ( (err == noErr) && bound ) { |
junk = OTUnbind(ep); |
MoreAssert(junk == noErr); //Assertion Fails if: OTUnbind failed |
} |
// Clean up. |
if (ep != kOTInvalidEndpointRef) { |
junk = OTCloseProvider(ep); |
MoreAssert(junk == noErr); //Assertion Fails if: OTCloseProvider failed |
} |
if (transferBuffer != nil) { |
OTFreeMem(transferBuffer); |
} |
return (err); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14