Newer
Older
Monty Brandenberg
committed
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
/**
* @file _httpoprequest.cpp
* @brief Definitions for internal class HttpOpRequest
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "_httpoprequest.h"
#include <cstdio>
#include <algorithm>
#include "httpcommon.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "bufferarray.h"
#include "httpheaders.h"
#include "httpoptions.h"
#include "_httprequestqueue.h"
#include "_httpreplyqueue.h"
#include "_httpservice.h"
#include "_httppolicy.h"
#include "_httppolicyglobal.h"
Monty Brandenberg
committed
#include "_httplibcurl.h"
#include "_httpinternal.h"
Monty Brandenberg
committed
#include "llhttpstatuscodes.h"
#include "llproxy.h"
Monty Brandenberg
committed
namespace
{
// Attempts to parse a 'Content-Range:' header. Caller must already
// have verified that the header tag is present. The 'buffer' argument
// will be processed by strtok_r calls which will modify the buffer.
//
// @return -1 if invalid and response should be dropped, 0 if valid an
// correct, 1 if couldn't be parsed. If 0, the first, last,
// and length arguments are also written. 'length' may be
// 0 if the length wasn't available to the server.
//
int parse_content_range_header(char * buffer,
unsigned int * first,
unsigned int * last,
unsigned int * length);
Monty Brandenberg
committed
// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and
// escape and format it for a tracing line in logging. Absolutely
// anything including NULs can be in the data. If @scrub is true,
// non-printing or non-ascii characters are replaced with spaces
// otherwise a %XX form of escaping is used.
void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub,
std::string & safe_line);
#if LL_WINDOWS
Monty Brandenberg
committed
// Not available on windows where the legacy strtok interface
// is thread-safe.
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif // LL_WINDOWS
Monty Brandenberg
committed
}
namespace LLCore
{
HttpOpRequest::HttpOpRequest()
: HttpOperation(),
mProcFlags(0U),
mReqMethod(HOR_GET),
mReqBody(NULL),
mReqOffset(0),
mReqLength(0),
Monty Brandenberg
committed
mReqHeaders(NULL),
mReqOptions(NULL),
mCurlActive(false),
mCurlHandle(NULL),
mCurlService(NULL),
mCurlHeaders(NULL),
mCurlBodyPos(0),
Monty Brandenberg
committed
mReplyBody(NULL),
mReplyOffset(0),
mReplyLength(0),
Monty Brandenberg
committed
mReplyFullLength(0),
mReplyHeaders(NULL),
mPolicyRetries(0),
mPolicyRetryAt(HttpTime(0)),
mPolicyRetryLimit(DEFAULT_RETRY_COUNT)
{
// *NOTE: As members are added, retry initialization/cleanup
// may need to be extended in @prepareRequest().
}
Monty Brandenberg
committed
HttpOpRequest::~HttpOpRequest()
{
if (mReqBody)
{
mReqBody->release();
mReqBody = NULL;
}
if (mReqOptions)
{
mReqOptions->release();
mReqOptions = NULL;
}
Monty Brandenberg
committed
if (mReqHeaders)
{
mReqHeaders->release();
Monty Brandenberg
committed
mReqHeaders = NULL;
}
if (mCurlHandle)
Monty Brandenberg
committed
{
curl_easy_cleanup(mCurlHandle);
mCurlHandle = NULL;
}
mCurlService = NULL;
if (mCurlHeaders)
{
curl_slist_free_all(mCurlHeaders);
mCurlHeaders = NULL;
Monty Brandenberg
committed
}
Monty Brandenberg
committed
mReplyOffset = 0;
mReplyLength = 0;
Monty Brandenberg
committed
mReplyFullLength = 0;
Monty Brandenberg
committed
if (mReplyBody)
{
mReplyBody->release();
mReplyBody = NULL;
}
if (mReplyHeaders)
{
mReplyHeaders->release();
mReplyHeaders = NULL;
}
}
void HttpOpRequest::stageFromRequest(HttpService * service)
{
addRef();
service->getPolicy().addOp(this); // transfers refcount
Monty Brandenberg
committed
}
void HttpOpRequest::stageFromReady(HttpService * service)
{
addRef();
service->getTransport().addOp(this); // transfers refcount
Monty Brandenberg
committed
}
void HttpOpRequest::stageFromActive(HttpService * service)
{
if (mReplyLength)
Monty Brandenberg
committed
{
// If non-zero, we received and processed a Content-Range
// header with the response. Verify that what it says
// is consistent with the received data.
if (mReplyLength != mReplyBody->size())
Monty Brandenberg
committed
{
// Not as expected, fail the request
mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
}
}
if (mCurlHeaders)
Monty Brandenberg
committed
{
// We take these headers out of the request now as they were
// allocated originally in this thread and the notifier doesn't
// need them. This eliminates one source of heap moving across
// threads.
curl_slist_free_all(mCurlHeaders);
mCurlHeaders = NULL;
Monty Brandenberg
committed
}
Monty Brandenberg
committed
addAsReply();
}
void HttpOpRequest::visitNotifier(HttpRequest * request)
{
static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS);
if (mUserHandler)
Monty Brandenberg
committed
{
HttpResponse * response = new HttpResponse();
response->setStatus(mStatus);
response->setBody(mReplyBody);
response->setHeaders(mReplyHeaders);
if (mReplyOffset || mReplyLength)
{
// Got an explicit offset/length in response
Monty Brandenberg
committed
response->setRange(mReplyOffset, mReplyLength, mReplyFullLength);
mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
Monty Brandenberg
committed
response->release();
}
}
HttpStatus HttpOpRequest::cancel()
{
mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED);
addAsReply();
return HttpStatus();
}
Monty Brandenberg
committed
HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
HttpOptions * options,
HttpHeaders * headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
mReqMethod = HOR_GET;
return HttpStatus();
}
HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
Monty Brandenberg
committed
const std::string & url,
size_t offset,
size_t len,
HttpOptions * options,
HttpHeaders * headers)
{
Monty Brandenberg
committed
setupCommon(policy_id, priority, url, NULL, options, headers);
Monty Brandenberg
committed
mReqMethod = HOR_GET;
mReqOffset = offset;
mReqLength = len;
Monty Brandenberg
committed
if (offset || len)
{
mProcFlags |= PF_SCAN_RANGE_HEADER;
}
Monty Brandenberg
committed
return HttpStatus();
Monty Brandenberg
committed
}
HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
{
Monty Brandenberg
committed
setupCommon(policy_id, priority, url, body, options, headers);
mReqMethod = HOR_POST;
Monty Brandenberg
committed
return HttpStatus();
HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
{
Monty Brandenberg
committed
setupCommon(policy_id, priority, url, body, options, headers);
mReqMethod = HOR_PUT;
return HttpStatus();
}
Monty Brandenberg
committed
void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
HttpOptions * options,
HttpHeaders * headers)
{
mProcFlags = 0;
mReqPolicy = policy_id;
mReqPriority = priority;
mReqURL = url;
if (body)
{
body->addRef();
mReqBody = body;
}
if (headers && ! mReqHeaders)
{
headers->addRef();
mReqHeaders = headers;
}
if (options && ! mReqOptions)
{
Monty Brandenberg
committed
options->addRef();
mReqOptions = options;
if (options->getWantHeaders())
{
mProcFlags |= PF_SAVE_HEADERS;
}
mPolicyRetryLimit = options->getRetries();
mPolicyRetryLimit = llclamp(mPolicyRetryLimit, LIMIT_RETRY_MIN, LIMIT_RETRY_MAX);
mTracing = (std::max)(mTracing, llclamp(options->getTrace(), TRACE_MIN, TRACE_MAX));
HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
Monty Brandenberg
committed
{
// Scrub transport and result data for retried op case
mCurlActive = false;
mCurlHandle = NULL;
mCurlService = NULL;
if (mCurlHeaders)
{
curl_slist_free_all(mCurlHeaders);
mCurlHeaders = NULL;
}
mCurlBodyPos = 0;
if (mReplyBody)
{
mReplyBody->release();
mReplyBody = NULL;
}
mReplyOffset = 0;
mReplyLength = 0;
Monty Brandenberg
committed
mReplyFullLength = 0;
if (mReplyHeaders)
{
mReplyHeaders->release();
mReplyHeaders = NULL;
}
Monty Brandenberg
committed
// *FIXME: better error handling later
HttpStatus status;
// Get policy options
HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
Monty Brandenberg
committed
mCurlHandle = curl_easy_init();
curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
Monty Brandenberg
committed
curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
// *FIXME: Revisit this old DNS timeout setting - may no longer be valid
Monty Brandenberg
committed
curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
Monty Brandenberg
committed
curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later
Monty Brandenberg
committed
curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle);
curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, mCurlHandle);
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
Monty Brandenberg
committed
const std::string * opt_value(NULL);
Monty Brandenberg
committed
long opt_long(0L);
policy.get(HttpRequest::GP_LLPROXY, &opt_long);
if (opt_long)
Monty Brandenberg
committed
// Use the viewer-based thread-safe API which has a
// fast/safe check for proxy enable. Would like to
// encapsulate this someway...
LLProxy::getInstance()->applyProxySettings(mCurlHandle);
Monty Brandenberg
committed
else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value))
Monty Brandenberg
committed
// *TODO: This is fine for now but get fuller socks/
// authentication thing going later....
curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str());
curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
Monty Brandenberg
committed
if (policy.get(HttpRequest::GP_CA_PATH, &opt_value))
Monty Brandenberg
committed
curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
}
if (policy.get(HttpRequest::GP_CA_FILE, &opt_value))
{
curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
switch (mReqMethod)
{
case HOR_GET:
curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
break;
case HOR_POST:
{
curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
long data_size(0);
if (mReqBody)
{
data_size = mReqBody->size();
}
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
}
break;
case HOR_PUT:
{
curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
long data_size(0);
if (mReqBody)
{
data_size = mReqBody->size();
}
curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
}
break;
default:
// *FIXME: fail out here
break;
}
Monty Brandenberg
committed
// Tracing
if (mTracing >= TRACE_CURL_HEADERS)
Monty Brandenberg
committed
{
curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle);
curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
}
// There's a CURLOPT for this now...
if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod)
Monty Brandenberg
committed
{
Monty Brandenberg
committed
static const char * const fmt1("Range: bytes=%lu-%lu");
static const char * const fmt2("Range: bytes=%lu-");
Monty Brandenberg
committed
char range_line[64];
#if LL_WINDOWS
Monty Brandenberg
committed
_snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1,
(mReqLength ? fmt1 : fmt2),
Monty Brandenberg
committed
(unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1));
Monty Brandenberg
committed
#else
snprintf(range_line, sizeof(range_line),
(mReqLength ? fmt1 : fmt2),
Monty Brandenberg
committed
(unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1));
#endif // LL_WINDOWS
Monty Brandenberg
committed
range_line[sizeof(range_line) - 1] = '\0';
mCurlHeaders = curl_slist_append(mCurlHeaders, range_line);
}
mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:");
// Request options
long timeout(DEFAULT_TIMEOUT);
if (mReqOptions)
{
timeout = mReqOptions->getTimeout();
timeout = llclamp(timeout, LIMIT_TIMEOUT_MIN, LIMIT_TIMEOUT_MAX);
}
curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
// Request headers
if (mReqHeaders)
{
// Caller's headers last to override
mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders);
}
curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
Monty Brandenberg
committed
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
if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS))
{
curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, mCurlHandle);
}
if (status)
{
mCurlService = service;
}
return status;
}
size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
CURL * handle(static_cast<CURL *>(userdata));
HttpOpRequest * op(NULL);
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op);
// *FIXME: check the pointer
if (! op->mReplyBody)
{
op->mReplyBody = new BufferArray();
}
const size_t req_size(size * nmemb);
const size_t write_size(op->mReplyBody->append(static_cast<char *>(data), req_size));
return write_size;
Monty Brandenberg
committed
}
size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
CURL * handle(static_cast<CURL *>(userdata));
HttpOpRequest * op(NULL);
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op);
// *FIXME: check the pointer
if (! op->mReqBody)
{
return 0;
}
const size_t req_size(size * nmemb);
const size_t body_size(op->mReqBody->size());
if (body_size <= op->mCurlBodyPos)
{
// *FIXME: should probably log this event - unexplained
return 0;
}
const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos));
const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast<char *>(data), do_size));
op->mCurlBodyPos += read_size;
return read_size;
Monty Brandenberg
committed
size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
static const char status_line[] = "HTTP/";
static const size_t status_line_len = sizeof(status_line) - 1;
static const char con_ran_line[] = "content-range:";
static const size_t con_ran_line_len = sizeof(con_ran_line) - 1;
CURL * handle(static_cast<CURL *>(userdata));
HttpOpRequest * op(NULL);
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op);
// *FIXME: check the pointer
const size_t hdr_size(size * nmemb);
const char * hdr_data(static_cast<const char *>(data)); // Not null terminated
if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len))
{
// One of possibly several status lines. Reset what we know and start over
// taking results from the last header stanza we receive.
Monty Brandenberg
committed
op->mReplyOffset = 0;
op->mReplyLength = 0;
Monty Brandenberg
committed
op->mReplyFullLength = 0;
Monty Brandenberg
committed
op->mStatus = HttpStatus();
if (op->mReplyHeaders)
{
op->mReplyHeaders->mHeaders.clear();
}
Monty Brandenberg
committed
}
else if (op->mProcFlags & PF_SCAN_RANGE_HEADER)
{
char hdr_buffer[128];
size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1));
memcpy(hdr_buffer, hdr_data, frag_size);
hdr_buffer[frag_size] = '\0';
#if LL_WINDOWS
Monty Brandenberg
committed
if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len)))
#else
if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len)))
#endif // LL_WINDOWS
Monty Brandenberg
committed
{
unsigned int first(0), last(0), length(0);
int status;
if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length)))
{
// Success, record the fragment position
op->mReplyOffset = first;
op->mReplyLength = last - first + 1;
Monty Brandenberg
committed
op->mReplyFullLength = length;
Monty Brandenberg
committed
}
else if (-1 == status)
{
// Response is badly formed and shouldn't be accepted
op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
}
else
{
// Ignore the unparsable.
// *FIXME: Maybe issue a warning into the log here
;
}
}
}
if (op->mProcFlags & PF_SAVE_HEADERS)
{
// Save headers in response
if (! op->mReplyHeaders)
{
op->mReplyHeaders = new HttpHeaders;
}
size_t wanted_size(hdr_size);
if (wanted_size && '\n' == hdr_data[wanted_size - 1])
{
if (--wanted_size && '\r' == hdr_data[wanted_size - 1])
{
--wanted_size;
}
}
op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_size));
Monty Brandenberg
committed
}
return hdr_size;
}
Monty Brandenberg
committed
int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata)
{
HttpOpRequest * op(NULL);
curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op);
// *FIXME: check the pointer
std::string safe_line;
std::string tag;
bool logit(false);
len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases
switch (info)
{
case CURLINFO_TEXT:
if (op->mTracing >= TRACE_CURL_HEADERS)
Monty Brandenberg
committed
{
tag = "TEXT";
escape_libcurl_debug_data(buffer, len, true, safe_line);
logit = true;
}
break;
case CURLINFO_HEADER_IN:
if (op->mTracing >= TRACE_CURL_HEADERS)
Monty Brandenberg
committed
{
tag = "HEADERIN";
escape_libcurl_debug_data(buffer, len, true, safe_line);
logit = true;
}
break;
case CURLINFO_HEADER_OUT:
if (op->mTracing >= TRACE_CURL_HEADERS)
Monty Brandenberg
committed
{
tag = "HEADEROUT";
escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line
Monty Brandenberg
committed
logit = true;
}
break;
case CURLINFO_DATA_IN:
if (op->mTracing >= TRACE_CURL_HEADERS)
Monty Brandenberg
committed
{
tag = "DATAIN";
logit = true;
if (op->mTracing >= TRACE_CURL_BODIES)
Monty Brandenberg
committed
{
escape_libcurl_debug_data(buffer, len, false, safe_line);
}
else
{
std::ostringstream out;
out << len << " Bytes";
safe_line = out.str();
}
}
break;
case CURLINFO_DATA_OUT:
if (op->mTracing >= TRACE_CURL_HEADERS)
Monty Brandenberg
committed
{
tag = "DATAOUT";
logit = true;
if (op->mTracing >= TRACE_CURL_BODIES)
Monty Brandenberg
committed
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
{
escape_libcurl_debug_data(buffer, len, false, safe_line);
}
else
{
std::ostringstream out;
out << len << " Bytes";
safe_line = out.str();
}
}
break;
default:
logit = false;
break;
}
if (logit)
{
LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: "
<< static_cast<HttpHandle>(op)
<< ", Type: " << tag
<< ", Data: " << safe_line
<< LL_ENDL;
}
return 0;
}
Monty Brandenberg
committed
} // end namespace LLCore
// =======================================
// Anonymous Namespace
// =======================================
namespace
{
int parse_content_range_header(char * buffer,
unsigned int * first,
unsigned int * last,
unsigned int * length)
{
char * tok_state(NULL), * tok(NULL);
Monty Brandenberg
committed
bool match(true);
if (! strtok_r(buffer, ": \t", &tok_state))
match = false;
if (match && (tok = strtok_r(NULL, " \t", &tok_state)))
#if LL_WINDOWS
Monty Brandenberg
committed
match = 0 == _stricmp("bytes", tok);
#else
match = 0 == strcasecmp("bytes", tok);
#endif // LL_WINDOWS
Monty Brandenberg
committed
if (match && ! (tok = strtok_r(NULL, " \t", &tok_state)))
match = false;
if (match)
{
unsigned int lcl_first(0), lcl_last(0), lcl_len(0);
#if LL_WINDOWS
Monty Brandenberg
committed
if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len))
#else
if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len))
#endif // LL_WINDOWS
Monty Brandenberg
committed
{
if (lcl_first > lcl_last || lcl_last >= lcl_len)
return -1;
*first = lcl_first;
*last = lcl_last;
*length = lcl_len;
return 0;
}
#if LL_WINDOWS
Monty Brandenberg
committed
if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last))
#else
if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last))
#endif // LL_WINDOWS
Monty Brandenberg
committed
{
if (lcl_first > lcl_last)
return -1;
*first = lcl_first;
*last = lcl_last;
*length = 0;
return 0;
}
}
// Header is there but badly/unexpectedly formed, try to ignore it.
return 1;
}
#if LL_WINDOWS
Monty Brandenberg
committed
char *strtok_r(char *str, const char *delim, char ** savestate)
{
return strtok_s(str, delim, savestate);
}
#endif // LL_WINDOWS
Monty Brandenberg
committed
Monty Brandenberg
committed
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line)
{
std::string out;
len = (std::min)(len, size_t(200));
out.reserve(3 * len);
for (int i(0); i < len; ++i)
{
unsigned char uc(static_cast<unsigned char>(buffer[i]));
if (uc < 32 || uc > 126)
{
if (scrub)
{
out.append(1, ' ');
}
else
{
static const char hex[] = "0123456789ABCDEF";
char convert[4];
convert[0] = '%';
convert[1] = hex[(uc >> 4) % 16];
convert[2] = hex[uc % 16];
convert[3] = '\0';
out.append(convert);
}
}
else
{
out.append(1, buffer[i]);
}
}
safe_line.swap(out);
}
Monty Brandenberg
committed
} // end anonymous namespace