Changeset 74250 in vbox for trunk/src/VBox/Runtime
- Timestamp:
- Sep 13, 2018 4:33:17 PM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 125044
- Location:
- trunk/src/VBox/Runtime
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/common/rest/RTCRestClientApiBase.cpp
r74192 r74250 108 108 109 109 /* 110 * Prepare the response side. This may install output callbacks and 111 * indicate this by clearing the ppvBody/ppvHdr variables. 110 * Prepare the response side. 112 111 */ 113 size_t cbHdrs = 0; 114 void *pvHdrs = NULL; 115 void **ppvHdrs = &pvHdrs; 116 117 size_t cbBody = 0; 118 void *pvBody = NULL; 119 void **ppvBody = &pvBody; 120 121 rc = a_pResponse->receivePrepare(hHttp, &ppvBody, &ppvHdrs); 112 rc = a_pResponse->receivePrepare(hHttp); 122 113 if (RT_SUCCESS(rc)) 123 114 { … … 159 150 */ 160 151 uint32_t uHttpStatus = 0; 152 size_t cbBody = 0; 153 void *pvBody = NULL; 161 154 rc = RTHttpPerform(hHttp, strFullUrl.c_str(), a_enmHttpMethod, 162 155 strXmitBody.c_str(), strXmitBody.length(), 163 &uHttpStatus, ppvHdrs, &cbHdrs, ppvBody, &cbBody);156 &uHttpStatus, NULL /*ppvHdrs*/, NULL /*pcbHdrs*/, &pvBody, &cbBody); 164 157 if (RT_SUCCESS(rc)) 165 158 { … … 170 163 */ 171 164 a_pResponse->receiveComplete(uHttpStatus, hHttp); 172 if (pvHdrs)173 {174 a_pResponse->consumeHeaders((const char *)pvHdrs, cbHdrs);175 RTHttpFreeResponse(pvHdrs);176 }177 165 if (pvBody) 178 166 { -
trunk/src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp
r74232 r74250 99 99 100 100 101 int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp, void ***a_pppvHdr, void ***a_pppvBody) 102 { 103 RT_NOREF(a_hHttp, a_pppvHdr, a_pppvBody); 101 int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp) 102 { 103 int rc = RTHttpSetHeaderCallback(a_hHttp, receiveHttpHeaderCallback, this); 104 AssertRCReturn(rc, rc); 105 104 106 return VINF_SUCCESS; 105 107 } … … 112 114 if (a_rcStatus >= 0) 113 115 m_rcHttp = a_rcStatus; 114 } 115 116 117 void RTCRestClientResponseBase::consumeHeaders(const char *a_pchData, size_t a_cbData) 118 { 119 /* 120 * Get the the content type. 121 */ 122 int rc = extractHeaderFromBlob(RT_STR_TUPLE("Content-Type"), a_pchData, a_cbData, &m_strContentType); 123 if (rc == VERR_NOT_FOUND) 124 rc = VINF_SUCCESS; 125 AssertRCReturnVoidStmt(rc, m_rcStatus = rc); 116 117 int rc = RTHttpSetHeaderCallback(a_hHttp, NULL, NULL); 118 AssertRC(rc); 119 } 120 121 122 int RTCRestClientResponseBase::consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField, 123 const char *a_pchValue, size_t a_cchValue) 124 { 125 if ( a_uMatchWord == RTHTTP_MAKE_HDR_MATCH_WORD(sizeof("Content-Type") - 1, 'c', 'o', 'n') 126 && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE("Content-Type")) == 0) 127 { 128 int rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH); 129 AssertRC(rc); 130 if (RT_SUCCESS(rc)) 131 return m_strContentType.assignNoThrow(a_pchValue, a_cchValue); 132 } 133 RT_NOREF(a_cchField); 134 return VINF_SUCCESS; 135 } 136 137 138 /*static*/ DECLCALLBACK(int) 139 RTCRestClientResponseBase::receiveHttpHeaderCallback(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField, 140 const char *pchValue, size_t cchValue, void *pvUser) 141 { 142 RTCRestClientResponseBase *pThis = (RTCRestClientResponseBase *)pvUser; 143 RT_NOREF(hHttp); 144 return pThis->consumeHeader(uMatchWord, pchField, cchField, pchValue, cchValue); 126 145 } 127 146 … … 194 213 195 214 196 void RTCRestClientResponseBase::extractHeaderFieldsFromBlob(HEADERFIELDDESC const *a_paFieldDescs,197 RTCRestObjectBase ***a_pappFieldValues,198 size_t a_cFields, const char *a_pchData, size_t a_cbData)199 200 {201 RTCString strValue; /* (Keep it out here to encourage buffer allocation reuse and default construction call.) */202 203 /*204 * Work our way through the header blob.205 */206 while (a_cbData >= 2)207 {208 /*209 * Determine length of the header name:value combo.210 * Note! Multi-line field values are not currently supported.211 */212 const char *pchEol = (const char *)memchr(a_pchData, '\n', a_cbData);213 while (pchEol && (pchEol == a_pchData || pchEol[-1] != '\r'))214 pchEol = (const char *)memchr(pchEol, '\n', a_cbData - (pchEol - a_pchData));215 216 size_t const cchField = pchEol ? pchEol - a_pchData + 1 : a_cbData;217 size_t const cchFieldNoCrLf = pchEol ? pchEol - a_pchData - 1 : a_cbData;218 219 const char *pchColon = (const char *)memchr(a_pchData, ':', cchFieldNoCrLf);220 if (pchColon)221 {222 size_t const cchName = pchColon - a_pchData;223 size_t const offValue = cchName + (RT_C_IS_BLANK(pchColon[1]) ? 2 : 1);224 size_t const cchValue = cchFieldNoCrLf - offValue;225 226 /*227 * Match headers.228 */229 bool fHaveValue = false;230 for (size_t i = 0; i < a_cFields; i++)231 {232 size_t const cchThisName = a_paFieldDescs[i].cchName;233 if ( (!(a_paFieldDescs[i].fFlags & kHdrField_MapCollection) ? cchThisName == cchName : cchThisName < cchName)234 && RTStrNICmpAscii(a_pchData, a_paFieldDescs[i].pszName, cchThisName) == 0)235 {236 /* Get and clean the value. */237 if (!fHaveValue)238 {239 int rc = strValue.assignNoThrow(&a_pchData[offValue], cchValue);240 if (RT_SUCCESS(rc))241 {242 RTStrPurgeEncoding(strValue.mutableRaw()); /** @todo this is probably a little wrong... */243 fHaveValue = true;244 }245 else246 {247 addError(rc, "Error allocating %u bytes for header field %s", a_paFieldDescs[i].pszName);248 break;249 }250 }251 252 /*253 * Create field to deserialize.254 */255 RTCRestStringMapBase *pMap = NULL;256 RTCRestObjectBase *pObj = NULL;257 if (!(a_paFieldDescs[i].fFlags & kHdrField_MapCollection))258 {259 /* Only once. */260 if (!*a_pappFieldValues[i])261 {262 pObj = a_paFieldDescs[i].pfnCreateInstance();263 if (pObj)264 *a_pappFieldValues[i] = pObj;265 else266 {267 addError(VERR_NO_MEMORY, "out of memory");268 break;269 }270 }271 else272 {273 addError(VERR_REST_RESPONSE_REPEAT_HEADER_FIELD, "Already saw header field '%s'", a_paFieldDescs[i].pszName);274 continue;275 }276 }277 else278 {279 /* Make sure we've got a map to work with. */280 if (!*a_pappFieldValues[i])281 *a_pappFieldValues[i] = pObj = a_paFieldDescs[i].pfnCreateInstance();282 else283 pObj = *a_pappFieldValues[i];284 AssertBreak(pObj->typeClass() == RTCRestObjectBase::kTypeClass_StringMap);285 pMap = (RTCRestStringMapBase *)pObj;286 287 /* Insert the header field name (sans prefix) into the map. We then use the288 new value object for the deserialization of the header field value below. */289 int rc = pMap->putNewValue(&pObj, &a_pchData[cchThisName], cchName - cchThisName);290 if (RT_SUCCESS(rc))291 { /* likely */ }292 else if (rc == VERR_ALREADY_EXISTS)293 {294 addError(VERR_REST_RESPONSE_REPEAT_HEADER_FIELD, "Already saw header field '%s'", a_paFieldDescs[i].pszName);295 continue;296 }297 else298 {299 addError(rc, "out of memory");300 break;301 }302 }303 304 /*305 * Deserialize it.306 */307 RTERRINFOSTATIC ErrInfo;308 int rc = pObj->fromString(strValue, a_paFieldDescs[i].pszName, RTErrInfoInitStatic(&ErrInfo),309 a_paFieldDescs[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask);310 if (RT_SUCCESS(rc))311 { /* likely */ }312 else if (RTErrInfoIsSet(&ErrInfo.Core))313 addError(rc, "Error %Rrc parsing header field '%s': %s",314 rc, a_paFieldDescs[i].pszName, ErrInfo.Core.pszMsg);315 else316 addError(rc, "Error %Rrc parsing header field '%s'", rc, a_paFieldDescs[i].pszName);317 }318 }319 }320 /*321 * else { verify that it's the HTTP/... line at the start }322 */323 324 /*325 * Advance to the next field.326 */327 a_cbData -= cchField;328 a_pchData += cchField;329 }330 }331 332 int RTCRestClientResponseBase::extractHeaderFromBlob(const char *a_pszField, size_t a_cchField,333 const char *a_pchData, size_t a_cbData,334 RTCString *a_pStrDst)335 {336 char const chUpper0 = RT_C_TO_UPPER(a_pszField[0]);337 char const chLower0 = RT_C_TO_LOWER(a_pszField[0]);338 Assert(!RT_C_IS_SPACE(chUpper0));339 340 while (a_cbData > a_cchField)341 {342 /* Determine length of the header name:value combo.343 Note! Multi-line field values are not currently supported. */344 const char *pchEol = (const char *)memchr(a_pchData, '\n', a_cbData);345 while (pchEol && (pchEol == a_pchData || pchEol[-1] != '\r'))346 pchEol = (const char *)memchr(pchEol, '\n', a_cbData - (pchEol - a_pchData));347 348 size_t cchField = pchEol ? pchEol - a_pchData + 1 : a_cbData;349 350 /* Try match */351 if ( a_pchData[a_cchField] == ':'352 && ( a_pchData[0] == chUpper0353 || a_pchData[0] == chLower0)354 && RTStrNICmpAscii(a_pchData, a_pszField, a_cchField) == 0)355 {356 /* Drop CRLF. */357 if (pchEol)358 cchField -= 2;359 360 /* Drop the field name and optional whitespace. */361 cchField -= a_cchField + 1;362 a_pchData += a_cchField + 1;363 if (cchField > 0 && RT_C_IS_BLANK(*a_pchData))364 {365 a_pchData++;366 cchField--;367 }368 369 /* Return the value. */370 int rc = a_pStrDst->assignNoThrow(a_pchData, cchField);371 if (RT_SUCCESS(rc))372 RTStrPurgeEncoding(a_pStrDst->mutableRaw()); /** @todo this is probably a little wrong... */373 return rc;374 }375 376 /* Advance to the next field. */377 a_pchData += cchField;378 a_cbData -= cchField;379 }380 381 return VERR_NOT_FOUND;382 }383 384 385 386 215 RTCRestClientResponseBase::PrimaryJsonCursorForBody::PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName, 387 216 RTCRestClientResponseBase *a_pThat) … … 410 239 getPath(a_rCursor, szPath, sizeof(szPath)), RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue))); 411 240 return VWRN_NOT_FOUND; 241 } 242 243 244 int RTCRestClientResponseBase::deserializeHeader(RTCRestObjectBase *a_pObj, const char *a_pchValue, size_t a_cchValue, 245 uint32_t a_fFlags, const char *a_pszErrorTag) 246 { 247 /* 248 * Start by checking the encoding and transfering the value to a RTCString object. 249 */ 250 int rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH); 251 if (RT_SUCCESS(rc)) 252 { 253 RTCString strValue; 254 rc = strValue.assignNoThrow(a_pchValue, a_cchValue); 255 if (RT_SUCCESS(rc)) 256 { 257 /* 258 * Try deserialize it. 259 */ 260 RTERRINFOSTATIC ErrInfo; 261 rc = a_pObj->fromString(strValue, a_pszErrorTag, RTErrInfoInitStatic(&ErrInfo), a_fFlags); 262 if (RT_SUCCESS(rc)) 263 { /* likely */ } 264 else if (RTErrInfoIsSet(&ErrInfo.Core)) 265 addError(rc, "Error %Rrc parsing header field '%s': %s", rc, a_pszErrorTag, ErrInfo.Core.pszMsg); 266 else 267 addError(rc, "Error %Rrc parsing header field '%s'", rc, a_pszErrorTag); 268 } 269 } 270 else 271 { 272 addError(rc, "Error %Rrc validating value necoding of header field '%s': %.*Rhxs", 273 rc, a_pszErrorTag, a_cchValue, a_pchValue); 274 rc = VINF_SUCCESS; /* ignore */ 275 } 276 return rc; 277 } 278 279 280 int RTCRestClientResponseBase::deserializeHeaderIntoMap(RTCRestStringMapBase *a_pMap, const char *a_pchField, size_t a_cchField, 281 const char *a_pchValue, size_t a_cchValue, uint32_t a_fFlags, 282 const char *a_pszErrorTag) 283 { 284 /* 285 * Start by checking the encoding of both the field and value, 286 * then transfering the value to a RTCString object. 287 */ 288 int rc = RTStrValidateEncodingEx(a_pchField, a_cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH); 289 if (RT_SUCCESS(rc)) 290 { 291 rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH); 292 if (RT_SUCCESS(rc)) 293 { 294 RTCString strValue; 295 rc = strValue.assignNoThrow(a_pchValue, a_cchValue); 296 if (RT_SUCCESS(rc)) 297 { 298 /* 299 * Create a value object and put it into the map. 300 */ 301 RTCRestObjectBase *pValue; 302 int rc = a_pMap->putNewValue(&pValue, a_pchField, a_cchField); 303 if (RT_SUCCESS(rc)) 304 { 305 /* 306 * Try deserialize the value. 307 */ 308 RTERRINFOSTATIC ErrInfo; 309 rc = pValue->fromString(strValue, a_pszErrorTag, RTErrInfoInitStatic(&ErrInfo), a_fFlags); 310 if (RT_SUCCESS(rc)) 311 { /* likely */ } 312 else if (RTErrInfoIsSet(&ErrInfo.Core)) 313 addError(rc, "Error %Rrc parsing header field '%s' subfield '%.*s': %s", 314 rc, a_pszErrorTag, a_cchField, a_pchField, ErrInfo.Core.pszMsg); 315 else 316 addError(rc, "Error %Rrc parsing header field '%s' subfield '%.*s'", 317 rc, a_pszErrorTag, a_cchField, a_pchField); 318 } 319 } 320 } 321 else 322 { 323 addError(rc, "Error %Rrc validating value encoding of header field '%s': %.*Rhxs", 324 rc, a_pszErrorTag, a_cchValue, a_pchValue); 325 rc = VINF_SUCCESS; /* ignore */ 326 } 327 } 328 else 329 { 330 addError(rc, "Error %Rrc validating sub-field encoding of header field '%s*': %.*Rhxs", 331 rc, a_pszErrorTag, a_cchField, a_pchField); 332 rc = VINF_SUCCESS; /* ignore */ 333 } 334 return rc; 412 335 } 413 336 -
trunk/src/VBox/Runtime/generic/http-curl.cpp
r74222 r74250 3041 3041 if (pThis->pfnHeaderCallback) 3042 3042 { 3043 /* Find the end of the field name first. Since cURL gives us the3044 "HTTP/{version} {code} {status}" line too, we slap a fictitious3045 field name ':http-status-line' in front of it.*/3043 /* 3044 * Find the end of the field name first. 3045 */ 3046 3046 uint32_t uMatchWord; 3047 3047 size_t cchField; … … 3063 3063 cchValue = cbToAppend - cchField - 1; 3064 3064 } 3065 else if (pchBuf[0] == 'H' && pchBuf[1] == 'T' && pchBuf[2] == 'T' && pchBuf[1] == 'P') 3065 /* Since cURL gives us the "HTTP/{version} {code} {status}" line too, 3066 we slap a fictitious field name ':http-status-line' in front of it. */ 3067 else if (cbToAppend > 5 && pchBuf[0] == 'H' && pchBuf[1] == 'T' && pchBuf[2] == 'T' && pchBuf[3] == 'P' && pchBuf[4] == '/') 3066 3068 { 3067 3069 pchField = ":http-status-line"; … … 3071 3073 cchValue = cbToAppend; 3072 3074 } 3075 /* cURL also gives us the empty line before the body, so we slap another 3076 fictitious field name ':end-of-headers' in front of it as well. */ 3077 else if (cbToAppend == 2 && pchBuf[0] == '\r' && pchBuf[1] == '\n') 3078 { 3079 pchField = ":end-of-headers"; 3080 cchField = 15; 3081 uMatchWord = RTHTTP_MAKE_HDR_MATCH_WORD(15, ':', 'e', 'n'); 3082 pchValue = pchBuf; 3083 cchValue = cbToAppend; 3084 } 3073 3085 else 3074 3086 AssertMsgFailedReturn(("pchBuf=%.*s\n", cbToAppend, pchBuf), cbToAppend); 3075 3087 3076 /* Determin the field value, stripping one leading blank and all 3077 trailing spaces. */ 3088 /* 3089 * Determin the field value, stripping one leading blank and all 3090 * trailing spaces. 3091 */ 3078 3092 if (cchValue > 0 && RT_C_IS_BLANK(*pchValue)) 3079 3093 pchValue++, cchValue--; … … 3081 3095 cchValue--; 3082 3096 3083 /* Pass it to the callback. */ 3097 /* 3098 * Pass it to the callback. 3099 */ 3100 Log6(("rtHttpWriteHeaderData: %.*s: %.*s\n", cchField, pchBuf, cchValue, pchValue)); 3084 3101 int rc = pThis->pfnHeaderCallback(pThis, uMatchWord, pchBuf, cchField, 3085 3102 pchValue, cchValue, pThis->pvHeaderCallbackUser); … … 3087 3104 return cbToAppend; 3088 3105 3106 /* Abort on error. */ 3089 3107 if (RT_SUCCESS(pThis->rcOutput)) 3090 3108 pThis->rcOutput = rc; … … 3547 3565 3548 3566 /* Headers. */ 3549 if ( ppvHeaders &&CURL_SUCCESS(rcCurl))3567 if (CURL_SUCCESS(rcCurl)) 3550 3568 { 3551 3569 RT_ZERO(pThis->HeadersOutput.uData.Mem); -
trunk/src/VBox/Runtime/testcase/tstRTCRest-1.cpp
r74202 r74250 34 34 #include <iprt/cpp/restclient.h> 35 35 36 #include <iprt/ctype.h> 36 37 #include <iprt/err.h> 37 38 #include <iprt/message.h> … … 2095 2096 { 2096 2097 RTTestSub(g_hTest, "RTCRestClientRequestBase"); 2097 2098 2098 { 2099 2099 TestRequest Req1("this-is-a-string", 123456789, 5, "1", "22", "333", "444", "555"); … … 2125 2125 2126 2126 2127 class TestResponse : public RTCRestClientResponseBase 2128 { 2129 public: 2130 RTCRestArray<RTCRestString> *m_pArray; 2131 RTCRestStringMap<RTCRestString> *m_pMap; 2132 RTCRestInt64 *m_pInteger; 2133 RTCRestString *m_pStrContentType; 2134 2135 TestResponse() : m_pArray(NULL), m_pMap(NULL), m_pInteger(NULL), m_pStrContentType(NULL) 2136 { } 2137 2138 protected: 2139 virtual int consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField, 2140 const char *a_pchValue, size_t a_cchValue) RT_OVERRIDE 2141 { 2142 int rc = RTCRestClientResponseBase::consumeHeader(a_uMatchWord, a_pchField, a_cchField, a_pchValue, a_cchValue); 2143 AssertRCReturn(rc, rc); 2144 2145 #define MATCH_FIELD(a_sz) (sizeof(a_sz) - 1 == a_cchField && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE(a_sz)) == 0) 2146 if (MATCH_FIELD("x-array")) 2147 { 2148 if (!m_pArray) 2149 { 2150 m_pArray = new (std::nothrow) RTCRestArray<RTCRestString>(); 2151 AssertReturn(m_pArray, VERR_NO_MEMORY); 2152 return deserializeHeader(m_pArray, a_pchValue, a_cchValue, RTCRestObjectBase::kCollectionFormat_csv, "x-array"); 2153 } 2154 } 2155 else if (a_cchField >= sizeof("x-map-") - 1 && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE("x-map-")) == 0) 2156 { 2157 if (!m_pMap) 2158 { 2159 m_pMap = new (std::nothrow) RTCRestStringMap<RTCRestString>(); 2160 AssertReturn(m_pMap, VERR_NO_MEMORY); 2161 } 2162 return deserializeHeaderIntoMap(m_pMap, a_pchField + 6, a_cchField - 6, a_pchValue, a_cchValue, 0, "x-map-"); 2163 } 2164 else if (MATCH_FIELD("x-integer")) 2165 { 2166 if (!m_pInteger) 2167 { 2168 m_pInteger = new (std::nothrow) RTCRestInt64(); 2169 AssertReturn(m_pInteger, VERR_NO_MEMORY); 2170 return deserializeHeader(m_pInteger, a_pchValue, a_cchValue, 0, "x-integer"); 2171 } 2172 } 2173 else if (MATCH_FIELD("content-type")) 2174 { 2175 if (!m_pStrContentType) 2176 { 2177 m_pStrContentType = new (std::nothrow) RTCRestString(); 2178 AssertReturn(m_pStrContentType, VERR_NO_MEMORY); 2179 return deserializeHeader(m_pStrContentType, a_pchValue, a_cchValue, 0, "content-type"); 2180 } 2181 } 2182 else 2183 return VWRN_NOT_FOUND; 2184 RT_NOREF(a_uMatchWord); 2185 return addError(VERR_ALREADY_EXISTS, "Already have field '%.*s'!", a_cchField, a_pchField); 2186 } 2187 2188 public: 2189 int pushHeader(const char *pszField, const char *pszValue) 2190 { 2191 size_t const cchField = strlen(pszField); 2192 void *pvFieldCopy = RTTestGuardedAllocTail(g_hTest, cchField); 2193 RTTESTI_CHECK_RET(pvFieldCopy, VERR_NO_MEMORY); 2194 memcpy(pvFieldCopy, pszField, cchField); 2195 2196 size_t const cchValue = strlen(pszValue); 2197 void *pvValueCopy = RTTestGuardedAllocTail(g_hTest, cchValue); 2198 RTTESTI_CHECK_RET(pvValueCopy, VERR_NO_MEMORY); 2199 memcpy(pvValueCopy, pszValue, cchValue); 2200 2201 uint32_t uWord = RTHTTP_MAKE_HDR_MATCH_WORD(cchField, 2202 cchField >= 1 ? RT_C_TO_LOWER(pszField[0]) : 0, 2203 cchField >= 2 ? RT_C_TO_LOWER(pszField[1]) : 0, 2204 cchField >= 3 ? RT_C_TO_LOWER(pszField[2]) : 0); 2205 int rc = consumeHeader(uWord, (const char *)pvFieldCopy, cchField, (const char *)pvValueCopy, cchValue); 2206 RTTestGuardedFree(g_hTest, pvValueCopy); 2207 RTTestGuardedFree(g_hTest, pvFieldCopy); 2208 return rc; 2209 } 2210 }; 2211 2212 2213 void testClientResponseBase() 2214 { 2215 RTTestSub(g_hTest, "RTCRestClientResponseBase"); 2216 { 2217 TestResponse Resp1; 2218 RTTESTI_CHECK_RC(Resp1.pushHeader("content-type", "application/json; charset=utf-8"), VINF_SUCCESS); 2219 RTTESTI_CHECK(Resp1.getContentType().equals("application/json; charset=utf-8")); 2220 RTTESTI_CHECK(Resp1.m_pStrContentType && Resp1.m_pStrContentType->equals("application/json; charset=utf-8")); 2221 2222 RTTESTI_CHECK_RC(Resp1.pushHeader("content-typ2", "oopsy daisy"), VWRN_NOT_FOUND); 2223 RTTESTI_CHECK_RC(Resp1.pushHeader("content-type2", "oopsy daisy"), VWRN_NOT_FOUND); 2224 RTTESTI_CHECK(Resp1.getContentType().equals("application/json; charset=utf-8")); 2225 RTTESTI_CHECK(Resp1.m_pStrContentType && Resp1.m_pStrContentType->equals("application/json; charset=utf-8")); 2226 2227 RTTESTI_CHECK_RC(Resp1.pushHeader("x-integer", "398679406"), VINF_SUCCESS); 2228 RTTESTI_CHECK(Resp1.m_pInteger && Resp1.m_pInteger->m_iValue == 398679406); 2229 2230 //RTTESTI_CHECK_RC(Resp1.pushHeader("x-array", "zero,one,two,three"), VINF_SUCCESS); 2231 //RTTESTI_CHECK(Resp1.m_pArray && Resp1.m_pArray->size() == 4); 2232 2233 RTTESTI_CHECK_RC(Resp1.pushHeader("x-map-", "empty-key"), VINF_SUCCESS); 2234 RTTESTI_CHECK(Resp1.m_pMap && Resp1.m_pMap->size() == 1 && Resp1.m_pMap->get("") != NULL && Resp1.m_pMap->get("")->equals("empty-key")); 2235 2236 RTTESTI_CHECK_RC(Resp1.pushHeader("x-map-42", "key-is-42"), VINF_SUCCESS); 2237 RTTESTI_CHECK(Resp1.m_pMap && Resp1.m_pMap->size() == 2 && Resp1.m_pMap->get("42") != NULL && Resp1.m_pMap->get("42")->equals("key-is-42")); 2238 } 2239 } 2240 2241 2127 2242 int main() 2128 2243 { … … 2140 2255 testStringMap(); 2141 2256 testClientRequestBase(); 2142 /** @todo test the response base class too. */2257 testClientResponseBase(); 2143 2258 2144 2259 rcExit = RTTestSummaryAndDestroy(g_hTest);
Note:
See TracChangeset
for help on using the changeset viewer.