X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fio%2FHTTPClient.cxx;h=ecc63f844054154a5a99230658848f2d46da05fb;hb=1bd9a440e8e2968769f8c34703ce9965f5d16ca4;hp=1686fb2193707359616723e94def32a4dfc80397;hpb=d896e71ae91009e19a3a490920684209a7eec2c0;p=simgear.git diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx index 1686fb21..ecc63f84 100644 --- a/simgear/io/HTTPClient.cxx +++ b/simgear/io/HTTPClient.cxx @@ -22,12 +22,12 @@ // #include "HTTPClient.hxx" +#include "HTTPFileRequest.hxx" #include #include #include // rand() #include -#include #include #include #include @@ -51,10 +51,6 @@ # endif #endif -using std::string; -using std::stringstream; -using std::vector; - namespace simgear { @@ -69,6 +65,12 @@ class Connection; typedef std::multimap ConnectionDict; typedef std::list RequestList; +static bool isFailureStatus(int httpStatus) +{ + int majorCode = httpStatus / 100; + return (majorCode != 2); +} + class Client::ClientPrivate { public: @@ -103,8 +105,21 @@ public: virtual ~Connection() { } + + virtual void handleBufferRead (NetBuffer& buffer) + { + if( !activeRequest || !activeRequest->isComplete() ) + return NetChat::handleBufferRead(buffer); + + // Request should be aborted (signaled by setting its state to complete). + + // force the state to GETTING_BODY, to simplify logic in + // responseComplete and handleClose + state = STATE_GETTING_BODY; + responseComplete(); + } - void setServer(const string& h, short p) + void setServer(const std::string& h, short p) { host = h; port = p; @@ -205,8 +220,12 @@ public: assert(state == STATE_WAITING_FOR_RESPONSE); activeRequest = sentRequests.front(); - - activeRequest->responseStart(buffer); + activeRequest->responseStart(buffer); + if (isFailureStatus(activeRequest->responseCode())) { + handleError(EIO); + return; + } + state = STATE_GETTING_HEADERS; buffer.clear(); if (activeRequest->responseCode() == 204) { @@ -224,6 +243,10 @@ public: void tryStartNextRequest() { + while( !queuedRequests.empty() + && queuedRequests.front()->isComplete() ) + queuedRequests.pop_front(); + if (queuedRequests.empty()) { idleTime.stamp(); return; @@ -244,28 +267,28 @@ public: Request_ptr r = queuedRequests.front(); r->requestStart(); - requestBodyBytesToSend = r->requestBodyLength(); - - stringstream headerData; - string path = r->path(); + + std::stringstream headerData; + std::string path = r->path(); assert(!path.empty()); - string query = r->query(); - string bodyData; + std::string query = r->query(); + std::string bodyData; if (!client->proxyHost().empty()) { path = r->scheme() + "://" + r->host() + r->path(); } - if (r->requestBodyType() == CONTENT_TYPE_URL_ENCODED) { + if (r->bodyType() == CONTENT_TYPE_URL_ENCODED) { headerData << r->method() << " " << path << " HTTP/1.1\r\n"; bodyData = query.substr(1); // URL-encode, drop the leading '?' headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n"; headerData << "Content-Length:" << bodyData.size() << "\r\n"; } else { headerData << r->method() << " " << path << query << " HTTP/1.1\r\n"; - if (requestBodyBytesToSend >= 0) { - headerData << "Content-Length:" << requestBodyBytesToSend << "\r\n"; - headerData << "Content-Type:" << r->requestBodyType() << "\r\n"; + if( r->hasBodyData() ) + { + headerData << "Content-Length:" << r->bodyLength() << "\r\n"; + headerData << "Content-Type:" << r->bodyType() << "\r\n"; } } @@ -276,8 +299,8 @@ public: headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n"; } - BOOST_FOREACH(string h, r->requestHeaders()) { - headerData << h << ": " << r->header(h) << "\r\n"; + BOOST_FOREACH(const StringMap::value_type& h, r->requestHeaders()) { + headerData << h.first << ": " << h.second << "\r\n"; } headerData << "\r\n"; // final CRLF to terminate the headers @@ -292,33 +315,42 @@ public: // drain down before trying to start any more requests. return; } - - while (requestBodyBytesToSend > 0) { - char buf[4096]; - int len = r->getBodyData(buf, 4096); - if (len > 0) { - requestBodyBytesToSend -= len; - if (!bufferSend(buf, len)) { - SG_LOG(SG_IO, SG_WARN, "overflow the HTTP::Connection output buffer"); - state = STATE_SOCKET_ERROR; - return; + + if( r->hasBodyData() ) + for(size_t body_bytes_sent = 0; body_bytes_sent < r->bodyLength();) + { + char buf[4096]; + size_t len = r->getBodyData(buf, body_bytes_sent, 4096); + if( len ) + { + if( !bufferSend(buf, len) ) + { + SG_LOG(SG_IO, + SG_WARN, + "overflow the HTTP::Connection output buffer"); + state = STATE_SOCKET_ERROR; + return; + } + body_bytes_sent += len; + } + else + { + SG_LOG(SG_IO, + SG_WARN, + "HTTP asynchronous request body generation is unsupported"); + break; } - // SG_LOG(SG_IO, SG_INFO, "sent body:\n" << string(buf, len) << "\n%%%%%%%%%"); - } else { - SG_LOG(SG_IO, SG_WARN, "HTTP asynchronous request body generation is unsupported"); - break; } - } - // SG_LOG(SG_IO, SG_INFO, "did start request:" << r->url() << - // "\n\t @ " << reinterpret_cast(r.ptr()) << - // "\n\t on connection " << this); - // successfully sent, remove from queue, and maybe send the next + // SG_LOG(SG_IO, SG_INFO, "did start request:" << r->url() << + // "\n\t @ " << reinterpret_cast(r.ptr()) << + // "\n\t on connection " << this); + // successfully sent, remove from queue, and maybe send the next queuedRequests.pop_front(); sentRequests.push_back(r); - state = STATE_WAITING_FOR_RESPONSE; + state = STATE_WAITING_FOR_RESPONSE; - // pipelining, let's maybe send the next request right away + // pipelining, let's maybe send the next request right away tryStartNextRequest(); } @@ -326,12 +358,12 @@ public: { idleTime.stamp(); client->receivedBytes(static_cast(n)); - - if ((state == STATE_GETTING_BODY) || (state == STATE_GETTING_CHUNKED_BYTES)) { - _contentDecoder.receivedBytes(s, n); - } else { - buffer += string(s, n); - } + + if( (state == STATE_GETTING_BODY) + || (state == STATE_GETTING_CHUNKED_BYTES) ) + _contentDecoder.receivedBytes(s, n); + else + buffer.append(s, n); } virtual void foundTerminator(void) @@ -428,7 +460,7 @@ private: void processHeader() { - string h = strutils::simplify(buffer); + std::string h = strutils::simplify(buffer); if (h.empty()) { // blank line terminates headers headersComplete(); return; @@ -440,9 +472,9 @@ private: return; } - string key = strutils::simplify(buffer.substr(0, colonPos)); - string lkey = boost::to_lower_copy(key); - string value = strutils::strip(buffer.substr(colonPos + 1)); + std::string key = strutils::simplify(buffer.substr(0, colonPos)); + std::string lkey = boost::to_lower_copy(key); + std::string value = strutils::strip(buffer.substr(colonPos + 1)); // only consider these if getting headers (as opposed to trailers // of a chunked transfer) @@ -466,7 +498,7 @@ private: activeRequest->responseHeader(lkey, value); } - void processTransferEncoding(const string& te) + void processTransferEncoding(const std::string& te) { if (te == "chunked") { chunkedTransfer = true; @@ -534,7 +566,7 @@ private: void responseComplete() { - Request_ptr completedRequest = activeRequest; + Request_ptr completedRequest = activeRequest; _contentDecoder.finish(); assert(sentRequests.front() == activeRequest); @@ -581,14 +613,13 @@ private: Client* client; Request_ptr activeRequest; ConnectionState state; - string host; + std::string host; short port; std::string buffer; int bodyTransferSize; SGTimeStamp idleTime; bool chunkedTransfer; bool noMessageBody; - int requestBodyBytesToSend; RequestList queuedRequests; RequestList sentRequests; @@ -624,9 +655,13 @@ void Client::setMaxConnections(unsigned int maxCon) void Client::update(int waitTimeout) { - d->poller.poll(waitTimeout); - bool waitingRequests = !d->pendingRequests.empty(); + if (!d->poller.hasChannels() && (waitTimeout > 0)) { + SGTimeStamp::sleepForMSec(waitTimeout); + } else { + d->poller.poll(waitTimeout); + } + bool waitingRequests = !d->pendingRequests.empty(); ConnectionDict::iterator it = d->connections.begin(); for (; it != d->connections.end(); ) { Connection* con = it->second; @@ -669,6 +704,9 @@ void Client::update(int waitTimeout) void Client::makeRequest(const Request_ptr& r) { + if( r->isComplete() ) + return; + if( r->url().find("://") == std::string::npos ) { r->setFailure(EINVAL, "malformed URL"); return; @@ -679,7 +717,7 @@ void Client::makeRequest(const Request_ptr& r) return; } - string host = r->host(); + std::string host = r->host(); int port = r->port(); if (!d->proxy.empty()) { host = d->proxy; @@ -687,9 +725,9 @@ void Client::makeRequest(const Request_ptr& r) } Connection* con = NULL; - stringstream ss; + std::stringstream ss; ss << host << "-" << port; - string connectionId = ss.str(); + std::string connectionId = ss.str(); bool havePending = !d->pendingRequests.empty(); bool atConnectionsLimit = d->connections.size() >= d->maxConnections; ConnectionDict::iterator consEnd = d->connections.end(); @@ -741,12 +779,29 @@ void Client::makeRequest(const Request_ptr& r) con->queueRequest(r); } +//------------------------------------------------------------------------------ +FileRequestRef Client::save( const std::string& url, + const std::string& filename ) +{ + FileRequestRef req = new FileRequest(url, filename); + makeRequest(req); + return req; +} + +//------------------------------------------------------------------------------ +MemoryRequestRef Client::load(const std::string& url) +{ + MemoryRequestRef req = new MemoryRequest(url); + makeRequest(req); + return req; +} + void Client::requestFinished(Connection* con) { } -void Client::setUserAgent(const string& ua) +void Client::setUserAgent(const std::string& ua) { d->userAgent = ua; } @@ -766,7 +821,9 @@ const std::string& Client::proxyAuth() const return d->proxyAuth; } -void Client::setProxy(const string& proxy, int port, const string& auth) +void Client::setProxy( const std::string& proxy, + int port, + const std::string& auth ) { d->proxy = proxy; d->proxyPort = port;