VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/http.cpp@ 100192

Last change on this file since 100192 was 98103, checked in by vboxsync, 23 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: 16.3 KB
Line 
1/* $Id: http.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - HTTP common API.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_HTTP
42#include <iprt/http-common.h>
43#include "internal/iprt.h"
44
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/ctype.h>
48#include <iprt/log.h>
49#include <iprt/mem.h>
50#include <iprt/string.h>
51
52#include "internal/magics.h"
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
59#define RTHTTPHEADERLIST_VALID_RETURN_RC(hList, a_rc) \
60 do { \
61 AssertPtrReturn((hList), (a_rc)); \
62 AssertReturn((hList)->u32Magic == RTHTTPHEADERLIST_MAGIC, (a_rc)); \
63 } while (0)
64
65/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
66#define RTHTTPHEADERLIST_VALID_RETURN(hList) RTHTTPHEADERLIST_VALID_RETURN_RC((hList), VERR_INVALID_HANDLE)
67
68/** Validates a handle and returns (void) if not valid. */
69#define RTHTTPHEADERLIST_VALID_RETURN_VOID(hList) \
70 do { \
71 AssertPtrReturnVoid(hList); \
72 AssertReturnVoid((hList)->u32Magic == RTHTTPHEADERLIST_MAGIC); \
73 } while (0)
74
75
76/*********************************************************************************************************************************
77* Internal structs. *
78*********************************************************************************************************************************/
79/**
80 * HTTP header list, internal definition.
81 */
82typedef struct RTHTTPHEADERLISTINTERNAL
83{
84 /** The list node. */
85 RTLISTANCHOR List;
86 /** Magic value. */
87 uint32_t u32Magic;
88} RTHTTPHEADERLISTINTERNAL;
89/** Pointer to an internal HTTP header list. */
90typedef RTHTTPHEADERLISTINTERNAL *PRTHTTPHEADERLISTINTERNAL;
91
92
93/*********************************************************************************************************************************
94* Prototypes. *
95*********************************************************************************************************************************/
96static void rtHttpHeaderListRemoveAll(PRTHTTPHEADERLISTINTERNAL pThis);
97
98
99/*********************************************************************************************************************************
100* Lookup / conversion functions *
101*********************************************************************************************************************************/
102
103RTR3DECL(const char *) RTHttpMethodToStr(RTHTTPMETHOD enmMethod)
104{
105 switch (enmMethod)
106 {
107 case RTHTTPMETHOD_INVALID: return "invalid";
108 case RTHTTPMETHOD_GET: return "GET";
109 case RTHTTPMETHOD_PUT: return "PUT";
110 case RTHTTPMETHOD_POST: return "POST";
111 case RTHTTPMETHOD_PATCH: return "PATCH";
112 case RTHTTPMETHOD_DELETE: return "DELETE";
113 case RTHTTPMETHOD_HEAD: return "HEAD";
114 case RTHTTPMETHOD_OPTIONS: return "OPTIONS";
115 case RTHTTPMETHOD_TRACE: return "TRACE";
116#ifdef IPRT_HTTP_WITH_WEBDAV
117 case RTHTTPMETHOD_PROPFIND: return "PROPFIND";
118#endif
119 case RTHTTPMETHOD_END:
120 RT_FALL_THROUGH();
121 case RTHTTPMETHOD_32BIT_HACK:
122 break;
123 }
124 return "unknown";
125}
126
127RTR3DECL(const char *) RTHttpStatusToStr(RTHTTPSTATUS enmSts)
128{
129 switch (enmSts)
130 {
131 case RTHTTPSTATUS_OK : return "OK";
132 case RTHTTPSTATUS_CREATED : return "Created";
133 case RTHTTPSTATUS_ACCEPTED : return "Accepted";
134 case RTHTTPSTATUS_NONAUTHORITATIVEINFORMATION : return "Non-Authoritative Information";
135 case RTHTTPSTATUS_NOCONTENT : return "No Content";
136 case RTHTTPSTATUS_RESETCONTENT : return "Reset Content";
137 case RTHTTPSTATUS_PARTIALCONTENT : return "Partial Content";
138 case RTHTTPSTATUS_MULTISTATUS : return "Multi-Status";
139 case RTHTTPSTATUS_ALREADYREPORTED : return "Already Reported";
140 case RTHTTPSTATUS_IMUSED : return "IM Used";
141
142 case RTHTTPSTATUS_BADREQUEST : return "Bad Request";
143 case RTHTTPSTATUS_UNAUTHORIZED : return "Unauthorized";
144 case RTHTTPSTATUS_PAYMENTREQUIRED : return "Payment Required";
145 case RTHTTPSTATUS_FORBIDDEN : return "Forbidden";
146 case RTHTTPSTATUS_NOTFOUND : return "Not Found";
147 case RTHTTPSTATUS_METHODNOTALLOWED : return "Method Not Allowed";
148 case RTHTTPSTATUS_NOTACCEPTABLE : return "Not Acceptable";
149 case RTHTTPSTATUS_PROXYAUTHENTICATIONREQUIRED : return "Proxy Authentication Required";
150 case RTHTTPSTATUS_REQUESTTIMEOUT : return "Request Timeout";
151 case RTHTTPSTATUS_CONFLICT : return "Conflict";
152 case RTHTTPSTATUS_GONE : return "Gone";
153 case RTHTTPSTATUS_LENGTHREQUIRED : return "Length Required";
154 case RTHTTPSTATUS_PRECONDITIONFAILED : return "Precondition Failed";
155 case RTHTTPSTATUS_PAYLOADTOOLARGE : return "Payload Too Large";
156 case RTHTTPSTATUS_URITOOLONG : return "URI Too Long";
157 case RTHTTPSTATUS_UNSUPPORTEDMEDIATYPE : return "Unsupported Media Type";
158 case RTHTTPSTATUS_RANGENOTSATISFIABLE : return "Range Not Satisfiable";
159 case RTHTTPSTATUS_EXPECTATIONFAILED : return "Expectation Failed";
160 case RTHTTPSTATUS_IMATEAPOT : return "I'm a teapot";
161 case RTHTTPSTATUS_UNPROCESSABLEENTITY : return "Unprocessable Entity";
162 case RTHTTPSTATUS_LOCKED : return "Locked";
163 case RTHTTPSTATUS_FAILEDDEPENDENCY : return "Failed Dependency";
164 case RTHTTPSTATUS_UPGRADEREQUIRED : return "Upgrade Required";
165 case RTHTTPSTATUS_PRECONDITIONREQUIRED : return "Precondition Required";
166 case RTHTTPSTATUS_TOOMANYREQUESTS : return "Too Many Requests";
167 case RTHTTPSTATUS_REQUESTHEADERFIELDSTOOLARGE : return "Request Header Fields Too Large";
168 case RTHTTPSTATUS_UNAVAILABLEFORLEGALREASONS : return "Unavailable For Legal Reasons";
169
170 case RTHTTPSTATUS_INTERNALSERVERERROR : return "Internal Server Error";
171 case RTHTTPSTATUS_NOTIMPLEMENTED : return "Not Implemented";
172 case RTHTTPSTATUS_BADGATEWAY : return "Bad Gateway";
173 case RTHTTPSTATUS_SERVICEUNAVAILABLE : return "Service Unavailable";
174 case RTHTTPSTATUS_GATEWAYTIMEOUT : return "Gateway Time-out";
175 case RTHTTPSTATUS_HTTPVERSIONNOTSUPPORTED : return "HTTP Version Not Supported";
176 case RTHTTPSTATUS_VARIANTALSONEGOTIATES : return "Variant Also Negotiates";
177 case RTHTTPSTATUS_INSUFFICIENTSTORAGE : return "Insufficient Storage";
178 case RTHTTPSTATUS_LOOPDETECTED : return "Loop Detected";
179 case RTHTTPSTATUS_NOTEXTENDED : return "Not Extended";
180 case RTHTTPSTATUS_NETWORKAUTHENTICATIONREQUIRED: return "Network Authentication Required";
181
182 default: break;
183 }
184
185 AssertFailed();
186 return "<Not implemented>";
187}
188
189
190/*********************************************************************************************************************************
191* HTTP Header List *
192*********************************************************************************************************************************/
193
194RTR3DECL(int) RTHttpHeaderListInit(PRTHTTPHEADERLIST hHdrLst)
195{
196 PRTHTTPHEADERLISTINTERNAL pThis = (PRTHTTPHEADERLISTINTERNAL)RTMemAllocZ(sizeof(RTHTTPHEADERLISTINTERNAL));
197 if (pThis)
198 {
199 pThis->u32Magic = RTHTTPHEADERLIST_MAGIC;
200
201 RTListInit(&pThis->List);
202
203 *hHdrLst = (RTHTTPHEADERLIST)pThis;
204
205 return VINF_SUCCESS;
206 }
207
208 return VERR_NO_MEMORY;
209}
210
211
212/**
213 * Destroys the headers associated with this list (w/o telling cURL about it).
214 *
215 * @param hHdrLst The HTTP header list instance.
216 */
217RTR3DECL(void) RTHttpHeaderListDestroy(RTHTTPHEADERLIST hHdrLst)
218{
219 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
220 RTHTTPHEADERLIST_VALID_RETURN_VOID(pThis);
221
222 rtHttpHeaderListRemoveAll(pThis);
223}
224
225
226static void rtHttpHeaderListRemoveAll(PRTHTTPHEADERLISTINTERNAL pThis)
227{
228 PRTHTTPHEADERENTRY pEntry, pNext;
229 RTListForEachSafe(&pThis->List, pEntry, pNext, RTHTTPHEADERENTRY, Node)
230 {
231 RTListNodeRemove(&pEntry->Node);
232 RTMemFree(pEntry);
233 }
234}
235
236/**
237 * Worker for RTHttpHeaderListSet and RTHttpHeaderListAdd.
238 *
239 * @returns IPRT status code.
240 * @param pThis The HTTP header list instance.
241 * @param pchName The field name. Does not need to be terminated.
242 * @param cchName The field name length.
243 * @param pchValue The field value. Does not need to be terminated.
244 * @param cchValue The field value length.
245 * @param fFlags RTHTTPADDHDR_F_XXX.
246 */
247static int rtHttpHeaderListAddWorker(PRTHTTPHEADERLISTINTERNAL pThis,
248 const char *pchName, size_t cchName, const char *pchValue, size_t cchValue, uint32_t fFlags)
249{
250 /*
251 * Create the list entry.
252 */
253 size_t cbData = cchName + 2 + cchValue + 1;
254 PRTHTTPHEADERENTRY pHdr = (PRTHTTPHEADERENTRY)RTMemAlloc(RT_UOFFSETOF_DYN(RTHTTPHEADERENTRY, szData[cbData]));
255 if (pHdr)
256 {
257 pHdr->cchName = (uint32_t)cchName;
258 pHdr->offValue = (uint32_t)(cchName + 2);
259 char *psz = pHdr->szData;
260 memcpy(psz, pchName, cchName);
261 psz += cchName;
262 *psz++ = ':';
263 *psz++ = ' ';
264 memcpy(psz, pchValue, cchValue);
265 psz[cchValue] = '\0';
266
267 /*
268 * Appending to an existing list requires no cURL interaction.
269 */
270 AssertCompile(RTHTTPHEADERLISTADD_F_FRONT != 0);
271 if (!(fFlags & RTHTTPHEADERLISTADD_F_FRONT))
272 {
273 RTListAppend(&pThis->List, &pHdr->Node);
274 return VINF_SUCCESS;
275 }
276
277 /*
278 * When prepending or adding the first header we need to inform cURL
279 * about the new list head.
280 */
281 RTListPrepend(&pThis->List, &pHdr->Node);
282 return VINF_SUCCESS;
283 }
284 return VERR_NO_MEMORY;
285}
286
287
288RTR3DECL(int) RTHttpHeaderListSet(RTHTTPHEADERLIST hHdrLst,
289 size_t cHeaders, const char * const *papszHeaders)
290{
291 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
292 RTHTTPHEADERLIST_VALID_RETURN(pThis);
293
294 /*
295 * Drop old headers and reset state.
296 */
297 rtHttpHeaderListRemoveAll(pThis);
298
299 /*
300 * We're done if no headers specified.
301 */
302 if (!cHeaders)
303 return VINF_SUCCESS;
304
305 /*
306 * Add the headers, one by one.
307 */
308 int rc = VINF_SUCCESS;
309 for (size_t i = 0; i < cHeaders; i++)
310 {
311 const char *pszHeader = papszHeaders[i];
312 size_t cchHeader = strlen(pszHeader);
313 size_t cchName = (const char *)memchr(pszHeader, ':', cchHeader) - pszHeader;
314 AssertBreakStmt(cchName < cchHeader, rc = VERR_INVALID_PARAMETER);
315 size_t offValue = RT_C_IS_BLANK(pszHeader[cchName + 1]) ? cchName + 2 : cchName + 1;
316 rc = rtHttpHeaderListAddWorker(pThis, pszHeader, cchName, &pszHeader[offValue], cchHeader - offValue,
317 RTHTTPHEADERLISTADD_F_BACK);
318 AssertRCBreak(rc);
319 }
320 if (RT_SUCCESS(rc))
321 return rc;
322 rtHttpHeaderListRemoveAll(pThis);
323 return rc;
324}
325
326
327RTR3DECL(int) RTHttpHeaderListAdd(RTHTTPHEADERLIST hHdrLst,
328 const char *pszField, const char *pszValue, size_t cchValue, uint32_t fFlags)
329{
330 /*
331 * Validate input and calc string lengths.
332 */
333 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
334 RTHTTPHEADERLIST_VALID_RETURN(pThis);
335 AssertReturn(!(fFlags & ~RTHTTPHEADERLISTADD_F_BACK), VERR_INVALID_FLAGS);
336 AssertPtr(pszField);
337 size_t const cchField = strlen(pszField);
338 AssertReturn(cchField > 0, VERR_INVALID_PARAMETER);
339 AssertReturn(pszField[cchField - 1] != ':', VERR_INVALID_PARAMETER);
340 AssertReturn(!RT_C_IS_SPACE(pszField[cchField - 1]), VERR_INVALID_PARAMETER);
341#ifdef RT_STRICT
342 for (size_t i = 0; i < cchField; i++)
343 {
344 char const ch = pszField[i];
345 Assert(RT_C_IS_PRINT(ch) && ch != ':');
346 }
347#endif
348
349 AssertPtr(pszValue);
350 if (cchValue == RTSTR_MAX)
351 cchValue = strlen(pszValue);
352
353 /*
354 * Just pass it along to the worker.
355 */
356 return rtHttpHeaderListAddWorker(pThis, pszField, cchField, pszValue, cchValue, fFlags);
357}
358
359
360RTR3DECL(const char *) RTHttpHeaderListGet(RTHTTPHEADERLIST hHdrLst, const char *pszField, size_t cchField)
361{
362 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
363 RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, NULL);
364
365 if (cchField == RTSTR_MAX)
366 cchField = strlen(pszField);
367
368 PRTHTTPHEADERENTRY pEntry;
369 RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
370 {
371 if ( pEntry->cchName == cchField
372 && RTStrNICmpAscii(pEntry->szData, pszField, cchField) == 0)
373 return &pEntry->szData[pEntry->offValue];
374 }
375 return NULL;
376}
377
378
379RTR3DECL(size_t) RTHttpHeaderListGetCount(RTHTTPHEADERLIST hHdrLst)
380{
381 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
382 RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, 0);
383
384 /* Note! Only for test cases and debugging, so we don't care about performance. */
385 size_t cHeaders = 0;
386 PRTHTTPHEADERENTRY pEntry;
387 RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
388 cHeaders++;
389 return cHeaders;
390}
391
392
393RTR3DECL(const char *) RTHttpHeaderListGetByOrdinal(RTHTTPHEADERLIST hHdrLst, size_t iOrdinal)
394{
395 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
396 RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, NULL);
397
398 /* Note! Only for test cases and debugging, so we don't care about performance. */
399 PRTHTTPHEADERENTRY pEntry;
400 RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
401 {
402 if (iOrdinal == 0)
403 return pEntry->szData;
404 iOrdinal--;
405 }
406
407 return NULL;
408}
409
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