VirtualBox

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

Last change on this file since 96159 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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