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.
OTSimpleServerHTTPTest.c
/* |
File: OTSimpleServerHTTPTest.c |
Contains: A test program for the simple HTTP server code. |
Written by: Quinn "The Eskimo!" |
Copyright: © Copyright 2001 Apple Computer, Inc. All rights reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
2/12/2001 MK Carbonized and updated to CodeWarrior 6.1 |
7/23/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
///////////////////////////////////////////////////////////////////// |
// Carbon.h |
#include <Carbon.h> |
///////////////////////////////////////////////////////////////////// |
// Standard C prototypes. |
#include <stdio.h> |
#include <stdlib.h> |
///////////////////////////////////////////////////////////////////// |
// Prototypes for the actual core HTTP server code. |
#include "OTSimpleServerHTTP.h" |
///////////////////////////////////////////////////////////////////// |
// Pick up SIOUXHandleOneEvent. |
#ifndef __APPLE_CC__ |
#include <SIOUX.h> |
#endif |
///////////////////////////////////////////////////////////////////// |
// The only way to tell whether OT supports IP single link |
// multihoming is to check the version number. The feature was |
// added in OT 1.3. The initialisation code sets |
// gHaveIPSingleLinkMultihoming depending on the version number |
// to avoid the rest of the code having to call Gestalt repeatedly. |
enum |
{ |
kOTIPSingleLinkMultihomingVersion = 0x01300000 |
}; |
static Boolean gHaveIPSingleLinkMultihoming; |
///////////////////////////////////////////////////////////////////// |
// gLastCallWNE is used to throttle calls to wait next event so that |
// we don't call it too often, which can be bad for system performance. |
static UInt32 gLastCallWNE = 0; |
// gRunningThreads contains the number of running HTTP listeners. |
// We spool an HTTP listener for each IP address on the computer. |
// Normally you would only spool one listener for the entire machine |
// (listening on kOTAnyInetAddress), but we want to actively distinguish |
// between each IP address so that we can server different information |
// for each IP address. |
static UInt32 gRunningThreads = 0; |
///////////////////////////////////////////////////////////////////// |
static OSErr FSpGetCatInfo(FSSpecPtr fss, short ioFDirIndex, CInfoPBPtr cpb) |
// A simple wrapper for GetCatInfo. |
{ |
cpb->hFileInfo.ioVRefNum = fss->vRefNum; |
cpb->hFileInfo.ioDirID = fss->parID; |
cpb->hFileInfo.ioNamePtr = fss->name; |
cpb->hFileInfo.ioFDirIndex = ioFDirIndex; |
return ( PBGetCatInfoSync(cpb) ); |
} |
///////////////////////////////////////////////////////////////////// |
ThreadEntryUPP gHTTPServerProcUPP; |
static pascal OSStatus HTTPServerProc(InetHost ipAddr) |
// This routine is the main line of the thread that runs |
// an HTTP server. ipAddr is the address on which the |
// server is listening. Specify kOTAnyInetAddress to listen |
// on all active IP addresses simultaneously. |
// |
// The routine uses a directory whose name is the |
// dotted decimal string representation of ipAddr as the |
// root directory of the HTTP server. |
{ |
OSStatus err; |
Str255 ipAddrString; |
long rootVRefNum; |
long rootDirID; |
FSSpec dirSpec; |
CInfoPBRec cpb; |
ProcessInfoRec processInfo; |
ProcessSerialNumber PSN; |
Str255 infoName; |
// Get the IP address of this machine as a C string. |
OTInetHostToString(ipAddr, (char *)ipAddrString); |
CopyCStringToPascal(ipAddrString, ipAddrString); |
err = GetCurrentProcess(&PSN); |
processInfo.processAppSpec = &dirSpec; |
processInfo.processName = infoName; |
processInfo.processInfoLength = sizeof(processInfo); |
err = GetProcessInformation(&PSN, &processInfo); |
// Find the associated dirID, creatintg the directory |
// if necessary. |
(void) FSMakeFSSpec(dirSpec.vRefNum, dirSpec.parID, ipAddrString, &dirSpec); |
rootVRefNum = dirSpec.vRefNum; |
err = FSpGetCatInfo(&dirSpec, 0, &cpb); |
if (err == noErr && ( (cpb.hFileInfo.ioFlAttrib & (1 << 4)) != 0) ) { |
rootDirID = cpb.hFileInfo.ioDirID; |
} else { |
err = FSpDirCreate(&dirSpec, 0, &rootDirID); |
} |
// Start running an HTTP server on the IP address. This |
// routine won't return under someone sets gQuitNow, which |
// is why we're calling it from a thread. |
if (err == noErr) { |
err = RunHTTPServer(ipAddr, rootVRefNum, rootDirID); |
} |
gRunningThreads -= 1; |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
static OSStatus RunOneServer(InetHost ipAddr) |
// Runs a single HTTP server thread, serving the |
// given ipAddr. |
{ |
OSStatus err; |
ThreadID junkServerThread; |
if (gHTTPServerProcUPP == NULL) |
{ |
gHTTPServerProcUPP = NewThreadEntryUPP((ThreadEntryProcPtr) HTTPServerProc); |
} |
err = NewThread(kCooperativeThread, |
gHTTPServerProcUPP, (void *) ipAddr, |
0, kCreateIfNeeded, |
nil, |
&junkServerThread); |
if (err == noErr) { |
gRunningThreads += 1; |
} |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
static OSStatus RunServersForInterface(InetInterfaceInfo* interfaceInfo, SInt32 interfaceIndex) |
// Run HTTP servers for all of the IP addresses associated |
// with the interface denoted by interfaceInfo and interfaceIndex. |
// This routine first starts a server for the primary address |
// of the interface, then iterates through the secondary addresses on |
// the interface, starting a server thread for each one. |
{ |
OSStatus err; |
InetHost *secondaryAddressBuffer; |
UInt32 numberOfSecondaryAddresses; |
UInt32 addressIndex; |
secondaryAddressBuffer = nil; |
// First run the server for the interfaces primary address. |
err = RunOneServer(interfaceInfo->fAddress); |
// Now start a server for each of the interface's secondary |
// addresses. This stuff can only be done on systems that |
// support IP single link multihoming. |
numberOfSecondaryAddresses = interfaceInfo->fIPSecondaryCount; |
if ( err == noErr && gHaveIPSingleLinkMultihoming && numberOfSecondaryAddresses > 0 ) { |
// Allocate a buffer for the secondary address info. |
secondaryAddressBuffer = (InetHost *) OTAllocMemInContext( numberOfSecondaryAddresses * sizeof(InetHost), NULL ); |
if (secondaryAddressBuffer == nil) { |
err = kENOMEMErr; |
} |
// Ask OT for the list of secondary addresses on this interface. |
if (err == noErr) { |
err = OTInetGetSecondaryAddresses(secondaryAddressBuffer, &numberOfSecondaryAddresses, interfaceIndex); |
} |
// Start a server for each secondary address. |
addressIndex = 0; |
while (err == noErr && addressIndex < numberOfSecondaryAddresses) { |
err = RunOneServer(secondaryAddressBuffer[addressIndex]); |
if (err == noErr) { |
addressIndex += 1; |
} |
} |
} |
// Clean up. |
if (secondaryAddressBuffer != nil) { |
OTFreeMem(secondaryAddressBuffer); |
} |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
static OSStatus RunAllHTTPServers(void) |
// Run HTTP servers for all of the IP addresses on the machine. |
// This routine iterates through the active Internet interfaces, |
// starting server threads for each active IP address on each |
// interface. |
{ |
OSStatus err; |
OSStatus junk; |
EndpointRef dummyEP; |
InetInterfaceInfo info; |
SInt32 interfaceIndex; |
Boolean done; |
TEndpointInfo epInfo; |
// Force TCP to load by creating a dummy endpoint. Otherwise, |
// if we're the first TCP application to run, OTInetGetInterfaceInfo |
// will not return any active interfaces )-: |
dummyEP = OTOpenEndpointInContext(OTCreateConfiguration("tcp"), 0, &epInfo, &err, NULL); |
if (err == noErr) { |
// Iterate through the interfaces, starting HTTP servers on each. |
done = false; |
interfaceIndex = 0; |
do { |
done = ( OTInetGetInterfaceInfo(&info, interfaceIndex) != noErr ); |
if ( ! done ) { |
err = RunServersForInterface(&info, interfaceIndex); |
interfaceIndex += 1; |
} |
} while (err == noErr && !done); |
} |
if (dummyEP != nil) { |
junk = OTCloseProvider(dummyEP); |
MoreAssertQ(junk == noErr); |
} |
return (err); |
} |
///////////////////////////////////////////////////////////////////// |
int main(void) |
// The main line of the application. This routine performs |
// two functions. At startup, it initialises the network and |
// starts HTTP servers on all the active IP addresses on the machine. |
// After that it goes into a loop calling WaitNextEvent and |
// waiting for all the listener threads to terminate. Events |
// are handled by sending them to SIOUX, except when the user |
// presses 'q' the routine sets the gQuitNow boolean to force |
// all the server threads to terminate. |
{ |
OSStatus err; |
OSStatus junk; |
EventRecord event; |
NumVersionVariant otVersion; |
printf("OTSimpleServerHTTP!\n"); |
printf("-- World's dumbest HTTP server.\n"); |
printf("-- Press 'q' to stop the server.\n"); |
printf("\n"); |
gQuitNow = false; |
err = InitOpenTransportInContext(kInitOTForApplicationMask, NULL); |
if (err == noErr) { |
gHaveIPSingleLinkMultihoming = ( Gestalt(gestaltOpenTptVersions, (long *) &otVersion) == noErr |
&& (otVersion.whole >= kOTIPSingleLinkMultihomingVersion ) ); |
gRunningThreads = 0; |
err = RunAllHTTPServers(); |
while ( gRunningThreads != 0 ) { |
if ( TickCount() > (gLastCallWNE + 3) ) { |
(void) WaitNextEvent(everyEvent, &event, 0, nil); |
if (event.what == keyDown) { |
if ( (event.message & charCodeMask) == 'q' ) { |
gQuitNow = true; |
printf("Setting gQuitNow.\n"); |
fflush(stdout); |
} |
} |
#ifndef __APPLE_CC__ |
(void) SIOUXHandleOneEvent(&event); |
#endif |
gLastCallWNE = TickCount(); |
} |
junk = YieldToAnyThread(); |
MoreAssertQ(junk == noErr); |
} |
CloseOpenTransportInContext(NULL); |
} |
if (err == noErr) { |
printf("Success.\n"); |
} else { |
printf("Failed with error %d.\n", (int)err); |
} |
printf("Done. Press command-Q to Quit.\n"); |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14