VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp

Last change on this file was 106061, checked in by vboxsync, 3 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: 14.3 KB
Line 
1/* $Id: RTCRestClientResponseBase.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - C++ REST, RTCRestClientResponseBase implementation.
4 */
5
6/*
7 * Copyright (C) 2018-2024 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_REST
42#include <iprt/cpp/restclient.h>
43
44#include <iprt/ctype.h>
45#include <iprt/err.h>
46#include <iprt/log.h>
47#include <iprt/cpp/reststringmap.h>
48
49
50/**
51 * Default constructor.
52 */
53RTCRestClientResponseBase::RTCRestClientResponseBase() RT_NOEXCEPT
54 : m_rcStatus(VERR_WRONG_ORDER)
55 , m_rcHttp(VERR_NOT_AVAILABLE)
56 , m_pErrInfo(NULL)
57{
58}
59
60
61/**
62 * Destructor.
63 */
64RTCRestClientResponseBase::~RTCRestClientResponseBase()
65{
66 deleteErrInfo();
67}
68
69
70/**
71 * Copy constructor.
72 */
73RTCRestClientResponseBase::RTCRestClientResponseBase(RTCRestClientResponseBase const &a_rThat)
74 : m_rcStatus(a_rThat.m_rcStatus)
75 , m_rcHttp(a_rThat.m_rcHttp)
76 , m_pErrInfo(NULL)
77 , m_strContentType(a_rThat.m_strContentType)
78{
79 if (a_rThat.m_pErrInfo)
80 copyErrInfo(a_rThat.m_pErrInfo);
81}
82
83
84/**
85 * Copy assignment operator.
86 */
87RTCRestClientResponseBase &RTCRestClientResponseBase::operator=(RTCRestClientResponseBase const &a_rThat)
88{
89 m_rcStatus = a_rThat.m_rcStatus;
90 m_rcHttp = a_rThat.m_rcHttp;
91 m_strContentType = a_rThat.m_strContentType;
92 if (a_rThat.m_pErrInfo)
93 copyErrInfo(a_rThat.m_pErrInfo);
94 else if (m_pErrInfo)
95 deleteErrInfo();
96
97 return *this;
98}
99
100
101void RTCRestClientResponseBase::reset() RT_NOEXCEPT
102{
103 /* Return to default constructor state. */
104 m_rcStatus = VERR_WRONG_ORDER;
105 m_rcHttp = VERR_NOT_AVAILABLE;
106 if (m_pErrInfo)
107 deleteErrInfo();
108 m_strContentType.setNull();
109}
110
111
112int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp) RT_NOEXCEPT
113{
114 int rc = RTHttpSetHeaderCallback(a_hHttp, receiveHttpHeaderCallback, this);
115 AssertRCReturn(rc, rc);
116
117 return VINF_SUCCESS;
118}
119
120
121void RTCRestClientResponseBase::receiveComplete(int a_rcStatus, RTHTTP a_hHttp) RT_NOEXCEPT
122{
123 RT_NOREF_PV(a_hHttp);
124 m_rcStatus = a_rcStatus;
125 if (a_rcStatus >= 0)
126 m_rcHttp = a_rcStatus;
127
128 int rc = RTHttpSetHeaderCallback(a_hHttp, NULL, NULL);
129 AssertRC(rc);
130}
131
132
133int RTCRestClientResponseBase::consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField,
134 const char *a_pchValue, size_t a_cchValue) RT_NOEXCEPT
135{
136 if ( a_uMatchWord == RTHTTP_MAKE_HDR_MATCH_WORD(sizeof("Content-Type") - 1, 'c', 'o', 'n')
137 && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE("Content-Type")) == 0)
138 {
139 int rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
140 AssertRC(rc);
141 if (RT_SUCCESS(rc))
142 return m_strContentType.assignNoThrow(a_pchValue, a_cchValue);
143 }
144 RT_NOREF(a_cchField);
145 return VINF_SUCCESS;
146}
147
148
149/*static*/ DECLCALLBACK(int)
150RTCRestClientResponseBase::receiveHttpHeaderCallback(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField,
151 const char *pchValue, size_t cchValue, void *pvUser) RT_NOEXCEPT
152{
153 RTCRestClientResponseBase *pThis = (RTCRestClientResponseBase *)pvUser;
154 RT_NOREF(hHttp);
155 return pThis->consumeHeader(uMatchWord, pchField, cchField, pchValue, cchValue);
156}
157
158
159void RTCRestClientResponseBase::consumeBody(const char *a_pchData, size_t a_cbData) RT_NOEXCEPT
160{
161 RT_NOREF(a_pchData, a_cbData);
162}
163
164
165void RTCRestClientResponseBase::receiveFinal() RT_NOEXCEPT
166{
167}
168
169
170PRTERRINFO RTCRestClientResponseBase::getErrInfoInternal(void) RT_NOEXCEPT
171{
172 if (m_pErrInfo)
173 return m_pErrInfo;
174 size_t cbMsg = _4K;
175 m_pErrInfo = (PRTERRINFO)RTMemAllocZ(sizeof(*m_pErrInfo) + cbMsg);
176 if (m_pErrInfo)
177 return RTErrInfoInit(m_pErrInfo, (char *)(m_pErrInfo + 1), cbMsg);
178 return NULL;
179}
180
181
182void RTCRestClientResponseBase::deleteErrInfo(void) RT_NOEXCEPT
183{
184 if (m_pErrInfo)
185 {
186 RTMemFree(m_pErrInfo);
187 m_pErrInfo = NULL;
188 }
189}
190
191
192void RTCRestClientResponseBase::copyErrInfo(PCRTERRINFO pErrInfo) RT_NOEXCEPT
193{
194 deleteErrInfo();
195 m_pErrInfo = (PRTERRINFO)RTMemDup(pErrInfo, pErrInfo->cbMsg + sizeof(*pErrInfo));
196 if (m_pErrInfo)
197 {
198 m_pErrInfo->pszMsg = (char *)(m_pErrInfo + 1);
199 m_pErrInfo->apvReserved[0] = NULL;
200 m_pErrInfo->apvReserved[1] = NULL;
201 }
202}
203
204
205int RTCRestClientResponseBase::addError(int rc, const char *pszFormat, ...) RT_NOEXCEPT
206{
207 PRTERRINFO pErrInfo = getErrInfoInternal();
208 if (pErrInfo)
209 {
210 va_list va;
211 va_start(va, pszFormat);
212 if ( !RTErrInfoIsSet(pErrInfo)
213 || pErrInfo->cbMsg == 0
214 || pErrInfo->pszMsg[pErrInfo->cbMsg - 1] == '\n')
215 RTErrInfoAddV(pErrInfo, rc, pszFormat, va);
216 else
217 RTErrInfoAddF(pErrInfo, rc, "\n%N", pszFormat, &va);
218 va_end(va);
219 }
220 if (RT_SUCCESS(m_rcStatus) && RT_FAILURE_NP(rc))
221 m_rcStatus = rc;
222 return rc;
223}
224
225
226RTCRestClientResponseBase::PrimaryJsonCursorForBody::PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName,
227 RTCRestClientResponseBase *a_pThat) RT_NOEXCEPT
228 : RTCRestJsonPrimaryCursor(hValue, pszName, a_pThat->getErrInfoInternal())
229 , m_pThat(a_pThat)
230{
231}
232
233
234int RTCRestClientResponseBase::PrimaryJsonCursorForBody::addError(RTCRestJsonCursor const &a_rCursor, int a_rc,
235 const char *a_pszFormat, ...) RT_NOEXCEPT
236{
237 va_list va;
238 va_start(va, a_pszFormat);
239 char szPath[256];
240 m_pThat->addError(a_rc, "response body/%s: %N", getPath(a_rCursor, szPath, sizeof(szPath)), a_pszFormat, &va);
241 va_end(va);
242 return a_rc;
243}
244
245
246int RTCRestClientResponseBase::PrimaryJsonCursorForBody::unknownField(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
247{
248 char szPath[256];
249 m_pThat->addError(VWRN_NOT_FOUND, "response body/%s: unknown field (type %s)",
250 getPath(a_rCursor, szPath, sizeof(szPath)), RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
251 return VWRN_NOT_FOUND;
252}
253
254
255int RTCRestClientResponseBase::deserializeHeader(RTCRestObjectBase *a_pObj, const char *a_pchValue, size_t a_cchValue,
256 uint32_t a_fFlags, const char *a_pszErrorTag) RT_NOEXCEPT
257{
258 /*
259 * Start by checking the encoding and transfering the value to a RTCString object.
260 */
261 int rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
262 if (RT_SUCCESS(rc))
263 {
264 RTCString strValue;
265 rc = strValue.assignNoThrow(a_pchValue, a_cchValue);
266 if (RT_SUCCESS(rc))
267 {
268 LogRel7(("< %s: :%s = %s\n",
269 getOperationName(), a_pszErrorTag, strValue.c_str()));
270
271 /*
272 * Try deserialize it.
273 */
274 RTERRINFOSTATIC ErrInfo;
275 rc = a_pObj->fromString(strValue, a_pszErrorTag, RTErrInfoInitStatic(&ErrInfo), a_fFlags);
276 if (RT_SUCCESS(rc))
277 { /* likely */ }
278 else if (RTErrInfoIsSet(&ErrInfo.Core))
279 addError(rc, "Error %Rrc parsing header field '%s': %s", rc, a_pszErrorTag, ErrInfo.Core.pszMsg);
280 else
281 addError(rc, "Error %Rrc parsing header field '%s'", rc, a_pszErrorTag);
282 }
283 }
284 else
285 {
286 addError(rc, "Error %Rrc validating value encoding of header field '%s': %.*Rhxs",
287 rc, a_pszErrorTag, a_cchValue, a_pchValue);
288 rc = VINF_SUCCESS; /* ignore */
289 }
290 return rc;
291}
292
293
294int RTCRestClientResponseBase::deserializeHeaderIntoMap(RTCRestStringMapBase *a_pMap, const char *a_pchField, size_t a_cchField,
295 const char *a_pchValue, size_t a_cchValue, uint32_t a_fFlags,
296 const char *a_pszErrorTag) RT_NOEXCEPT
297{
298 /*
299 * Start by checking the encoding of both the field and value,
300 * then transfering the value to a RTCString object.
301 */
302 int rc = RTStrValidateEncodingEx(a_pchField, a_cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
303 if (RT_SUCCESS(rc))
304 {
305 rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
306 if (RT_SUCCESS(rc))
307 {
308 RTCString strValue;
309 rc = strValue.assignNoThrow(a_pchValue, a_cchValue);
310 if (RT_SUCCESS(rc))
311 {
312 /*
313 * Create a value object and put it into the map.
314 */
315 RTCRestObjectBase *pValue;
316 rc = a_pMap->putNewValue(&pValue, a_pchField, a_cchField);
317 if (RT_SUCCESS(rc))
318 {
319 LogRel7(("< %s: :%s%.*s = %s\n",
320 getOperationName(), a_pszErrorTag, a_cchField, a_pchField, strValue.c_str()));
321
322 /*
323 * Try deserialize the value.
324 */
325 RTERRINFOSTATIC ErrInfo;
326 rc = pValue->fromString(strValue, a_pszErrorTag, RTErrInfoInitStatic(&ErrInfo), a_fFlags);
327 if (RT_SUCCESS(rc))
328 { /* likely */ }
329 else if (RTErrInfoIsSet(&ErrInfo.Core))
330 addError(rc, "Error %Rrc parsing header field '%s' subfield '%.*s': %s",
331 rc, a_pszErrorTag, a_cchField, a_pchField, ErrInfo.Core.pszMsg);
332 else
333 addError(rc, "Error %Rrc parsing header field '%s' subfield '%.*s'",
334 rc, a_pszErrorTag, a_cchField, a_pchField);
335 }
336 }
337 }
338 else
339 {
340 addError(rc, "Error %Rrc validating value encoding of header field '%s': %.*Rhxs",
341 rc, a_pszErrorTag, a_cchValue, a_pchValue);
342 rc = VINF_SUCCESS; /* ignore */
343 }
344 }
345 else
346 {
347 addError(rc, "Error %Rrc validating sub-field encoding of header field '%s*': %.*Rhxs",
348 rc, a_pszErrorTag, a_cchField, a_pchField);
349 rc = VINF_SUCCESS; /* ignore */
350 }
351 return rc;
352}
353
354
355void RTCRestClientResponseBase::deserializeBody(const char *a_pchData, size_t a_cbData, const char *a_pszBodyName) RT_NOEXCEPT
356{
357 if (m_strContentType.startsWith("application/json"))
358 {
359 int rc = RTStrValidateEncodingEx(a_pchData, a_cbData, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
360 if (RT_SUCCESS(rc))
361 {
362 if (LogRelIs7Enabled())
363 {
364 /* skip m_ or m_p prefix */
365 const char *pszName = a_pszBodyName;
366 if (pszName[0] == 'm' && pszName[1] == '_')
367 {
368 if (pszName[2] == 'p')
369 pszName += 3;
370 else
371 pszName += 2;
372 }
373
374 LogRel7(("< %s: %d: %s = %.*s\n",
375 getOperationName(), m_rcHttp, pszName, a_cbData, a_pchData));
376 }
377
378 RTERRINFOSTATIC ErrInfo;
379 RTJSONVAL hValue;
380 rc = RTJsonParseFromBuf(&hValue, (const uint8_t *)a_pchData, a_cbData, RTErrInfoInitStatic(&ErrInfo));
381 if (RT_SUCCESS(rc))
382 {
383 PrimaryJsonCursorForBody PrimaryCursor(hValue, a_pszBodyName, this); /* note: consumes hValue */
384 deserializeBodyFromJsonCursor(PrimaryCursor.m_Cursor);
385 }
386 else if (RTErrInfoIsSet(&ErrInfo.Core))
387 addError(rc, "Error %Rrc parsing server response as JSON (type %s): %s",
388 rc, a_pszBodyName, ErrInfo.Core.pszMsg);
389 else
390 addError(rc, "Error %Rrc parsing server response as JSON (type %s)", rc, a_pszBodyName);
391 }
392 else if (rc == VERR_INVALID_UTF8_ENCODING)
393 addError(VERR_REST_RESPONSE_INVALID_UTF8_ENCODING, "Invalid UTF-8 body encoding (object type %s; Content-Type: %s)",
394 a_pszBodyName, m_strContentType.c_str());
395 else if (rc == VERR_BUFFER_UNDERFLOW)
396 addError(VERR_REST_RESPONSE_EMBEDDED_ZERO_CHAR, "Embedded zero character in response (object type %s; Content-Type: %s)",
397 a_pszBodyName, m_strContentType.c_str());
398 else
399 addError(rc, "Unexpected body validation error (object type %s; Content-Type: %s): %Rrc",
400 a_pszBodyName, m_strContentType.c_str(), rc);
401 }
402 else
403 addError(VERR_REST_RESPONSE_CONTENT_TYPE_NOT_SUPPORTED, "Unsupported content type for '%s': %s",
404 a_pszBodyName, m_strContentType.c_str());
405}
406
407
408void RTCRestClientResponseBase::deserializeBodyFromJsonCursor(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
409{
410 a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_INTERNAL_ERROR_8, "deserializeBodyFromJsonCursor must be overridden!");
411 AssertFailed();
412}
413
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