home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / OTSimpleServerHTTP / OTSimpleServerHTTPTest.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  10.3 KB  |  355 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTSimpleServerHTTPTest.c
  3.  
  4.     Contains:    A test program for the simple HTTP server code.
  5.  
  6.     Written by: Quinn "The Eskimo!"    
  7.  
  8.     Copyright:    Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/23/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23. #include <Files.h>
  24. #include <TextUtils.h>
  25. #include <Gestalt.h>
  26. /////////////////////////////////////////////////////////////////////
  27. // The OT debugging macros in <OTDebug.h> require this variable to
  28. // be set.
  29.  
  30. #ifndef qDebug
  31. #define qDebug    1
  32. #endif
  33.  
  34. /////////////////////////////////////////////////////////////////////
  35. // Pick up all the standard OT stuff.
  36.  
  37. #include <OpenTransport.h>
  38.  
  39. /////////////////////////////////////////////////////////////////////
  40. // Pick up all the OT TCP/IP stuff.
  41.  
  42. #include <OpenTptInternet.h>
  43.  
  44. /////////////////////////////////////////////////////////////////////
  45. // Pick up the OTDebugBreak and OTAssert macros.
  46.  
  47. #include <OTDebug.h>
  48.  
  49. /////////////////////////////////////////////////////////////////////
  50. // Some common Mac OS prototypes.
  51.  
  52. #include <Threads.h>
  53.  
  54. /////////////////////////////////////////////////////////////////////
  55. // Pick up SIOUXHandleOneEvent.
  56.  
  57. #include <SIOUX.h>
  58.  
  59. /////////////////////////////////////////////////////////////////////
  60. // Standard C prototypes.
  61.  
  62. #include <stdio.h>
  63. #include <stdlib.h>
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66. // Prototypes for the actual core HTTP server code.
  67.  
  68. #include "OTSimpleServerHTTP.h"
  69.  
  70. /////////////////////////////////////////////////////////////////////
  71. // OTDebugStr is not defined in any OT header files, but it is
  72. // exported by the libraries, so we define the prototype here.
  73.  
  74. extern pascal void OTDebugStr(const char* str);
  75.  
  76. /////////////////////////////////////////////////////////////////////
  77. // The only way to tell whether OT supports IP single link
  78. // multihoming is to check the version number.  The feature was
  79. // added in OT 1.3.  The initialisation code sets 
  80. // gHaveIPSingleLinkMultihoming depending on the version number
  81. // to avoid the rest of the code having to call Gestalt repeatedly.
  82.  
  83. enum
  84. {
  85.     kOTIPSingleLinkMultihomingVersion = 0x01300000
  86. };
  87.  
  88. static Boolean gHaveIPSingleLinkMultihoming;
  89.  
  90. /////////////////////////////////////////////////////////////////////
  91.  
  92. // gLastCallWNE is used to throttle calls to wait next event so that
  93. // we don't call it too often, which can be bad for system performance.
  94.  
  95. static UInt32 gLastCallWNE = 0;
  96.  
  97. // gRunningThreads contains the number of running HTTP listeners.
  98. // We spool an HTTP listener for each IP address on the computer.
  99. // Normally you would only spool one listener for the entire machine
  100. // (listening on kOTAnyInetAddress), but we want to actively distinguish
  101. // between each IP address so that we can server different information
  102. // for each IP address.
  103.  
  104. static UInt32 gRunningThreads = 0;
  105.  
  106. /////////////////////////////////////////////////////////////////////
  107.  
  108. static OSErr FSpGetCatInfo(FSSpecPtr fss, short ioFDirIndex, CInfoPBPtr cpb)
  109.     // A simple wrapper for GetCatInfo.
  110. {
  111.     cpb->hFileInfo.ioVRefNum = fss->vRefNum;
  112.     cpb->hFileInfo.ioDirID = fss->parID;
  113.     cpb->hFileInfo.ioNamePtr = fss->name;
  114.     cpb->hFileInfo.ioFDirIndex = ioFDirIndex;
  115.     return ( PBGetCatInfoSync(cpb) );
  116. }
  117.  
  118. /////////////////////////////////////////////////////////////////////
  119.  
  120. static pascal OSStatus HTTPServerProc(InetHost ipAddr)
  121.     // This routine is the main line of the thread that runs
  122.     // an HTTP server.  ipAddr is the address on which the
  123.     // server is listening.  Specify kOTAnyInetAddress to listen
  124.     // on all active IP addresses simultaneously.
  125.     //
  126.     // The routine uses a directory whose name is the
  127.     // dotted decimal string representation of ipAddr as the
  128.     // root directory of the HTTP server.
  129. {
  130.     OSStatus err;
  131.     Str255 ipAddrString;
  132.     long rootVRefNum;
  133.     long rootDirID;
  134.     FSSpec dirSpec;
  135.     CInfoPBRec cpb;
  136.     
  137.     // Get ipAddr as a dotted decimal Pascal string.
  138.     OTInetHostToString(ipAddr, (char *) ipAddrString);
  139.     C2PStr( (char *) ipAddrString);
  140.     
  141.     // Find the associated dirID, creatintg the directory
  142.     // if necessary.
  143.     
  144.     (void) FSMakeFSSpec(0, 0, ipAddrString, &dirSpec);
  145.     rootVRefNum = dirSpec.vRefNum;
  146.     err = FSpGetCatInfo(&dirSpec, 0, &cpb);
  147.     if (err == noErr && ( (cpb.hFileInfo.ioFlAttrib & (1 << 4)) != 0) ) {
  148.         rootDirID = cpb.hFileInfo.ioDirID;
  149.     } else {
  150.         err = FSpDirCreate(&dirSpec, 0, &rootDirID);
  151.     }
  152.     
  153.     // Start running an HTTP server on the IP address.  This
  154.     // routine won't return under someone sets gQuitNow, which
  155.     // is why we're calling it from a thread.
  156.     
  157.     if (err == noErr) {
  158.         err = RunHTTPServer(ipAddr, rootVRefNum, rootDirID);
  159.     }
  160.     
  161.     gRunningThreads -= 1;
  162.     
  163.     return (err);
  164. }
  165.  
  166. /////////////////////////////////////////////////////////////////////
  167.  
  168. static OSStatus RunOneServer(InetHost ipAddr)
  169.     // Runs a single HTTP server thread, serving the
  170.     // given ipAddr.
  171. {
  172.     OSStatus err;
  173.     ThreadID junkServerThread;
  174.     
  175.     err = NewThread(kCooperativeThread,
  176.                         (ThreadEntryProcPtr) HTTPServerProc, (void *) ipAddr,
  177.                         0, kCreateIfNeeded,
  178.                         nil,
  179.                         &junkServerThread);
  180.     if (err == noErr) {
  181.         gRunningThreads += 1;
  182.     }
  183.     
  184.     return (err);
  185. }
  186.  
  187. /////////////////////////////////////////////////////////////////////
  188.  
  189. static OSStatus RunServersForInterface(InetInterfaceInfo* interfaceInfo, SInt32 interfaceIndex)
  190.     // Run HTTP servers for all of the IP addresses associated
  191.     // with the interface denoted by interfaceInfo and interfaceIndex.
  192.     // This routine first starts a server for the primary address
  193.     // of the interface, then iterates through the secondary addresses on
  194.     // the interface, starting a server thread for each one.
  195. {
  196.     OSStatus err;
  197.     InetHost *secondaryAddressBuffer;
  198.     UInt32   numberOfSecondaryAddresses;
  199.     UInt32   addressIndex;
  200.  
  201.     secondaryAddressBuffer = nil;
  202.     
  203.     // First run the server for the interfaces primary address.
  204.     
  205.     err = RunOneServer(interfaceInfo->fAddress);
  206.     
  207.     // Now start a server for each of the interface's secondary
  208.     // addresses.  This stuff can only be done on systems that
  209.     // support IP single link multihoming.
  210.  
  211.     numberOfSecondaryAddresses = interfaceInfo->fIPSecondaryCount;
  212.     
  213.     if ( err == noErr && gHaveIPSingleLinkMultihoming && numberOfSecondaryAddresses > 0 ) {
  214.  
  215.         // Allocate a buffer for the secondary address info.
  216.         
  217.         secondaryAddressBuffer = (InetHost *) OTAllocMem( numberOfSecondaryAddresses * sizeof(InetHost) );
  218.         if (secondaryAddressBuffer == nil) {
  219.             err = kENOMEMErr;
  220.         }
  221.         
  222.         // Ask OT for the list of secondary addresses on this interface.
  223.         
  224.         if (err == noErr) {
  225.             err = OTInetGetSecondaryAddresses(secondaryAddressBuffer, &numberOfSecondaryAddresses, interfaceIndex);
  226.         }
  227.         
  228.         // Start a server for each secondary address.
  229.         
  230.         addressIndex = 0;
  231.         while (err == noErr && addressIndex < numberOfSecondaryAddresses) {
  232.             err = RunOneServer(secondaryAddressBuffer[addressIndex]);
  233.             if (err == noErr) {
  234.                 addressIndex += 1;
  235.             }
  236.         }
  237.     }
  238.  
  239.     // Clean up.
  240.     
  241.     if (secondaryAddressBuffer != nil) {
  242.         OTFreeMem(secondaryAddressBuffer);
  243.     }
  244.  
  245.     return (err);
  246. }
  247.  
  248. /////////////////////////////////////////////////////////////////////
  249.  
  250. static OSStatus RunAllHTTPServers(void)
  251.     // Run HTTP servers for all of the IP addresses on the machine.
  252.     // This routine iterates through the active Internet interfaces, 
  253.     // starting server threads for each active IP address on each
  254.     // interface.
  255. {
  256.     OSStatus err;
  257.     OSStatus junk;
  258.     EndpointRef dummyEP;
  259.     InetInterfaceInfo info;
  260.     SInt32 interfaceIndex;
  261.     Boolean done;
  262.     TEndpointInfo epInfo;
  263.     
  264.     // Force TCP to load by creating a dummy endpoint.  Otherwise,
  265.     // if we're the first TCP application to run, OTInetGetInterfaceInfo
  266.     // will not return any active interfaces )-:
  267.     
  268.     dummyEP = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, &epInfo, &err);
  269.     if (err == noErr) {
  270.     
  271.         // Iterate through the interfaces, starting HTTP servers on each.
  272.         
  273.         done = false;
  274.         interfaceIndex = 0; 
  275.         do {
  276.             done = ( OTInetGetInterfaceInfo(&info, interfaceIndex) != noErr );
  277.             if ( ! done ) {
  278.                 err = RunServersForInterface(&info, interfaceIndex);
  279.                 interfaceIndex += 1;
  280.             }
  281.         } while (err == noErr && !done);
  282.     }
  283.     
  284.     if (dummyEP != nil) {
  285.         junk = OTCloseProvider(dummyEP);
  286.         OTAssert("RunAllHTTPServers: Failed closing dummy endpoint", junk == noErr);
  287.     }
  288.     
  289.     return (err);
  290. }
  291.  
  292.  
  293. /////////////////////////////////////////////////////////////////////
  294.  
  295. void main(void)
  296.     // The main line of the application.  This routine performs
  297.     // two functions.  At startup, it initialises the network and
  298.     // starts HTTP servers on all the active IP addresses on the machine.
  299.     // After that it goes into a loop calling WaitNextEvent and
  300.     // waiting for all the listener threads to terminate.  Events
  301.     // are handled by sending them to SIOUX, except when the user
  302.     // presses 'q' the routine sets the gQuitNow boolean to force
  303.     // all the server threads to terminate.
  304. {
  305.     OSStatus err;
  306.     OSStatus junk;
  307.     EventRecord event;
  308.     NumVersionVariant otVersion;
  309.     
  310.     printf("OTSimpleServerHTTP!\n");
  311.     printf("-- World's dumbest HTTP server.\n");
  312.     printf("-- Press 'q' to stop the server.\n");
  313.     printf("\n");
  314.  
  315.     gQuitNow = false;
  316.     
  317.     err = InitOpenTransport();
  318.     if (err == noErr) {
  319.  
  320.         gHaveIPSingleLinkMultihoming = ( Gestalt(gestaltOpenTptVersions, (long *) &otVersion) == noErr
  321.                                             && (otVersion.whole >= kOTIPSingleLinkMultihomingVersion ) );
  322.         gRunningThreads = 0;
  323.         
  324.         err = RunAllHTTPServers();
  325.         
  326.         while ( gRunningThreads != 0 ) {
  327.             if ( TickCount() > (gLastCallWNE + 3) ) {
  328.                 (void) WaitNextEvent(everyEvent, &event, 0, nil);
  329.                 if (event.what == keyDown) {
  330.                     if ( (event.message & charCodeMask) == 'q' ) {
  331.                         gQuitNow = true;
  332.                         printf("Setting gQuitNow.\n");
  333.                         fflush(stdout);
  334.                     }
  335.                 }
  336.                 (void) SIOUXHandleOneEvent(&event);
  337.                 gLastCallWNE = TickCount();
  338.             }
  339.             junk = YieldToAnyThread();
  340.             OTAssert("main: YieldToAnyThread failed", junk == noErr);
  341.         }
  342.         
  343.         CloseOpenTransport();
  344.     }
  345.     
  346.     if (err == noErr) {
  347.         printf("Success.\n");
  348.     } else {
  349.         printf("Failed with error %d.\n", err);
  350.     }    
  351.     printf("Done.  Press command-Q to Quit.\n");
  352. }
  353.  
  354.  
  355.