home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1999 / MacHack 1999.toast / The Hacks / LiveFastStartServer / LiveFastStartServerTest.c < prev    next >
Encoding:
Text File  |  1999-06-26  |  10.9 KB  |  375 lines  |  [TEXT/CWIE]

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