home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / software / pelne / optionp / iis4_07.cab / ctetest.c < prev    next >
C/C++ Source or Header  |  1997-10-25  |  15KB  |  719 lines

  1. /*++
  2.  
  3. Copyright (c) 1997  Microsoft Corporation
  4.  
  5. Module Name:    ctetest.c
  6.  
  7. Abstract:
  8.  
  9.     ISAPI Extension sample illustrating Chunked Transfer Encoding. 
  10.  
  11. --*/
  12.  
  13. #include "ctetest.h"
  14.  
  15. //
  16. // if chunksize= is not specified, use this value
  17. //
  18.  
  19. #define DEFAULT_CHUNK_SIZE 1024
  20.  
  21.  
  22. //
  23. // auxiliary functions prototypes
  24. //
  25.  
  26. static BOOL SendChunkedFile( EXTENSION_CONTROL_BLOCK *, DWORD, LPCSTR );
  27. static BOOL SendHttpHeaders( EXTENSION_CONTROL_BLOCK *, LPCSTR, LPCSTR, BOOL );
  28. static BOOL GetFileMimeType( LPCSTR, LPSTR, DWORD ); 
  29. static BOOL GetQueryStringField( LPCSTR, LPCSTR, LPSTR, DWORD );
  30. static void DisplayExampleUsage( EXTENSION_CONTROL_BLOCK * );
  31.  
  32.  
  33.  
  34. DWORD WINAPI
  35. HttpExtensionProc(
  36.     IN EXTENSION_CONTROL_BLOCK *pECB
  37. )
  38. /*++
  39.  
  40. Purpose:
  41.  
  42.     Illustrate chunk transfer encoding in ISAPI HTTP Extension DLL.
  43.     Process "GET" requests that specify a filename and transfer 
  44.     chunk size.
  45.  
  46. Arguments:
  47.  
  48.     pECB - pointer to the extenstion control block 
  49.  
  50. Returns:
  51.  
  52.     HSE_STATUS_SUCCESS on successful transmission completion
  53.     HSE_STATUS_ERROR on failure
  54.  
  55. --*/
  56. {
  57.     DWORD dwChunksize = 0;
  58.     char szPath[MAX_PATH];
  59.     char szHeaders[1024];
  60.     DWORD cchPath, cchHeaders;
  61.     char szChunkSize[32];
  62.  
  63.     //
  64.     // if request method is not "GET", bail out
  65.     //
  66.     
  67.     if( _stricmp( pECB->lpszMethod, "GET" ) != 0 ) {
  68.         return HSE_STATUS_ERROR;
  69.     }        
  70.     
  71.     //
  72.     // process chunksize= query argument, if any
  73.     //
  74.     
  75.     if( GetQueryStringField( pECB->lpszQueryString, "chunksize", 
  76.             szChunkSize, sizeof szChunkSize )) {
  77.         dwChunksize = atoi( szChunkSize );
  78.     }
  79.     
  80.     if( dwChunksize == 0 ) {
  81.         dwChunksize = DEFAULT_CHUNK_SIZE;
  82.     }        
  83.  
  84.     //
  85.     // process file= query argument
  86.     //
  87.  
  88.     cchPath = sizeof szPath;
  89.     if( !GetQueryStringField( 
  90.             pECB->lpszQueryString, 
  91.             "file", 
  92.             szPath, 
  93.             cchPath )) {
  94.  
  95.         // 
  96.         // no file specified - display usage and report success
  97.         //
  98.         
  99.         DisplayExampleUsage( pECB );
  100.  
  101.         return HSE_STATUS_SUCCESS;
  102.     }
  103.  
  104.     //
  105.     // use ServerSupportFunction to map virtual file name to local
  106.     // path (otherwise users get access to any file on the system)
  107.     //
  108.  
  109.     if( !pECB->ServerSupportFunction(
  110.              pECB->ConnID,
  111.              HSE_REQ_MAP_URL_TO_PATH,
  112.              szPath,
  113.              &cchPath,
  114.              NULL ) ) {
  115.  
  116.         return HSE_STATUS_ERROR;
  117.     }
  118.  
  119.     //
  120.     // see if we can get file attributes, report error if not
  121.     //
  122.  
  123.     if( GetFileAttributes( szPath ) == 0xFFFFFFFF ) {
  124.  
  125.         return HSE_STATUS_ERROR;
  126.     }
  127.  
  128.     //
  129.     // begin preparing the headers
  130.     //
  131.     
  132.     strcpy( szHeaders, "Transfer-encoding: chunked\r\nContent-type: " );
  133.  
  134.     //
  135.     // obtain MIME type for this file and append it to the headers
  136.     //
  137.  
  138.     cchHeaders = strlen( szHeaders );
  139.     GetFileMimeType( 
  140.         szPath, 
  141.         szHeaders + cchHeaders, 
  142.         sizeof szHeaders - cchHeaders 
  143.         );
  144.  
  145.     //
  146.     // terminate headers with empty line
  147.     //
  148.  
  149.     strcat( szHeaders, "\r\n\r\n" );
  150.  
  151.     //
  152.     // try sending headers to the client
  153.     //
  154.  
  155.     if( !SendHttpHeaders( pECB, "200 OK", szHeaders, TRUE ) ) {
  156.  
  157.         return HSE_STATUS_ERROR;
  158.     }
  159.  
  160.     //
  161.     // try sending the file using CTE encoding
  162.     //
  163.  
  164.     if( !SendChunkedFile( pECB, dwChunksize, szPath ) ) {
  165.  
  166.         return HSE_STATUS_ERROR;
  167.     }    
  168.     
  169.     return HSE_STATUS_SUCCESS;
  170. }
  171.  
  172.  
  173. BOOL WINAPI
  174. GetExtensionVersion(
  175.     OUT HSE_VERSION_INFO *pVer
  176. )
  177. /*++
  178.  
  179. Purpose:
  180.  
  181.     This is required ISAPI Extension DLL entry point.
  182.  
  183. Arguments:
  184.  
  185.     pVer - poins to extension version info structure 
  186.  
  187. Returns:
  188.  
  189.     always returns TRUE
  190.  
  191. --*/
  192. {
  193.  
  194.     //
  195.     // tell the server our version number and extension description
  196.     //
  197.  
  198.     pVer->dwExtensionVersion =
  199.         MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );
  200.  
  201.     lstrcpyn(
  202.         pVer->lpszExtensionDesc, 
  203.         "ISAPI CTE test",
  204.         HSE_MAX_EXT_DLL_NAME_LEN);
  205.  
  206.     return TRUE;
  207. }
  208.  
  209.  
  210. BOOL WINAPI
  211. TerminateExtension(
  212.     DWORD dwFlags
  213. )
  214. /*++
  215.  
  216. Purpose:
  217.  
  218.     This is optional ISAPI extension DLL entry point.
  219.     If present, it will be called before unloading the DLL,
  220.     giving it a chance to perform any shutdown procedures.
  221.     
  222. Arguments:
  223.     
  224.     dwFlags - specifies whether the DLL can refuse to unload or not
  225.     
  226. Returns:
  227.     
  228.     TRUE, if the DLL can be unloaded
  229.     
  230. --*/
  231. {
  232.     return TRUE;
  233. }
  234.  
  235.  
  236.  
  237. BOOL WINAPI
  238. DllMain (
  239.     IN HINSTANCE hInstance,
  240.     IN DWORD fdwReason,
  241.     IN LPVOID lpvReserved    
  242. )
  243. /*++
  244.  
  245. Purpose:
  246.  
  247.     Perform any required DLL initialization here.
  248.  
  249. Returns:
  250.  
  251.     TRUE if DLL was successfully initialized
  252.     FALSE otherwise
  253.  
  254. --*/
  255. {
  256.  
  257.     //
  258.     // Nothing needs to be done. This function exists a template.
  259.     //
  260.  
  261.     return TRUE;
  262. }
  263.  
  264.  
  265. static BOOL 
  266. SendChunkedFile( 
  267.     EXTENSION_CONTROL_BLOCK *pECB, 
  268.     DWORD dwChunkSize,
  269.     LPCSTR pszPath
  270. )
  271. /*++
  272.  
  273. Purpose:
  274.  
  275.     Transfer the specified file using chunked encoding.
  276.  
  277.     Illustrates the usage of CteBeginWrite(), CteWrite() and
  278.     CteEndWrite() functions.
  279.  
  280.  
  281. Arguments:
  282.  
  283.     pECB - pointer to extenstion control block 
  284.     dwChunkSize - chunk size for transfer encoding
  285.     pszPath - local file path
  286.  
  287. Returns:
  288.  
  289.     TRUE if the file was successfully transfered,
  290.     FALSE otherwise
  291.     
  292. --*/
  293. {
  294.     HANDLE hFile;
  295.     HCTE_ENCODER hCteWrite;
  296.     BOOL fSuccess = FALSE;
  297.  
  298.     //
  299.     // try accessing file
  300.     //
  301.  
  302.     hFile = CreateFile(
  303.                 pszPath, 
  304.                 GENERIC_READ, 
  305.                 FILE_SHARE_READ,
  306.                 NULL,
  307.                 OPEN_EXISTING,
  308.                 FILE_FLAG_SEQUENTIAL_SCAN,
  309.                 NULL);
  310.  
  311.     if( hFile != INVALID_HANDLE_VALUE ) {
  312.         BYTE buf[4096];
  313.         DWORD cbread;
  314.  
  315.         //
  316.         // prepare chunk transfer encoder
  317.         //
  318.  
  319.         hCteWrite = CteBeginWrite( pECB, dwChunkSize );
  320.         if ( hCteWrite ) {
  321.         
  322.             for( ;; ) {
  323.  
  324.                 if( !ReadFile( hFile, buf, sizeof buf, &cbread, NULL ) ) {
  325.                     
  326.                     //
  327.                     // if ReadFile fails, break out of loop and cause 
  328.                     // the function to return FALSE (failure)
  329.                     //
  330.                     
  331.                     break;
  332.                 }
  333.             
  334.                 if( cbread == 0 ) {
  335.                 
  336.                     //
  337.                     // ReadFile succeded, but read 0 bytes -
  338.                     // we've achieved EOF and everything is transmitted.
  339.                     // break out and return success!
  340.                     //
  341.                     
  342.                     fSuccess = TRUE;
  343.                     break;
  344.                 }
  345.  
  346.                 //
  347.                 // transmit one buffer full of data,
  348.                 //
  349.                 
  350.                 if( !CteWrite( hCteWrite, buf, cbread ) ) {
  351.                 
  352.                     //
  353.                     // CteWrite failed - break out and return FALSE
  354.                     //
  355.                     
  356.                     break;
  357.                 }                
  358.             }
  359.  
  360.             //
  361.             // finish transfer and release encoder context
  362.             //
  363.             
  364.             if( !CteEndWrite( hCteWrite ) ) {
  365.             
  366.                 fSuccess = FALSE;
  367.             }
  368.         }
  369.  
  370.         CloseHandle( hFile );
  371.     }
  372.  
  373.     return fSuccess;
  374. }
  375.  
  376.  
  377.  
  378. static BOOL 
  379. SendHttpHeaders( 
  380.     EXTENSION_CONTROL_BLOCK *pECB, 
  381.     LPCSTR pszStatus,
  382.     LPCSTR pszHeaders,
  383.     BOOL fKeepConn
  384. )
  385. /*++
  386.  
  387. Purpose:
  388.     Send specified HTTP status string and any additional header strings
  389.     using new ServerSupportFunction() request HSE_SEND_HEADER_EX_INFO
  390.  
  391. Arguments:
  392.  
  393.     pECB - pointer to the extension control block
  394.     pszStatus - HTTP status string (e.g. "200 OK")
  395.     pszHeaders - any additional headers, separated by CRLFs and 
  396.                  terminated by empty line
  397.     fKeepConn - specifies whether to keep TCP connection open or close it
  398.                 after request is processed.
  399.  
  400. Returns:
  401.  
  402.     TRUE if headers were successfully sent
  403.     FALSE otherwise
  404.  
  405. --*/
  406. {
  407.     HSE_SEND_HEADER_EX_INFO header_ex_info;
  408.     BOOL success;
  409.  
  410.     header_ex_info.pszStatus = pszStatus;
  411.     header_ex_info.pszHeader = pszHeaders;
  412.     header_ex_info.cchStatus = strlen( pszStatus );
  413.     header_ex_info.cchHeader = strlen( pszHeaders );
  414.     header_ex_info.fKeepConn = fKeepConn;
  415.  
  416.  
  417.     success = pECB->ServerSupportFunction(
  418.                   pECB->ConnID,
  419.                   HSE_REQ_SEND_RESPONSE_HEADER_EX,
  420.                   &header_ex_info,
  421.                   NULL,
  422.                   NULL
  423.                   );
  424.  
  425.     return success;
  426. }
  427.  
  428.  
  429.  
  430. static BOOL 
  431. GetFileMimeType( 
  432.     LPCSTR pszPath, 
  433.     LPSTR pszType, 
  434.     DWORD cbMax 
  435. )
  436. /*++
  437.  
  438. Purpose:
  439.  
  440.     Given the file name, obtain MIME type for "Content-type:" header field.
  441.     We try to find MIME type string under HCR\.xyz key, "Content Type" value.
  442.     If that fails, we return default "application/ocetet-stream".
  443.  
  444. Arguments:
  445.     
  446.     pszPath - file path
  447.     pszType - points to the buffer that will receive MIME type string
  448.     cbMax - specifies the maximum number of characters to copy to the buffer,
  449.             including the NUL character. If the text exceed this limit, it
  450.             will be truncated.
  451.  
  452. Returns:
  453.  
  454.     TRUE, because we can always use default MIME type.
  455.   
  456. --*/
  457. {
  458.     LPSTR pszExt;
  459.     HKEY hKey;
  460.     DWORD value_type;
  461.     LONG result;
  462.  
  463.  
  464.     //
  465.     // set MIME type to empty string
  466.     //
  467.  
  468.     *pszType = '\0';
  469.  
  470.  
  471.     //
  472.     // try to locate file extension
  473.     //
  474.  
  475.     pszExt = strrchr( pszPath, '.' );
  476.     
  477.     if( pszExt != NULL ) {
  478.     
  479.         // 
  480.         // for file extension .xyz, MIME Type can be found
  481.         // HKEY_CLASSES_ROOT\.xyz key in the registry
  482.         //
  483.  
  484.         result = RegOpenKeyEx( 
  485.                      HKEY_CLASSES_ROOT, 
  486.                      pszExt, 
  487.                      0L, 
  488.                      KEY_READ, 
  489.                      &hKey 
  490.                      );
  491.                      
  492.         if( result == ERROR_SUCCESS) {
  493.         
  494.             //
  495.             // we sucessfully opened the key.
  496.             // try getting the "Content Type" value
  497.             //
  498.             
  499.             result = RegQueryValueEx( 
  500.                          hKey, 
  501.                          "Content Type", 
  502.                          NULL, 
  503.                          &value_type, 
  504.                          (BYTE *)pszType, 
  505.                          &cbMax );
  506.  
  507.             //
  508.             // if we failed to get the value or it is not string,
  509.             // clear content-type field
  510.             //
  511.             
  512.             if( result != ERROR_SUCCESS || value_type != REG_SZ ) {
  513.                 *pszType = '\0';
  514.             }
  515.             
  516.             RegCloseKey( hKey );
  517.         }
  518.     }
  519.     
  520.     //
  521.     // if at this point we don't have MIME type, use default
  522.     //
  523.     
  524.     if( *pszType == '\0' ) {
  525.         strncpy( pszType, "application/octet_stream", cbMax );
  526.     }
  527.  
  528.     return TRUE;
  529. }
  530.  
  531.  
  532.  
  533. static void
  534. DisplayExampleUsage(
  535.     EXTENSION_CONTROL_BLOCK *pECB
  536. )
  537. /*++
  538.  
  539. Purpose:
  540.     
  541.     Send short plaintext description of our usage to the user.
  542.  
  543. Arguments:
  544.     
  545.     pECB - pointer to the extension control block
  546.  
  547. --*/
  548. {
  549.     DWORD dwLength;
  550.     static char szUsage[] = 
  551.         "Example usage:\r\n"
  552.         "http://localhost/scripts/ctetest.dll"
  553.         "?file=/default.htm+chunksize=512\r\n";
  554.         
  555.     char szHeaders[1024];
  556.     
  557.  
  558.     //
  559.     // send simple headers and sample usage instruction
  560.     //
  561.     dwLength = sizeof szUsage - 1;
  562.     
  563.     sprintf( 
  564.         szHeaders, 
  565.         "Content-Length: %u\r\n"
  566.         "Content-Type: text/plain\r\n\r\n",
  567.         dwLength 
  568.         );
  569.     
  570.     if( SendHttpHeaders( pECB, "200 OK", szHeaders, FALSE ) ) {
  571.         pECB->WriteClient(
  572.             pECB->ConnID,
  573.             szUsage,
  574.             &dwLength,
  575.             HSE_IO_SYNC
  576.             );
  577.     }        
  578. }
  579.  
  580.  
  581.  
  582. BOOL 
  583. GetQueryStringField(
  584.     LPCSTR pszQueryString,
  585.     LPCSTR pszKey, 
  586.     LPSTR buf, 
  587.     DWORD cbuf
  588. )
  589. /*++
  590.  
  591. Purpose:
  592.     Assuming "key1=value1+key2=value2" syntax,
  593.     extract the value for specified key.
  594.     
  595.  
  596. Arguments:
  597.     pszQueryString - query string provided by ECB
  598.     pszKey - key name
  599.     buf - buffer for parameter value
  600.     cbuf - buffer size
  601.  
  602. Returns:
  603.  
  604.     TRUE if the value was successfully extracted
  605.     FALSE otherwise
  606.  
  607. --*/
  608. {
  609.     int len, keylen;
  610.     LPCSTR p = pszQueryString;
  611.  
  612.     //
  613.     // compute key and query lengths, bail out if either is missing
  614.     //
  615.     
  616.     keylen = strlen( pszKey );
  617.     len = strlen( p );
  618.  
  619.     if( keylen == 0 || len == 0 ) return FALSE;
  620.  
  621.     //
  622.     // process one "+" delimited section at a time  
  623.     //
  624.  
  625.     for( ;; ) {
  626.     
  627.         //
  628.         // skip any leading blanks, bail out if end of line found
  629.         //
  630.         
  631.         while( *p <= ' ' ) {
  632.  
  633.             if( *p == '\0' ) {
  634.                 return FALSE;
  635.             }
  636.             
  637.             p++;
  638.             len--;
  639.         }
  640.  
  641.         //
  642.         // if the key won't fit into the rest of the command line, bail out
  643.         //
  644.         
  645.         if( keylen + 1 > len ) {
  646.             return FALSE;
  647.         }
  648.             
  649.         //
  650.         // is this the key we are looking for?
  651.         //
  652.         
  653.         if( _memicmp( p, pszKey, keylen ) == 0 && p[keylen] == '=' ) {
  654.         
  655.             //
  656.             // found it - skip '=' and break out of the loop
  657.             //
  658.             
  659.             p += keylen + 1;
  660.             break;
  661.         }
  662.  
  663.         //    
  664.         // no match, try advancing to next '+' section
  665.         //
  666.         
  667.         while( *p != '+' ) {
  668.  
  669.             if( *p == '\0' ) {
  670.                 return FALSE;
  671.             }
  672.             
  673.             p++;
  674.             len--;
  675.         }
  676.         
  677.         //
  678.         // found '+', skip it
  679.         //
  680.         
  681.         p++;
  682.         len--;
  683.     }   
  684.  
  685.     //
  686.     // copy the value up to: the end of line, cbuf chars, or
  687.     // '+' separator, whichever comes first
  688.     //
  689.     
  690.     while( *p && *p != '+' ) {
  691.  
  692.         if( cbuf <= 1 ) {
  693.             break;
  694.         }
  695.         
  696.         *(buf++) = *(p++);
  697.         cbuf--;
  698.     }
  699.  
  700.     //
  701.     // zero-terminate the value, report success
  702.     //
  703.     
  704.     *buf = '\0';
  705.     return TRUE;
  706. }
  707.  
  708.  
  709.  
  710.  
  711.  
  712.  
  713.  
  714.  
  715.  
  716.  
  717.  
  718.  
  719.