VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/rest/RTCRestBinaryString.cpp@ 74127

Last change on this file since 74127 was 74127, checked in by vboxsync, 6 years ago

IPRT/rest: More work on binary downloads and uploads. bugref:9167 [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: RTCRestBinaryString.cpp 74127 2018-09-06 18:32:37Z vboxsync $ */
2/** @file
3 * IPRT - C++ REST, RTCRestBinaryString implementation.
4 */
5
6/*
7 * Copyright (C) 2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_REST
32#include <iprt/cpp/restbinarystring.h>
33
34#include <iprt/assert.h>
35#include <iprt/err.h>
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41/** The default maximum download size. */
42#if ARCH_BITS == 32
43# define RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT _32M
44#else
45# define RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT _128M
46#endif
47
48
49/** Default constructor. */
50RTCRestBinaryString::RTCRestBinaryString()
51 : RTCRestObjectBase()
52 , m_pbData(NULL)
53 , m_cbAllocated(0)
54 , m_fFreeData(false)
55 , m_cbContentLength(UINT64_MAX)
56 , m_pvCallbackData(NULL)
57 , m_pfnProducer(NULL)
58 , m_strContentType()
59 , m_pfnConsumer(NULL)
60 , m_cbDownloaded(0)
61 , m_cbMaxDownload(RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT)
62{
63}
64
65
66/**
67 * Destructor.
68 */
69RTCRestBinaryString::~RTCRestBinaryString()
70{
71 freeData();
72 m_pvCallbackData = NULL;
73 m_pfnProducer = NULL;
74 m_pfnConsumer = NULL;
75}
76
77
78/**
79 * Safe copy assignment method.
80 */
81int RTCRestBinaryString::assignCopy(RTCRestBinaryString const &a_rThat)
82{
83 RT_NOREF(a_rThat);
84 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
85}
86
87
88void RTCRestBinaryString::freeData()
89{
90 if (m_pbData)
91 {
92 if (m_fFreeData)
93 RTMemFree(m_pbData);
94 m_pbData = NULL;
95 }
96 m_fFreeData = false;
97 m_cbAllocated = 0;
98 m_cbDownloaded = 0;
99 m_cbContentLength = UINT64_MAX;
100}
101
102
103/*********************************************************************************************************************************
104* Overridden methods *
105*********************************************************************************************************************************/
106
107int RTCRestBinaryString::resetToDefault()
108{
109 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
110}
111
112
113RTCRestOutputBase &RTCRestBinaryString::serializeAsJson(RTCRestOutputBase &a_rDst) const
114{
115 AssertMsgFailed(("We should never get here!\n"));
116 a_rDst.printf("null");
117 return a_rDst;
118}
119
120
121int RTCRestBinaryString::deserializeFromJson(RTCRestJsonCursor const &a_rCursor)
122{
123 return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NOT_SUPPORTED, "RTCRestBinaryString does not support deserialization!");
124}
125
126
127int RTCRestBinaryString::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const
128{
129 RT_NOREF(a_pDst, a_fFlags);
130 AssertFailedReturn(VERR_NOT_SUPPORTED);
131}
132
133
134int RTCRestBinaryString::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo/*= NULL*/,
135 uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/)
136{
137 RT_NOREF(a_rValue, a_pszName, a_fFlags);
138 AssertFailedReturn(RTErrInfoSet(a_pErrInfo, VERR_NOT_SUPPORTED, "RTCRestBinaryString does not support fromString()!"));
139}
140
141
142RTCRestObjectBase::kTypeClass RTCRestBinaryString::typeClass(void) const
143{
144 return kTypeClass_BinaryString;
145}
146
147
148const char *RTCRestBinaryString::typeName(void) const
149{
150 return "RTCRestBinaryString";
151}
152
153
154/** Factory method. */
155/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestBinaryString::createInstance(void)
156{
157 return new (std::nothrow) RTCRestBinaryString();
158}
159
160
161/*********************************************************************************************************************************
162* Transmit related methods *
163*********************************************************************************************************************************/
164
165int RTCRestBinaryString::setContentType(const char *a_pszContentType)
166{
167 return m_strContentType.assignNoThrow(a_pszContentType);
168}
169
170
171int RTCRestBinaryString::setUploadData(void const *a_pvData, size_t a_cbData, bool a_fCopy /*= true*/)
172{
173 freeData();
174
175 if (a_cbData != 0)
176 {
177 if (a_fCopy)
178 {
179 m_pbData = (uint8_t *)RTMemDup(a_pvData, a_cbData);
180 AssertReturn(m_pbData, VERR_NO_MEMORY);
181 m_fFreeData = true;
182 m_cbAllocated = a_cbData;
183 }
184 else
185 {
186 AssertPtrReturn(a_pvData, VERR_INVALID_POINTER);
187 m_pbData = (uint8_t *)a_pvData;
188 }
189 }
190 m_cbContentLength = a_cbData;
191
192 return VINF_SUCCESS;
193}
194
195
196void RTCRestBinaryString::setProducerCallback(PFNPRODUCER a_pfnProducer, void *a_pvCallbackData /*= NULL*/,
197 uint64_t a_cbContentLength /*= UINT64_MAX*/)
198{
199 freeData();
200
201 m_cbContentLength = a_cbContentLength;
202 m_pfnProducer = a_pfnProducer;
203 m_pvCallbackData = a_pvCallbackData;
204}
205
206
207int RTCRestBinaryString::xmitPrepare(RTHTTP a_hHttp) const
208{
209 AssertReturn(m_pbData != NULL || m_pfnProducer != NULL || m_cbContentLength == 0, VERR_INVALID_STATE);
210
211
212 /*
213 * Set the content type if given.
214 */
215 if (m_strContentType.isNotEmpty())
216 {
217 Assert(!RTHttpGetHeader(a_hHttp, RT_STR_TUPLE("Content-Type")));
218 int rc = RTHttpAddHeader(a_hHttp, "Content-Type", m_strContentType.c_str(), m_strContentType.length(),
219 RTHTTPADDHDR_F_BACK);
220 AssertRCReturn(rc, rc);
221 }
222
223 /*
224 * Set the content length if given.
225 */
226 if (m_cbContentLength != UINT64_MAX)
227 {
228 const char *pszContentLength = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE("Content-Length"));
229 AssertMsgReturn(!pszContentLength || RTStrToUInt64(pszContentLength) == m_cbContentLength,
230 ("pszContentLength=%s does not match m_cbContentLength=%RU64\n", pszContentLength, m_cbContentLength),
231 VERR_MISMATCH);
232 if (!pszContentLength)
233 {
234 char szValue[64];
235 ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), m_cbContentLength, 10, 0, 0, 0);
236 int rc = RTHttpAddHeader(a_hHttp, "Content-Length", szValue, cchValue, RTHTTPADDHDR_F_BACK);
237 AssertRCReturn(rc, rc);
238 }
239 }
240
241 /*
242 * Register an upload callback.
243 */
244 int rc = RTHttpSetUploadCallback(a_hHttp, m_cbContentLength, xmitHttpCallback, (RTCRestBinaryString *)this);
245 AssertRCReturn(rc, rc);
246
247 return VINF_SUCCESS;
248}
249
250
251/*static*/ DECLCALLBACK(int)
252RTCRestBinaryString::xmitHttpCallback(RTHTTP hHttp, void *pvBuf, size_t cbBuf,
253 uint64_t offContent, size_t *pcbActual, void *pvUser)
254{
255 RTCRestBinaryString *pThis = (RTCRestBinaryString *)pvUser;
256
257 /*
258 * Call the user upload callback if we've got one.
259 */
260 if (pThis->m_pfnProducer)
261 return pThis->m_pfnProducer(pThis, pvBuf, cbBuf, offContent, pcbActual);
262
263 /*
264 * Feed from the memory buffer.
265 */
266 if (offContent < pThis->m_cbContentLength)
267 {
268 uint64_t const cbLeft = pThis->m_cbContentLength - offContent;
269 size_t const cbToCopy = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
270 memcpy(pvBuf, &pThis->m_pbData[(size_t)offContent], cbToCopy);
271 *pcbActual = cbToCopy;
272 }
273 else
274 *pcbActual = 0;
275
276 RT_NOREF(hHttp);
277 return VINF_SUCCESS;
278}
279
280
281void RTCRestBinaryString::xmitComplete(RTHTTP a_hHttp) const
282{
283 /* Unset the callback. */
284 int rc = RTHttpSetUploadCallback(a_hHttp, UINT64_MAX, NULL, NULL);
285 AssertRC(rc);
286}
287
288
289/*********************************************************************************************************************************
290* Download related methods *
291*********************************************************************************************************************************/
292
293void RTCRestBinaryString::setMaxDownloadSize(size_t a_cbMaxDownload)
294{
295 if (a_cbMaxDownload == 0)
296 m_cbMaxDownload = RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT;
297 else
298 m_cbMaxDownload = a_cbMaxDownload;
299}
300
301
302void RTCRestBinaryString::setConsumerCallback(PFNCONSUMER a_pfnConsumer, void *a_pvCallbackData /*= NULL*/)
303{
304 freeData();
305
306 a_pfnConsumer = a_pfnConsumer;
307 m_pvCallbackData = a_pvCallbackData;
308}
309
310
311int RTCRestBinaryString::receivePrepare(RTHTTP a_hHttp, uint32_t a_fCallbackFlags)
312{
313 /*
314 * Register an download callback.
315 */
316 int rc = RTHttpSetDownloadCallback(a_hHttp, a_fCallbackFlags, receiveHttpCallback, this);
317 AssertRCReturn(rc, rc);
318
319 return VINF_SUCCESS;
320}
321
322
323void RTCRestBinaryString::receiveComplete(RTHTTP a_hHttp)
324{
325 /* Unset the callback. */
326 int rc = RTHttpSetDownloadCallback(a_hHttp, RTHTTPDOWNLOAD_F_ANY_STATUS, NULL, NULL);
327 AssertRC(rc);
328}
329
330
331/*static*/ DECLCALLBACK(int)
332RTCRestBinaryString::receiveHttpCallback(RTHTTP hHttp, void const *pvBuf, size_t cbBuf, uint32_t uHttpStatus,
333 uint64_t offContent, uint64_t cbContent, void *pvUser)
334{
335 RTCRestBinaryString *pThis = (RTCRestBinaryString *)pvUser;
336 Assert(offContent == pThis->m_cbDownloaded);
337 pThis->m_cbContentLength = cbContent;
338
339 /*
340 * Call the user download callback if we've got one.
341 */
342 if (pThis->m_pfnConsumer)
343 {
344 int rc = pThis->m_pfnConsumer(pThis, pvBuf, cbBuf, uHttpStatus, offContent, cbContent);
345 if (RT_SUCCESS(rc))
346 pThis->m_cbDownloaded = offContent + cbBuf;
347 return rc;
348 }
349
350 /*
351 * Check download limit before adding more data.
352 */
353 AssertMsgReturn(offContent + cbBuf <= pThis->m_cbMaxDownload,
354 ("%RU64 + %zu = %RU64; max=%RU64", offContent, cbBuf, offContent + cbBuf, pThis->m_cbMaxDownload),
355 VERR_TOO_MUCH_DATA);
356 if (offContent == 0 && cbContent != UINT64_MAX)
357 AssertMsgReturn(cbContent <= pThis->m_cbMaxDownload, ("cbContent: %RU64; max=%RU64", cbContent, pThis->m_cbMaxDownload),
358 VERR_TOO_MUCH_DATA);
359
360 /*
361 * Make sure we've got buffer space before we copy in the data.
362 */
363 if (offContent + cbBuf <= pThis->m_cbAllocated)
364 { /* likely, except for the first time. */ }
365 else if (offContent == 0 && cbContent != UINT64_MAX)
366 {
367 void *pvNew = RTMemRealloc(pThis->m_pbData, (size_t)cbContent);
368 if (!pvNew)
369 return VERR_NO_MEMORY;
370 pThis->m_pbData = (uint8_t *)pvNew;
371 pThis->m_cbAllocated = (size_t)cbContent;
372 }
373 else
374 {
375 size_t cbNeeded = offContent + cbBuf;
376 size_t cbNew;
377 if (pThis->m_cbAllocated == 0)
378 cbNew = RT_MAX(_64K, RT_ALIGN_Z(cbNeeded, _64K));
379 else if (pThis->m_cbAllocated < _64M && cbNeeded <= _64M)
380 {
381 cbNew = pThis->m_cbAllocated * 2;
382 while (cbNew < cbNeeded)
383 cbNew *= 2;
384 }
385 else
386 cbNew = RT_ALIGN_Z(cbNeeded, _32M);
387
388 void *pvNew = RTMemRealloc(pThis->m_pbData, cbNew);
389 if (!pvNew)
390 return VERR_NO_MEMORY;
391 pThis->m_pbData = (uint8_t *)pvNew;
392 pThis->m_cbAllocated = cbNew;
393 }
394
395 /*
396 * Do the copying.
397 */
398 memcpy(&pThis->m_pbData[(size_t)offContent], pvBuf, cbBuf);
399 pThis->m_cbDownloaded = offContent + cbBuf;
400
401 RT_NOREF(hHttp);
402 return VINF_SUCCESS;
403}
404
Note: See TracBrowser for help on using the repository browser.

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