/* $Id: RTCRestClientRequestBase.cpp $ */ /** @file * IPRT - C++ REST, RTCRestClientRequestBase implementation. */ /* * Copyright (C) 2018-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP RTLOGGROUP_REST #include #include #include #include #include #include /** * Default constructor. */ RTCRestClientRequestBase::RTCRestClientRequestBase() RT_NOEXCEPT : m_fIsSet(0) , m_fErrorSet(0) { } /** * Copy constructor. */ RTCRestClientRequestBase::RTCRestClientRequestBase(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT : m_fIsSet(a_rThat.m_fIsSet) , m_fErrorSet(a_rThat.m_fErrorSet) { } /** * Destructor */ RTCRestClientRequestBase::~RTCRestClientRequestBase() { /* nothing to do */ } /** * Copy assignment operator. */ RTCRestClientRequestBase &RTCRestClientRequestBase::operator=(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT { m_fIsSet = a_rThat.m_fIsSet; m_fErrorSet = a_rThat.m_fErrorSet; return *this; } int RTCRestClientRequestBase::doPathParameters(RTCString *a_pStrPath, const char *a_pszPathTemplate, size_t a_cchPathTemplate, PATHPARAMDESC const *a_paPathParams, PATHPARAMSTATE *a_paPathParamStates, size_t a_cPathParams) const RT_NOEXCEPT { int rc = a_pStrPath->assignNoThrow(a_pszPathTemplate, a_cchPathTemplate); AssertRCReturn(rc, rc); /* Locate the sub-string to replace with values first: */ for (size_t i = 0; i < a_cPathParams; i++) { char const *psz = strstr(a_pszPathTemplate, a_paPathParams[i].pszName); AssertReturn(psz, VERR_INTERNAL_ERROR_5); a_paPathParamStates[i].offName = psz - a_pszPathTemplate; } /* Replace with actual values: */ for (size_t i = 0; i < a_cPathParams; i++) { AssertReturn( (a_paPathParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask) != RTCRestObjectBase::kCollectionFormat_multi, VERR_INTERNAL_ERROR_3); AssertMsgReturn(a_paPathParamStates[i].pObj != NULL, ("%s: Path parameter '%s' is not set!\n", getOperationName(), a_paPathParams[i].pszName), VERR_REST_PATH_PARAMETER_NOT_SET); AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paPathParams[i].iBitNo), ("%s: Path parameter '%s' is not set!\n", getOperationName(), a_paPathParams[i].pszName), VERR_REST_PATH_PARAMETER_NOT_SET); RTCString strPathParam; rc = a_paPathParamStates[i].pObj->toString(&strPathParam, a_paPathParams[i].fFlags); AssertRCReturn(rc, rc); LogRel5(("> %s: /%s = %s\n", getOperationName(), a_paPathParams[i].pszName, strPathParam.c_str())); RTCString strTmpVal; rc = strTmpVal.printfNoThrow("%RMpa", strPathParam.c_str()); /* urlencode */ AssertRCReturn(rc, rc); /* Replace. */ ssize_t cchAdjust = strTmpVal.length() - a_paPathParams[i].cchName; rc = a_pStrPath->replaceNoThrow(a_paPathParamStates[i].offName, a_paPathParams[i].cchName, strTmpVal); AssertRCReturn(rc, rc); /* Adjust subsequent fields. */ if (cchAdjust != 0) for (size_t j = i + 1; j < a_cPathParams; j++) if (a_paPathParamStates[j].offName > a_paPathParamStates[i].offName) a_paPathParamStates[j].offName += cchAdjust; } return VINF_SUCCESS; } int RTCRestClientRequestBase::doQueryParameters(RTCString *a_pStrQuery, QUERYPARAMDESC const *a_paQueryParams, RTCRestObjectBase const **a_papQueryParamObjs, size_t a_cQueryParams) const RT_NOEXCEPT { RTCString strTmpVal; char chSep = a_pStrQuery->isEmpty() ? '?' : '&'; for (size_t i = 0; i < a_cQueryParams; i++) { if ( a_paQueryParams[i].fRequired || (m_fIsSet & RT_BIT_64(a_paQueryParams[i].iBitNo)) ) { AssertMsgReturn(a_papQueryParamObjs[i] != NULL, ("%s: Required query parameter '%s' is not set!\n", getOperationName(), a_paQueryParams[i].pszName), VERR_REST_REQUIRED_QUERY_PARAMETER_NOT_SET); AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paQueryParams[i].iBitNo), ("%s: Required query parameter '%s' is not set!\n", getOperationName(), a_paQueryParams[i].pszName), VERR_REST_REQUIRED_QUERY_PARAMETER_NOT_SET); if ( (a_paQueryParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask) != RTCRestObjectBase::kCollectionFormat_multi) { int rc = a_papQueryParamObjs[i]->toString(&strTmpVal, a_paQueryParams[i].fFlags); AssertRCReturn(rc, rc); rc = a_pStrQuery->appendPrintfNoThrow("%c%RMpa=%RMpa", chSep, a_paQueryParams[i].pszName, strTmpVal.c_str()); AssertRCReturn(rc, rc); LogRel5(("> %s: ?%s = %s\n", getOperationName(), a_paQueryParams[i].pszName, strTmpVal.c_str())); chSep = '&'; } else { /* * Enumerate array and add 'name=element' for each element in it. */ AssertReturn(a_papQueryParamObjs[i]->typeClass() == RTCRestObjectBase::kTypeClass_Array, VERR_REST_INTERNAL_ERROR_2); RTCRestArrayBase const *pArray = (RTCRestArrayBase const *)a_papQueryParamObjs[i]; for (size_t j = 0; j < pArray->size(); j++) { RTCRestObjectBase const *pObj = pArray->atBase(j); int rc = pObj->toString(&strTmpVal, a_paQueryParams[i].fFlags & ~RTCRestObjectBase::kCollectionFormat_Mask); AssertRCReturn(rc, rc); rc = a_pStrQuery->appendPrintfNoThrow("%c%RMpa=%RMpa", chSep, a_paQueryParams[i].pszName, strTmpVal.c_str()); AssertRCReturn(rc, rc); LogRel5(("> %s: ?%s[%d] = %s\n", getOperationName(), a_paQueryParams[i].pszName, j, strTmpVal.c_str())); chSep = '&'; } } } } return VINF_SUCCESS; } int RTCRestClientRequestBase::doHeaderParameters(RTHTTP a_hHttp, HEADERPARAMDESC const *a_paHeaderParams, RTCRestObjectBase const **a_papHeaderParamObjs, size_t a_cHeaderParams) const RT_NOEXCEPT { RTCString strTmpVal; for (size_t i = 0; i < a_cHeaderParams; i++) { AssertReturn( (a_paHeaderParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask) != RTCRestObjectBase::kCollectionFormat_multi, VERR_INTERNAL_ERROR_3); if ( a_paHeaderParams[i].fRequired || (m_fIsSet & RT_BIT_64(a_paHeaderParams[i].iBitNo)) ) { AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paHeaderParams[i].iBitNo), ("%s: Required header parameter '%s' is not set!\n", getOperationName(), a_paHeaderParams[i].pszName), VERR_REST_REQUIRED_HEADER_PARAMETER_NOT_SET); AssertMsgReturn(a_papHeaderParamObjs[i] != NULL, ("%s: Required header parameter '%s' is not set!\n", getOperationName(), a_paHeaderParams[i].pszName), VERR_REST_REQUIRED_HEADER_PARAMETER_NOT_SET); if (!a_paHeaderParams[i].fMapCollection) { int rc = a_papHeaderParamObjs[i]->toString(&strTmpVal, a_paHeaderParams[i].fFlags); AssertRCReturn(rc, rc); rc = RTHttpAddHeader(a_hHttp, a_paHeaderParams[i].pszName, strTmpVal.c_str(), strTmpVal.length(), RTHTTPADDHDR_F_BACK); AssertRCReturn(rc, rc); LogRel5(("> %s: :%s = %s\n", getOperationName(), a_paHeaderParams[i].pszName, strTmpVal.c_str())); } else if (!a_papHeaderParamObjs[i]->isNull()) { /* * Enumerate the map and produce a series of head fields on the form: * (a_paHeaderParams[i].pszName + key): value.toString() */ AssertReturn(a_papHeaderParamObjs[i]->typeClass() == RTCRestObjectBase::kTypeClass_StringMap, VERR_REST_INTERNAL_ERROR_1); RTCRestStringMapBase const *pMap = (RTCRestStringMapBase const *)a_papHeaderParamObjs[i]; const size_t cchName = strlen(a_paHeaderParams[i].pszName); Assert(a_paHeaderParams[i].pszName[cchName - 1] != '*'); RTCString strTmpName; for (RTCRestStringMapBase::ConstIterator it = pMap->begin(); it != pMap->end(); ++it) { int rc = strTmpName.assignNoThrow(a_paHeaderParams[i].pszName, cchName); AssertRCReturn(rc, rc); rc = strTmpName.appendNoThrow(it.getKey()); AssertRCReturn(rc, rc); rc = it.getValue()->toString(&strTmpVal, a_paHeaderParams[i].fFlags); AssertRCReturn(rc, rc); rc = RTHttpAddHeader(a_hHttp, strTmpName.c_str(), strTmpVal.c_str(), strTmpVal.length(), RTHTTPADDHDR_F_BACK); AssertRCReturn(rc, rc); LogRel5(("> %s: :%s = %s\n", getOperationName(), strTmpName.c_str(), strTmpVal.c_str())); } } else Assert(!a_paHeaderParams[i].fRequired); } } return VINF_SUCCESS; }