VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/http-server.cpp@ 87042

最後變更 在這個檔案從87042是 87042,由 vboxsync 提交於 4 年 前

Shared Clipboard/Transfers: More cleanup / documentation work. bugref:9874

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 46.7 KB
 
1/* $Id: http-server.cpp 87042 2020-12-04 12:10:36Z vboxsync $ */
2/** @file
3 * Simple HTTP server (RFC 7231) implementation.
4 *
5 * Known limitations so far:
6 * - Only HTTP 1.1.
7 * - Only supports GET + HEAD methods so far.
8 * - Only supports UTF-8 charset.
9 * - Only supports plain text and octet stream MIME types.
10 * - No content compression ("gzip", "x-gzip", ++).
11 * - No caching.
12 * - No redirections (via 302).
13 * - No encryption (TLS).
14 * - No IPv6 support.
15 * - No multi-threading.
16 *
17 * For WebDAV (optional via IPRT_HTTP_WITH_WEBDAV):
18 * - Only OPTIONS + PROPLIST methods are implemented (e.g. simple read-only support).
19 * - No pagination support for directory listings.
20 */
21
22/*
23 * Copyright (C) 2020 Oracle Corporation
24 *
25 * This file is part of VirtualBox Open Source Edition (OSE), as
26 * available from http://www.alldomusa.eu.org. This file is free software;
27 * you can redistribute it and/or modify it under the terms of the GNU
28 * General Public License (GPL) as published by the Free Software
29 * Foundation, in version 2 as it comes in the "COPYING" file of the
30 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
31 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
32 *
33 * The contents of this file may alternatively be used under the terms
34 * of the Common Development and Distribution License Version 1.0
35 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
36 * VirtualBox OSE distribution, in which case the provisions of the
37 * CDDL are applicable instead of those of the GPL.
38 *
39 * You may elect to license modified versions of this file under the
40 * terms and conditions of either the GPL or the CDDL or both.
41 */
42
43
44/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47#define LOG_GROUP RTLOGGROUP_HTTP
48#include <iprt/http.h>
49#include <iprt/http-server.h>
50#include "internal/iprt.h"
51#include "internal/magics.h"
52
53#include <iprt/asm.h>
54#include <iprt/assert.h>
55#include <iprt/circbuf.h>
56#include <iprt/ctype.h>
57#include <iprt/err.h>
58#include <iprt/file.h> /* For file mode flags. */
59#include <iprt/getopt.h>
60#include <iprt/mem.h>
61#include <iprt/log.h>
62#include <iprt/path.h>
63#include <iprt/poll.h>
64#include <iprt/socket.h>
65#include <iprt/sort.h>
66#include <iprt/string.h>
67#include <iprt/system.h>
68#include <iprt/tcp.h>
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74/**
75 * Internal HTTP server instance.
76 */
77typedef struct RTHTTPSERVERINTERNAL
78{
79 /** Magic value. */
80 uint32_t u32Magic;
81 /** Callback table. */
82 RTHTTPSERVERCALLBACKS Callbacks;
83 /** Pointer to TCP server instance. */
84 PRTTCPSERVER pTCPServer;
85 /** Pointer to user-specific data. Optional. */
86 void *pvUser;
87 /** Size of user-specific data. Optional. */
88 size_t cbUser;
89} RTHTTPSERVERINTERNAL;
90/** Pointer to an internal HTTP server instance. */
91typedef RTHTTPSERVERINTERNAL *PRTHTTPSERVERINTERNAL;
92
93
94/*********************************************************************************************************************************
95* Defined Constants And Macros *
96*********************************************************************************************************************************/
97/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
98#define RTHTTPSERVER_VALID_RETURN_RC(hHttpServer, a_rc) \
99 do { \
100 AssertPtrReturn((hHttpServer), (a_rc)); \
101 AssertReturn((hHttpServer)->u32Magic == RTHTTPSERVER_MAGIC, (a_rc)); \
102 } while (0)
103
104/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
105#define RTHTTPSERVER_VALID_RETURN(hHttpServer) RTHTTPSERVER_VALID_RETURN_RC((hHttpServer), VERR_INVALID_HANDLE)
106
107/** Validates a handle and returns (void) if not valid. */
108#define RTHTTPSERVER_VALID_RETURN_VOID(hHttpServer) \
109 do { \
110 AssertPtrReturnVoid(hHttpServer); \
111 AssertReturnVoid((hHttpServer)->u32Magic == RTHTTPSERVER_MAGIC); \
112 } while (0)
113
114
115/** Handles a HTTP server callback with no arguments and returns. */
116#define RTHTTPSERVER_HANDLE_CALLBACK_RET(a_Name) \
117 do \
118 { \
119 PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
120 if (pCallbacks->a_Name) \
121 { \
122 RTHTTPCALLBACKDATA Data = { &pClient->State }; \
123 return pCallbacks->a_Name(&Data); \
124 } \
125 return VERR_NOT_IMPLEMENTED; \
126 } while (0)
127
128/** Handles a HTTP server callback with no arguments and sets rc accordingly. */
129#define RTHTTPSERVER_HANDLE_CALLBACK(a_Name) \
130 do \
131 { \
132 PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
133 if (pCallbacks->a_Name) \
134 { \
135 RTHTTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
136 rc = pCallbacks->a_Name(&Data); \
137 } \
138 } while (0)
139
140/** Handles a HTTP server callback with arguments and sets rc accordingly. */
141#define RTHTTPSERVER_HANDLE_CALLBACK_VA(a_Name, ...) \
142 do \
143 { \
144 PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
145 if (pCallbacks->a_Name) \
146 { \
147 RTHTTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
148 rc = pCallbacks->a_Name(&Data, __VA_ARGS__); \
149 } \
150 } while (0)
151
152/** Handles a HTTP server callback with arguments and returns. */
153#define RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(a_Name, ...) \
154 do \
155 { \
156 PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
157 if (pCallbacks->a_Name) \
158 { \
159 RTHTTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
160 return pCallbacks->a_Name(&Data, __VA_ARGS__); \
161 } \
162 } while (0)
163
164
165/*********************************************************************************************************************************
166* Structures and Typedefs *
167*********************************************************************************************************************************/
168
169/**
170 * Structure for maintaining an internal HTTP server client.
171 */
172typedef struct RTHTTPSERVERCLIENT
173{
174 /** Pointer to internal server state. */
175 PRTHTTPSERVERINTERNAL pServer;
176 /** Socket handle the client is bound to. */
177 RTSOCKET hSocket;
178 /** Actual client state. */
179 RTHTTPSERVERCLIENTSTATE State;
180} RTHTTPSERVERCLIENT;
181/** Pointer to an internal HTTP server client state. */
182typedef RTHTTPSERVERCLIENT *PRTHTTPSERVERCLIENT;
183
184/** Function pointer declaration for a specific HTTP server method handler. */
185typedef DECLCALLBACKTYPE(int, FNRTHTTPSERVERMETHOD,(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq));
186/** Pointer to a FNRTHTTPSERVERMETHOD(). */
187typedef FNRTHTTPSERVERMETHOD *PFNRTHTTPSERVERMETHOD;
188
189/**
190 * Static lookup table for some file extensions <-> MIME type. Add more as needed.
191 * Keep this alphabetical (file extension).
192 */
193static const struct
194{
195 /** File extension. */
196 const char *pszExt;
197 /** MIME type. */
198 const char *pszMIMEType;
199} s_aFileExtMIMEType[] = {
200 { ".arj", "application/x-arj-compressed" },
201 { ".asf", "video/x-ms-asf" },
202 { ".avi", "video/x-msvideo" },
203 { ".bmp", "image/bmp" },
204 { ".css", "text/css" },
205 { ".doc", "application/msword" },
206 { ".exe", "application/octet-stream" },
207 { ".gif", "image/gif" },
208 { ".gz", "application/x-gunzip" },
209 { ".htm", "text/html" },
210 { ".html", "text/html" },
211 { ".ico", "image/x-icon" },
212 { ".js", "application/x-javascript" },
213 { ".json", "text/json" },
214 { ".jpg", "image/jpeg" },
215 { ".jpeg", "image/jpeg" },
216 { ".ogg", "application/ogg" },
217 { ".m3u", "audio/x-mpegurl" },
218 { ".m4v", "video/x-m4v" },
219 { ".mid", "audio/mid" },
220 { ".mov", "video/quicktime" },
221 { ".mp3", "audio/x-mp3" },
222 { ".mp4", "video/mp4" },
223 { ".mpg", "video/mpeg" },
224 { ".mpeg", "video/mpeg" },
225 { ".pdf", "application/pdf" },
226 { ".png", "image/png" },
227 { ".ra", "audio/x-pn-realaudio" },
228 { ".ram", "audio/x-pn-realaudio" },
229 { ".rar", "application/x-arj-compressed" },
230 { ".rtf", "application/rtf" },
231 { ".shtm", "text/html" },
232 { ".shtml", "text/html" },
233 { ".svg", "image/svg+xml" },
234 { ".swf", "application/x-shockwave-flash" },
235 { ".torrent", "application/x-bittorrent" },
236 { ".tar", "application/x-tar" },
237 { ".tgz", "application/x-tar-gz" },
238 { ".ttf", "application/x-font-ttf" },
239 { ".txt", "text/plain" },
240 { ".wav", "audio/x-wav" },
241 { ".webm", "video/webm" },
242 { ".xml", "text/xml" },
243 { ".xls", "application/excel" },
244 { ".xsl", "application/xml" },
245 { ".xslt", "application/xml" },
246 { ".zip", "application/x-zip-compressed" },
247 { NULL, NULL }
248};
249
250
251/*********************************************************************************************************************************
252* Internal Functions *
253*********************************************************************************************************************************/
254
255/** @name Method handlers.
256 * @{
257 */
258static FNRTHTTPSERVERMETHOD rtHttpServerHandleGET;
259static FNRTHTTPSERVERMETHOD rtHttpServerHandleHEAD;
260#ifdef IPRT_HTTP_WITH_WEBDAV
261 static FNRTHTTPSERVERMETHOD rtHttpServerHandleOPTIONS;
262 static FNRTHTTPSERVERMETHOD rtHttpServerHandlePROPFIND;
263#endif
264/** @} */
265
266/**
267 * Structure for maintaining a single method entry for the methods table.
268 */
269typedef struct RTHTTPSERVERMETHOD_ENTRY
270{
271 /** Method ID. */
272 RTHTTPMETHOD enmMethod;
273 /** Function pointer invoked to handle the command. */
274 PFNRTHTTPSERVERMETHOD pfnMethod;
275} RTHTTPSERVERMETHOD_ENTRY;
276/** Pointer to a command entry. */
277typedef RTHTTPSERVERMETHOD_ENTRY *PRTHTTPMETHOD_ENTRY;
278
279
280
281/*********************************************************************************************************************************
282* Global Variables *
283*********************************************************************************************************************************/
284/**
285 * Table of handled methods.
286 */
287static const RTHTTPSERVERMETHOD_ENTRY g_aMethodMap[] =
288{
289 { RTHTTPMETHOD_GET, rtHttpServerHandleGET },
290 { RTHTTPMETHOD_HEAD, rtHttpServerHandleHEAD },
291#ifdef IPRT_HTTP_WITH_WEBDAV
292 { RTHTTPMETHOD_OPTIONS, rtHttpServerHandleOPTIONS },
293 { RTHTTPMETHOD_PROPFIND, rtHttpServerHandlePROPFIND },
294#endif
295 { RTHTTPMETHOD_END, NULL }
296};
297
298/** Maximum length in characters a HTTP server path can have (excluding termination). */
299#define RTHTTPSERVER_MAX_PATH RTPATH_MAX
300
301
302/*********************************************************************************************************************************
303* Internal functions *
304*********************************************************************************************************************************/
305
306/**
307 * Guesses the HTTP MIME type based on a given file extension.
308 *
309 * Note: Has to include the beginning dot, e.g. ".mp3" (see IPRT).
310 *
311 * @returns Guessed MIME type, or "application/octet-stream" if not found.
312 * @param pszFileExt File extension to guess MIME type for.
313 */
314static const char *rtHttpServerGuessMIMEType(const char *pszFileExt)
315{
316 if (pszFileExt)
317 {
318 size_t i = 0;
319 while (s_aFileExtMIMEType[i++].pszExt) /* Slow, but does the job for now. */
320 {
321 if (!RTStrICmp(pszFileExt, s_aFileExtMIMEType[i].pszExt))
322 return s_aFileExtMIMEType[i].pszMIMEType;
323 }
324 }
325
326 return "application/octet-stream";
327}
328
329/**
330 * Initializes a HTTP body.
331 *
332 * @param pBody Body to initialize.
333 * @param cbSize Size of body (in bytes) to allocate. Optional and can be 0.
334 */
335static int rtHttpServerBodyInit(PRTHTTPBODY pBody, size_t cbSize)
336{
337 if (cbSize)
338 {
339 pBody->pvBody = RTMemAlloc(cbSize);
340 AssertPtrReturn(pBody->pvBody, VERR_NO_MEMORY);
341 pBody->cbBodyAlloc = cbSize;
342 }
343 else
344 {
345 pBody->pvBody = NULL;
346 pBody->cbBodyAlloc = 0;
347 }
348
349 pBody->cbBodyUsed = 0;
350 pBody->offBody = 0;
351
352 return VINF_SUCCESS;
353}
354
355/**
356 * Destroys a HTTP body.
357 *
358 * @param pBody Body to destroy.
359 */
360static void rtHttpServerBodyDestroy(PRTHTTPBODY pBody)
361{
362 if (!pBody)
363 return;
364
365 if (pBody->pvBody)
366 {
367 Assert(pBody->cbBodyAlloc);
368
369 RTMemFree(pBody->pvBody);
370 pBody->pvBody = NULL;
371 }
372
373 pBody->cbBodyAlloc = 0;
374 pBody->cbBodyUsed = 0;
375 pBody->offBody = 0;
376}
377
378/**
379 * Allocates and initializes a new client request.
380 *
381 * @returns Pointer to the new client request, or NULL on OOM.
382 * Needs to be free'd with rtHttpServerReqFree().
383 */
384static PRTHTTPSERVERREQ rtHttpServerReqAlloc(void)
385{
386 PRTHTTPSERVERREQ pReq = (PRTHTTPSERVERREQ)RTMemAllocZ(sizeof(RTHTTPSERVERREQ));
387 AssertPtrReturn(pReq, NULL);
388
389 int rc2 = RTHttpHeaderListInit(&pReq->hHdrLst);
390 AssertRC(rc2);
391
392 rc2 = rtHttpServerBodyInit(&pReq->Body, 0 /* cbSize */);
393 AssertRC(rc2);
394
395 return pReq;
396}
397
398/**
399 * Frees a formerly allocated client request.
400 *
401 * @param pReq Pointer to client request to free.
402 */
403static void rtHttpServerReqFree(PRTHTTPSERVERREQ pReq)
404{
405 if (!pReq)
406 return;
407
408 RTHttpHeaderListDestroy(pReq->hHdrLst);
409
410 rtHttpServerBodyDestroy(&pReq->Body);
411
412 RTMemFree(pReq);
413}
414
415/**
416 * Initializes a HTTP server response with an allocated body size.
417 *
418 * @returns VBox status code.
419 * @param pResp HTTP server response to initialize.
420 * @param cbBody Body size (in bytes) to allocate.
421 */
422RTR3DECL(int) RTHttpServerResponseInitEx(PRTHTTPSERVERRESP pResp, size_t cbBody)
423{
424 pResp->enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
425
426 int rc = RTHttpHeaderListInit(&pResp->hHdrLst);
427 AssertRCReturn(rc, rc);
428
429 rc = rtHttpServerBodyInit(&pResp->Body, cbBody);
430
431 return rc;
432}
433
434/**
435 * Initializes a HTTP server response.
436 *
437 * @returns VBox status code.
438 * @param pResp HTTP server response to initialize.
439 */
440RTR3DECL(int) RTHttpServerResponseInit(PRTHTTPSERVERRESP pResp)
441{
442 return RTHttpServerResponseInitEx(pResp, 0 /* cbBody */);
443}
444
445/**
446 * Destroys a formerly initialized HTTP server response.
447 *
448 * @param pResp Pointer to HTTP server response to destroy.
449 */
450RTR3DECL(void) RTHttpServerResponseDestroy(PRTHTTPSERVERRESP pResp)
451{
452 if (!pResp)
453 return;
454
455 pResp->enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
456
457 RTHttpHeaderListDestroy(pResp->hHdrLst);
458
459 rtHttpServerBodyDestroy(&pResp->Body);
460}
461
462
463/*********************************************************************************************************************************
464* Protocol Functions *
465*********************************************************************************************************************************/
466
467/**
468 * Logs the HTTP protocol communication to the debug logger (2).
469 *
470 * @param pClient Client to log communication for.
471 * @param fWrite Whether the server is writing (to client) or reading (from client).
472 * @param pszData Actual protocol communication data to log.
473 */
474static void rtHttpServerLogProto(PRTHTTPSERVERCLIENT pClient, bool fWrite, const char *pszData)
475{
476 RT_NOREF(pClient);
477
478#ifdef LOG_ENABLED
479 if (!pszData) /* Nothing to log? Bail out. */
480 return;
481
482 char **ppapszStrings;
483 size_t cStrings;
484 int rc2 = RTStrSplit(pszData, strlen(pszData), RTHTTPSERVER_HTTP11_EOL_STR, &ppapszStrings, &cStrings);
485 if (RT_SUCCESS(rc2))
486 {
487 for (size_t i = 0; i < cStrings; i++)
488 {
489 Log2(("%s %s\n", fWrite ? ">" : "<", ppapszStrings[i]));
490 RTStrFree(ppapszStrings[i]);
491 }
492
493 RTMemFree(ppapszStrings);
494 }
495#else
496 RT_NOREF(fWrite, pszData);
497#endif
498}
499
500/**
501 * Writes HTTP protocol communication data to a connected client.
502 *
503 * @returns VBox status code.
504 * @param pClient Client to write data to.
505 * @param pszData Data to write. Must be zero-terminated.
506 */
507static int rtHttpServerWriteProto(PRTHTTPSERVERCLIENT pClient, const char *pszData)
508{
509 rtHttpServerLogProto(pClient, true /* fWrite */, pszData);
510 return RTTcpWrite(pClient->hSocket, pszData, strlen(pszData));
511}
512
513/**
514 * Main function for sending a response back to the client.
515 *
516 * @returns VBox status code.
517 * @param pClient Client to reply to.
518 * @param enmSts Status code to send.
519 */
520static int rtHttpServerSendResponse(PRTHTTPSERVERCLIENT pClient, RTHTTPSTATUS enmSts)
521{
522 char *pszResp;
523 int rc = RTStrAPrintf(&pszResp,
524 "%s %RU32 %s\r\n", RTHTTPVER_1_1_STR, enmSts, RTHttpStatusToStr(enmSts));
525 AssertRCReturn(rc, rc);
526 rc = rtHttpServerWriteProto(pClient, pszResp);
527 RTStrFree(pszResp);
528
529 LogFlowFuncLeaveRC(rc);
530 return rc;
531}
532
533/**
534 * Main function for sending response headers back to the client.
535 *
536 * @returns VBox status code.
537 * @param pClient Client to reply to.
538 * @param pHdrLst Header list to send. Optional and can be NULL.
539 */
540static int rtHttpServerSendResponseHdrEx(PRTHTTPSERVERCLIENT pClient, PRTHTTPHEADERLIST pHdrLst)
541{
542 RTHTTPHEADERLIST HdrLst;
543 int rc = RTHttpHeaderListInit(&HdrLst);
544 AssertRCReturn(rc, rc);
545
546#ifdef DEBUG
547 /* Include a timestamp when running a debug build. */
548 RTTIMESPEC tsNow;
549 char szTS[64];
550 RTTimeSpecToString(RTTimeNow(&tsNow), szTS, sizeof(szTS));
551 rc = RTHttpHeaderListAdd(HdrLst, "Date", szTS, strlen(szTS), RTHTTPHEADERLISTADD_F_BACK);
552 AssertRCReturn(rc, rc);
553#endif
554
555 /* Note: Deliberately don't include the VBox version due to security reasons. */
556 rc = RTHttpHeaderListAdd(HdrLst, "Server", "Oracle VirtualBox", strlen("Oracle VirtualBox"), RTHTTPHEADERLISTADD_F_BACK);
557 AssertRCReturn(rc, rc);
558
559#ifdef IPRT_HTTP_WITH_WEBDAV
560 rc = RTHttpHeaderListAdd(HdrLst, "Allow", "GET, HEAD, PROPFIND", strlen("GET, HEAD, PROPFIND"), RTHTTPHEADERLISTADD_F_BACK);
561 AssertRCReturn(rc, rc);
562 rc = RTHttpHeaderListAdd(HdrLst, "DAV", "1", strlen("1"), RTHTTPHEADERLISTADD_F_BACK); /* Note: v1 is sufficient for read-only access. */
563 AssertRCReturn(rc, rc);
564#endif
565
566 char *pszHdr = NULL;
567
568 size_t i = 0;
569 const char *pszEntry;
570 while ((pszEntry = RTHttpHeaderListGetByOrdinal(HdrLst, i++)) != NULL)
571 {
572 rc = RTStrAAppend(&pszHdr, pszEntry);
573 AssertRCBreak(rc);
574 rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
575 AssertRCBreak(rc);
576 }
577
578 /* Append optional headers, if any. */
579 if (pHdrLst)
580 {
581 i = 0;
582 while ((pszEntry = RTHttpHeaderListGetByOrdinal(*pHdrLst, i++)) != NULL)
583 {
584 rc = RTStrAAppend(&pszHdr, pszEntry);
585 AssertRCBreak(rc);
586 rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
587 AssertRCBreak(rc);
588 }
589 }
590
591 if (RT_SUCCESS(rc))
592 {
593 /* Append trailing EOL. */
594 rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
595 if (RT_SUCCESS(rc))
596 rc = rtHttpServerWriteProto(pClient, pszHdr);
597 }
598
599 RTStrFree(pszHdr);
600
601 RTHttpHeaderListDestroy(HdrLst);
602
603 LogFlowFunc(("rc=%Rrc\n", rc));
604 return rc;
605}
606
607/**
608 * Replies with (three digit) response status back to the client, extended version.
609 *
610 * @returns VBox status code.
611 * @param pClient Client to reply to.
612 * @param enmSts Status code to send.
613 * @param pHdrLst Header list to send. Optional and can be NULL.
614 */
615static int rtHttpServerSendResponseEx(PRTHTTPSERVERCLIENT pClient, RTHTTPSTATUS enmSts, PRTHTTPHEADERLIST pHdrLst)
616{
617 int rc = rtHttpServerSendResponse(pClient, enmSts);
618 if (RT_SUCCESS(rc))
619 rc = rtHttpServerSendResponseHdrEx(pClient, pHdrLst);
620
621 return rc;
622}
623
624/**
625 * Replies with (three digit) response status back to the client.
626 *
627 * @returns VBox status code.
628 * @param pClient Client to reply to.
629 * @param enmSts Status code to send.
630 */
631static int rtHttpServerSendResponseSimple(PRTHTTPSERVERCLIENT pClient, RTHTTPSTATUS enmSts)
632{
633 return rtHttpServerSendResponseEx(pClient, enmSts, NULL /* pHdrLst */);
634}
635
636/**
637 * Sends a chunk of the response body to the client.
638 *
639 * @returns VBox status code.
640 * @param pClient Client to send body to.
641 * @param pvBuf Data buffer to send.
642 * @param cbBuf Size (in bytes) of data buffer to send.
643 * @param pcbSent Where to store the sent bytes. Optional and can be NULL.
644 */
645static int rtHttpServerSendResponseBody(PRTHTTPSERVERCLIENT pClient, void *pvBuf, size_t cbBuf, size_t *pcbSent)
646{
647 int rc = RTTcpWrite(pClient->hSocket, pvBuf, cbBuf);
648 if ( RT_SUCCESS(rc)
649 && pcbSent)
650 *pcbSent = cbBuf;
651
652 return rc;
653}
654
655/**
656 * Resolves a VBox status code to a HTTP status code.
657 *
658 * @returns Resolved HTTP status code, or RTHTTPSTATUS_INTERNALSERVERERROR if not able to resolve.
659 * @param rc VBox status code to resolve.
660 */
661static RTHTTPSTATUS rtHttpServerRcToStatus(int rc)
662{
663 switch (rc)
664 {
665 case VINF_SUCCESS: return RTHTTPSTATUS_OK;
666 case VERR_INVALID_PARAMETER: return RTHTTPSTATUS_BADREQUEST;
667 case VERR_INVALID_POINTER: return RTHTTPSTATUS_BADREQUEST;
668 case VERR_NOT_IMPLEMENTED: return RTHTTPSTATUS_NOTIMPLEMENTED;
669 case VERR_NOT_SUPPORTED: return RTHTTPSTATUS_NOTIMPLEMENTED;
670 case VERR_PATH_NOT_FOUND: return RTHTTPSTATUS_NOTFOUND;
671 case VERR_FILE_NOT_FOUND: return RTHTTPSTATUS_NOTFOUND;
672 case VERR_IS_A_DIRECTORY: return RTHTTPSTATUS_FORBIDDEN;
673 case VERR_NOT_FOUND: return RTHTTPSTATUS_NOTFOUND;
674 default:
675 break;
676 }
677
678 AssertMsgFailed(("rc=%Rrc not handled for HTTP status\n", rc));
679 return RTHTTPSTATUS_INTERNALSERVERERROR;
680}
681
682
683/*********************************************************************************************************************************
684* Command Protocol Handlers *
685*********************************************************************************************************************************/
686
687/**
688 * Handler for the GET method.
689 *
690 * @returns VBox status code.
691 * @param pClient Client to handle GET method for.
692 * @param pReq Client request to handle.
693 */
694static DECLCALLBACK(int) rtHttpServerHandleGET(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
695{
696 LogFlowFuncEnter();
697
698 int rc = VINF_SUCCESS;
699
700 /* If a low-level GET request handler is defined, call it and return. */
701 RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnGetRequest, pReq);
702
703 RTFSOBJINFO fsObj;
704 RT_ZERO(fsObj); /* Shut up MSVC. */
705
706 char *pszMIMEHint = NULL;
707
708 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, &pszMIMEHint);
709 if (RT_FAILURE(rc))
710 return rc;
711
712 void *pvHandle = NULL;
713 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnOpen, pReq, &pvHandle);
714
715 if (RT_SUCCESS(rc))
716 {
717 size_t cbBuf = _64K;
718 void *pvBuf = RTMemAlloc(cbBuf);
719 AssertPtrReturn(pvBuf, VERR_NO_MEMORY);
720
721 for (;;)
722 {
723 RTHTTPHEADERLIST HdrLst;
724 rc = RTHttpHeaderListInit(&HdrLst);
725 AssertRCReturn(rc, rc);
726
727 char szVal[16];
728
729 /* Note: For directories fsObj.cbObject contains the actual size (in bytes)
730 * of the body data for the directory listing. */
731
732 ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
733 AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
734 rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
735 AssertRCBreak(rc);
736
737 cch = RTStrPrintf2(szVal, sizeof(szVal), "identity");
738 AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
739 rc = RTHttpHeaderListAdd(HdrLst, "Content-Encoding", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
740 AssertRCBreak(rc);
741
742 if (pszMIMEHint == NULL)
743 {
744 const char *pszMIME = rtHttpServerGuessMIMEType(RTPathSuffix(pReq->pszUrl));
745 rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIME, strlen(pszMIME), RTHTTPHEADERLISTADD_F_BACK);
746 }
747 else
748 {
749 rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIMEHint, strlen(pszMIMEHint), RTHTTPHEADERLISTADD_F_BACK);
750 RTStrFree(pszMIMEHint);
751 pszMIMEHint = NULL;
752 }
753 AssertRCReturn(rc, rc);
754
755 if (pClient->State.msKeepAlive)
756 {
757 /* If the client requested to keep alive the connection,
758 * always override this with 30s and report this back to the client. */
759 pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Make this configurable. */
760#ifdef DEBUG_andy
761 pClient->State.msKeepAlive = 5000;
762#endif
763 cch = RTStrPrintf2(szVal, sizeof(szVal), "timeout=%RU64", pClient->State.msKeepAlive / RT_MS_1SEC); /** @todo No pipelining support here yet. */
764 AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
765 rc = RTHttpHeaderListAdd(HdrLst, "Keep-Alive", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
766 AssertRCReturn(rc, rc);
767 }
768
769 rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst);
770 AssertRCReturn(rc, rc);
771
772 RTHttpHeaderListDestroy(HdrLst);
773
774 size_t cbToRead = fsObj.cbObject;
775 size_t cbRead = 0; /* Shut up GCC. */
776 size_t cbWritten = 0; /* Ditto. */
777 while (cbToRead)
778 {
779 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
780 if (RT_FAILURE(rc))
781 break;
782 rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
783 AssertBreak(cbToRead >= cbWritten);
784 cbToRead -= cbWritten;
785 if (rc == VERR_NET_CONNECTION_RESET_BY_PEER) /* Clients often apruptly abort the connection when done. */
786 {
787 rc = VINF_SUCCESS;
788 break;
789 }
790 AssertRCBreak(rc);
791 }
792
793 break;
794 } /* for (;;) */
795
796 RTMemFree(pvBuf);
797
798 int rc2 = rc; /* Save rc. */
799
800 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
801
802 if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
803 rc = rc2;
804 }
805
806 LogFlowFuncLeaveRC(rc);
807 return rc;
808}
809
810/**
811 * Handler for the HEAD method.
812 *
813 * @returns VBox status code.
814 * @param pClient Client to handle HEAD method for.
815 * @param pReq Client request to handle.
816 */
817static DECLCALLBACK(int) rtHttpServerHandleHEAD(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
818{
819 LogFlowFuncEnter();
820
821 /* If a low-level HEAD request handler is defined, call it and return. */
822 RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnHeadRequest, pReq);
823
824 int rc = VINF_SUCCESS;
825
826 RTFSOBJINFO fsObj;
827 RT_ZERO(fsObj); /* Shut up MSVC. */
828
829 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, NULL /* pszMIMEHint */);
830 if (RT_SUCCESS(rc))
831 {
832 RTHTTPHEADERLIST HdrLst;
833 rc = RTHttpHeaderListInit(&HdrLst);
834 AssertRCReturn(rc, rc);
835
836 /*
837 * Note: A response to a HEAD request does not have a body.
838 * All entity headers below are assumed to describe the the response a similar GET
839 * request would return (but then with a body).
840 */
841 char szVal[16];
842
843 ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
844 AssertReturn(cch, VERR_BUFFER_OVERFLOW);
845 rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
846 AssertRCReturn(rc, rc);
847
848 cch = RTStrPrintf2(szVal, sizeof(szVal), "identity");
849 AssertReturn(cch, VERR_BUFFER_OVERFLOW);
850 rc = RTHttpHeaderListAdd(HdrLst, "Content-Encoding", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
851 AssertRCReturn(rc, rc);
852
853 const char *pszMIME = rtHttpServerGuessMIMEType(RTPathSuffix(pReq->pszUrl));
854 rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIME, strlen(pszMIME), RTHTTPHEADERLISTADD_F_BACK);
855 AssertRCReturn(rc, rc);
856
857 rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst);
858 AssertRCReturn(rc, rc);
859
860 RTHttpHeaderListDestroy(HdrLst);
861 }
862
863 LogFlowFuncLeaveRC(rc);
864 return rc;
865}
866
867#ifdef IPRT_HTTP_WITH_WEBDAV
868/**
869 * Handler for the OPTIONS method.
870 *
871 * @returns VBox status code.
872 * @param pClient Client to handle OPTIONS method for.
873 * @param pReq Client request to handle.
874 */
875static DECLCALLBACK(int) rtHttpServerHandleOPTIONS(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
876{
877 LogFlowFuncEnter();
878
879 RT_NOREF(pReq);
880
881 int rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, NULL /* pHdrLst */);
882
883 LogFlowFuncLeaveRC(rc);
884 return rc;
885}
886
887/**
888 * Handler for the PROPFIND (WebDAV) method.
889 *
890 * @returns VBox status code.
891 * @param pClient Client to handle PROPFIND method for.
892 * @param pReq Client request to handle.
893 */
894static DECLCALLBACK(int) rtHttpServerHandlePROPFIND(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
895{
896 LogFlowFuncEnter();
897
898 int rc = VINF_SUCCESS;
899
900 /* If a low-level GET request handler is defined, call it and return. */
901 RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnGetRequest, pReq);
902
903 RTFSOBJINFO fsObj;
904 RT_ZERO(fsObj); /* Shut up MSVC. */
905
906 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, NULL /* pszMIMEHint */);
907 if (RT_FAILURE(rc))
908 return rc;
909
910 void *pvHandle = NULL;
911 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnOpen, pReq, &pvHandle);
912
913 if (RT_SUCCESS(rc))
914 {
915 size_t cbBuf = _64K;
916 void *pvBuf = RTMemAlloc(cbBuf);
917 AssertPtrReturn(pvBuf, VERR_NO_MEMORY);
918
919 for (;;)
920 {
921 RTHTTPHEADERLIST HdrLst;
922 rc = RTHttpHeaderListInit(&HdrLst);
923 AssertRCReturn(rc, rc);
924
925 char szVal[16];
926
927 rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", "text/xml; charset=utf-8", strlen("text/xml; charset=utf-8"), RTHTTPHEADERLISTADD_F_BACK);
928 AssertRCBreak(rc);
929
930 /* Note: For directories fsObj.cbObject contains the actual size (in bytes)
931 * of the body data for the directory listing. */
932
933 ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
934 AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
935 rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
936 AssertRCBreak(rc);
937
938 rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_MULTISTATUS, &HdrLst);
939 AssertRCReturn(rc, rc);
940
941 RTHttpHeaderListDestroy(HdrLst);
942
943 size_t cbToRead = fsObj.cbObject;
944 size_t cbRead = 0; /* Shut up GCC. */
945 size_t cbWritten = 0; /* Ditto. */
946 while (cbToRead)
947 {
948 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
949 if (RT_FAILURE(rc))
950 break;
951 //rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf);
952 rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
953 AssertBreak(cbToRead >= cbWritten);
954 cbToRead -= cbWritten;
955 if (rc == VERR_NET_CONNECTION_RESET_BY_PEER) /* Clients often apruptly abort the connection when done. */
956 {
957 rc = VINF_SUCCESS;
958 break;
959 }
960 AssertRCBreak(rc);
961 }
962
963 break;
964 } /* for (;;) */
965
966 RTMemFree(pvBuf);
967
968 int rc2 = rc; /* Save rc. */
969
970 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
971
972 if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
973 rc = rc2;
974 }
975
976 LogFlowFuncLeaveRC(rc);
977 return rc;
978}
979#endif /* IPRT_HTTP_WITH_WEBDAV */
980
981/**
982 * Validates if a given path is valid or not.
983 *
984 * @returns \c true if path is valid, or \c false if not.
985 * @param pszPath Path to check.
986 * @param fIsAbsolute Whether the path to check is an absolute path or not.
987 */
988static bool rtHttpServerPathIsValid(const char *pszPath, bool fIsAbsolute)
989{
990 if (!pszPath)
991 return false;
992
993 bool fIsValid = strlen(pszPath)
994 && RTStrIsValidEncoding(pszPath)
995 && RTStrStr(pszPath, "..") == NULL; /** @todo Very crude for now -- improve this. */
996 if ( fIsValid
997 && fIsAbsolute)
998 {
999 RTFSOBJINFO objInfo;
1000 int rc2 = RTPathQueryInfo(pszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1001 if (RT_SUCCESS(rc2))
1002 {
1003 fIsValid = RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
1004 || RTFS_IS_FILE(objInfo.Attr.fMode);
1005
1006 /* No symlinks and other stuff not allowed. */
1007 }
1008 else
1009 fIsValid = false;
1010 }
1011
1012 LogFlowFunc(("pszPath=%s -> %RTbool\n", pszPath, fIsValid));
1013 return fIsValid;
1014
1015}
1016
1017/**
1018 * Parses headers and sets (replaces) a given header list.
1019 *
1020 * @returns VBox status code.
1021 * @param hList Header list to fill parsed headers in.
1022 * @param cStrings Number of strings to parse for \a papszStrings.
1023 * @param papszStrings Array of strings to parse.
1024 */
1025static int rtHttpServerParseHeaders(RTHTTPHEADERLIST hList, size_t cStrings, char **papszStrings)
1026{
1027 /* Nothing to parse left? Bail out early. */
1028 if ( !cStrings
1029 || !papszStrings)
1030 return VINF_SUCCESS;
1031
1032#ifdef LOG_ENABLED
1033 for (size_t i = 0; i < cStrings; i++)
1034 LogFlowFunc(("Header: %s\n", papszStrings[i]));
1035#endif
1036
1037 int rc = RTHttpHeaderListSet(hList, cStrings, papszStrings);
1038
1039 LogFlowFunc(("rc=%Rrc, cHeaders=%zu\n", rc, cStrings));
1040 return rc;
1041}
1042
1043/**
1044 * Main function for parsing and allocating a client request.
1045 *
1046 * See: https://tools.ietf.org/html/rfc2616#section-2.2
1047 *
1048 * @returns VBox status code.
1049 * @param pClient Client to parse request from.
1050 * @param pszReq Request string with headers to parse.
1051 * @param cbReq Size (in bytes) of request string to parse.
1052 * @param ppReq Where to store the allocated client request on success.
1053 * Needs to be free'd via rtHttpServerReqFree().
1054 */
1055static int rtHttpServerParseRequest(PRTHTTPSERVERCLIENT pClient, char *pszReq, size_t cbReq,
1056 PRTHTTPSERVERREQ *ppReq)
1057{
1058 RT_NOREF(pClient);
1059
1060 AssertPtrReturn(pszReq, VERR_INVALID_POINTER);
1061 AssertReturn(cbReq, VERR_INVALID_PARAMETER);
1062
1063 /* We only support UTF-8 charset for now. */
1064 AssertReturn(RTStrIsValidEncoding(pszReq), VERR_INVALID_PARAMETER);
1065
1066 char **ppapszStrings = NULL;
1067 size_t cStrings = 0;
1068 int rc = RTStrSplit(pszReq, cbReq, RTHTTPSERVER_HTTP11_EOL_STR, &ppapszStrings, &cStrings);
1069 if (RT_FAILURE(rc))
1070 return rc;
1071
1072 if (!cStrings)
1073 return VERR_INVALID_PARAMETER;
1074
1075 /** Advances pszReq to the next string in the current line.
1076 ** @todo Can we do better here? */
1077#define REQ_GET_NEXT_STRING \
1078 pszReq = psz; \
1079 while (psz && !RT_C_IS_SPACE(*psz)) \
1080 psz++; \
1081 if (psz) \
1082 { \
1083 *psz = '\0'; \
1084 psz++; \
1085 } \
1086 else /* Be strict for now. */ \
1087 AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER);
1088
1089 PRTHTTPSERVERREQ pReq = NULL;
1090
1091 for (;;) /* To use break. */
1092 {
1093 /* Start with the first line. */
1094 char *psz = ppapszStrings[0];
1095 AssertPtrBreakStmt(psz, rc = VERR_INVALID_PARAMETER);
1096
1097 /* A tiny bit of sanitation. */
1098 RTStrStrip(psz);
1099
1100 pReq = rtHttpServerReqAlloc();
1101 AssertPtrBreakStmt(pReq, rc = VERR_NO_MEMORY);
1102
1103 REQ_GET_NEXT_STRING
1104
1105 /*
1106 * Parse method to use. Method names are case sensitive.
1107 */
1108 if (!RTStrCmp(pszReq, "GET")) pReq->enmMethod = RTHTTPMETHOD_GET;
1109 else if (!RTStrCmp(pszReq, "HEAD")) pReq->enmMethod = RTHTTPMETHOD_HEAD;
1110#ifdef IPRT_HTTP_WITH_WEBDAV
1111 else if (!RTStrCmp(pszReq, "OPTIONS")) pReq->enmMethod = RTHTTPMETHOD_OPTIONS;
1112 else if (!RTStrCmp(pszReq, "PROPFIND")) pReq->enmMethod = RTHTTPMETHOD_PROPFIND;
1113#endif
1114 else
1115 {
1116 rc = VERR_NOT_SUPPORTED;
1117 break;
1118 }
1119
1120 REQ_GET_NEXT_STRING
1121
1122 /** @todo Do URL unescaping here. */
1123
1124 if (!rtHttpServerPathIsValid(pszReq, false /* fIsAbsolute */))
1125 {
1126 rc = VERR_PATH_NOT_FOUND;
1127 break;
1128 }
1129
1130 pReq->pszUrl = RTStrDup(pszReq);
1131
1132 REQ_GET_NEXT_STRING
1133
1134 /* We're picky heree: Only HTTP 1.1 is supported by now. */
1135 if (RTStrCmp(pszReq, RTHTTPVER_1_1_STR)) /** @todo Use RTStrVersionCompare. Later. */
1136 {
1137 rc = VERR_NOT_SUPPORTED;
1138 break;
1139 }
1140
1141 /** @todo Anything else needed for the first line here? */
1142
1143 /*
1144 * Process headers, if any.
1145 */
1146 if (cStrings > 1)
1147 {
1148 rc = rtHttpServerParseHeaders(pReq->hHdrLst, cStrings - 1, &ppapszStrings[1]);
1149 if (RT_SUCCESS(rc))
1150 {
1151 if (RTHttpHeaderListGet(pReq->hHdrLst, "Connection", RTSTR_MAX))
1152 pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Insert the real value here. */
1153 }
1154 }
1155 break;
1156 } /* for (;;) */
1157
1158 /*
1159 * Cleanup.
1160 */
1161 for (size_t i = 0; i < cStrings; i++)
1162 RTStrFree(ppapszStrings[i]);
1163 RTMemFree(ppapszStrings);
1164
1165 if (RT_FAILURE(rc))
1166 {
1167 rtHttpServerReqFree(pReq);
1168 pReq = NULL;
1169 }
1170 else
1171 *ppReq = pReq;
1172
1173 return rc;
1174}
1175
1176/**
1177 * Main function for processing client requests.
1178 *
1179 * @returns VBox status code.
1180 * @param pClient Client to process request for.
1181 * @param pszReq Request string to parse and handle.
1182 * @param cbReq Size (in bytes) of request string.
1183 */
1184static int rtHttpServerProcessRequest(PRTHTTPSERVERCLIENT pClient, char *pszReq, size_t cbReq)
1185{
1186 RTHTTPSTATUS enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
1187
1188 PRTHTTPSERVERREQ pReq = NULL; /* Shut up GCC. */
1189 int rc = rtHttpServerParseRequest(pClient, pszReq, cbReq, &pReq);
1190 if (RT_SUCCESS(rc))
1191 {
1192 LogFlowFunc(("Request %s %s\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl));
1193
1194 unsigned i = 0;
1195 for (; i < RT_ELEMENTS(g_aMethodMap); i++)
1196 {
1197 const RTHTTPSERVERMETHOD_ENTRY *pMethodEntry = &g_aMethodMap[i];
1198 if (pReq->enmMethod == pMethodEntry->enmMethod)
1199 {
1200 /* Hand in request to method handler. */
1201 int rcMethod = pMethodEntry->pfnMethod(pClient, pReq);
1202 if (RT_FAILURE(rcMethod))
1203 LogFunc(("Request %s %s failed with %Rrc\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl, rcMethod));
1204
1205 enmSts = rtHttpServerRcToStatus(rcMethod);
1206 break;
1207 }
1208 }
1209
1210 if (i == RT_ELEMENTS(g_aMethodMap))
1211 enmSts = RTHTTPSTATUS_NOTIMPLEMENTED;
1212
1213 rtHttpServerReqFree(pReq);
1214 }
1215 else
1216 enmSts = RTHTTPSTATUS_BADREQUEST;
1217
1218 if (enmSts != RTHTTPSTATUS_INTERNAL_NOT_SET)
1219 {
1220 int rc2 = rtHttpServerSendResponseSimple(pClient, enmSts);
1221 if (RT_SUCCESS(rc))
1222 rc = rc2;
1223 }
1224
1225 LogFlowFuncLeaveRC(rc);
1226 return rc;
1227}
1228
1229/**
1230 * Main loop for processing client requests.
1231 *
1232 * @returns VBox status code.
1233 * @param pClient Client to process requests for.
1234 */
1235static int rtHttpServerClientMain(PRTHTTPSERVERCLIENT pClient)
1236{
1237 int rc;
1238
1239 char szReq[RTHTTPSERVER_MAX_REQ_LEN + 1];
1240
1241 LogFlowFunc(("Client connected\n"));
1242
1243 /* Initialize client state. */
1244 pClient->State.msKeepAlive = 0;
1245
1246 RTMSINTERVAL cWaitMs = RT_INDEFINITE_WAIT; /* The first wait always waits indefinitely. */
1247 uint64_t tsLastReadMs = 0;
1248
1249 for (;;)
1250 {
1251 rc = RTTcpSelectOne(pClient->hSocket, cWaitMs);
1252 if (RT_FAILURE(rc))
1253 {
1254 LogFlowFunc(("RTTcpSelectOne=%Rrc (cWaitMs=%RU64)\n", rc, cWaitMs));
1255 if (rc == VERR_TIMEOUT)
1256 {
1257 if (pClient->State.msKeepAlive) /* Keep alive handling needed? */
1258 {
1259 if (!tsLastReadMs)
1260 tsLastReadMs = RTTimeMilliTS();
1261 const uint64_t tsDeltaMs = pClient->State.msKeepAlive - (RTTimeMilliTS() - tsLastReadMs);
1262 LogFlowFunc(("tsLastReadMs=%RU64, tsDeltaMs=%RU64\n", tsLastReadMs, tsDeltaMs));
1263 Log3Func(("Keep alive active (%RU32ms): %RU64ms remaining\n", pClient->State.msKeepAlive, tsDeltaMs));
1264 if ( tsDeltaMs > cWaitMs
1265 && tsDeltaMs < pClient->State.msKeepAlive)
1266 continue;
1267
1268 LogFunc(("Keep alive active: Client did not respond within %RU32ms, closing\n", pClient->State.msKeepAlive));
1269 rc = VINF_SUCCESS;
1270 break;
1271 }
1272 }
1273
1274 break;
1275 }
1276
1277 LogFlowFunc(("Reading client request ...\n"));
1278
1279 tsLastReadMs = RTTimeMilliTS();
1280 cWaitMs = 200; /* All consequtive waits do busy waiting for now. */
1281
1282 char *pszReq = szReq;
1283 size_t cbRead;
1284 size_t cbToRead = sizeof(szReq);
1285 size_t cbReadTotal = 0;
1286
1287 do
1288 {
1289 rc = RTTcpReadNB(pClient->hSocket, pszReq, cbToRead, &cbRead);
1290 if (RT_FAILURE(rc))
1291 break;
1292
1293 if (!cbRead)
1294 break;
1295
1296 /* Make sure to terminate string read so far. */
1297 pszReq[cbRead] = '\0';
1298
1299 /* End of request reached? */
1300 /** @todo BUGBUG Improve this. */
1301 char *pszEOR = RTStrStr(&pszReq[cbReadTotal], "\r\n\r\n");
1302 if (pszEOR)
1303 {
1304 cbReadTotal = pszEOR - pszReq;
1305 *pszEOR = '\0';
1306 break;
1307 }
1308
1309 AssertBreak(cbToRead >= cbRead);
1310 cbToRead -= cbRead;
1311 cbReadTotal += cbRead;
1312 AssertBreak(cbReadTotal <= sizeof(szReq));
1313 pszReq += cbRead;
1314
1315 } while (cbToRead);
1316
1317 if ( RT_SUCCESS(rc)
1318 && cbReadTotal)
1319 {
1320 LogFlowFunc(("Received client request (%zu bytes)\n", cbReadTotal));
1321
1322 rtHttpServerLogProto(pClient, false /* fWrite */, szReq);
1323
1324 rc = rtHttpServerProcessRequest(pClient, szReq, cbReadTotal);
1325 }
1326 else
1327 break;
1328
1329 } /* for */
1330
1331 if (RT_FAILURE(rc))
1332 {
1333 switch (rc)
1334 {
1335 case VERR_NET_CONNECTION_RESET_BY_PEER:
1336 {
1337 LogFunc(("Client closed the connection\n"));
1338 rc = VINF_SUCCESS;
1339 break;
1340 }
1341
1342 default:
1343 LogFunc(("Client processing failed with %Rrc\n", rc));
1344 break;
1345 }
1346 }
1347
1348 LogFlowFuncLeaveRC(rc);
1349 return rc;
1350}
1351
1352/**
1353 * Per-client thread for serving the server's control connection.
1354 *
1355 * @returns VBox status code.
1356 * @param hSocket Socket handle to use for the control connection.
1357 * @param pvUser User-provided arguments. Of type PRTHTTPSERVERINTERNAL.
1358 */
1359static DECLCALLBACK(int) rtHttpServerClientThread(RTSOCKET hSocket, void *pvUser)
1360{
1361 PRTHTTPSERVERINTERNAL pThis = (PRTHTTPSERVERINTERNAL)pvUser;
1362 RTHTTPSERVER_VALID_RETURN(pThis);
1363
1364 LogFlowFuncEnter();
1365
1366 RTHTTPSERVERCLIENT Client;
1367 RT_ZERO(Client);
1368
1369 Client.pServer = pThis;
1370 Client.hSocket = hSocket;
1371
1372 return rtHttpServerClientMain(&Client);
1373}
1374
1375RTR3DECL(int) RTHttpServerCreate(PRTHTTPSERVER hHttpServer, const char *pszAddress, uint16_t uPort,
1376 PRTHTTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser)
1377{
1378 AssertPtrReturn(hHttpServer, VERR_INVALID_POINTER);
1379 AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
1380 AssertReturn (uPort, VERR_INVALID_PARAMETER);
1381 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER);
1382 /* pvUser is optional. */
1383
1384 int rc;
1385
1386 PRTHTTPSERVERINTERNAL pThis = (PRTHTTPSERVERINTERNAL)RTMemAllocZ(sizeof(RTHTTPSERVERINTERNAL));
1387 if (pThis)
1388 {
1389 pThis->u32Magic = RTHTTPSERVER_MAGIC;
1390 pThis->Callbacks = *pCallbacks;
1391 pThis->pvUser = pvUser;
1392 pThis->cbUser = cbUser;
1393
1394 rc = RTTcpServerCreate(pszAddress, uPort, RTTHREADTYPE_DEFAULT, "httpsrv",
1395 rtHttpServerClientThread, pThis /* pvUser */, &pThis->pTCPServer);
1396 if (RT_SUCCESS(rc))
1397 {
1398 *hHttpServer = (RTHTTPSERVER)pThis;
1399 }
1400 }
1401 else
1402 rc = VERR_NO_MEMORY;
1403
1404 return rc;
1405}
1406
1407RTR3DECL(int) RTHttpServerDestroy(RTHTTPSERVER hHttpServer)
1408{
1409 if (hHttpServer == NIL_RTHTTPSERVER)
1410 return VINF_SUCCESS;
1411
1412 PRTHTTPSERVERINTERNAL pThis = hHttpServer;
1413 RTHTTPSERVER_VALID_RETURN(pThis);
1414
1415 AssertPtr(pThis->pTCPServer);
1416
1417 int rc = VINF_SUCCESS;
1418
1419 PRTHTTPSERVERCALLBACKS pCallbacks = &pThis->Callbacks;
1420 if (pCallbacks->pfnDestroy)
1421 {
1422 RTHTTPCALLBACKDATA Data = { NULL /* pClient */, pThis->pvUser, pThis->cbUser };
1423 rc = pCallbacks->pfnDestroy(&Data);
1424 }
1425
1426 if (RT_SUCCESS(rc))
1427 {
1428 rc = RTTcpServerDestroy(pThis->pTCPServer);
1429 if (RT_SUCCESS(rc))
1430 {
1431 pThis->u32Magic = RTHTTPSERVER_MAGIC_DEAD;
1432
1433 RTMemFree(pThis);
1434 }
1435 }
1436
1437 return rc;
1438}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette