VirtualBox

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

Last change on this file since 73930 was 73930, checked in by vboxsync, 7 years ago

IPRT/rest: Implemented parsing json response bodies. bugref:9167

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.7 KB
Line 
1/* $Id: RTCRestClientResponseBase.cpp 73930 2018-08-28 18:09:54Z vboxsync $ */
2/** @file
3 * IPRT - C++ REST, RTCRestClientResponseBase implementation.
4 */
5
6/*
7 * Copyright (C) 2018 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#include <iprt/cpp/restbase.h>
32
33#include <iprt/ctype.h>
34#include <iprt/err.h>
35
36
37/**
38 * Default constructor.
39 */
40RTCRestClientResponseBase::RTCRestClientResponseBase()
41 : m_rcStatus(VERR_WRONG_ORDER)
42 , m_rcHttp(VERR_NOT_AVAILABLE)
43 , m_pErrInfo(NULL)
44{
45}
46
47
48/**
49 * Destructor.
50 */
51RTCRestClientResponseBase::~RTCRestClientResponseBase()
52{
53 deleteErrInfo();
54}
55
56
57/**
58 * Copy constructor.
59 */
60RTCRestClientResponseBase::RTCRestClientResponseBase(RTCRestClientResponseBase const &a_rThat)
61 : m_rcStatus(a_rThat.m_rcStatus)
62 , m_rcHttp(a_rThat.m_rcHttp)
63 , m_pErrInfo(NULL)
64 , m_strContentType(a_rThat.m_strContentType)
65{
66 if (m_pErrInfo)
67 copyErrInfo(m_pErrInfo);
68}
69
70
71/**
72 * Copy assignment operator.
73 */
74RTCRestClientResponseBase &RTCRestClientResponseBase::operator=(RTCRestClientResponseBase const &a_rThat)
75{
76 m_rcStatus = a_rThat.m_rcStatus;
77 m_rcHttp = a_rThat.m_rcHttp;
78 m_strContentType = a_rThat.m_strContentType;
79 if (a_rThat.m_pErrInfo)
80 copyErrInfo(a_rThat.m_pErrInfo);
81 else if (m_pErrInfo)
82 deleteErrInfo();
83
84 return *this;
85}
86
87
88int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp, void ***a_pppvHdr, void ***a_pppvBody)
89{
90 RT_NOREF(a_hHttp, a_pppvHdr, a_pppvBody);
91 return VINF_SUCCESS;
92}
93
94
95void RTCRestClientResponseBase::receiveComplete(int a_rcStatus, RTHTTP a_hHttp)
96{
97 RT_NOREF_PV(a_hHttp);
98 m_rcStatus = a_rcStatus;
99 if (a_rcStatus >= 0)
100 m_rcHttp = a_rcStatus;
101}
102
103
104void RTCRestClientResponseBase::consumeHeaders(const char *a_pchData, size_t a_cbData)
105{
106 /*
107 * Get the the content type.
108 */
109 int rc = extractHeaderFromBlob(RT_STR_TUPLE("Content-Type"), a_pchData, a_cbData, &m_strContentType);
110 if (rc == VERR_NOT_FOUND)
111 rc = VINF_SUCCESS;
112 AssertRCReturnVoidStmt(rc, m_rcStatus = rc);
113}
114
115
116void RTCRestClientResponseBase::consumeBody(const char *a_pchData, size_t a_cbData)
117{
118 RT_NOREF(a_pchData, a_cbData);
119}
120
121
122void RTCRestClientResponseBase::receiveFinal()
123{
124}
125
126
127PRTERRINFO RTCRestClientResponseBase::getErrInfo(void)
128{
129 if (m_pErrInfo)
130 return m_pErrInfo;
131 size_t cbMsg = _4K;
132 m_pErrInfo = (PRTERRINFO)RTMemAllocZ(sizeof(*m_pErrInfo) + cbMsg);
133 if (m_pErrInfo)
134 return RTErrInfoInit(m_pErrInfo, (char *)(m_pErrInfo + 1), cbMsg);
135 return NULL;
136}
137
138
139void RTCRestClientResponseBase::deleteErrInfo(void)
140{
141 if (m_pErrInfo)
142 {
143 RTMemFree(m_pErrInfo);
144 m_pErrInfo = NULL;
145 }
146}
147
148
149void RTCRestClientResponseBase::copyErrInfo(PCRTERRINFO pErrInfo)
150{
151 deleteErrInfo();
152 m_pErrInfo = (PRTERRINFO)RTMemDup(pErrInfo, pErrInfo->cbMsg + sizeof(*pErrInfo));
153 if (m_pErrInfo)
154 {
155 m_pErrInfo->pszMsg = (char *)(m_pErrInfo + 1);
156 m_pErrInfo->apvReserved[0] = NULL;
157 m_pErrInfo->apvReserved[1] = NULL;
158 }
159}
160
161
162int RTCRestClientResponseBase::addError(int rc, const char *pszFormat, ...)
163{
164 PRTERRINFO pErrInfo = getErrInfo();
165 if (pErrInfo)
166 {
167 va_list va;
168 va_start(va, pszFormat);
169 if ( !RTErrInfoIsSet(pErrInfo)
170 || pErrInfo->cbMsg == 0
171 || pErrInfo->pszMsg[pErrInfo->cbMsg - 1] == '\n')
172 RTErrInfoAddV(pErrInfo, rc, pszFormat, va);
173 else
174 RTErrInfoAddF(pErrInfo, rc, "\n%N", pszFormat, &va);
175 va_end(va);
176 }
177 if (RT_SUCCESS(m_rcStatus) && RT_FAILURE_NP(rc))
178 m_rcStatus = rc;
179 return rc;
180}
181
182
183int RTCRestClientResponseBase::extractHeaderFromBlob(const char *a_pszField, size_t a_cchField,
184 const char *a_pchData, size_t a_cbData,
185 RTCString *a_pStrDst)
186{
187 char const chUpper0 = RT_C_TO_UPPER(a_pszField[0]);
188 char const chLower0 = RT_C_TO_LOWER(a_pszField[0]);
189 Assert(!RT_C_IS_SPACE(chUpper0));
190
191 while (a_cbData > a_cchField)
192 {
193 /* Determine length of the header name:value combo.
194 Note! Multi-line field values are not currently supported. */
195 const char *pchEol = (const char *)memchr(a_pchData, '\n', a_cbData);
196 while (pchEol && (pchEol == a_pchData || pchEol[-1] != '\r'))
197 pchEol = (const char *)memchr(pchEol, '\n', a_cbData - (pchEol - a_pchData));
198
199 size_t cchField = pchEol ? pchEol - a_pchData + 1 : a_cbData;
200
201 /* Try match */
202 if ( a_pchData[a_cchField] == ':'
203 && ( a_pchData[0] == chUpper0
204 || a_pchData[0] == chLower0)
205 && RTStrNICmpAscii(a_pchData, a_pszField, a_cchField) == 0)
206 {
207 /* Drop CRLF. */
208 if (pchEol)
209 cchField -= 2;
210
211 /* Drop the field name and optional whitespace. */
212 cchField -= a_cchField + 1;
213 a_pchData += a_cchField + 1;
214 if (cchField > 0 && RT_C_IS_BLANK(*a_pchData))
215 {
216 a_pchData++;
217 cchField--;
218 }
219
220 /* Return the value. */
221 int rc = a_pStrDst->assignNoThrow(a_pchData, cchField);
222 if (RT_SUCCESS(rc))
223 RTStrPurgeEncoding(a_pStrDst->mutableRaw()); /** @todo this is probably a little wrong... */
224 return rc;
225 }
226
227 /* Advance to the next field. */
228 a_pchData += cchField;
229 a_cbData -= cchField;
230 }
231
232 return VERR_NOT_FOUND;
233}
234
235
236
237RTCRestClientResponseBase::PrimaryJsonCursorForBody::PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName,
238 RTCRestClientResponseBase *a_pThat)
239 : RTCRestJsonPrimaryCursor(hValue, pszName, a_pThat->getErrInfo())
240 , m_pThat(a_pThat)
241{
242}
243
244
245int RTCRestClientResponseBase::PrimaryJsonCursorForBody::addError(RTCRestJsonCursor const &a_rCursor, int a_rc,
246 const char *a_pszFormat, ...)
247{
248 va_list va;
249 va_start(va, a_pszFormat);
250 char szPath[256];
251 m_pThat->addError(a_rc, "response body/%s: %N", getPath(a_rCursor, szPath, sizeof(szPath)), a_pszFormat, &va);
252 va_end(va);
253 return a_rc;
254}
255
256
257int RTCRestClientResponseBase::PrimaryJsonCursorForBody::unknownField(RTCRestJsonCursor const &a_rCursor)
258{
259 char szPath[256];
260 m_pThat->addError(VWRN_NOT_FOUND, "response body/%s: unknown field (type %s)",
261 getPath(a_rCursor, szPath, sizeof(szPath)), RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
262 return VWRN_NOT_FOUND;
263}
264
265
266void RTCRestClientResponseBase::deserializeBody(RTCRestObjectBase *a_pDst, const char *a_pchData, size_t a_cbData)
267{
268 if (m_strContentType.startsWith("application/json;"))
269 {
270 int rc = RTStrValidateEncodingEx(a_pchData, a_cbData, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
271 if (RT_SUCCESS(rc))
272 {
273 RTERRINFOSTATIC ErrInfo;
274 RTJSONVAL hValue;
275 rc = RTJsonParseFromBuf(&hValue, (const uint8_t *)a_pchData, a_cbData, RTErrInfoInitStatic(&ErrInfo));
276 if (RT_SUCCESS(rc))
277 {
278 PrimaryJsonCursorForBody PrimaryCursor(hValue, a_pDst->getType(), this); /* note: consumes hValue */
279 a_pDst->deserializeFromJson(PrimaryCursor.m_Cursor);
280 }
281 else if (RTErrInfoIsSet(&ErrInfo.Core))
282 addError(rc, "Error %Rrc parsing server response as JSON (type %s): %s",
283 rc, a_pDst->getType(), ErrInfo.Core.pszMsg);
284 else
285 addError(rc, "Error %Rrc parsing server response as JSON (type %s)", rc, a_pDst->getType());
286 }
287 else if (rc == VERR_INVALID_UTF8_ENCODING)
288 addError(VERR_REST_RESPONSE_INVALID_UTF8_ENCODING, "Invalid UTF-8 body encoding (object type %s; Content-Type: %s)",
289 a_pDst->getType(), m_strContentType.c_str());
290 else if (rc == VERR_BUFFER_UNDERFLOW)
291 addError(VERR_REST_RESPONSE_EMBEDDED_ZERO_CHAR, "Embedded zero character in response (object type %s; Content-Type: %s)",
292 a_pDst->getType(), m_strContentType.c_str());
293 else
294 addError(rc, "Unexpected body validation error (object type %s; Content-Type: %s): %Rrc",
295 a_pDst->getType(), m_strContentType.c_str(), rc);
296 }
297 else
298 addError(VERR_REST_RESPONSE_CONTENT_TYPE_NOT_SUPPORTED, "Unsupported content type for '%s': %s",
299 a_pDst->getType(), m_strContentType.c_str());
300}
301
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette