VirtualBox

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

Last change on this file since 102899 was 102562, checked in by vboxsync, 12 months ago

IPRT/http: Fixed a memory leak, updated docs. bugref:9437

  • 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 102562 2023-12-11 08:32:42Z 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
212RTR3DECL(void) RTHttpHeaderListDestroy(RTHTTPHEADERLIST hHdrLst)
213{
214 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
215 RTHTTPHEADERLIST_VALID_RETURN_VOID(pThis);
216
217 rtHttpHeaderListRemoveAll(pThis);
218
219 RTMemFree(hHdrLst);
220 hHdrLst = NIL_RTHTTPHEADERLIST;
221}
222
223
224static void rtHttpHeaderListRemoveAll(PRTHTTPHEADERLISTINTERNAL pThis)
225{
226 PRTHTTPHEADERENTRY pEntry, pNext;
227 RTListForEachSafe(&pThis->List, pEntry, pNext, RTHTTPHEADERENTRY, Node)
228 {
229 RTListNodeRemove(&pEntry->Node);
230 RTMemFree(pEntry);
231 }
232}
233
234/**
235 * Worker for RTHttpHeaderListSet and RTHttpHeaderListAdd.
236 *
237 * @returns IPRT status code.
238 * @param pThis The HTTP header list instance.
239 * @param pchName The field name. Does not need to be terminated.
240 * @param cchName The field name length.
241 * @param pchValue The field value. Does not need to be terminated.
242 * @param cchValue The field value length.
243 * @param fFlags RTHTTPADDHDR_F_XXX.
244 */
245static int rtHttpHeaderListAddWorker(PRTHTTPHEADERLISTINTERNAL pThis,
246 const char *pchName, size_t cchName, const char *pchValue, size_t cchValue, uint32_t fFlags)
247{
248 /*
249 * Create the list entry.
250 */
251 size_t cbData = cchName + 2 + cchValue + 1;
252 PRTHTTPHEADERENTRY pHdr = (PRTHTTPHEADERENTRY)RTMemAlloc(RT_UOFFSETOF_DYN(RTHTTPHEADERENTRY, szData[cbData]));
253 if (pHdr)
254 {
255 pHdr->cchName = (uint32_t)cchName;
256 pHdr->offValue = (uint32_t)(cchName + 2);
257 char *psz = pHdr->szData;
258 memcpy(psz, pchName, cchName);
259 psz += cchName;
260 *psz++ = ':';
261 *psz++ = ' ';
262 memcpy(psz, pchValue, cchValue);
263 psz[cchValue] = '\0';
264
265 /*
266 * Appending to an existing list requires no cURL interaction.
267 */
268 AssertCompile(RTHTTPHEADERLISTADD_F_FRONT != 0);
269 if (!(fFlags & RTHTTPHEADERLISTADD_F_FRONT))
270 {
271 RTListAppend(&pThis->List, &pHdr->Node);
272 return VINF_SUCCESS;
273 }
274
275 /*
276 * When prepending or adding the first header we need to inform cURL
277 * about the new list head.
278 */
279 RTListPrepend(&pThis->List, &pHdr->Node);
280 return VINF_SUCCESS;
281 }
282 return VERR_NO_MEMORY;
283}
284
285
286RTR3DECL(int) RTHttpHeaderListSet(RTHTTPHEADERLIST hHdrLst,
287 size_t cHeaders, const char * const *papszHeaders)
288{
289 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
290 RTHTTPHEADERLIST_VALID_RETURN(pThis);
291
292 /*
293 * Drop old headers and reset state.
294 */
295 rtHttpHeaderListRemoveAll(pThis);
296
297 /*
298 * We're done if no headers specified.
299 */
300 if (!cHeaders)
301 return VINF_SUCCESS;
302
303 /*
304 * Add the headers, one by one.
305 */
306 int rc = VINF_SUCCESS;
307 for (size_t i = 0; i < cHeaders; i++)
308 {
309 const char *pszHeader = papszHeaders[i];
310 size_t cchHeader = strlen(pszHeader);
311 size_t cchName = (const char *)memchr(pszHeader, ':', cchHeader) - pszHeader;
312 AssertBreakStmt(cchName < cchHeader, rc = VERR_INVALID_PARAMETER);
313 size_t offValue = RT_C_IS_BLANK(pszHeader[cchName + 1]) ? cchName + 2 : cchName + 1;
314 rc = rtHttpHeaderListAddWorker(pThis, pszHeader, cchName, &pszHeader[offValue], cchHeader - offValue,
315 RTHTTPHEADERLISTADD_F_BACK);
316 AssertRCBreak(rc);
317 }
318 if (RT_SUCCESS(rc))
319 return rc;
320 rtHttpHeaderListRemoveAll(pThis);
321 return rc;
322}
323
324
325RTR3DECL(int) RTHttpHeaderListAdd(RTHTTPHEADERLIST hHdrLst,
326 const char *pszField, const char *pszValue, size_t cchValue, uint32_t fFlags)
327{
328 /*
329 * Validate input and calc string lengths.
330 */
331 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
332 RTHTTPHEADERLIST_VALID_RETURN(pThis);
333 AssertReturn(!(fFlags & ~RTHTTPHEADERLISTADD_F_BACK), VERR_INVALID_FLAGS);
334 AssertPtr(pszField);
335 size_t const cchField = strlen(pszField);
336 AssertReturn(cchField > 0, VERR_INVALID_PARAMETER);
337 AssertReturn(pszField[cchField - 1] != ':', VERR_INVALID_PARAMETER);
338 AssertReturn(!RT_C_IS_SPACE(pszField[cchField - 1]), VERR_INVALID_PARAMETER);
339#ifdef RT_STRICT
340 for (size_t i = 0; i < cchField; i++)
341 {
342 char const ch = pszField[i];
343 Assert(RT_C_IS_PRINT(ch) && ch != ':');
344 }
345#endif
346
347 AssertPtr(pszValue);
348 if (cchValue == RTSTR_MAX)
349 cchValue = strlen(pszValue);
350
351 /*
352 * Just pass it along to the worker.
353 */
354 return rtHttpHeaderListAddWorker(pThis, pszField, cchField, pszValue, cchValue, fFlags);
355}
356
357
358RTR3DECL(const char *) RTHttpHeaderListGet(RTHTTPHEADERLIST hHdrLst, const char *pszField, size_t cchField)
359{
360 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
361 RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, NULL);
362
363 if (cchField == RTSTR_MAX)
364 cchField = strlen(pszField);
365
366 PRTHTTPHEADERENTRY pEntry;
367 RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
368 {
369 if ( pEntry->cchName == cchField
370 && RTStrNICmpAscii(pEntry->szData, pszField, cchField) == 0)
371 return &pEntry->szData[pEntry->offValue];
372 }
373 return NULL;
374}
375
376
377RTR3DECL(size_t) RTHttpHeaderListGetCount(RTHTTPHEADERLIST hHdrLst)
378{
379 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
380 RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, 0);
381
382 /* Note! Only for test cases and debugging, so we don't care about performance. */
383 size_t cHeaders = 0;
384 PRTHTTPHEADERENTRY pEntry;
385 RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
386 cHeaders++;
387 return cHeaders;
388}
389
390
391RTR3DECL(const char *) RTHttpHeaderListGetByOrdinal(RTHTTPHEADERLIST hHdrLst, size_t iOrdinal)
392{
393 PRTHTTPHEADERLISTINTERNAL pThis = hHdrLst;
394 RTHTTPHEADERLIST_VALID_RETURN_RC(pThis, NULL);
395
396 /* Note! Only for test cases and debugging, so we don't care about performance. */
397 PRTHTTPHEADERENTRY pEntry;
398 RTListForEach(&pThis->List, pEntry, RTHTTPHEADERENTRY, Node)
399 {
400 if (iOrdinal == 0)
401 return pEntry->szData;
402 iOrdinal--;
403 }
404
405 return NULL;
406}
407
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