summaryrefslogtreecommitdiffstats
path: root/include/iprt/cpp/restclient.h
blob: b62a89eaa6be6657e1d48405336f0d0503d1584b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
/** @file
 * IPRT - C++ Representational State Transfer (REST) Client Classes.
 */

/*
 * Copyright (C) 2008-2022 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 <https://www.gnu.org/licenses>.
 *
 * 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
 */

#ifndef IPRT_INCLUDED_cpp_restclient_h
#define IPRT_INCLUDED_cpp_restclient_h
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#include <iprt/http.h>
#include <iprt/cpp/restbase.h>
#include <iprt/cpp/reststringmap.h>


/** @defgroup grp_rt_cpp_restclient     C++ Representational State Transfer (REST) Client Classes.
 * @ingroup grp_rt_cpp
 * @{
 */

/**
 * Specialization of RTCRestBinary for use with body parameters in a client.
 *
 * This enables registering data callbacks for provinding data to upload.
 */
class RT_DECL_CLASS RTCRestBinaryParameter : public RTCRestBinary
{
public:
    /** Default constructor. */
    RTCRestBinaryParameter() RT_NOEXCEPT;

    /** Safe copy assignment method. */
    virtual int assignCopy(RTCRestBinaryParameter const &a_rThat) RT_NOEXCEPT;
    /** Safe copy assignment method.
      * @note Resets callbacks and ASSUMES that @a a_cbData is the content length. */
    virtual int assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT RT_OVERRIDE;
    /** Safe copy assignment method.
     * @note Resets callbacks and ASSUMES that @a a_cbData is the content length. */
    virtual int assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;

    /**
     * Use the specified data buffer directly.
     * @note Resets callbacks and ASSUMES that @a a_cbData is the content length. */
    virtual int assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;
    /**
     * Use the specified data buffer directly.
     * @note This will assert and work like assignReadOnly. */
    virtual int assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT RT_OVERRIDE;

    /** Make a clone of this object. */
    inline RTCRestBinaryParameter *clone() const RT_NOEXCEPT { return (RTCRestBinaryParameter *)baseClone(); }

    /* Overridden methods: */
    virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
    virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
    virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;

    /** Factory method. */
    static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;

    /**
     * Retrieves the callback data.
     */
    inline void *getCallbackData() const RT_NOEXCEPT  { return m_pvCallbackData; }

    /**
     * Sets the content-type for an upload.
     *
     * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
     * @param   a_pszContentType    The content type to set.
     *                              If NULL, no content type is set.
     */
    int setContentType(const char *a_pszContentType) RT_NOEXCEPT;

    /**
     * Gets the content type that was set.
     */
    inline RTCString const &getContentType() const RT_NOEXCEPT { return m_strContentType; }

    /**
     * Gets the content-length value (UINT64_MAX if not available).
     */
    inline uint64_t getContentLength() const RT_NOEXCEPT { return m_cbContentLength; }

    /**
     * Callback for producing bytes to upload.
     *
     * @returns IPRT status code.
     * @param   a_pThis         The related string object.
     * @param   a_pvDst         Where to put the bytes.
     * @param   a_cbDst         Max number of bytes to produce.
     * @param   a_offContent    The byte offset corresponding to the start of @a a_pvDst.
     * @param   a_pcbActual     Where to return the number of bytes actually produced.
     *
     * @remarks Use getCallbackData to get the user data.
     *
     * @note    The @a a_offContent parameter does not imply random access or anthing
     *          like that, it is just a convenience provided by the caller.  The value
     *          is the sum of the previously returned @a *pcbActual values.
     */
    typedef DECLCALLBACKTYPE(int, FNPRODUCER,(RTCRestBinaryParameter *a_pThis, void *a_pvDst, size_t a_cbDst,
                                              uint64_t a_offContent, size_t *a_pcbActual)) /*RT_NOEXCEPT*/;
    /** Pointer to a byte producer callback. */
    typedef FNPRODUCER *PFNPRODUCER;

    /**
     * Sets the producer callback.
     *
     * @param   a_pfnProducer       The callback function for producing data.
     * @param   a_pvCallbackData    Data the can be retrieved from the callback
     *                              using getCallbackData().
     * @param   a_cbContentLength   The amount of data that will be uploaded and
     *                              to be set as the value of the content-length
     *                              header field.  Pass UINT64_MAX if not known.
     *
     * @note    This will drop any buffer previously registered using setUploadData().
     */
    void setProducerCallback(PFNPRODUCER a_pfnProducer, void *a_pvCallbackData = NULL, uint64_t a_cbContentLength = UINT64_MAX) RT_NOEXCEPT;

    /**
     * Preprares transmission via the @a a_hHttp client instance.
     *
     * @returns IPRT status code.
     * @param   a_hHttp             The HTTP client instance.
     * @internal
     */
    virtual int xmitPrepare(RTHTTP a_hHttp) const RT_NOEXCEPT;

    /**
     * For completing and/or undoing setup from xmitPrepare.
     *
     * @param   a_hHttp             The HTTP client instance.
     * @internal
     */
    virtual void xmitComplete(RTHTTP a_hHttp) const RT_NOEXCEPT;

protected:
    /** Number of bytes corresponding to content-length.
     * UINT64_MAX if not known.  Used both for unploads and downloads. */
    uint64_t    m_cbContentLength;
    /** The content type if set (upload only). */
    RTCString   m_strContentType;
    /** Pointer to user-registered producer callback function (upload only). */
    PFNPRODUCER m_pfnProducer;
    /** User argument for both callbacks (both). */
    void       *m_pvCallbackData;

    /** @copydoc FNRTHTTPUPLOADCALLBACK */
    static DECLCALLBACK(int) xmitHttpCallback(RTHTTP hHttp, void *pvBuf, size_t cbBuf, uint64_t offContent,
                                              size_t *pcbActual, void *pvUser) RT_NOEXCEPT;

private:
    /* No copy constructor or copy assignment: */
    RTCRestBinaryParameter(RTCRestBinaryParameter const &a_rThat);
    RTCRestBinaryParameter &operator=(RTCRestBinaryParameter const &a_rThat);
};


/**
 * Specialization of RTCRestBinary for use with responses in a client.
 *
 * This enables registering data callbacks for consuming downloaded data.
 */
class RT_DECL_CLASS RTCRestBinaryResponse : public RTCRestBinary
{
public:
    /** Default constructor. */
    RTCRestBinaryResponse() RT_NOEXCEPT;

    /** Safe copy assignment method. */
    virtual int assignCopy(RTCRestBinaryResponse const &a_rThat) RT_NOEXCEPT;
    /** Safe copy assignment method. */
    virtual int assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT RT_OVERRIDE;
    /** Safe copy assignment method.
     * @note This will assert and fail as it makes no sense for a download.  */
    virtual int assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;

    /**
     * Use the specified data buffer directly.
     * @note This will assert and fail as it makes no sense for a download.
     */
    virtual int assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT RT_OVERRIDE;
    /**
     * Use the specified data buffer directly.
     * @note This will drop any previously registered producer callback and user data.
     */
    virtual int assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT RT_OVERRIDE;

    /** Make a clone of this object. */
    inline RTCRestBinaryResponse *clone() const RT_NOEXCEPT { return (RTCRestBinaryResponse *)baseClone(); }

    /* Overridden methods: */
    virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE;
    virtual int resetToDefault() RT_NOEXCEPT RT_OVERRIDE;
    virtual const char *typeName(void) const RT_NOEXCEPT RT_OVERRIDE;

    /** Factory method. */
    static DECLCALLBACK(RTCRestObjectBase *) createInstance(void) RT_NOEXCEPT;

    /**
     * Retrieves the callback data.
     */
    inline void *getCallbackData() const RT_NOEXCEPT { return m_pvCallbackData; }

    /**
     * Sets the max size to download to memory.
     *
     * This also indicates the intention to download to a memory buffer, so it
     * will drop any previously registered consumer callback and its user data.
     *
     * @param   a_cbMaxDownload Maximum number of bytes to download to memory.
     *                          If 0, a default is selected (currently 32MiB for
     *                          32-bit hosts and 128MiB for 64-bit).
     */
    void setMaxDownloadSize(size_t a_cbMaxDownload) RT_NOEXCEPT;

    /**
     * Gets the content-length value (UINT64_MAX if not available).
     */
    inline uint64_t getContentLength() const RT_NOEXCEPT { return m_cbContentLength; }

    /**
     * Callback for consuming downloaded bytes.
     *
     * @returns IPRT status code.
     * @param   a_pThis         The related string object.
     * @param   a_pvSrc         Buffer containing the bytes.
     * @param   a_cbSrc         The number of bytes in the buffer.
     * @param   a_uHttpStatus   The HTTP status code.
     * @param   a_offContent    The byte offset corresponding to the start of @a a_pvSrc.
     * @param   a_cbContent     The content length field value, UINT64_MAX if not available.
     *
     * @remarks Use getCallbackData to get the user data.
     *
     * @note    The @a a_offContent parameter does not imply random access or anthing
     *          like that, it is just a convenience provided by the caller.  The value
     *          is the sum of the previous @a a_cbSrc values.
     */
    typedef DECLCALLBACKTYPE(int, FNCONSUMER,(RTCRestBinaryResponse *a_pThis, const void *a_pvSrc, size_t a_cbSrc,
                                              uint32_t a_uHttpStatus, uint64_t a_offContent, uint64_t a_cbContent)) /*RT_NOEXCEPT*/;
    /** Pointer to a byte consumer callback. */
    typedef FNCONSUMER *PFNCONSUMER;

    /**
     * Sets the consumer callback.
     *
     * @param   a_pfnConsumer       The callback function for consuming downloaded data.
     *                              NULL if data should be stored in m_pbData (the default).
     * @param   a_pvCallbackData    Data the can be retrieved from the callback
     *                              using getCallbackData().
     */
    void setConsumerCallback(PFNCONSUMER a_pfnConsumer, void *a_pvCallbackData = NULL) RT_NOEXCEPT;

    /**
     * Preprares for receiving via the @a a_hHttp client instance.
     *
     * @returns IPRT status code.
     * @param   a_hHttp             The HTTP client instance.
     * @param   a_fCallbackFlags    The HTTP callback flags (status code spec).
     * @internal
     */
    virtual int receivePrepare(RTHTTP a_hHttp, uint32_t a_fCallbackFlags) RT_NOEXCEPT;

    /**
     * For completing and/or undoing setup from receivePrepare.
     *
     * @param   a_hHttp             The HTTP client instance.
     * @internal
     */
    virtual void receiveComplete(RTHTTP a_hHttp) RT_NOEXCEPT;

protected:
    /** Number of bytes corresponding to content-length.
     * UINT64_MAX if not known.  Used both for unploads and downloads. */
    uint64_t    m_cbContentLength;
    /** Number of bytes downloaded thus far. */
    uint64_t    m_cbDownloaded;
    /** Pointer to user-registered consumer callback function (download only). */
    PFNCONSUMER m_pfnConsumer;
    /** User argument for both callbacks (both). */
    void       *m_pvCallbackData;
    /** Maximum data to download to memory (download only). */
    size_t      m_cbMaxDownload;

    /** @copydoc FNRTHTTPDOWNLOADCALLBACK */
    static DECLCALLBACK(int) receiveHttpCallback(RTHTTP hHttp, void const *pvBuf, size_t cbBuf, uint32_t uHttpStatus,
                                                 uint64_t offContent, uint64_t cbContent, void *pvUser) RT_NOEXCEPT;

private:
    /* No copy constructor or copy assignment: */
    RTCRestBinaryResponse(RTCRestBinaryResponse const &a_rThat);
    RTCRestBinaryResponse &operator=(RTCRestBinaryResponse const &a_rThat);
};


/**
 * Base class for REST client requests.
 *
 * This encapsulates parameters and helps transform them into a HTTP request.
 *
 * Parameters can be transfered in a number of places:
 *      - Path part of the URL.
 *      - Query part of the URL.
 *      - HTTP header fields.
 *      - FORM body.
 *      - JSON body.
 *      - XML body.
 *      - ...
 *
 * They can be require or optional.  The latter may have default values.  In
 * swagger 3 they can also be nullable, which means the null-indicator cannot
 * be used for tracking optional parameters.
 */
class RT_DECL_CLASS RTCRestClientRequestBase
{
public:
    RTCRestClientRequestBase() RT_NOEXCEPT;
    virtual ~RTCRestClientRequestBase();
    RTCRestClientRequestBase(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT;
    RTCRestClientRequestBase &operator=(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT;

    /**
     * Reset all members to default values.
     * @returns IPRT status code.
     */
    virtual int resetToDefault() RT_NOEXCEPT = 0;

    /**
     * Getter for the operation name.  Provided by the generated
     * subclasses so that base class code may use it for more
     * informative logs.
     */
    virtual const char *getOperationName() const RT_NOEXCEPT = 0;

    /**
     * Prepares the HTTP handle for transmitting this request.
     *
     * @returns IPRT status code.
     * @param   a_pStrPath  Where to set path parameters.  Will be appended to the base path.
     * @param   a_pStrQuery Where to set query parameters.
     * @param   a_hHttp     Where to set header parameters and such.
     * @param   a_pStrBody  Where to set body parameters.
     */
    virtual int xmitPrepare(RTCString *a_pStrPath, RTCString *a_pStrQuery, RTHTTP a_hHttp, RTCString *a_pStrBody) const RT_NOEXCEPT = 0;

    /**
     * Always called after the request has been transmitted.
     *
     * @param   a_rcStatus  Negative numbers are IPRT errors, positive are HTTP status codes.
     * @param   a_hHttp     The HTTP handle the request was performed on.
     */
    virtual void xmitComplete(int a_rcStatus, RTHTTP a_hHttp) const RT_NOEXCEPT = 0;

    /**
     * Checks if there are were any assignment errors.
     */
    inline bool hasAssignmentErrors() const RT_NOEXCEPT { return m_fErrorSet != 0; }

protected:
    /** Set of fields that have been explicitly assigned a value. */
    uint64_t m_fIsSet;
    /** Set of fields where value assigning failed. */
    uint64_t m_fErrorSet;

    /** Path parameter descriptor. */
    typedef struct
    {
        const char                 *pszName;    /**< The name string to replace (including {}). */
        size_t                      cchName;    /**< Length of pszName. */
        uint32_t                    fFlags;     /**< The toString flags. */
        uint8_t                     iBitNo;     /**< The parameter bit number. */
    } PATHPARAMDESC;

    /** Path parameter state. */
    typedef struct
    {
        RTCRestObjectBase const    *pObj;       /**< Pointer to the parameter object. */
        size_t                      offName;    /**< Maintained by worker. */
    } PATHPARAMSTATE;

    /**
     * Do path parameters.
     *
     * @returns IPRT status code
     * @param   a_pStrPath          The destination path.
     * @param   a_pszPathTemplate   The path template string.
     * @param   a_cchPathTemplate   The length of the path template string.
     * @param   a_paPathParams      The path parameter descriptors (static).
     * @param   a_paPathParamStates The path parameter objects and states.
     * @param   a_cPathParams       Number of path parameters.
     */
    int 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;

    /** Query parameter descriptor. */
    typedef struct
    {
        const char *pszName;            /**< The parameter name. */
        uint32_t    fFlags;             /**< The toString flags. */
        bool        fRequired;          /**< Required or not. */
        uint8_t     iBitNo;             /**< The parameter bit number. */
    } QUERYPARAMDESC;

    /**
     * Do query parameters.
     *
     * @returns IPRT status code
     * @param   a_pStrQuery         The destination string.
     * @param   a_paQueryParams     The query parameter descriptors.
     * @param   a_papQueryParamObjs The query parameter objects, parallel to @a a_paQueryParams.
     * @param   a_cQueryParams      Number of query parameters.
     */
    int doQueryParameters(RTCString *a_pStrQuery, QUERYPARAMDESC const *a_paQueryParams,
                          RTCRestObjectBase const **a_papQueryParamObjs, size_t a_cQueryParams) const RT_NOEXCEPT;

    /** Header parameter descriptor. */
    typedef struct
    {
        const char *pszName;            /**< The parameter name. */
        uint32_t    fFlags;             /**< The toString flags. */
        bool        fRequired;          /**< Required or not. */
        uint8_t     iBitNo;             /**< The parameter bit number. */
        bool        fMapCollection;     /**< Collect headers starting with pszName into a map. */
    } HEADERPARAMDESC;

    /**
     * Do header parameters.
     *
     * @returns IPRT status code
     * @param   a_hHttp                 Where to set header parameters.
     * @param   a_paHeaderParams        The header parameter descriptors.
     * @param   a_papHeaderParamObjs    The header parameter objects, parallel to @a a_paHeaderParams.
     * @param   a_cHeaderParams         Number of header parameters.
     */
    int doHeaderParameters(RTHTTP a_hHttp, HEADERPARAMDESC const *a_paHeaderParams,
                           RTCRestObjectBase const **a_papHeaderParamObjs, size_t a_cHeaderParams) const RT_NOEXCEPT;
};


/**
 * Base class for REST client responses.
 */
class RT_DECL_CLASS RTCRestClientResponseBase
{
public:
    /** Default constructor. */
    RTCRestClientResponseBase() RT_NOEXCEPT;
    /** Destructor. */
    virtual ~RTCRestClientResponseBase();
    /** Copy constructor. */
    RTCRestClientResponseBase(RTCRestClientResponseBase const &a_rThat);
    /** Copy assignment operator. */
    RTCRestClientResponseBase &operator=(RTCRestClientResponseBase const &a_rThat);

    /**
     * Resets the object state.
     */
    virtual void reset(void) RT_NOEXCEPT;

    /**
     * Getter for the operation name.  Provided by the generated
     * subclasses so that base class code may use it for more
     * informative logs.
     */
    virtual const char *getOperationName() const RT_NOEXCEPT = 0;

    /**
     * Prepares the HTTP handle for receiving the response.
     *
     * This may install callbacks and such like.
     *
     * When overridden, the parent class must always be called.
     *
     * @returns IPRT status code.
     * @param   a_hHttp     The HTTP handle to prepare for receiving.
     */
    virtual int receivePrepare(RTHTTP a_hHttp) RT_NOEXCEPT;

    /**
     * Called when the HTTP request has been completely received.
     *
     * @param   a_rcStatus  Negative numbers are IPRT errors, positive are HTTP status codes.
     * @param   a_hHttp     The HTTP handle the request was performed on.
     *                      This can be NIL_RTHTTP should something fail early, in
     *                      which case it is possible receivePrepare() wasn't called.
     *
     * @note    Called before consumeBody() but after consumeHeader().
     */
    virtual void receiveComplete(int a_rcStatus, RTHTTP a_hHttp) RT_NOEXCEPT;

    /**
     * Callback that consumes HTTP body data from the server.
     *
     * @param   a_pchData  Body data.
     * @param   a_cbData   Amount of body data.
     *
     * @note    Called after consumeHeader().
     */
    virtual void consumeBody(const char *a_pchData, size_t a_cbData) RT_NOEXCEPT;

    /**
     * Called after status, headers and body all have been presented.
     *
     * @returns IPRT status code.
     */
    virtual void receiveFinal() RT_NOEXCEPT;

    /**
     * Getter for m_rcStatus.
     * @returns Negative numbers are IPRT errors, positive are HTTP status codes.
     */
    inline int getStatus() const RT_NOEXCEPT { return m_rcStatus; }

    /**
     * Getter for m_rcHttp.
     * @returns HTTP status code or VERR_NOT_AVAILABLE.
     */
    inline int getHttpStatus() const RT_NOEXCEPT { return m_rcHttp; }

    /**
     * Getter for m_pErrInfo.
     */
    inline PCRTERRINFO getErrInfo(void) const RT_NOEXCEPT { return m_pErrInfo; }

    /**
     * Getter for m_strContentType.
     */
    inline RTCString const &getContentType(void) const RT_NOEXCEPT { return m_strContentType; }


protected:
    /** Negative numbers are IPRT errors, positive are HTTP status codes. */
    int         m_rcStatus;
    /** The HTTP status code, VERR_NOT_AVAILABLE if not set. */
    int         m_rcHttp;
    /** Error information. */
    PRTERRINFO  m_pErrInfo;
    /** The value of the Content-Type header field. */
    RTCString   m_strContentType;

    PRTERRINFO  getErrInfoInternal(void) RT_NOEXCEPT;
    void        deleteErrInfo(void) RT_NOEXCEPT;
    void        copyErrInfo(PCRTERRINFO pErrInfo) RT_NOEXCEPT;

    /**
     * Reports an error (or warning if a_rc non-negative).
     *
     * @returns a_rc
     * @param   a_rc        The status code to report and return.  The first
     *                      error status is assigned to m_rcStatus, subsequent
     *                      ones as well as informational statuses are not
     *                      recorded by m_rcStatus.
     * @param   a_pszFormat The message format string.
     * @param   ...         Message arguments.
     */
    int addError(int a_rc, const char *a_pszFormat, ...) RT_NOEXCEPT;

    /**
     * Deserializes a header field value.
     *
     * @returns IPRT status code.
     * @param   a_pObj              The object to deserialize into.
     * @param   a_pchValue          Pointer to the value (not zero terminated).
     *                              Not necessarily valid UTF-8!
     * @param   a_cchValue          The value length.
     * @param   a_fFlags            Flags to pass to fromString().
     * @param   a_pszErrorTag       The error tag (field name).
     */
    int deserializeHeader(RTCRestObjectBase *a_pObj, const char *a_pchValue, size_t a_cchValue,
                          uint32_t a_fFlags, const char *a_pszErrorTag) RT_NOEXCEPT;

    /**
     * Deserializes a header field value.
     *
     * @returns IPRT status code.
     * @param   a_pMap              The string map object to deserialize into.
     * @param   a_pchField          Pointer to the map field name.  (Caller dropped the prefix.)
     *                              Not necessarily valid UTF-8!
     * @param   a_cchField          Length of field name.
     * @param   a_pchValue          Pointer to the value (not zero terminated).
     *                              Not necessarily valid UTF-8!
     * @param   a_cchValue          The value length.
     * @param   a_fFlags            Flags to pass to fromString().
     * @param   a_pszErrorTag       The error tag (field name).
     */
    int deserializeHeaderIntoMap(RTCRestStringMapBase *a_pMap, const char *a_pchField, size_t a_cchField,
                                 const char *a_pchValue, size_t a_cchValue, uint32_t a_fFlags, const char *a_pszErrorTag) RT_NOEXCEPT;

    /**
     * Helper that does the deserializing of the response body
     * via deserializeBodyFromJsonCursor().
     *
     * @param   a_pchData       The body blob.
     * @param   a_cbData        The size of the body blob.
     * @param   a_pszBodyName   The name of the body parameter.
     */
    void deserializeBody(const char *a_pchData, size_t a_cbData, const char *a_pszBodyName) RT_NOEXCEPT;

    /**
     * Called by deserializeBody to do the actual body deserialization.
     *
     * @param   a_rCursor       The JSON cursor.
     */
    virtual void deserializeBodyFromJsonCursor(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT;

    /**
     * Primary json cursor for parsing bodies.
     */
    class PrimaryJsonCursorForBody : public RTCRestJsonPrimaryCursor
    {
    public:
        RTCRestClientResponseBase *m_pThat; /**< Pointer to response object. */
        PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName, RTCRestClientResponseBase *a_pThat) RT_NOEXCEPT;
        virtual int addError(RTCRestJsonCursor const &a_rCursor, int a_rc, const char *a_pszFormat, ...) RT_NOEXCEPT RT_OVERRIDE;
        virtual int unknownField(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT RT_OVERRIDE;
    };


    /**
     * Consumes a header.
     *
     * Child classes can override this to pick up their header fields, but must
     * always call the parent class.
     *
     * @returns IPRT status code.
     * @param   a_uMatchWord    Match word constructed by RTHTTP_MAKE_HDR_MATCH_WORD
     * @param   a_pchField      The field name (not zero terminated).
     *                          Not necessarily valid UTF-8!
     * @param   a_cchField      The length of the field.
     * @param   a_pchValue      The field value (not zero terminated).
     * @param   a_cchValue      The length of the value.
     */
    virtual int consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField,
                              const char *a_pchValue, size_t a_cchValue) RT_NOEXCEPT;

private:
    /** Callback for use with RTHttpSetHeaderCallback. */
    static DECLCALLBACK(int) receiveHttpHeaderCallback(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField,
                                                       const char *pchValue, size_t cchValue, void *pvUser) RT_NOEXCEPT;
};


/**
 * Base class for REST client responses.
 */
class RT_DECL_CLASS RTCRestClientApiBase
{
public:
    RTCRestClientApiBase() RT_NOEXCEPT;
    virtual ~RTCRestClientApiBase();

    /** @name Host and Base path (URL) handling.
     * @{ */
    /**
     * Gets the server URL.
     */
    const char *getServerUrl(void) const RT_NOEXCEPT;

    /**
     * Sets the whole server URL.
     * @returns IPRT status code.
     * @param   a_pszUrl        The new server URL.  NULL/empty to reset to default.
     */
    int setServerUrl(const char *a_pszUrl) RT_NOEXCEPT;

    /**
     * Sets the scheme part of the the server URL.
     * @returns IPRT status code.
     * @param   a_pszScheme     The new scheme.  Does not accept NULL or empty string.
     */
    int setServerScheme(const char *a_pszScheme) RT_NOEXCEPT;

    /**
     * Sets the authority (hostname + port) part of the the server URL.
     * @returns IPRT status code.
     * @param   a_pszAuthority  The new authority.  Does not accept NULL or empty string.
     */
    int setServerAuthority(const char *a_pszAuthority) RT_NOEXCEPT;

    /**
     * Sets the base path part of the the server URL.
     * @returns IPRT status code.
     * @param   a_pszBasePath   The new base path.  Does not accept NULL or empty string.
     */
    int setServerBasePath(const char *a_pszBasePath) RT_NOEXCEPT;

    /**
     * Gets the default server URL as specified in the specs.
     * @returns Server URL.
     */
    virtual const char *getDefaultServerUrl() const RT_NOEXCEPT = 0;

    /**
     * Gets the default server base path as specified in the specs.
     * @returns Host string (start of URL).
     */
    virtual const char *getDefaultServerBasePath() const RT_NOEXCEPT = 0;
    /** @} */

    /**
     * Sets the CA file to use for HTTPS.
     */
    int setCAFile(const char *pcszCAFile) RT_NOEXCEPT;
    /** @overload */
    int setCAFile(const RTCString &strCAFile) RT_NOEXCEPT;

    /** Flags to doCall. */
    enum
    {
        kDoCall_OciReqSignExcludeBody = 1, /**< Exclude the body when doing OCI request signing. */
        kDoCall_RequireBody           = 2  /**< The body is required. */
    };

protected:
    /** Handle to the HTTP connection object. */
    RTHTTP  m_hHttp;
    /** The server URL to use.  If empty use the default. */
    RTCString m_strServerUrl;
    /** The CA file to use.  If empty use the default. */
    RTCString m_strCAFile;

    /* Make non-copyable (RTCNonCopyable causes warnings): */
    RTCRestClientApiBase(RTCRestClientApiBase const &);
    RTCRestClientApiBase *operator=(RTCRestClientApiBase const &);

    /**
     * Re-initializes the HTTP instance.
     *
     * @returns IPRT status code.
     */
    virtual int reinitHttpInstance() RT_NOEXCEPT;

    /**
     * Hook that's called when doCall has fully assembled the request.
     *
     * Can be used for request signing and similar final steps.
     *
     * @returns IPRT status code.
     * @param   a_hHttp         The HTTP client instance.
     * @param   a_rStrFullUrl   The full URL.
     * @param   a_enmHttpMethod The HTTP request method.
     * @param   a_rStrXmitBody  The body text.
     * @param   a_fFlags        kDoCall_XXX.
     */
    virtual int xmitReady(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod,
                          RTCString const &a_rStrXmitBody, uint32_t a_fFlags) RT_NOEXCEPT;

    /**
     * Implements stuff for making an API call.
     *
     * @returns a_pResponse->getStatus()
     * @param   a_rRequest      Reference to the request object.
     * @param   a_enmHttpMethod The HTTP request method.
     * @param   a_pResponse     Pointer to the response object.
     * @param   a_pszMethod     The method name, for logging purposes.
     * @param   a_fFlags        kDoCall_XXX.
     */
    virtual int doCall(RTCRestClientRequestBase const &a_rRequest, RTHTTPMETHOD a_enmHttpMethod,
                       RTCRestClientResponseBase *a_pResponse, const char *a_pszMethod, uint32_t a_fFlags) RT_NOEXCEPT;

    /**
     * Implements OCI style request signing.
     *
     * @returns IPRT status code.
     * @param   a_hHttp         The HTTP client instance.
     * @param   a_rStrFullUrl   The full URL.
     * @param   a_enmHttpMethod The HTTP request method.
     * @param   a_rStrXmitBody  The body text.
     * @param   a_fFlags        kDoCall_XXX.
     * @param   a_hKey          The key to use for signing.
     * @param   a_rStrKeyId     The key ID.
     *
     * @remarks The signing scheme is covered by a series of drafts RFC, the latest being:
     *                  https://tools.ietf.org/html/draft-cavage-http-signatures-10
     */
    int ociSignRequest(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod,
                       RTCString const &a_rStrXmitBody, uint32_t a_fFlags, RTCRKEY a_hKey, RTCString const &a_rStrKeyId) RT_NOEXCEPT;

    /**
     * Worker for the server URL modifiers.
     *
     * @returns IPRT status code.
     * @param   a_pszServerUrl  The current server URL (for comparing).
     * @param   a_offDst        The offset of the component in the current server URL.
     * @param   a_cchDst        The current component length.
     * @param   a_pszSrc        The new URL component value.
     * @param   a_cchSrc        The length of the new component.
     */
    int setServerUrlPart(const char *a_pszServerUrl, size_t a_offDst, size_t a_cchDst, const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT;
};

/** @} */

#endif /* !IPRT_INCLUDED_cpp_restclient_h */