HTTP request cancellation
- replaces abort() with something more structured.
This commit is contained in:
@@ -155,8 +155,9 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
{
|
{
|
||||||
if( _http_request )
|
if( _http_request ) {
|
||||||
_http_request->abort("image destroyed");
|
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "image destroyed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -618,7 +619,7 @@ namespace canvas
|
|||||||
// Abort pending request
|
// Abort pending request
|
||||||
if( _http_request )
|
if( _http_request )
|
||||||
{
|
{
|
||||||
_http_request->abort("setting new image");
|
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "setting new image");
|
||||||
_http_request.reset();
|
_http_request.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+101
-19
@@ -78,7 +78,6 @@ class Client::ClientPrivate
|
|||||||
public:
|
public:
|
||||||
#if defined(ENABLE_CURL)
|
#if defined(ENABLE_CURL)
|
||||||
CURLM* curlMulti;
|
CURLM* curlMulti;
|
||||||
bool haveActiveRequests;
|
|
||||||
|
|
||||||
void createCurlMulti()
|
void createCurlMulti()
|
||||||
{
|
{
|
||||||
@@ -94,6 +93,9 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef std::map<Request_ptr, CURL*> RequestCurlMap;
|
||||||
|
RequestCurlMap requests;
|
||||||
#else
|
#else
|
||||||
NetChannelPoller poller;
|
NetChannelPoller poller;
|
||||||
// connections by host (potentially more than one)
|
// connections by host (potentially more than one)
|
||||||
@@ -205,6 +207,8 @@ public:
|
|||||||
|
|
||||||
// closing of the connection from the server side when getting the body,
|
// closing of the connection from the server side when getting the body,
|
||||||
bool canCloseState = (state == STATE_GETTING_BODY);
|
bool canCloseState = (state == STATE_GETTING_BODY);
|
||||||
|
bool isCancelling = (state == STATE_CANCELLING);
|
||||||
|
|
||||||
if (canCloseState && activeRequest) {
|
if (canCloseState && activeRequest) {
|
||||||
// check bodyTransferSize matches how much we actually transferred
|
// check bodyTransferSize matches how much we actually transferred
|
||||||
if (bodyTransferSize > 0) {
|
if (bodyTransferSize > 0) {
|
||||||
@@ -229,7 +233,7 @@ public:
|
|||||||
sentRequests.erase(sentRequests.begin());
|
sentRequests.erase(sentRequests.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeRequest) {
|
if (activeRequest && !isCancelling) {
|
||||||
activeRequest->setFailure(500, "server closed connection");
|
activeRequest->setFailure(500, "server closed connection");
|
||||||
// remove the failed request from sentRequests, so it does
|
// remove the failed request from sentRequests, so it does
|
||||||
// not get restored
|
// not get restored
|
||||||
@@ -262,6 +266,39 @@ public:
|
|||||||
tryStartNextRequest();
|
tryStartNextRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cancelRequest(const Request_ptr& r)
|
||||||
|
{
|
||||||
|
RequestList::iterator it = std::find(sentRequests.begin(),
|
||||||
|
sentRequests.end(), r);
|
||||||
|
if (it != sentRequests.end()) {
|
||||||
|
sentRequests.erase(it);
|
||||||
|
|
||||||
|
if ((r == activeRequest) || !activeRequest) {
|
||||||
|
// either the cancelling request is active, or we're in waiting
|
||||||
|
// for response state - close now
|
||||||
|
setState(STATE_CANCELLING);
|
||||||
|
close();
|
||||||
|
|
||||||
|
setState(STATE_CLOSED);
|
||||||
|
activeRequest = NULL;
|
||||||
|
_contentDecoder.reset();
|
||||||
|
} else if (activeRequest) {
|
||||||
|
SG_LOG(SG_IO, SG_INFO, "con:" << _connectionId << " cancelling non-active: " << r->url());
|
||||||
|
|
||||||
|
// has been sent but not active, let the active finish and
|
||||||
|
// then close. Otherwise cancelling request #2 would mess up
|
||||||
|
// active transfer #1
|
||||||
|
activeRequest->setCloseAfterComplete();
|
||||||
|
}
|
||||||
|
} // of request has been sent
|
||||||
|
|
||||||
|
// simpler case, not sent yet just remove from the queue
|
||||||
|
it = std::find(queuedRequests.begin(), queuedRequests.end(), r);
|
||||||
|
if (it != queuedRequests.end()) {
|
||||||
|
queuedRequests.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void beginResponse()
|
void beginResponse()
|
||||||
{
|
{
|
||||||
assert(!sentRequests.empty());
|
assert(!sentRequests.empty());
|
||||||
@@ -528,6 +565,7 @@ private:
|
|||||||
STATE_GETTING_CHUNKED_BYTES,
|
STATE_GETTING_CHUNKED_BYTES,
|
||||||
STATE_GETTING_TRAILER,
|
STATE_GETTING_TRAILER,
|
||||||
STATE_SOCKET_ERROR,
|
STATE_SOCKET_ERROR,
|
||||||
|
STATE_CANCELLING, ///< cancelling an acitve request
|
||||||
STATE_CLOSED ///< connection should be closed now
|
STATE_CLOSED ///< connection should be closed now
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -787,34 +825,43 @@ void Client::update(int waitTimeout)
|
|||||||
#if defined(ENABLE_CURL)
|
#if defined(ENABLE_CURL)
|
||||||
int remainingActive, messagesInQueue;
|
int remainingActive, messagesInQueue;
|
||||||
curl_multi_perform(d->curlMulti, &remainingActive);
|
curl_multi_perform(d->curlMulti, &remainingActive);
|
||||||
d->haveActiveRequests = (remainingActive > 0);
|
|
||||||
|
|
||||||
CURLMsg* msg;
|
CURLMsg* msg;
|
||||||
while ((msg = curl_multi_info_read(d->curlMulti, &messagesInQueue))) {
|
while ((msg = curl_multi_info_read(d->curlMulti, &messagesInQueue))) {
|
||||||
if (msg->msg == CURLMSG_DONE) {
|
if (msg->msg == CURLMSG_DONE) {
|
||||||
Request* req;
|
Request* rawReq = 0;
|
||||||
CURL *e = msg->easy_handle;
|
CURL *e = msg->easy_handle;
|
||||||
curl_easy_getinfo(e, CURLINFO_PRIVATE, &req);
|
curl_easy_getinfo(e, CURLINFO_PRIVATE, &rawReq);
|
||||||
|
|
||||||
|
// ensure request stays valid for the moment
|
||||||
|
// eg if responseComplete cancels us
|
||||||
|
Request_ptr req(rawReq);
|
||||||
|
|
||||||
long responseCode;
|
long responseCode;
|
||||||
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &responseCode);
|
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||||
|
|
||||||
|
// remove from the requests map now,
|
||||||
|
// in case the callbacks perform a cancel. We'll use
|
||||||
|
// the absence from the request dict in cancel to avoid
|
||||||
|
// a double remove
|
||||||
|
ClientPrivate::RequestCurlMap::iterator it = d->requests.find(req);
|
||||||
|
assert(it != d->requests.end());
|
||||||
|
assert(it->second == e);
|
||||||
|
d->requests.erase(it);
|
||||||
|
|
||||||
if (msg->data.result == 0) {
|
if (msg->data.result == 0) {
|
||||||
req->responseComplete();
|
req->responseComplete();
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Result: %d - %s\n",
|
SG_LOG(SG_IO, SG_WARN, "CURL Result:" << msg->data.result << " " << curl_easy_strerror(msg->data.result));
|
||||||
msg->data.result, curl_easy_strerror(msg->data.result));
|
|
||||||
req->setFailure(msg->data.result, curl_easy_strerror(msg->data.result));
|
req->setFailure(msg->data.result, curl_easy_strerror(msg->data.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_multi_remove_handle(d->curlMulti, e);
|
curl_multi_remove_handle(d->curlMulti, e);
|
||||||
|
|
||||||
// balance the reference we take in makeRequest
|
|
||||||
SGReferenced::put(req);
|
|
||||||
curl_easy_cleanup(e);
|
curl_easy_cleanup(e);
|
||||||
}
|
} else {
|
||||||
else {
|
// should never happen since CURLMSG_DONE is the only code
|
||||||
SG_LOG(SG_IO, SG_ALERT, "CurlMSG:" << msg->msg);
|
// defined!
|
||||||
|
SG_LOG(SG_IO, SG_ALERT, "unknown CurlMSG:" << msg->msg);
|
||||||
}
|
}
|
||||||
} // of curl message processing loop
|
} // of curl message processing loop
|
||||||
SGTimeStamp::sleepForMSec(waitTimeout);
|
SGTimeStamp::sleepForMSec(waitTimeout);
|
||||||
@@ -880,11 +927,14 @@ void Client::makeRequest(const Request_ptr& r)
|
|||||||
r->_client = this;
|
r->_client = this;
|
||||||
|
|
||||||
#if defined(ENABLE_CURL)
|
#if defined(ENABLE_CURL)
|
||||||
|
ClientPrivate::RequestCurlMap::iterator rit = d->requests.find(r);
|
||||||
|
assert(rit == d->requests.end());
|
||||||
|
|
||||||
CURL* curlRequest = curl_easy_init();
|
CURL* curlRequest = curl_easy_init();
|
||||||
curl_easy_setopt(curlRequest, CURLOPT_URL, r->url().c_str());
|
curl_easy_setopt(curlRequest, CURLOPT_URL, r->url().c_str());
|
||||||
|
|
||||||
// manually increase the ref count of the request
|
d->requests[r] = curlRequest;
|
||||||
SGReferenced::get(r.get());
|
|
||||||
curl_easy_setopt(curlRequest, CURLOPT_PRIVATE, r.get());
|
curl_easy_setopt(curlRequest, CURLOPT_PRIVATE, r.get());
|
||||||
// disable built-in libCurl progress feedback
|
// disable built-in libCurl progress feedback
|
||||||
curl_easy_setopt(curlRequest, CURLOPT_NOPROGRESS, 1);
|
curl_easy_setopt(curlRequest, CURLOPT_NOPROGRESS, 1);
|
||||||
@@ -950,9 +1000,9 @@ void Client::makeRequest(const Request_ptr& r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
curl_multi_add_handle(d->curlMulti, curlRequest);
|
curl_multi_add_handle(d->curlMulti, curlRequest);
|
||||||
d->haveActiveRequests = true;
|
|
||||||
|
|
||||||
// FIXME - premature?
|
// this seems premature, but we don't have a callback from Curl we could
|
||||||
|
// use to trigger when the requst is actually sent.
|
||||||
r->requestStart();
|
r->requestStart();
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -1034,6 +1084,33 @@ void Client::makeRequest(const Request_ptr& r)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::cancelRequest(const Request_ptr &r, std::string reason)
|
||||||
|
{
|
||||||
|
#if defined(ENABLE_CURL)
|
||||||
|
ClientPrivate::RequestCurlMap::iterator it = d->requests.find(r);
|
||||||
|
if(it == d->requests.end()) {
|
||||||
|
// already being removed, presumably inside ::update()
|
||||||
|
// nothing more to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLMcode err = curl_multi_remove_handle(d->curlMulti, it->second);
|
||||||
|
assert(err == CURLM_OK);
|
||||||
|
|
||||||
|
// clear the request pointer form the curl-easy object
|
||||||
|
curl_easy_setopt(it->second, CURLOPT_PRIVATE, 0);
|
||||||
|
|
||||||
|
curl_easy_cleanup(it->second);
|
||||||
|
d->requests.erase(it);
|
||||||
|
#else
|
||||||
|
ConnectionDict::iterator it = d->connections.begin();
|
||||||
|
for (; it != d->connections.end(); ++it) {
|
||||||
|
(it->second)->cancelRequest(r);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
r->setFailure(-1, reason);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
FileRequestRef Client::save( const std::string& url,
|
FileRequestRef Client::save( const std::string& url,
|
||||||
const std::string& filename )
|
const std::string& filename )
|
||||||
@@ -1088,7 +1165,7 @@ void Client::setProxy( const std::string& proxy,
|
|||||||
bool Client::hasActiveRequests() const
|
bool Client::hasActiveRequests() const
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_CURL)
|
#if defined(ENABLE_CURL)
|
||||||
return d->haveActiveRequests;
|
return !d->requests.empty();
|
||||||
#else
|
#else
|
||||||
ConnectionDict::const_iterator it = d->connections.begin();
|
ConnectionDict::const_iterator it = d->connections.begin();
|
||||||
for (; it != d->connections.end(); ++it) {
|
for (; it != d->connections.end(); ++it) {
|
||||||
@@ -1201,7 +1278,12 @@ size_t Client::requestHeaderCallback(char *rawBuffer, size_t size, size_t nitems
|
|||||||
void Client::debugDumpRequests()
|
void Client::debugDumpRequests()
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_CURL)
|
#if defined(ENABLE_CURL)
|
||||||
|
SG_LOG(SG_IO, SG_INFO, "== HTTP request dump");
|
||||||
|
ClientPrivate::RequestCurlMap::iterator it = d->requests.begin();
|
||||||
|
for (; it != d->requests.end(); ++it) {
|
||||||
|
SG_LOG(SG_IO, SG_INFO, "\t" << it->first->url());
|
||||||
|
}
|
||||||
|
SG_LOG(SG_IO, SG_INFO, "==");
|
||||||
#else
|
#else
|
||||||
SG_LOG(SG_IO, SG_INFO, "== HTTP connection dump");
|
SG_LOG(SG_IO, SG_INFO, "== HTTP connection dump");
|
||||||
ConnectionDict::iterator it = d->connections.begin();
|
ConnectionDict::iterator it = d->connections.begin();
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ public:
|
|||||||
|
|
||||||
void makeRequest(const Request_ptr& r);
|
void makeRequest(const Request_ptr& r);
|
||||||
|
|
||||||
|
void cancelRequest(const Request_ptr& r, std::string reason = std::string());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download a resource and save it to a file.
|
* Download a resource and save it to a file.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ namespace HTTP
|
|||||||
SG_WARN,
|
SG_WARN,
|
||||||
"HTTP::FileRequest: failed to open file '" << _filename << "'"
|
"HTTP::FileRequest: failed to open file '" << _filename << "'"
|
||||||
);
|
);
|
||||||
|
|
||||||
abort("Failed to open file.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -365,19 +365,6 @@ void Request::setReadyState(ReadyState state)
|
|||||||
_cb_always(this);
|
_cb_always(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void Request::abort()
|
|
||||||
{
|
|
||||||
abort("Request aborted.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void Request::abort(const std::string& reason)
|
|
||||||
{
|
|
||||||
setFailure(-1, reason);
|
|
||||||
_willClose = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
bool Request::closeAfterComplete() const
|
bool Request::closeAfterComplete() const
|
||||||
{
|
{
|
||||||
@@ -385,6 +372,13 @@ bool Request::closeAfterComplete() const
|
|||||||
return _willClose || (_responseVersion != HTTP_1_1);
|
return _willClose || (_responseVersion != HTTP_1_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Request::setCloseAfterComplete()
|
||||||
|
{
|
||||||
|
_willClose = true;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
bool Request::serverSupportsPipelining() const
|
bool Request::serverSupportsPipelining() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -199,16 +199,6 @@ public:
|
|||||||
|
|
||||||
ReadyState readyState() const { return _ready_state; }
|
ReadyState readyState() const { return _ready_state; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Request aborting this request.
|
|
||||||
*/
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request aborting this request and specify the reported reaseon for it.
|
|
||||||
*/
|
|
||||||
void abort(const std::string& reason);
|
|
||||||
|
|
||||||
bool closeAfterComplete() const;
|
bool closeAfterComplete() const;
|
||||||
bool isComplete() const;
|
bool isComplete() const;
|
||||||
|
|
||||||
@@ -246,6 +236,8 @@ private:
|
|||||||
void processBodyBytes(const char* s, int n);
|
void processBodyBytes(const char* s, int n);
|
||||||
void setReadyState(ReadyState state);
|
void setReadyState(ReadyState state);
|
||||||
|
|
||||||
|
void setCloseAfterComplete();
|
||||||
|
|
||||||
Client* _client; // HTTP client we're active on
|
Client* _client; // HTTP client we're active on
|
||||||
|
|
||||||
std::string _method;
|
std::string _method;
|
||||||
|
|||||||
@@ -698,6 +698,74 @@ cout << "testing proxy close" << endl;
|
|||||||
COMPARE(tr->responseBytesReceived(), 0);
|
COMPARE(tr->responseBytesReceived(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test cancel
|
||||||
|
{
|
||||||
|
cout << "cancel request" << endl;
|
||||||
|
testServer.resetConnectCount();
|
||||||
|
cl.clearAllConnections();
|
||||||
|
|
||||||
|
cl.setProxy("", 80);
|
||||||
|
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
||||||
|
HTTP::Request_ptr own(tr);
|
||||||
|
cl.makeRequest(tr);
|
||||||
|
|
||||||
|
TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
|
||||||
|
HTTP::Request_ptr own2(tr2);
|
||||||
|
cl.makeRequest(tr2);
|
||||||
|
|
||||||
|
TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
|
||||||
|
HTTP::Request_ptr own3(tr3);
|
||||||
|
cl.makeRequest(tr3);
|
||||||
|
|
||||||
|
cl.cancelRequest(tr, "my reason 1");
|
||||||
|
|
||||||
|
cl.cancelRequest(tr2, "my reason 2");
|
||||||
|
|
||||||
|
waitForComplete(&cl, tr3);
|
||||||
|
|
||||||
|
COMPARE(tr->responseCode(), -1);
|
||||||
|
COMPARE(tr2->responseReason(), "my reason 2");
|
||||||
|
|
||||||
|
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||||
|
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||||
|
COMPARE(tr3->bodyData, string(BODY1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test cancel
|
||||||
|
{
|
||||||
|
cout << "cancel middle request" << endl;
|
||||||
|
testServer.resetConnectCount();
|
||||||
|
cl.clearAllConnections();
|
||||||
|
|
||||||
|
cl.setProxy("", 80);
|
||||||
|
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
||||||
|
HTTP::Request_ptr own(tr);
|
||||||
|
cl.makeRequest(tr);
|
||||||
|
|
||||||
|
TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
|
||||||
|
HTTP::Request_ptr own2(tr2);
|
||||||
|
cl.makeRequest(tr2);
|
||||||
|
|
||||||
|
TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
|
||||||
|
HTTP::Request_ptr own3(tr3);
|
||||||
|
cl.makeRequest(tr3);
|
||||||
|
|
||||||
|
cl.cancelRequest(tr2, "middle request");
|
||||||
|
|
||||||
|
waitForComplete(&cl, tr3);
|
||||||
|
|
||||||
|
COMPARE(tr->responseCode(), 200);
|
||||||
|
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||||
|
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||||
|
COMPARE(tr->bodyData, string(BODY1));
|
||||||
|
|
||||||
|
COMPARE(tr2->responseCode(), -1);
|
||||||
|
|
||||||
|
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||||
|
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||||
|
COMPARE(tr3->bodyData, string(BODY1));
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
cout << "get-during-response-send" << endl;
|
cout << "get-during-response-send" << endl;
|
||||||
cl.clearAllConnections();
|
cl.clearAllConnections();
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ size_t Install::downloadedBytes() const
|
|||||||
void Install::cancelDownload()
|
void Install::cancelDownload()
|
||||||
{
|
{
|
||||||
if (m_download.valid()) {
|
if (m_download.valid()) {
|
||||||
m_download->abort("User cancelled download");
|
m_package->catalog()->root()->cancelHTTPRequest(m_download, "User cancelled download");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_revision == 0) {
|
if (m_revision == 0) {
|
||||||
|
|||||||
@@ -226,6 +226,20 @@ void Root::makeHTTPRequest(HTTP::Request *req)
|
|||||||
d->httpPendingRequests.push_back(req);
|
d->httpPendingRequests.push_back(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Root::cancelHTTPRequest(HTTP::Request *req, const std::string &reason)
|
||||||
|
{
|
||||||
|
if (d->http) {
|
||||||
|
d->http->cancelRequest(req, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<HTTP::Request_ptr>::iterator it = std::find(d->httpPendingRequests.begin(),
|
||||||
|
d->httpPendingRequests.end(),
|
||||||
|
req);
|
||||||
|
if (it != d->httpPendingRequests.end()) {
|
||||||
|
d->httpPendingRequests.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Root::Root(const SGPath& aPath, const std::string& aVersion) :
|
Root::Root(const SGPath& aPath, const std::string& aVersion) :
|
||||||
d(new RootPrivate)
|
d(new RootPrivate)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ public:
|
|||||||
* set yet.
|
* set yet.
|
||||||
*/
|
*/
|
||||||
void makeHTTPRequest(HTTP::Request* req);
|
void makeHTTPRequest(HTTP::Request* req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel an HTTP request.
|
||||||
|
*/
|
||||||
|
void cancelHTTPRequest(HTTP::Request* req, const std::string& reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The catalog XML/property version in use. This is used to make incomaptible
|
* The catalog XML/property version in use. This is used to make incomaptible
|
||||||
|
|||||||
Reference in New Issue
Block a user