VirtualBox

Changeset 74250 in vbox for trunk


Ignore:
Timestamp:
Sep 13, 2018 4:33:17 PM (6 years ago)
Author:
vboxsync
Message:

IRPT/rest,http: Use header callbacks for capturing header values in responses. Removed [P]FNCREATEINSTANCE as it isn't needed any more (and it didn't work like expected for RTCRestString). Some HTTP header callback updates. bugref:9167

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/cpp/restbase.h

    r74181 r74250  
    386386     */
    387387    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;
    396388
    397389protected:
  • trunk/include/iprt/cpp/restclient.h

    r74232 r74250  
    2929#include <iprt/http.h>
    3030#include <iprt/cpp/restbase.h>
     31#include <iprt/cpp/reststringmap.h>
    3132
    3233
     
    463464     * This may install callbacks and such like.
    464465     *
     466     * When overridden, the parent class must always be called.
     467     *
    465468     * @returns IPRT status code.
    466469     * @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);
    471472
    472473    /**
     
    478479     *                      which case it is possible receivePrepare() wasn't called.
    479480     *
    480      * @note    Called before consumeHeaders() and consumeBody().
     481     * @note    Called before consumeBody() but after consumeHeader().
    481482     */
    482483    virtual void receiveComplete(int a_rcStatus, RTHTTP a_hHttp);
    483484
    484485    /**
    485      * Callback that consumes HTTP header data from the server.
     486     * Callback that consumes HTTP body data from the server.
    486487     *
    487488     * @param   a_pchData  Body data.
    488489     * @param   a_cbData   Amount of body data.
    489490     *
    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().
    501492     */
    502493    virtual void consumeBody(const char *a_pchData, size_t a_cbData);
     
    559550    int addError(int a_rc, const char *a_pszFormat, ...);
    560551
    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);
    607582
    608583    /**
     
    626601        virtual int unknownField(RTCRestJsonCursor const &a_rCursor) RT_OVERRIDE;
    627602    };
     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
     622private:
     623    /** Callback for use with RTHttpSetHeaderCallback. */
     624    static FNRTHTTPHEADERCALLBACK receiveHttpHeaderCallback;
    628625};
    629626
  • trunk/include/iprt/http.h

    r74224 r74250  
    546546 * @param   uMatchWord      Match word constructed by RTHTTP_MAKE_HDR_MATCH_WORD
    547547 * @param   pchField        The field name (not zero terminated).
     548 *                          Not necessarily valid UTF-8!
    548549 * @param   cchField        The length of the field.
    549550 * @param   pchValue        The field value (not zero terminated).
     551 *                          Not necessarily valid UTF-8!
    550552 * @param   cchValue        The length of the value.
    551553 * @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.
    552558 */
    553559typedef DECLCALLBACK(int) FNRTHTTPHEADERCALLBACK(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField,
  • trunk/src/VBox/Runtime/common/rest/RTCRestClientApiBase.cpp

    r74192 r74250  
    108108
    109109            /*
    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.
    112111             */
    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);
    122113            if (RT_SUCCESS(rc))
    123114            {
     
    159150                             */
    160151                            uint32_t uHttpStatus = 0;
     152                            size_t   cbBody      = 0;
     153                            void    *pvBody      = NULL;
    161154                            rc = RTHttpPerform(hHttp, strFullUrl.c_str(), a_enmHttpMethod,
    162155                                               strXmitBody.c_str(), strXmitBody.length(),
    163                                                &uHttpStatus, ppvHdrs, &cbHdrs, ppvBody, &cbBody);
     156                                               &uHttpStatus, NULL /*ppvHdrs*/, NULL /*pcbHdrs*/, &pvBody, &cbBody);
    164157                            if (RT_SUCCESS(rc))
    165158                            {
     
    170163                                 */
    171164                                a_pResponse->receiveComplete(uHttpStatus, hHttp);
    172                                 if (pvHdrs)
    173                                 {
    174                                     a_pResponse->consumeHeaders((const char *)pvHdrs, cbHdrs);
    175                                     RTHttpFreeResponse(pvHdrs);
    176                                 }
    177165                                if (pvBody)
    178166                                {
  • trunk/src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp

    r74232 r74250  
    9999
    100100
    101 int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp, void ***a_pppvHdr, void ***a_pppvBody)
    102 {
    103     RT_NOREF(a_hHttp, a_pppvHdr, a_pppvBody);
     101int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp)
     102{
     103    int rc = RTHttpSetHeaderCallback(a_hHttp, receiveHttpHeaderCallback, this);
     104    AssertRCReturn(rc, rc);
     105
    104106    return VINF_SUCCESS;
    105107}
     
    112114    if (a_rcStatus >= 0)
    113115        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
     122int 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)
     139RTCRestClientResponseBase::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);
    126145}
    127146
     
    194213
    195214
    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                         else
    246                         {
    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                             else
    266                             {
    267                                 addError(VERR_NO_MEMORY, "out of memory");
    268                                 break;
    269                             }
    270                         }
    271                         else
    272                         {
    273                             addError(VERR_REST_RESPONSE_REPEAT_HEADER_FIELD, "Already saw header field '%s'", a_paFieldDescs[i].pszName);
    274                             continue;
    275                         }
    276                     }
    277                     else
    278                     {
    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                         else
    283                             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 the
    288                            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                         else
    298                         {
    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                     else
    316                         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] == chUpper0
    353                 || 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 
    386215RTCRestClientResponseBase::PrimaryJsonCursorForBody::PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName,
    387216                                                                              RTCRestClientResponseBase *a_pThat)
     
    410239                      getPath(a_rCursor, szPath, sizeof(szPath)), RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
    411240    return VWRN_NOT_FOUND;
     241}
     242
     243
     244int 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
     280int 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;
    412335}
    413336
  • trunk/src/VBox/Runtime/generic/http-curl.cpp

    r74222 r74250  
    30413041    if (pThis->pfnHeaderCallback)
    30423042    {
    3043         /* Find the end of the field name first.  Since cURL gives us the
    3044            "HTTP/{version} {code} {status}" line too, we slap a fictitious
    3045            field name ':http-status-line' in front of it. */
     3043        /*
     3044         * Find the end of the field name first.
     3045         */
    30463046        uint32_t    uMatchWord;
    30473047        size_t      cchField;
     
    30633063            cchValue = cbToAppend - cchField - 1;
    30643064        }
    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] == '/')
    30663068        {
    30673069            pchField   = ":http-status-line";
     
    30713073            cchValue   = cbToAppend;
    30723074        }
     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        }
    30733085        else
    30743086            AssertMsgFailedReturn(("pchBuf=%.*s\n", cbToAppend, pchBuf), cbToAppend);
    30753087
    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         */
    30783092        if (cchValue > 0 && RT_C_IS_BLANK(*pchValue))
    30793093            pchValue++, cchValue--;
     
    30813095            cchValue--;
    30823096
    3083         /* Pass it to the callback. */
     3097        /*
     3098         * Pass it to the callback.
     3099         */
     3100        Log6(("rtHttpWriteHeaderData: %.*s: %.*s\n", cchField, pchBuf, cchValue, pchValue));
    30843101        int rc = pThis->pfnHeaderCallback(pThis, uMatchWord, pchBuf, cchField,
    30853102                                          pchValue, cchValue, pThis->pvHeaderCallbackUser);
     
    30873104            return cbToAppend;
    30883105
     3106        /* Abort on error. */
    30893107        if (RT_SUCCESS(pThis->rcOutput))
    30903108            pThis->rcOutput = rc;
     
    35473565
    35483566        /* Headers. */
    3549         if (ppvHeaders && CURL_SUCCESS(rcCurl))
     3567        if (CURL_SUCCESS(rcCurl))
    35503568        {
    35513569            RT_ZERO(pThis->HeadersOutput.uData.Mem);
  • trunk/src/VBox/Runtime/testcase/tstRTCRest-1.cpp

    r74202 r74250  
    3434#include <iprt/cpp/restclient.h>
    3535
     36#include <iprt/ctype.h>
    3637#include <iprt/err.h>
    3738#include <iprt/message.h>
     
    20952096{
    20962097    RTTestSub(g_hTest, "RTCRestClientRequestBase");
    2097 
    20982098    {
    20992099        TestRequest Req1("this-is-a-string", 123456789, 5, "1", "22", "333", "444", "555");
     
    21252125
    21262126
     2127class TestResponse : public RTCRestClientResponseBase
     2128{
     2129public:
     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
     2138protected:
     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
     2188public:
     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
     2213void 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
    21272242int main()
    21282243{
     
    21402255        testStringMap();
    21412256        testClientRequestBase();
    2142         /** @todo test the response base class too. */
     2257        testClientResponseBase();
    21432258
    21442259        rcExit = RTTestSummaryAndDestroy(g_hTest);
Note: See TracChangeset for help on using the changeset viewer.

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