Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

727 lines
22 KiB

  1. /*++
  2. Copyright (c) 2002 - 2002 Microsoft Corporation. All Rights Reserved.
  3. THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF
  4. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  5. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  6. PARTICULAR PURPOSE.
  7. THIS CODE IS NOT SUPPORTED BY MICROSOFT.
  8. --*/
  9. #include "precomp.h"
  10. //
  11. // Macros.
  12. //
  13. #define INITIALIZE_HTTP_RESPONSE( resp, status, reason ) \
  14. do \
  15. { \
  16. RtlZeroMemory( (resp), sizeof(*(resp)) ); \
  17. (resp)->StatusCode = (status); \
  18. (resp)->pReason = (reason); \
  19. (resp)->ReasonLength = (USHORT) strlen(reason); \
  20. } while (FALSE)
  21. #define ADD_KNOWN_HEADER(Response, HeaderId, RawValue) \
  22. do \
  23. { \
  24. (Response).Headers.KnownHeaders[(HeaderId)].pRawValue = (RawValue); \
  25. (Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \
  26. (USHORT) strlen(RawValue); \
  27. } while(FALSE)
  28. #define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb))
  29. #define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
  30. //
  31. // Prototypes.
  32. //
  33. DWORD
  34. DoReceiveRequests(
  35. HANDLE hReqQueue
  36. );
  37. DWORD
  38. SendHttpResponse(
  39. IN HANDLE hReqQueue,
  40. IN PHTTP_REQUEST pRequest,
  41. IN USHORT StatusCode,
  42. IN PSTR pReason,
  43. IN PSTR pEntity
  44. );
  45. DWORD
  46. SendHttpPostResponse(
  47. IN HANDLE hReqQueue,
  48. IN PHTTP_REQUEST pRequest
  49. );
  50. /***************************************************************************++
  51. Routine Description:
  52. main routine.
  53. Arguments:
  54. argc - # of command line arguments.
  55. argv - Arguments.
  56. Return Value:
  57. Success/Failure.
  58. --***************************************************************************/
  59. int __cdecl wmain(
  60. int argc,
  61. wchar_t * argv[]
  62. )
  63. {
  64. ULONG retCode;
  65. int i;
  66. HANDLE hReqQueue = NULL;
  67. int UrlAdded = 0;
  68. HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
  69. if (argc < 2)
  70. {
  71. wprintf(L"%ws: <Url1> [Url2] ... \n", argv[0]);
  72. return -1;
  73. }
  74. //
  75. // Initialize HTTP APIs.
  76. //
  77. retCode = HttpInitialize(
  78. HttpApiVersion,
  79. HTTP_INITIALIZE_SERVER, // Flags
  80. NULL // Reserved
  81. );
  82. if (retCode != NO_ERROR)
  83. {
  84. wprintf(L"HttpInitialize failed with %lu \n", retCode);
  85. return retCode;
  86. }
  87. //
  88. // Create a Request Queue Handle
  89. //
  90. retCode = HttpCreateHttpHandle(
  91. &hReqQueue, // Req Queue
  92. 0 // Reserved
  93. );
  94. if (retCode != NO_ERROR)
  95. {
  96. wprintf(L"HttpCreateHttpHandle failed with %lu \n", retCode);
  97. goto CleanUp;
  98. }
  99. //
  100. // The command line arguments represent URIs that we want to listen on.
  101. // We will call HttpAddUrl for each of these URIs.
  102. //
  103. // The URI is a fully qualified URI and MUST include the terminating '/'
  104. //
  105. for (i = 1; i < argc; i++)
  106. {
  107. wprintf(
  108. L"we are listening for requests on the following url: %s\n",
  109. argv[i]);
  110. retCode = HttpAddUrl(
  111. hReqQueue, // Req Queue
  112. argv[i], // Fully qualified URL
  113. NULL // Reserved
  114. );
  115. if (retCode != NO_ERROR)
  116. {
  117. wprintf(L"HttpAddUrl failed with %lu \n", retCode);
  118. goto CleanUp;
  119. }
  120. else
  121. {
  122. //
  123. // Keep track of the URLs that we've currently added.
  124. //
  125. UrlAdded ++;
  126. }
  127. }
  128. // Loop while receiving requests
  129. for(;;)
  130. {
  131. retCode = DoReceiveRequests(hReqQueue);
  132. if(NO_ERROR == retCode)
  133. {
  134. wprintf(
  135. L"DoReceiveRequests failed with %lu \n",
  136. retCode
  137. );
  138. break;
  139. }
  140. } // CTRL C to break out of loop
  141. CleanUp:
  142. //
  143. // Call HttpRemoveUrl for all the URLs that we added.
  144. //
  145. for(i=1; i<=UrlAdded; i++)
  146. {
  147. HttpRemoveUrl(
  148. hReqQueue, // Req Queue
  149. argv[i] // Fully qualified URL
  150. );
  151. }
  152. //
  153. // Close the Request Queue handle.
  154. //
  155. if(hReqQueue)
  156. {
  157. CloseHandle(hReqQueue);
  158. }
  159. //
  160. // Call HttpTerminate.
  161. //
  162. HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
  163. return retCode;
  164. }
  165. /***************************************************************************++
  166. Routine Description:
  167. The routine to receive a request. This routine calls the corresponding
  168. routine to deal with the response.
  169. Arguments:
  170. hReqQueue - Handle to the request queue.
  171. Return Value:
  172. Success/Failure.
  173. --***************************************************************************/
  174. DWORD
  175. DoReceiveRequests(
  176. IN HANDLE hReqQueue
  177. )
  178. {
  179. ULONG result;
  180. HTTP_REQUEST_ID requestId;
  181. DWORD bytesRead;
  182. PHTTP_REQUEST pRequest;
  183. PCHAR pRequestBuffer;
  184. ULONG RequestBufferLength;
  185. //
  186. // Allocate a 2K buffer. Should be good for most requests, we'll grow
  187. // this if required. We also need space for a HTTP_REQUEST structure.
  188. //
  189. RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
  190. pRequestBuffer = ALLOC_MEM( RequestBufferLength );
  191. if (pRequestBuffer == NULL)
  192. {
  193. return ERROR_NOT_ENOUGH_MEMORY;
  194. }
  195. pRequest = (PHTTP_REQUEST)pRequestBuffer;
  196. //
  197. // Wait for a new request -- This is indicated by a NULL request ID.
  198. //
  199. HTTP_SET_NULL_ID( &requestId );
  200. for(;;)
  201. {
  202. RtlZeroMemory(pRequest, RequestBufferLength);
  203. result = HttpReceiveHttpRequest(
  204. hReqQueue, // Req Queue
  205. requestId, // Req ID
  206. 0, // Flags
  207. pRequest, // HTTP request buffer
  208. RequestBufferLength,// req buffer length
  209. &bytesRead, // bytes received
  210. NULL // LPOVERLAPPED
  211. );
  212. if(NO_ERROR == result)
  213. {
  214. //
  215. // Worked!
  216. //
  217. switch(pRequest->Verb)
  218. {
  219. case HttpVerbGET:
  220. wprintf(L"Got a GET request for %ws \n",
  221. pRequest->CookedUrl.pFullUrl);
  222. result = SendHttpResponse(
  223. hReqQueue,
  224. pRequest,
  225. 200,
  226. "OK",
  227. "Hey! You hit the server \r\n"
  228. );
  229. break;
  230. case HttpVerbPOST:
  231. wprintf(L"Got a POST request for %ws \n",
  232. pRequest->CookedUrl.pFullUrl);
  233. result = SendHttpPostResponse(hReqQueue, pRequest);
  234. break;
  235. default:
  236. wprintf(L"Got a unknown request for %ws \n",
  237. pRequest->CookedUrl.pFullUrl);
  238. result = SendHttpResponse(
  239. hReqQueue,
  240. pRequest,
  241. 503,
  242. "Not Implemented",
  243. NULL
  244. );
  245. break;
  246. }
  247. if(result != NO_ERROR)
  248. {
  249. break;
  250. }
  251. //
  252. // Reset the Request ID so that we pick up the next request.
  253. //
  254. HTTP_SET_NULL_ID( &requestId );
  255. }
  256. else if(result == ERROR_MORE_DATA)
  257. {
  258. //
  259. // The input buffer was too small to hold the request headers
  260. // We have to allocate more buffer & call the API again.
  261. //
  262. // When we call the API again, we want to pick up the request
  263. // that just failed. This is done by passing a RequestID.
  264. //
  265. // This RequestID is picked from the old buffer.
  266. //
  267. requestId = pRequest->RequestId;
  268. //
  269. // Free the old buffer and allocate a new one.
  270. //
  271. RequestBufferLength = bytesRead;
  272. FREE_MEM( pRequestBuffer );
  273. pRequestBuffer = ALLOC_MEM( RequestBufferLength );
  274. if (pRequestBuffer == NULL)
  275. {
  276. result = ERROR_NOT_ENOUGH_MEMORY;
  277. break;
  278. }
  279. pRequest = (PHTTP_REQUEST)pRequestBuffer;
  280. }
  281. else if(ERROR_CONNECTION_INVALID == result &&
  282. !HTTP_IS_NULL_ID(&requestId))
  283. {
  284. // The TCP connection got torn down by the peer when we were
  285. // trying to pick up a request with more buffer. We'll just move
  286. // onto the next request.
  287. HTTP_SET_NULL_ID( &requestId );
  288. }
  289. else
  290. {
  291. break;
  292. }
  293. } // for(;;)
  294. if(pRequestBuffer)
  295. {
  296. FREE_MEM( pRequestBuffer );
  297. }
  298. return result;
  299. }
  300. /***************************************************************************++
  301. Routine Description:
  302. The routine sends a HTTP response.
  303. Arguments:
  304. hReqQueue - Handle to the request queue.
  305. pRequest - The parsed HTTP request.
  306. StatusCode - Response Status Code.
  307. pReason - Response reason phrase.
  308. pEntityString - Response entity body.
  309. Return Value:
  310. Success/Failure.
  311. --***************************************************************************/
  312. DWORD
  313. SendHttpResponse(
  314. IN HANDLE hReqQueue,
  315. IN PHTTP_REQUEST pRequest,
  316. IN USHORT StatusCode,
  317. IN PSTR pReason,
  318. IN PSTR pEntityString
  319. )
  320. {
  321. HTTP_RESPONSE response;
  322. HTTP_DATA_CHUNK dataChunk;
  323. DWORD result;
  324. DWORD bytesSent;
  325. //
  326. // Initialize the HTTP response structure.
  327. //
  328. INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason);
  329. //
  330. // Add a known header.
  331. //
  332. ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html");
  333. if(pEntityString)
  334. {
  335. //
  336. // Add an entity chunk
  337. //
  338. dataChunk.DataChunkType = HttpDataChunkFromMemory;
  339. dataChunk.FromMemory.pBuffer = pEntityString;
  340. dataChunk.FromMemory.BufferLength = (ULONG) strlen(pEntityString);
  341. response.EntityChunkCount = 1;
  342. response.pEntityChunks = &dataChunk;
  343. }
  344. //
  345. // Since we are sending all the entity body in one call, we don't have
  346. // to specify the Content-Length.
  347. //
  348. result = HttpSendHttpResponse(
  349. hReqQueue, // ReqQueueHandle
  350. pRequest->RequestId, // Request ID
  351. 0, // Flags
  352. &response, // HTTP response
  353. NULL, // pReserved1
  354. &bytesSent, // bytes sent (OPTIONAL)
  355. NULL, // pReserved2 (must be NULL)
  356. 0, // Reserved3 (must be 0)
  357. NULL, // LPOVERLAPPED (OPTIONAL)
  358. NULL // pReserved4 (must be NULL)
  359. );
  360. if(result != NO_ERROR)
  361. {
  362. wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
  363. }
  364. return result;
  365. }
  366. /***************************************************************************++
  367. Routine Description:
  368. The routine sends a HTTP response after reading the entity body.
  369. Arguments:
  370. hReqQueue - Handle to the request queue.
  371. pRequest - The parsed HTTP request.
  372. Return Value:
  373. Success/Failure.
  374. --***************************************************************************/
  375. DWORD
  376. SendHttpPostResponse(
  377. IN HANDLE hReqQueue,
  378. IN PHTTP_REQUEST pRequest
  379. )
  380. {
  381. HTTP_RESPONSE response;
  382. DWORD result;
  383. DWORD bytesSent;
  384. PUCHAR pEntityBuffer;
  385. ULONG EntityBufferLength;
  386. ULONG BytesRead;
  387. ULONG TempFileBytesWritten;
  388. HANDLE hTempFile;
  389. TCHAR szTempName[MAX_PATH + 1];
  390. #define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
  391. CHAR szContentLength[MAX_ULONG_STR];
  392. HTTP_DATA_CHUNK dataChunk;
  393. ULONG TotalBytesRead = 0;
  394. BytesRead = 0;
  395. hTempFile = INVALID_HANDLE_VALUE;
  396. //
  397. // Allocate some space for an entity buffer. We'll grow this on demand.
  398. //
  399. EntityBufferLength = 2048;
  400. pEntityBuffer = ALLOC_MEM( EntityBufferLength );
  401. if (pEntityBuffer == NULL)
  402. {
  403. result = ERROR_NOT_ENOUGH_MEMORY;
  404. wprintf(L"Insufficient resources \n");
  405. goto Done;
  406. }
  407. //
  408. // Initialize the HTTP response structure.
  409. //
  410. INITIALIZE_HTTP_RESPONSE(&response, 200, "OK");
  411. //
  412. // For POST, we'll echo back the entity that we got from the client.
  413. //
  414. // NOTE: If we had passed the HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
  415. // flag with HttpReceiveHttpRequest(), the entity would have
  416. // been a part of HTTP_REQUEST (using the pEntityChunks field).
  417. // Since we have not passed that flag, we can be assured that
  418. // there are no entity bodies in HTTP_REQUEST.
  419. //
  420. if(pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
  421. {
  422. // The entity body is send over multiple calls. Let's collect all
  423. // of these in a file & send it back. We'll create a temp file
  424. //
  425. if(GetTempFileName(
  426. L".",
  427. L"New",
  428. 0,
  429. szTempName
  430. ) == 0)
  431. {
  432. result = GetLastError();
  433. wprintf(L"GetTempFileName failed with %lu \n", result);
  434. goto Done;
  435. }
  436. hTempFile = CreateFile(
  437. szTempName,
  438. GENERIC_READ | GENERIC_WRITE,
  439. 0, // don't share.
  440. NULL, // no security descriptor
  441. CREATE_ALWAYS, // overrwrite existing
  442. FILE_ATTRIBUTE_NORMAL, // normal file.
  443. NULL
  444. );
  445. if(hTempFile == INVALID_HANDLE_VALUE)
  446. {
  447. result = GetLastError();
  448. wprintf(L"Could not create temporary file. Error %lu \n", result);
  449. goto Done;
  450. }
  451. do
  452. {
  453. //
  454. // Read the entity chunk from the request.
  455. //
  456. BytesRead = 0;
  457. result = HttpReceiveRequestEntityBody(
  458. hReqQueue,
  459. pRequest->RequestId,
  460. 0,
  461. pEntityBuffer,
  462. EntityBufferLength,
  463. &BytesRead,
  464. NULL
  465. );
  466. switch(result)
  467. {
  468. case NO_ERROR:
  469. if(BytesRead != 0)
  470. {
  471. TotalBytesRead += BytesRead;
  472. WriteFile(
  473. hTempFile,
  474. pEntityBuffer,
  475. BytesRead,
  476. &TempFileBytesWritten,
  477. NULL
  478. );
  479. }
  480. break;
  481. case ERROR_HANDLE_EOF:
  482. //
  483. // We have read the last request entity body. We can send
  484. // back a response.
  485. //
  486. // To illustrate entity sends via
  487. // HttpSendResponseEntityBody, we will send the response
  488. // over multiple calls. This is achieved by passing the
  489. // HTTP_SEND_RESPONSE_FLAG_MORE_DATA flag.
  490. if(BytesRead != 0)
  491. {
  492. TotalBytesRead += BytesRead;
  493. WriteFile(
  494. hTempFile,
  495. pEntityBuffer,
  496. BytesRead,
  497. &TempFileBytesWritten,
  498. NULL
  499. );
  500. }
  501. //
  502. // Since we are sending the response over multiple API
  503. // calls, we have to add a content-length.
  504. //
  505. // Alternatively, we could have sent using chunked transfer
  506. // encoding, by passing "Transfer-Encoding: Chunked".
  507. //
  508. // NOTE: Since we are accumulating the TotalBytesRead in
  509. // a ULONG, this will not work for entity bodies that
  510. // are larger than 4 GB. For supporting large entity
  511. // bodies, we would have to use a ULONGLONG.
  512. //
  513. sprintf(szContentLength, "%lu", TotalBytesRead);
  514. ADD_KNOWN_HEADER(
  515. response,
  516. HttpHeaderContentLength,
  517. szContentLength
  518. );
  519. result =
  520. HttpSendHttpResponse(
  521. hReqQueue, // ReqQueueHandle
  522. pRequest->RequestId, // Request ID
  523. HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
  524. &response, // HTTP response
  525. NULL, // pReserved1
  526. &bytesSent, // bytes sent (optional)
  527. NULL, // pReserved2
  528. 0, // Reserved3
  529. NULL, // LPOVERLAPPED
  530. NULL // pReserved4
  531. );
  532. if(result != NO_ERROR)
  533. {
  534. wprintf(L"HttpSendHttpResponse failed with %lu \n",
  535. result);
  536. goto Done;
  537. }
  538. //
  539. // Send entity body from a file handle.
  540. //
  541. dataChunk.DataChunkType =
  542. HttpDataChunkFromFileHandle;
  543. dataChunk.FromFileHandle.
  544. ByteRange.StartingOffset.QuadPart = 0;
  545. dataChunk.FromFileHandle.
  546. ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF;
  547. dataChunk.FromFileHandle.FileHandle = hTempFile;
  548. result = HttpSendResponseEntityBody(
  549. hReqQueue,
  550. pRequest->RequestId,
  551. 0, // This is the last send.
  552. 1, // Entity Chunk Count.
  553. &dataChunk,
  554. NULL,
  555. NULL,
  556. 0,
  557. NULL,
  558. NULL
  559. );
  560. if(result != NO_ERROR)
  561. {
  562. wprintf(
  563. L"HttpSendResponseEntityBody failed with %lu \n",
  564. result
  565. );
  566. }
  567. goto Done;
  568. break;
  569. default:
  570. wprintf(L"HttpReceiveRequestEntityBody failed with %lu \n",
  571. result);
  572. goto Done;
  573. }
  574. } while(TRUE);
  575. }
  576. else
  577. {
  578. // This request does not have any entity body.
  579. //
  580. result = HttpSendHttpResponse(
  581. hReqQueue, // ReqQueueHandle
  582. pRequest->RequestId, // Request ID
  583. 0,
  584. &response, // HTTP response
  585. NULL, // pReserved1
  586. &bytesSent, // bytes sent (optional)
  587. NULL, // pReserved2
  588. 0, // Reserved3
  589. NULL, // LPOVERLAPPED
  590. NULL // pReserved4
  591. );
  592. if(result != NO_ERROR)
  593. {
  594. wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
  595. }
  596. }
  597. Done:
  598. if(pEntityBuffer)
  599. {
  600. FREE_MEM(pEntityBuffer);
  601. }
  602. if(INVALID_HANDLE_VALUE != hTempFile)
  603. {
  604. CloseHandle(hTempFile);
  605. DeleteFile(szTempName);
  606. }
  607. return result;
  608. }