VirtualBox

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

Last change on this file since 99828 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use