- Timestamp:
- Sep 13, 2018 4:33:17 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/cpp/restbase.h
r74181 r74250 386 386 */ 387 387 virtual const char *typeName(void) const = 0; 388 389 /**390 * Factory method.391 * @returns Pointer to new object on success, NULL if out of memory.392 */393 typedef DECLCALLBACK(RTCRestObjectBase *) FNCREATEINSTANCE(void);394 /** Pointer to factory method. */395 typedef FNCREATEINSTANCE *PFNCREATEINSTANCE;396 388 397 389 protected: -
trunk/include/iprt/cpp/restclient.h
r74232 r74250 29 29 #include <iprt/http.h> 30 30 #include <iprt/cpp/restbase.h> 31 #include <iprt/cpp/reststringmap.h> 31 32 32 33 … … 463 464 * This may install callbacks and such like. 464 465 * 466 * When overridden, the parent class must always be called. 467 * 465 468 * @returns IPRT status code. 466 469 * @param a_hHttp The HTTP handle to prepare for receiving. 467 * @param a_pppvHdr If a header callback handler is installed, set the value pointed to to NULL. 468 * @param a_pppvBody If a body callback handler is installed, set the value pointed to to NULL. 469 */ 470 virtual int receivePrepare(RTHTTP a_hHttp, void ***a_pppvHdr, void ***a_pppvBody); 470 */ 471 virtual int receivePrepare(RTHTTP a_hHttp); 471 472 472 473 /** … … 478 479 * which case it is possible receivePrepare() wasn't called. 479 480 * 480 * @note Called before consume Headers() and consumeBody().481 * @note Called before consumeBody() but after consumeHeader(). 481 482 */ 482 483 virtual void receiveComplete(int a_rcStatus, RTHTTP a_hHttp); 483 484 484 485 /** 485 * Callback that consumes HTTP headerdata from the server.486 * Callback that consumes HTTP body data from the server. 486 487 * 487 488 * @param a_pchData Body data. 488 489 * @param a_cbData Amount of body data. 489 490 * 490 * @note Called after receiveComplete().. 491 */ 492 virtual void consumeHeaders(const char *a_pchData, size_t a_cbData); 493 494 /** 495 * Callback that consumes HTTP body data from the server. 496 * 497 * @param a_pchData Body data. 498 * @param a_cbData Amount of body data. 499 * 500 * @note Called after consumeHeaders(). 491 * @note Called after consumeHeader(). 501 492 */ 502 493 virtual void consumeBody(const char *a_pchData, size_t a_cbData); … … 559 550 int addError(int a_rc, const char *a_pszFormat, ...); 560 551 561 /** Field flags. */ 562 enum 563 { 564 /** Collection map, name is a prefix followed by '*'. */ 565 kHdrField_MapCollection = RT_BIT_32(24) 566 }; 567 568 /** Header field descriptor. */ 569 typedef struct 570 { 571 /** The header field name. */ 572 const char *pszName; 573 /** The length of the field name.*/ 574 uint32_t cchName; 575 /** Flags, TBD. */ 576 uint32_t fFlags; 577 /** Object factory. */ 578 RTCRestObjectBase::PFNCREATEINSTANCE pfnCreateInstance; 579 } HEADERFIELDDESC; 580 581 /** 582 * Helper that extracts fields from the HTTP headers. 583 * 584 * @param a_paFieldDescs Pointer to an array of field descriptors. 585 * @param a_pappFieldValues Pointer to a parallel array of value pointer pointers. 586 * @param a_cFields Number of field descriptors.. 587 * @param a_pchData The header blob to search. 588 * @param a_cbData The size of the header blob to search. 589 */ 590 void extractHeaderFieldsFromBlob(HEADERFIELDDESC const *a_paFieldDescs, RTCRestObjectBase ***a_pappFieldValues, 591 size_t a_cFields, const char *a_pchData, size_t a_cbData); 592 593 /** 594 * Helper that extracts a header field. 595 * 596 * @retval VINF_SUCCESS 597 * @retval VERR_NOT_FOUND if not found. 598 * @retval VERR_NO_STR_MEMORY 599 * @param a_pszField The header field header name. 600 * @param a_cchField The length of the header field name. 601 * @param a_pchData The header blob to search. 602 * @param a_cbData The size of the header blob to search. 603 * @param a_pStrDst Where to store the header value on successs. 604 */ 605 int extractHeaderFromBlob(const char *a_pszField, size_t a_cchField, const char *a_pchData, size_t a_cbData, 606 RTCString *a_pStrDst); 552 /** 553 * Deserializes a header field value. 554 * 555 * @returns IPRT status code. 556 * @param a_pObj The object to deserialize into. 557 * @param a_pchValue Pointer to the value (not zero terminated). 558 * Not necessarily valid UTF-8! 559 * @param a_cchValue The value length. 560 * @param a_fFlags Flags to pass to fromString(). 561 * @param a_pszErrorTag The error tag (field name). 562 */ 563 int deserializeHeader(RTCRestObjectBase *a_pObj, const char *a_pchValue, size_t a_cchValue, 564 uint32_t a_fFlags, const char *a_pszErrorTag); 565 566 /** 567 * Deserializes a header field value. 568 * 569 * @returns IPRT status code. 570 * @param a_pMap The string map object to deserialize into. 571 * @param a_pchField Pointer to the map field name. (Caller dropped the prefix.) 572 * Not necessarily valid UTF-8! 573 * @param a_cchField Length of field name. 574 * @param a_pchValue Pointer to the value (not zero terminated). 575 * Not necessarily valid UTF-8! 576 * @param a_cchValue The value length. 577 * @param a_fFlags Flags to pass to fromString(). 578 * @param a_pszErrorTag The error tag (field name). 579 */ 580 int deserializeHeaderIntoMap(RTCRestStringMapBase *a_pMap, const char *a_pchField, size_t a_cchField, 581 const char *a_pchValue, size_t a_cchValue, uint32_t a_fFlags, const char *a_pszErrorTag); 607 582 608 583 /** … … 626 601 virtual int unknownField(RTCRestJsonCursor const &a_rCursor) RT_OVERRIDE; 627 602 }; 603 604 605 /** 606 * Consumes a header. 607 * 608 * Child classes can override this to pick up their header fields, but must 609 * always call the parent class. 610 * 611 * @returns IPRT status code. 612 * @param a_uMatchWord Match word constructed by RTHTTP_MAKE_HDR_MATCH_WORD 613 * @param a_pchField The field name (not zero terminated). 614 * Not necessarily valid UTF-8! 615 * @param a_cchField The length of the field. 616 * @param a_pchValue The field value (not zero terminated). 617 * @param a_cchValue The length of the value. 618 */ 619 virtual int consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField, 620 const char *a_pchValue, size_t a_cchValue); 621 622 private: 623 /** Callback for use with RTHttpSetHeaderCallback. */ 624 static FNRTHTTPHEADERCALLBACK receiveHttpHeaderCallback; 628 625 }; 629 626 -
trunk/include/iprt/http.h
r74224 r74250 546 546 * @param uMatchWord Match word constructed by RTHTTP_MAKE_HDR_MATCH_WORD 547 547 * @param pchField The field name (not zero terminated). 548 * Not necessarily valid UTF-8! 548 549 * @param cchField The length of the field. 549 550 * @param pchValue The field value (not zero terminated). 551 * Not necessarily valid UTF-8! 550 552 * @param cchValue The length of the value. 551 553 * @param pvUser The user parameter. 554 * 555 * @remarks This is called with two fictitious header fields too: 556 * - ':http-status-line' -- the HTTP/{version} {status-code} stuff. 557 * - ':end-of-headers' -- marks the end of header callbacks. 552 558 */ 553 559 typedef DECLCALLBACK(int) FNRTHTTPHEADERCALLBACK(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField, -
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.