VirtualBox

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

Last change on this file since 89669 was 87044, checked in by vboxsync, 4 years ago

Shared Clipboard/Transfers: A bit of cleanup rtHttpServerParseRequest(). bugref:9874

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.9 KB
Line 
1/* $Id: http-server.cpp 87044 2020-12-04 17:42:55Z 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.virtualbox.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
771 RTHttpHeaderListDestroy(HdrLst);
772
773 if (rc == VERR_BROKEN_PIPE) /* Could happen on fast reloads. */
774 break;
775 AssertRCReturn(rc, rc);
776
777 size_t cbToRead = fsObj.cbObject;
778 size_t cbRead = 0; /* Shut up GCC. */
779 size_t cbWritten = 0; /* Ditto. */
780 while (cbToRead)
781 {
782 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
783 if (RT_FAILURE(rc))
784 break;
785 rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
786 AssertBreak(cbToRead >= cbWritten);
787 cbToRead -= cbWritten;
788 if (rc == VERR_NET_CONNECTION_RESET_BY_PEER) /* Clients often apruptly abort the connection when done. */
789 {
790 rc = VINF_SUCCESS;
791 break;
792 }
793 AssertRCBreak(rc);
794 }
795
796 break;
797 } /* for (;;) */
798
799 RTMemFree(pvBuf);
800
801 int rc2 = rc; /* Save rc. */
802
803 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
804
805 if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
806 rc = rc2;
807 }
808
809 LogFlowFuncLeaveRC(rc);
810 return rc;
811}
812
813/**
814 * Handler for the HEAD method.
815 *
816 * @returns VBox status code.
817 * @param pClient Client to handle HEAD method for.
818 * @param pReq Client request to handle.
819 */
820static DECLCALLBACK(int) rtHttpServerHandleHEAD(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
821{
822 LogFlowFuncEnter();
823
824 /* If a low-level HEAD request handler is defined, call it and return. */
825 RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnHeadRequest, pReq);
826
827 int rc = VINF_SUCCESS;
828
829 RTFSOBJINFO fsObj;
830 RT_ZERO(fsObj); /* Shut up MSVC. */
831
832 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, NULL /* pszMIMEHint */);
833 if (RT_SUCCESS(rc))
834 {
835 RTHTTPHEADERLIST HdrLst;
836 rc = RTHttpHeaderListInit(&HdrLst);
837 AssertRCReturn(rc, rc);
838
839 /*
840 * Note: A response to a HEAD request does not have a body.
841 * All entity headers below are assumed to describe the the response a similar GET
842 * request would return (but then with a body).
843 */
844 char szVal[16];
845
846 ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
847 AssertReturn(cch, VERR_BUFFER_OVERFLOW);
848 rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
849 AssertRCReturn(rc, rc);
850
851 cch = RTStrPrintf2(szVal, sizeof(szVal), "identity");
852 AssertReturn(cch, VERR_BUFFER_OVERFLOW);
853 rc = RTHttpHeaderListAdd(HdrLst, "Content-Encoding", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
854 AssertRCReturn(rc, rc);
855
856 const char *pszMIME = rtHttpServerGuessMIMEType(RTPathSuffix(pReq->pszUrl));
857 rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIME, strlen(pszMIME), RTHTTPHEADERLISTADD_F_BACK);
858 AssertRCReturn(rc, rc);
859
860 rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst);
861 AssertRCReturn(rc, rc);
862
863 RTHttpHeaderListDestroy(HdrLst);
864 }
865
866 LogFlowFuncLeaveRC(rc);
867 return rc;
868}
869
870#ifdef IPRT_HTTP_WITH_WEBDAV
871/**
872 * Handler for the OPTIONS method.
873 *
874 * @returns VBox status code.
875 * @param pClient Client to handle OPTIONS method for.
876 * @param pReq Client request to handle.
877 */
878static DECLCALLBACK(int) rtHttpServerHandleOPTIONS(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
879{
880 LogFlowFuncEnter();
881
882 RT_NOREF(pReq);
883
884 int rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, NULL /* pHdrLst */);
885
886 LogFlowFuncLeaveRC(rc);
887 return rc;
888}
889
890/**
891 * Handler for the PROPFIND (WebDAV) method.
892 *
893 * @returns VBox status code.
894 * @param pClient Client to handle PROPFIND method for.
895 * @param pReq Client request to handle.
896 */
897static DECLCALLBACK(int) rtHttpServerHandlePROPFIND(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
898{
899 LogFlowFuncEnter();
900
901 int rc = VINF_SUCCESS;
902
903 /* If a low-level GET request handler is defined, call it and return. */
904 RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnGetRequest, pReq);
905
906 RTFSOBJINFO fsObj;
907 RT_ZERO(fsObj); /* Shut up MSVC. */
908
909 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, NULL /* pszMIMEHint */);
910 if (RT_FAILURE(rc))
911 return rc;
912
913 void *pvHandle = NULL;
914 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnOpen, pReq, &pvHandle);
915
916 if (RT_SUCCESS(rc))
917 {
918 size_t cbBuf = _64K;
919 void *pvBuf = RTMemAlloc(cbBuf);
920 AssertPtrReturn(pvBuf, VERR_NO_MEMORY);
921
922 for (;;)
923 {
924 RTHTTPHEADERLIST HdrLst;
925 rc = RTHttpHeaderListInit(&HdrLst);
926 AssertRCReturn(rc, rc);
927
928 char szVal[16];
929
930 rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", "text/xml; charset=utf-8", strlen("text/xml; charset=utf-8"), RTHTTPHEADERLISTADD_F_BACK);
931 AssertRCBreak(rc);
932
933 /* Note: For directories fsObj.cbObject contains the actual size (in bytes)
934 * of the body data for the directory listing. */
935
936 ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
937 AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
938 rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
939 AssertRCBreak(rc);
940
941 rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_MULTISTATUS, &HdrLst);
942 AssertRCReturn(rc, rc);
943
944 RTHttpHeaderListDestroy(HdrLst);
945
946 size_t cbToRead = fsObj.cbObject;
947 size_t cbRead = 0; /* Shut up GCC. */
948 size_t cbWritten = 0; /* Ditto. */
949 while (cbToRead)
950 {
951 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
952 if (RT_FAILURE(rc))
953 break;
954 //rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf);
955 rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
956 AssertBreak(cbToRead >= cbWritten);
957 cbToRead -= cbWritten;
958 if (rc == VERR_NET_CONNECTION_RESET_BY_PEER) /* Clients often apruptly abort the connection when done. */
959 {
960 rc = VINF_SUCCESS;
961 break;
962 }
963 AssertRCBreak(rc);
964 }
965
966 break;
967 } /* for (;;) */
968
969 RTMemFree(pvBuf);
970
971 int rc2 = rc; /* Save rc. */
972
973 RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
974
975 if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
976 rc = rc2;
977 }
978
979 LogFlowFuncLeaveRC(rc);
980 return rc;
981}
982#endif /* IPRT_HTTP_WITH_WEBDAV */
983
984/**
985 * Validates if a given path is valid or not.
986 *
987 * @returns \c true if path is valid, or \c false if not.
988 * @param pszPath Path to check.
989 * @param fIsAbsolute Whether the path to check is an absolute path or not.
990 */
991static bool rtHttpServerPathIsValid(const char *pszPath, bool fIsAbsolute)
992{
993 if (!pszPath)
994 return false;
995
996 bool fIsValid = strlen(pszPath)
997 && RTStrIsValidEncoding(pszPath)
998 && RTStrStr(pszPath, "..") == NULL; /** @todo Very crude for now -- improve this. */
999 if ( fIsValid
1000 && fIsAbsolute)
1001 {
1002 RTFSOBJINFO objInfo;
1003 int rc2 = RTPathQueryInfo(pszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1004 if (RT_SUCCESS(rc2))
1005 {
1006 fIsValid = RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
1007 || RTFS_IS_FILE(objInfo.Attr.fMode);
1008
1009 /* No symlinks and other stuff not allowed. */
1010 }
1011 else
1012 fIsValid = false;
1013 }
1014
1015 LogFlowFunc(("pszPath=%s -> %RTbool\n", pszPath, fIsValid));
1016 return fIsValid;
1017
1018}
1019
1020/**
1021 * Parses headers and sets (replaces) a given header list.
1022 *
1023 * @returns VBox status code.
1024 * @param hList Header list to fill parsed headers in.
1025 * @param cStrings Number of strings to parse for \a papszStrings.
1026 * @param papszStrings Array of strings to parse.
1027 */
1028static int rtHttpServerParseHeaders(RTHTTPHEADERLIST hList, size_t cStrings, char **papszStrings)
1029{
1030 /* Nothing to parse left? Bail out early. */
1031 if ( !cStrings
1032 || !papszStrings)
1033 return VINF_SUCCESS;
1034
1035#ifdef LOG_ENABLED
1036 for (size_t i = 0; i < cStrings; i++)
1037 LogFlowFunc(("Header: %s\n", papszStrings[i]));
1038#endif
1039
1040 int rc = RTHttpHeaderListSet(hList, cStrings, papszStrings);
1041
1042 LogFlowFunc(("rc=%Rrc, cHeaders=%zu\n", rc, cStrings));
1043 return rc;
1044}
1045
1046/**
1047 * Main function for parsing and allocating a client request.
1048 *
1049 * See: https://tools.ietf.org/html/rfc2616#section-2.2
1050 *
1051 * @returns VBox status code.
1052 * @param pClient Client to parse request from.
1053 * @param pszReq Request string with headers to parse.
1054 * @param cbReq Size (in bytes) of request string to parse.
1055 * @param ppReq Where to store the allocated client request on success.
1056 * Needs to be free'd via rtHttpServerReqFree().
1057 */
1058static int rtHttpServerParseRequest(PRTHTTPSERVERCLIENT pClient, const char *pszReq, size_t cbReq,
1059 PRTHTTPSERVERREQ *ppReq)
1060{
1061 RT_NOREF(pClient);
1062
1063 AssertPtrReturn(pszReq, VERR_INVALID_POINTER);
1064 AssertReturn(cbReq, VERR_INVALID_PARAMETER);
1065
1066 /* We only support UTF-8 charset for now. */
1067 AssertReturn(RTStrIsValidEncoding(pszReq), VERR_INVALID_PARAMETER);
1068
1069 char **ppapszReq = NULL;
1070 size_t cReq = 0;
1071 int rc = RTStrSplit(pszReq, cbReq, RTHTTPSERVER_HTTP11_EOL_STR, &ppapszReq, &cReq);
1072 if (RT_FAILURE(rc))
1073 return rc;
1074
1075 if (!cReq)
1076 return VERR_INVALID_PARAMETER;
1077
1078#ifdef LOG_ENABLED
1079 for (size_t i = 0; i < cReq; i++)
1080 LogFlowFunc(("%s\n", ppapszReq[i]));
1081#endif
1082
1083 PRTHTTPSERVERREQ pReq = NULL;
1084
1085 char **ppapszFirstLine = NULL;
1086 size_t cFirstLine = 0;
1087 rc = RTStrSplit(ppapszReq[0], strlen(ppapszReq[0]), " ", &ppapszFirstLine, &cFirstLine);
1088 if (RT_SUCCESS(rc))
1089 {
1090 if (cFirstLine < 3) /* At leat the method, path and version has to be present. */
1091 rc = VERR_INVALID_PARAMETER;
1092 }
1093
1094 while (RT_SUCCESS(rc)) /* To use break. */
1095 {
1096 pReq = rtHttpServerReqAlloc();
1097 AssertPtrBreakStmt(pReq, rc = VERR_NO_MEMORY);
1098
1099 /*
1100 * Parse method to use. Method names are case sensitive.
1101 */
1102 const char *pszMethod = ppapszFirstLine[0];
1103 if (!RTStrCmp(pszMethod, "GET")) pReq->enmMethod = RTHTTPMETHOD_GET;
1104 else if (!RTStrCmp(pszMethod, "HEAD")) pReq->enmMethod = RTHTTPMETHOD_HEAD;
1105#ifdef IPRT_HTTP_WITH_WEBDAV
1106 else if (!RTStrCmp(pszMethod, "OPTIONS")) pReq->enmMethod = RTHTTPMETHOD_OPTIONS;
1107 else if (!RTStrCmp(pszMethod, "PROPFIND")) pReq->enmMethod = RTHTTPMETHOD_PROPFIND;
1108#endif
1109 else
1110 {
1111 rc = VERR_NOT_SUPPORTED;
1112 break;
1113 }
1114
1115 /*
1116 * Parse requested path.
1117 */
1118 /** @todo Do URL unescaping here. */
1119 const char *pszPath = ppapszFirstLine[1];
1120 if (!rtHttpServerPathIsValid(pszPath, false /* fIsAbsolute */))
1121 {
1122 rc = VERR_PATH_NOT_FOUND;
1123 break;
1124 }
1125
1126 pReq->pszUrl = RTStrDup(pszPath);
1127
1128 /*
1129 * Parse HTTP version to use.
1130 * We're picky heree: Only HTTP 1.1 is supported by now.
1131 */
1132 const char *pszVer = ppapszFirstLine[2];
1133 if (RTStrCmp(pszVer, RTHTTPVER_1_1_STR)) /** @todo Use RTStrVersionCompare. Later. */
1134 {
1135 rc = VERR_NOT_SUPPORTED;
1136 break;
1137 }
1138
1139 /** @todo Anything else needed for the first line here? */
1140
1141 /*
1142 * Process headers, if any.
1143 */
1144 if (cReq > 1)
1145 {
1146 rc = rtHttpServerParseHeaders(pReq->hHdrLst, cReq - 1, &ppapszReq[1]);
1147 if (RT_SUCCESS(rc))
1148 {
1149 if (RTHttpHeaderListGet(pReq->hHdrLst, "Connection", RTSTR_MAX))
1150 pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Insert the real value here. */
1151 }
1152 }
1153 break;
1154 } /* for (;;) */
1155
1156 /*
1157 * Cleanup.
1158 */
1159
1160 for (size_t i = 0; i < cFirstLine; i++)
1161 RTStrFree(ppapszFirstLine[i]);
1162 RTMemFree(ppapszFirstLine);
1163
1164 for (size_t i = 0; i < cReq; i++)
1165 RTStrFree(ppapszReq[i]);
1166 RTMemFree(ppapszReq);
1167
1168 if (RT_FAILURE(rc))
1169 {
1170 rtHttpServerReqFree(pReq);
1171 pReq = NULL;
1172 }
1173 else
1174 *ppReq = pReq;
1175
1176 return rc;
1177}
1178
1179/**
1180 * Main function for processing client requests.
1181 *
1182 * @returns VBox status code.
1183 * @param pClient Client to process request for.
1184 * @param pszReq Request string to parse and handle.
1185 * @param cbReq Size (in bytes) of request string.
1186 */
1187static int rtHttpServerProcessRequest(PRTHTTPSERVERCLIENT pClient, char *pszReq, size_t cbReq)
1188{
1189 RTHTTPSTATUS enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
1190
1191 PRTHTTPSERVERREQ pReq = NULL; /* Shut up GCC. */
1192 int rc = rtHttpServerParseRequest(pClient, pszReq, cbReq, &pReq);
1193 if (RT_SUCCESS(rc))
1194 {
1195 LogFlowFunc(("Request %s %s\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl));
1196
1197 unsigned i = 0;
1198 for (; i < RT_ELEMENTS(g_aMethodMap); i++)
1199 {
1200 const RTHTTPSERVERMETHOD_ENTRY *pMethodEntry = &g_aMethodMap[i];
1201 if (pReq->enmMethod == pMethodEntry->enmMethod)
1202 {
1203 /* Hand in request to method handler. */
1204 int rcMethod = pMethodEntry->pfnMethod(pClient, pReq);
1205 if (RT_FAILURE(rcMethod))
1206 LogFunc(("Request %s %s failed with %Rrc\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl, rcMethod));
1207
1208 enmSts = rtHttpServerRcToStatus(rcMethod);
1209 break;
1210 }
1211 }
1212
1213 if (i == RT_ELEMENTS(g_aMethodMap))
1214 enmSts = RTHTTPSTATUS_NOTIMPLEMENTED;
1215
1216 rtHttpServerReqFree(pReq);
1217 }
1218 else
1219 enmSts = RTHTTPSTATUS_BADREQUEST;
1220
1221 if (enmSts != RTHTTPSTATUS_INTERNAL_NOT_SET)
1222 {
1223 int rc2 = rtHttpServerSendResponseSimple(pClient, enmSts);
1224 if (RT_SUCCESS(rc))
1225 rc = rc2;
1226 }
1227
1228 LogFlowFuncLeaveRC(rc);
1229 return rc;
1230}
1231
1232/**
1233 * Main loop for processing client requests.
1234 *
1235 * @returns VBox status code.
1236 * @param pClient Client to process requests for.
1237 */
1238static int rtHttpServerClientMain(PRTHTTPSERVERCLIENT pClient)
1239{
1240 int rc;
1241
1242 char szReq[RTHTTPSERVER_MAX_REQ_LEN + 1];
1243
1244 LogFlowFunc(("Client connected\n"));
1245
1246 /* Initialize client state. */
1247 pClient->State.msKeepAlive = 0;
1248
1249 RTMSINTERVAL cWaitMs = RT_INDEFINITE_WAIT; /* The first wait always waits indefinitely. */
1250 uint64_t tsLastReadMs = 0;
1251
1252 for (;;)
1253 {
1254 rc = RTTcpSelectOne(pClient->hSocket, cWaitMs);
1255 if (RT_FAILURE(rc))
1256 {
1257 LogFlowFunc(("RTTcpSelectOne=%Rrc (cWaitMs=%RU64)\n", rc, cWaitMs));
1258 if (rc == VERR_TIMEOUT)
1259 {
1260 if (pClient->State.msKeepAlive) /* Keep alive handling needed? */
1261 {
1262 if (!tsLastReadMs)
1263 tsLastReadMs = RTTimeMilliTS();
1264 const uint64_t tsDeltaMs = pClient->State.msKeepAlive - (RTTimeMilliTS() - tsLastReadMs);
1265 LogFlowFunc(("tsLastReadMs=%RU64, tsDeltaMs=%RU64\n", tsLastReadMs, tsDeltaMs));
1266 Log3Func(("Keep alive active (%RU32ms): %RU64ms remaining\n", pClient->State.msKeepAlive, tsDeltaMs));
1267 if ( tsDeltaMs > cWaitMs
1268 && tsDeltaMs < pClient->State.msKeepAlive)
1269 continue;
1270
1271 LogFunc(("Keep alive active: Client did not respond within %RU32ms, closing\n", pClient->State.msKeepAlive));
1272 rc = VINF_SUCCESS;
1273 break;
1274 }
1275 }
1276
1277 break;
1278 }
1279
1280 LogFlowFunc(("Reading client request ...\n"));
1281
1282 tsLastReadMs = RTTimeMilliTS();
1283 cWaitMs = 200; /* All consequtive waits do busy waiting for now. */
1284
1285 char *pszReq = szReq;
1286 size_t cbRead;
1287 size_t cbToRead = sizeof(szReq);
1288 size_t cbReadTotal = 0;
1289
1290 do
1291 {
1292 rc = RTTcpReadNB(pClient->hSocket, pszReq, cbToRead, &cbRead);
1293 if (RT_FAILURE(rc))
1294 break;
1295
1296 if (!cbRead)
1297 break;
1298
1299 /* Make sure to terminate string read so far. */
1300 pszReq[cbRead] = '\0';
1301
1302 /* End of request reached? */
1303 /** @todo BUGBUG Improve this. */
1304 char *pszEOR = RTStrStr(&pszReq[cbReadTotal], "\r\n\r\n");
1305 if (pszEOR)
1306 {
1307 cbReadTotal = pszEOR - pszReq;
1308 *pszEOR = '\0';
1309 break;
1310 }
1311
1312 AssertBreak(cbToRead >= cbRead);
1313 cbToRead -= cbRead;
1314 cbReadTotal += cbRead;
1315 AssertBreak(cbReadTotal <= sizeof(szReq));
1316 pszReq += cbRead;
1317
1318 } while (cbToRead);
1319
1320 if ( RT_SUCCESS(rc)
1321 && cbReadTotal)
1322 {
1323 LogFlowFunc(("Received client request (%zu bytes)\n", cbReadTotal));
1324
1325 rtHttpServerLogProto(pClient, false /* fWrite */, szReq);
1326
1327 rc = rtHttpServerProcessRequest(pClient, szReq, cbReadTotal);
1328 }
1329 else
1330 break;
1331
1332 } /* for */
1333
1334 if (RT_FAILURE(rc))
1335 {
1336 switch (rc)
1337 {
1338 case VERR_NET_CONNECTION_RESET_BY_PEER:
1339 {
1340 LogFunc(("Client closed the connection\n"));
1341 rc = VINF_SUCCESS;
1342 break;
1343 }
1344
1345 default:
1346 LogFunc(("Client processing failed with %Rrc\n", rc));
1347 break;
1348 }
1349 }
1350
1351 LogFlowFuncLeaveRC(rc);
1352 return rc;
1353}
1354
1355/**
1356 * Per-client thread for serving the server's control connection.
1357 *
1358 * @returns VBox status code.
1359 * @param hSocket Socket handle to use for the control connection.
1360 * @param pvUser User-provided arguments. Of type PRTHTTPSERVERINTERNAL.
1361 */
1362static DECLCALLBACK(int) rtHttpServerClientThread(RTSOCKET hSocket, void *pvUser)
1363{
1364 PRTHTTPSERVERINTERNAL pThis = (PRTHTTPSERVERINTERNAL)pvUser;
1365 RTHTTPSERVER_VALID_RETURN(pThis);
1366
1367 LogFlowFuncEnter();
1368
1369 RTHTTPSERVERCLIENT Client;
1370 RT_ZERO(Client);
1371
1372 Client.pServer = pThis;
1373 Client.hSocket = hSocket;
1374
1375 return rtHttpServerClientMain(&Client);
1376}
1377
1378RTR3DECL(int) RTHttpServerCreate(PRTHTTPSERVER hHttpServer, const char *pszAddress, uint16_t uPort,
1379 PRTHTTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser)
1380{
1381 AssertPtrReturn(hHttpServer, VERR_INVALID_POINTER);
1382 AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
1383 AssertReturn (uPort, VERR_INVALID_PARAMETER);
1384 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER);
1385 /* pvUser is optional. */
1386
1387 int rc;
1388
1389 PRTHTTPSERVERINTERNAL pThis = (PRTHTTPSERVERINTERNAL)RTMemAllocZ(sizeof(RTHTTPSERVERINTERNAL));
1390 if (pThis)
1391 {
1392 pThis->u32Magic = RTHTTPSERVER_MAGIC;
1393 pThis->Callbacks = *pCallbacks;
1394 pThis->pvUser = pvUser;
1395 pThis->cbUser = cbUser;
1396
1397 rc = RTTcpServerCreate(pszAddress, uPort, RTTHREADTYPE_DEFAULT, "httpsrv",
1398 rtHttpServerClientThread, pThis /* pvUser */, &pThis->pTCPServer);
1399 if (RT_SUCCESS(rc))
1400 {
1401 *hHttpServer = (RTHTTPSERVER)pThis;
1402 }
1403 }
1404 else
1405 rc = VERR_NO_MEMORY;
1406
1407 return rc;
1408}
1409
1410RTR3DECL(int) RTHttpServerDestroy(RTHTTPSERVER hHttpServer)
1411{
1412 if (hHttpServer == NIL_RTHTTPSERVER)
1413 return VINF_SUCCESS;
1414
1415 PRTHTTPSERVERINTERNAL pThis = hHttpServer;
1416 RTHTTPSERVER_VALID_RETURN(pThis);
1417
1418 AssertPtr(pThis->pTCPServer);
1419
1420 int rc = VINF_SUCCESS;
1421
1422 PRTHTTPSERVERCALLBACKS pCallbacks = &pThis->Callbacks;
1423 if (pCallbacks->pfnDestroy)
1424 {
1425 RTHTTPCALLBACKDATA Data = { NULL /* pClient */, pThis->pvUser, pThis->cbUser };
1426 rc = pCallbacks->pfnDestroy(&Data);
1427 }
1428
1429 if (RT_SUCCESS(rc))
1430 {
1431 rc = RTTcpServerDestroy(pThis->pTCPServer);
1432 if (RT_SUCCESS(rc))
1433 {
1434 pThis->u32Magic = RTHTTPSERVER_MAGIC_DEAD;
1435
1436 RTMemFree(pThis);
1437 }
1438 }
1439
1440 return rc;
1441}
Note: See TracBrowser for help on using the repository browser.

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