VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/rest/RTCRestClientRequestBase.cpp@ 86681

Last change on this file since 86681 was 86681, checked in by vboxsync, 4 years ago

IPRT/rest: bugref:9167 - Add release logging for REST operations.
Request are logged at level 5, and responses at level 7, so they are
not on by default. The format is a bit ad-hoc, but all the essential
information should be there. It's mostly done in base classes except
for request bodies (it can probably be done in the base class too, but
would require more runtime logic, while here we can only generate them
as needed).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.3 KB
Line 
1/* $Id: RTCRestClientRequestBase.cpp 86681 2020-10-22 23:36:16Z vboxsync $ */
2/** @file
3 * IPRT - C++ REST, RTCRestClientRequestBase implementation.
4 */
5
6/*
7 * Copyright (C) 2018-2020 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/assert.h>
35#include <iprt/err.h>
36#include <iprt/log.h>
37#include <iprt/cpp/restarray.h>
38#include <iprt/cpp/reststringmap.h>
39
40
41/**
42 * Default constructor.
43 */
44RTCRestClientRequestBase::RTCRestClientRequestBase() RT_NOEXCEPT
45 : m_fIsSet(0)
46 , m_fErrorSet(0)
47{
48}
49
50
51/**
52 * Copy constructor.
53 */
54RTCRestClientRequestBase::RTCRestClientRequestBase(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT
55 : m_fIsSet(a_rThat.m_fIsSet)
56 , m_fErrorSet(a_rThat.m_fErrorSet)
57{
58}
59
60
61/**
62 * Destructor
63 */
64RTCRestClientRequestBase::~RTCRestClientRequestBase()
65{
66 /* nothing to do */
67}
68
69
70/**
71 * Copy assignment operator.
72 */
73RTCRestClientRequestBase &RTCRestClientRequestBase::operator=(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT
74{
75 m_fIsSet = a_rThat.m_fIsSet;
76 m_fErrorSet = a_rThat.m_fErrorSet;
77 return *this;
78}
79
80
81int RTCRestClientRequestBase::doPathParameters(RTCString *a_pStrPath, const char *a_pszPathTemplate, size_t a_cchPathTemplate,
82 PATHPARAMDESC const *a_paPathParams, PATHPARAMSTATE *a_paPathParamStates,
83 size_t a_cPathParams) const RT_NOEXCEPT
84{
85 int rc = a_pStrPath->assignNoThrow(a_pszPathTemplate, a_cchPathTemplate);
86 AssertRCReturn(rc, rc);
87
88 /* Locate the sub-string to replace with values first: */
89 for (size_t i = 0; i < a_cPathParams; i++)
90 {
91 char const *psz = strstr(a_pszPathTemplate, a_paPathParams[i].pszName);
92 AssertReturn(psz, VERR_INTERNAL_ERROR_5);
93 a_paPathParamStates[i].offName = psz - a_pszPathTemplate;
94 }
95
96 /* Replace with actual values: */
97 for (size_t i = 0; i < a_cPathParams; i++)
98 {
99 AssertReturn( (a_paPathParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask)
100 != RTCRestObjectBase::kCollectionFormat_multi,
101 VERR_INTERNAL_ERROR_3);
102 AssertMsgReturn(a_paPathParamStates[i].pObj != NULL,
103 ("%s: Path parameter '%s' is not set!\n",
104 getOperationName(), a_paPathParams[i].pszName),
105 VERR_REST_PATH_PARAMETER_NOT_SET);
106 AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paPathParams[i].iBitNo),
107 ("%s: Path parameter '%s' is not set!\n",
108 getOperationName(), a_paPathParams[i].pszName),
109 VERR_REST_PATH_PARAMETER_NOT_SET);
110
111 RTCString strPathParam;
112 rc = a_paPathParamStates[i].pObj->toString(&strPathParam, a_paPathParams[i].fFlags);
113 AssertRCReturn(rc, rc);
114
115 LogRel5(("> %s: /%s = %s\n",
116 getOperationName(), a_paPathParams[i].pszName, strPathParam.c_str()));
117
118 RTCString strTmpVal;
119 rc = strTmpVal.printfNoThrow("%RMpa", strPathParam.c_str()); /* urlencode */
120 AssertRCReturn(rc, rc);
121
122 /* Replace. */
123 ssize_t cchAdjust = strTmpVal.length() - a_paPathParams[i].cchName;
124 rc = a_pStrPath->replaceNoThrow(a_paPathParamStates[i].offName, a_paPathParams[i].cchName, strTmpVal);
125 AssertRCReturn(rc, rc);
126
127 /* Adjust subsequent fields. */
128 if (cchAdjust != 0)
129 for (size_t j = i + 1; j < a_cPathParams; j++)
130 if (a_paPathParamStates[j].offName > a_paPathParamStates[i].offName)
131 a_paPathParamStates[j].offName += cchAdjust;
132 }
133
134 return VINF_SUCCESS;
135}
136
137
138int RTCRestClientRequestBase::doQueryParameters(RTCString *a_pStrQuery, QUERYPARAMDESC const *a_paQueryParams,
139 RTCRestObjectBase const **a_papQueryParamObjs, size_t a_cQueryParams) const RT_NOEXCEPT
140{
141 RTCString strTmpVal;
142 char chSep = a_pStrQuery->isEmpty() ? '?' : '&';
143 for (size_t i = 0; i < a_cQueryParams; i++)
144 {
145 if ( a_paQueryParams[i].fRequired
146 || (m_fIsSet & RT_BIT_64(a_paQueryParams[i].iBitNo)) )
147 {
148 AssertMsgReturn(a_papQueryParamObjs[i] != NULL,
149 ("%s: Required query parameter '%s' is not set!\n",
150 getOperationName(), a_paQueryParams[i].pszName),
151 VERR_REST_REQUIRED_QUERY_PARAMETER_NOT_SET);
152 AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paQueryParams[i].iBitNo),
153 ("%s: Required query parameter '%s' is not set!\n",
154 getOperationName(), a_paQueryParams[i].pszName),
155 VERR_REST_REQUIRED_QUERY_PARAMETER_NOT_SET);
156
157 if ( (a_paQueryParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask)
158 != RTCRestObjectBase::kCollectionFormat_multi)
159 {
160 int rc = a_papQueryParamObjs[i]->toString(&strTmpVal, a_paQueryParams[i].fFlags);
161 AssertRCReturn(rc, rc);
162
163 rc = a_pStrQuery->appendPrintfNoThrow("%c%RMpa=%RMpa", chSep, a_paQueryParams[i].pszName, strTmpVal.c_str());
164 AssertRCReturn(rc, rc);
165
166 LogRel5(("> %s: ?%s = %s\n",
167 getOperationName(), a_paQueryParams[i].pszName, strTmpVal.c_str()));
168
169 chSep = '&';
170 }
171 else
172 {
173 /*
174 * Enumerate array and add 'name=element' for each element in it.
175 */
176 AssertReturn(a_papQueryParamObjs[i]->typeClass() == RTCRestObjectBase::kTypeClass_Array,
177 VERR_REST_INTERNAL_ERROR_2);
178 RTCRestArrayBase const *pArray = (RTCRestArrayBase const *)a_papQueryParamObjs[i];
179 for (size_t j = 0; j < pArray->size(); j++)
180 {
181 RTCRestObjectBase const *pObj = pArray->atBase(j);
182 int rc = pObj->toString(&strTmpVal, a_paQueryParams[i].fFlags & ~RTCRestObjectBase::kCollectionFormat_Mask);
183 AssertRCReturn(rc, rc);
184
185 rc = a_pStrQuery->appendPrintfNoThrow("%c%RMpa=%RMpa", chSep, a_paQueryParams[i].pszName, strTmpVal.c_str());
186 AssertRCReturn(rc, rc);
187
188 LogRel5(("> %s: ?%s[%d] = %s\n",
189 getOperationName(), a_paQueryParams[i].pszName, j, strTmpVal.c_str()));
190
191 chSep = '&';
192 }
193 }
194 }
195 }
196 return VINF_SUCCESS;
197}
198
199
200int RTCRestClientRequestBase::doHeaderParameters(RTHTTP a_hHttp, HEADERPARAMDESC const *a_paHeaderParams,
201 RTCRestObjectBase const **a_papHeaderParamObjs, size_t a_cHeaderParams) const RT_NOEXCEPT
202{
203 RTCString strTmpVal;
204 for (size_t i = 0; i < a_cHeaderParams; i++)
205 {
206 AssertReturn( (a_paHeaderParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask)
207 != RTCRestObjectBase::kCollectionFormat_multi,
208 VERR_INTERNAL_ERROR_3);
209
210 if ( a_paHeaderParams[i].fRequired
211 || (m_fIsSet & RT_BIT_64(a_paHeaderParams[i].iBitNo)) )
212 {
213 AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paHeaderParams[i].iBitNo),
214 ("%s: Required header parameter '%s' is not set!\n",
215 getOperationName(), a_paHeaderParams[i].pszName),
216 VERR_REST_REQUIRED_HEADER_PARAMETER_NOT_SET);
217 AssertMsgReturn(a_papHeaderParamObjs[i] != NULL,
218 ("Required header parameter '%s' is not set!\n",
219 getOperationName(), a_paHeaderParams[i].pszName),
220 VERR_REST_REQUIRED_HEADER_PARAMETER_NOT_SET);
221
222 if (!a_paHeaderParams[i].fMapCollection)
223 {
224 int rc = a_papHeaderParamObjs[i]->toString(&strTmpVal, a_paHeaderParams[i].fFlags);
225 AssertRCReturn(rc, rc);
226
227 rc = RTHttpAddHeader(a_hHttp, a_paHeaderParams[i].pszName, strTmpVal.c_str(), strTmpVal.length(),
228 RTHTTPADDHDR_F_BACK);
229 AssertRCReturn(rc, rc);
230
231 LogRel5(("> %s: :%s = %s\n",
232 getOperationName(), a_paHeaderParams[i].pszName, strTmpVal.c_str()));
233 }
234 else if (!a_papHeaderParamObjs[i]->isNull())
235 {
236 /*
237 * Enumerate the map and produce a series of head fields on the form:
238 * (a_paHeaderParams[i].pszName + key): value.toString()
239 */
240 AssertReturn(a_papHeaderParamObjs[i]->typeClass() == RTCRestObjectBase::kTypeClass_StringMap,
241 VERR_REST_INTERNAL_ERROR_1);
242 RTCRestStringMapBase const *pMap = (RTCRestStringMapBase const *)a_papHeaderParamObjs[i];
243 const size_t cchName = strlen(a_paHeaderParams[i].pszName);
244 Assert(a_paHeaderParams[i].pszName[cchName - 1] != '*');
245 RTCString strTmpName;
246 for (RTCRestStringMapBase::ConstIterator it = pMap->begin(); it != pMap->end(); ++it)
247 {
248 int rc = strTmpName.assignNoThrow(a_paHeaderParams[i].pszName, cchName);
249 AssertRCReturn(rc, rc);
250 rc = strTmpName.appendNoThrow(it.getKey());
251 AssertRCReturn(rc, rc);
252
253 rc = it.getValue()->toString(&strTmpVal, a_paHeaderParams[i].fFlags);
254 AssertRCReturn(rc, rc);
255
256 rc = RTHttpAddHeader(a_hHttp, strTmpName.c_str(), strTmpVal.c_str(), strTmpVal.length(),
257 RTHTTPADDHDR_F_BACK);
258 AssertRCReturn(rc, rc);
259
260 LogRel5(("> %s: :%s = %s\n",
261 getOperationName(), strTmpName.c_str(), strTmpVal.c_str()));
262 }
263 }
264 else
265 Assert(!a_paHeaderParams[i].fRequired);
266 }
267 }
268 return VINF_SUCCESS;
269}
270
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