VirtualBox

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

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