#include "client_side.h"
#include "client_side_request.h"
#include "client_side_reply.h"
+#include "ClientRequestContext.h"
#include "Downloader.h"
#include "http/one/RequestParser.h"
#include "http/Stream.h"
+CBDATA_CLASS_INIT(DownloaderContext);
CBDATA_CLASS_INIT(Downloader);
-Downloader::Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level):
+DownloaderContext::~DownloaderContext()
+{
+ debugs(33, 5, HERE);
+ cbdataReference(downloader);
+ if (http)
+ finished();
+}
+
+void
+DownloaderContext::finished()
+{
+ cbdataReference(http);
+ delete http;
+ http = NULL;
+}
+
+Downloader::Downloader(SBuf &url, AsyncCall::Pointer &aCallback, unsigned int level):
AsyncJob("Downloader"),
- ConnStateData(xact),
url_(url),
callback(aCallback),
status(Http::scNone),
level_(level)
{
- transferProtocol = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1);
}
Downloader::~Downloader()
{
- debugs(33 , 2, "Downloader Finished");
+ debugs(33 , 2, HERE);
}
bool
return (!callback || callback->canceled()) && AsyncJob::doneAll();
}
-void
-Downloader::start()
+static void
+downloaderRecipient(clientStreamNode * node, ClientHttpRequest * http,
+ HttpReply * rep, StoreIOBuffer receivedData)
{
- ConnStateData::start();
- if (Http::Stream *context = parseOneRequest()) {
- context->registerWithConn();
- processParsedRequest(context);
- if (context->flags.deferred) {
- if (context != context->http->getConn()->pipeline.front().getRaw())
- context->deferRecipientForLater(context->deferredparams.node, context->deferredparams.rep, context->deferredparams.queuedBuffer);
- else
- context->http->getConn()->handleReply(context->deferredparams.rep, context->deferredparams.queuedBuffer);
- }
- } else {
- status = Http::scInternalServerError;
- callBack();
- }
-}
+ debugs(33, 6, HERE);
+ /* Test preconditions */
+ assert(node != NULL);
+
+ /* TODO: handle this rather than asserting
+ * - it should only ever happen if we cause an abort and
+ * the callback chain loops back to here, so we can simply return.
+ * However, that itself shouldn't happen, so it stays as an assert for now.
+ */
+ assert(cbdataReferenceValid(node));
+ assert(node->node.next == NULL);
+ DownloaderContext::Pointer context = dynamic_cast<DownloaderContext *>(node->data.getRaw());
+ assert(context != NULL);
+
+ if (!cbdataReferenceValid(context->downloader))
+ return;
-void
-Downloader::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
-{
- // This method required only if we need to support uploading data to server.
- // Currently only GET requests are supported.
- assert(false);
+ context->downloader->handleReply(node, http, rep, receivedData);
}
-void
-Downloader::noteBodyConsumerAborted(BodyPipe::Pointer)
+static void
+downloaderDetach(clientStreamNode * node, ClientHttpRequest * http)
{
- // This method required only if we need to support uploading data to server.
- // Currently only GET requests are supported.
- assert(false);
+ debugs(33, 5, HERE);
+ clientStreamDetach(node, http);
}
-Http::Stream *
-Downloader::parseOneRequest()
+bool
+Downloader::buildRequest()
{
const HttpRequestMethod method = Http::METHOD_GET;
if (!request) {
debugs(33, 5, "Invalid FTP URL: " << uri);
safe_free(uri);
- return nullptr; //earlyError(...)
+ return false; //earlyError(...)
}
request->http_ver = Http::ProtocolVersion();
request->header.putStr(Http::HdrType::HOST, request->url.host());
request->header.putTime(Http::HdrType::DATE, squid_curtime);
-
- ClientHttpRequest *const http = new ClientHttpRequest(this);
+ request->flags.internalClient = true;
+ request->client_addr.setNoAddr();
+#if FOLLOW_X_FORWARDED_FOR
+ request->indirect_client_addr.setNoAddr();
+#endif /* FOLLOW_X_FORWARDED_FOR */
+ request->my_addr.setNoAddr(); /* undefined for internal requests */
+ request->my_addr.port(0);
+ request->downloader = this;
+
+ ClientHttpRequest *const http = new ClientHttpRequest(NULL);
http->request = request;
HTTPMSGLOCK(http->request);
http->req_sz = 0;
http->uri = uri;
- Http::Stream *const context = new Http::Stream(nullptr, http);
+ context_ = new DownloaderContext(this, http);
StoreIOBuffer tempBuffer;
- tempBuffer.data = context->reqbuf;
+ tempBuffer.data = context_->requestBuffer;
tempBuffer.length = HTTP_REQBUF_SZ;
ClientStreamData newServer = new clientReplyContext(http);
- ClientStreamData newClient = context;
+ ClientStreamData newClient = context_.getRaw();
clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
- clientReplyStatus, newServer, clientSocketRecipient,
- clientSocketDetach, newClient, tempBuffer);
-
- context->flags.parsed_ok = 1;
- return context;
-}
+ clientReplyStatus, newServer, downloaderRecipient,
+ downloaderDetach, newClient, tempBuffer);
-void
-Downloader::processParsedRequest(Http::Stream *context)
-{
- Must(context);
- Must(pipeline.nrequests == 1);
+ // Build a ClientRequestContext to start doCallouts
+ http->calloutContext = new ClientRequestContext(http);
- ClientHttpRequest *const http = context->http;
- Must(http);
+ // Do not check for redirect, tos,nfmark and sslBump
+ http->calloutContext->redirect_done = true;
+ http->calloutContext->tosToClientDone = true;
+ http->calloutContext->nfmarkToClientDone = true;
+ http->calloutContext->sslBumpCheckDone = true;
+ http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
- debugs(33, 4, "forwarding request to server side");
- Must(http->storeEntry() == nullptr);
- clientProcessRequest(this, Http1::RequestParserPointer(), context);
-}
-
-time_t
-Downloader::idleTimeout() const
-{
- // No need to be implemented for connection-less ConnStateData object.
- assert(false);
- return 0;
+ http->doCallouts();
+ return true;
}
void
-Downloader::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
+Downloader::start()
{
- // nobody to forward the control message to
+ if (!buildRequest()) {
+ status = Http::scInternalServerError;
+ callBack();
+ }
}
void
-Downloader::handleReply(HttpReply *reply, StoreIOBuffer receivedData)
+Downloader::handleReply(clientStreamNode * node, ClientHttpRequest *http, HttpReply *reply, StoreIOBuffer receivedData)
{
- Http::StreamPointer context = pipeline.front();
+ // TODO: remove the following check:
+ DownloaderContext::Pointer callerContext = dynamic_cast<DownloaderContext *>(node->data.getRaw());
+ assert(callerContext == context_);
+
bool existingContent = reply ? reply->content_length : 0;
- bool exceedSize = (context->startOfOutput() && existingContent > -1 && (size_t)existingContent > MaxObjectSize) ||
+ bool exceedSize = (existingContent > -1 && (size_t)existingContent > MaxObjectSize) ||
((object.length() + receivedData.length) > MaxObjectSize);
if (exceedSize) {
if (receivedData.length > 0) {
object.append(receivedData.data, receivedData.length);
- context->http->out.size += receivedData.length;
- context->noteSentBodyBytes(receivedData.length);
+ http->out.size += receivedData.length;
+ http->out.offset += receivedData.length;
}
- switch (context->socketState()) {
- case STREAM_NONE:
- debugs(33, 3, "Get more data");
- context->pullData();
+ switch (clientStreamStatus (node, http)) {
+ case STREAM_NONE: {
+ debugs(33, 3, HERE << "Get more data");
+ StoreIOBuffer tempBuffer;
+ tempBuffer.offset = http->out.offset;
+ tempBuffer.data = context_->requestBuffer;
+ tempBuffer.length = HTTP_REQBUF_SZ;
+ clientStreamRead (node, http, tempBuffer);
+ }
break;
case STREAM_COMPLETE:
- debugs(33, 3, "Object data transfer successfully complete");
+ debugs(33, 3, HERE << "Object data transfer successfully complete");
status = Http::scOkay;
callBack();
break;
case STREAM_UNPLANNED_COMPLETE:
- debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE");
+ debugs(33, 3, HERE << "Object data transfer failed: STREAM_UNPLANNED_COMPLETE");
status = Http::scInternalServerError;
callBack();
break;
case STREAM_FAILED:
- debugs(33, 3, "Object data transfer failed: STREAM_FAILED");
+ debugs(33, 3, HERE << "Object data transfer failed: STREAM_FAILED");
status = Http::scInternalServerError;
callBack();
break;
Downloader::downloadFinished()
{
debugs(33, 7, this);
+ context_->finished();
+ context_ = NULL;
Must(done());
// Not really needed. Squid will delete this object because "doneAll" is true.
//deleteThis("completed");
CallJobHere(33, 7, CbcPointer<Downloader>(this), Downloader, downloadFinished);
}
-bool
-Downloader::isOpen() const
-{
- return cbdataReferenceValid(this) && !doneAll();
-}
#ifndef SQUID_DOWNLOADER_H
#define SQUID_DOWNLOADER_H
-#include "client_side.h"
+#include "base/AsyncCall.h"
+#include "base/AsyncJob.h"
#include "cbdata.h"
+#include "defines.h"
+#include "http/StatusCode.h"
+#include "sbuf/SBuf.h"
-class Downloader: public ConnStateData
+class ClientHttpRequest;
+class StoreIOBuffer;
+class clientStreamNode;
+class HttpReply;
+class Downloader;
+
+class DownloaderContext: public RefCountable
+{
+ CBDATA_CLASS(DownloaderContext);
+
+public:
+ typedef RefCount<DownloaderContext> Pointer;
+
+ DownloaderContext(Downloader *dl, ClientHttpRequest *h):
+ downloader(cbdataReference(dl)),
+ http(cbdataReference(h))
+ {}
+ ~DownloaderContext();
+ void finished();
+ Downloader* downloader;
+ ClientHttpRequest *http;
+ char requestBuffer[HTTP_REQBUF_SZ];
+};
+
+class Downloader: virtual public AsyncJob
{
CBDATA_CLASS(Downloader);
public:
Http::StatusCode status;
};
- Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level = 0);
+ Downloader(SBuf &url, AsyncCall::Pointer &aCallback, unsigned int level = 0);
virtual ~Downloader();
/// Fake call used internally by Downloader.
/// The nested level of Downloader object (downloads inside downloads).
unsigned int nestedLevel() const {return level_;}
- /* ConnStateData API */
- virtual bool isOpen() const;
-
/* AsyncJob API */
virtual bool doneAll() const;
- /*Bodypipe API*/
- virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer);
- virtual void noteBodyConsumerAborted(BodyPipe::Pointer);
-
+ DownloaderContext::Pointer const &context() {return context_;};
+ void handleReply(clientStreamNode * node, ClientHttpRequest *http, HttpReply *header, StoreIOBuffer receivedData);
protected:
- /* ConnStateData API */
- virtual Http::Stream *parseOneRequest();
- virtual void processParsedRequest(Http::Stream *context);
- virtual time_t idleTimeout() const;
- virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call);
- virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData);
/* AsyncJob API */
virtual void start();
virtual void prepUserConnection() {};
private:
+
+ bool buildRequest();
+
/// Schedules for execution the "callback" with parameters the status
/// and object.
void callBack();
Http::StatusCode status; ///< the download status code
SBuf object; ///< the object body data
unsigned int level_; ///< holds the nested downloads level
+
+ DownloaderContext::Pointer context_;
};
#endif
#include "acl/FilledChecklist.h"
#include "client_side.h"
#include "dns/LookupDetails.h"
+#include "Downloader.h"
#include "err_detail_type.h"
#include "globals.h"
#include "gopher.h"
// main property is which connection the request was received on (if any)
clientConnectionManager = aReq->clientConnectionManager;
+ downloader = aReq->downloader;
+
notes = aReq->notes;
sources = aReq->sources;
#endif
class ConnStateData;
+class Downloader;
/* Http Request */
void httpRequestPack(void *obj, Packable *p);
*/
CbcPointer<ConnStateData> clientConnectionManager;
+ /// The Downloader object intiated the HTTP request if exist
+ CbcPointer<Downloader> downloader;
+
/// forgets about the cached Range header (for a reason)
void ignoreRange(const char *reason);
int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */
debugs(33, 2, HERE << clientConnection);
flags.readMore = false;
DeregisterRunner(this);
- if (clientConnection != nullptr)
- clientdbEstablished(clientConnection->remote, -1); /* decrement */
+ clientdbEstablished(clientConnection->remote, -1); /* decrement */
pipeline.terminateAll(0);
unpinConnection(true);
HttpReply * rep, StoreIOBuffer receivedData)
{
// dont tryt to deliver if client already ABORTED
- if (!http->getConn() || !cbdataReferenceValid(http->getConn()))
+ if (!http->getConn() || !cbdataReferenceValid(http->getConn()) || !Comm::IsConnOpen(http->getConn()->clientConnection))
return;
- // If it is not connectionless and connection is closed return
- if (!http->getConn()->connectionless() && !Comm::IsConnOpen(http->getConn()->clientConnection))
- return;
-
-
/* Test preconditions */
assert(node != NULL);
PROF_start(clientSocketRecipient);
request->flags.accelerated = http->flags.accel;
request->flags.sslBumped=conn->switchedToHttps();
- if (!conn->connectionless()) {
- request->flags.ignoreCc = conn->port->ignore_cc;
- // TODO: decouple http->flags.accel from request->flags.sslBumped
- request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
- !conn->port->allow_direct : 0;
- request->sources |= isFtp ? HttpMsg::srcFtp :
- ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
- }
+ request->flags.ignoreCc = conn->port->ignore_cc;
+ // TODO: decouple http->flags.accel from request->flags.sslBumped
+ request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
+ !conn->port->allow_direct : 0;
+ request->sources |= isFtp ? HttpMsg::srcFtp :
+ ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
#if USE_AUTH
if (request->flags.sslBumped) {
if (conn->getAuth() != NULL)
request->flags.internal = http->flags.internal;
setLogUri (http, urlCanonicalClean(request.getRaw()));
- if (!conn->connectionless()) {
- request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
+ request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
#if FOLLOW_X_FORWARDED_FOR
// indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
// not a details about teh TCP connection itself
- request->indirect_client_addr = conn->clientConnection->remote;
+ request->indirect_client_addr = conn->clientConnection->remote;
#endif /* FOLLOW_X_FORWARDED_FOR */
- request->my_addr = conn->clientConnection->local;
- request->myportname = conn->port->name;
- }
+ request->my_addr = conn->clientConnection->local;
+ request->myportname = conn->port->name;
if (!isFtp) {
// XXX: for non-HTTP messages instantiate a different HttpMsg child type
pinning.peer = NULL;
// store the details required for creating more MasterXaction objects as new requests come in
- if (xact->tcpClient)
- log_addr = xact->tcpClient->remote;
-
+ log_addr = xact->tcpClient->remote;
log_addr.applyMask(Config.Addrs.client_netmask);
// register to receive notice of Squid signal events
{
BodyProducer::start();
HttpControlMsgSink::start();
- prepUserConnection();
-}
-void
-ConnStateData::prepUserConnection()
-{
if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
(transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
ConnStateData::splice()
{
// normally we can splice here, because we just got client hello message
+
if (fd_table[clientConnection->fd].ssl.get()) {
// Restore default read methods
fd_table[clientConnection->fd].read_method = &default_read_method;
/// try to make progress on a transaction or read more I/O
void kick();
- virtual bool isOpen() const;
+ bool isOpen() const;
Http1::TeChunkedParser *bodyParser; ///< parses HTTP/1.1 chunked request body
AsyncCall::Pointer closeHandler; /*The close handler for pinned server side connection*/
} pinning;
- /// If the port is not set then it is a connection-less object
- /// created by an internal squid subsystem
- bool connectionless() const { return port == nullptr; }
-
bool transparent() const;
/// true if we stopped receiving the request
virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
virtual void swanSong();
- /// Do the related hooks related to start retrieving requests from
- /// client connection
- virtual void prepUserConnection();
-
/// Changes state so that we close the connection and quit after serving
/// the client-side-detected error response instead of getting stuck.
void quitAfterError(HttpRequest *request); // meant to be private
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
hdr->delById(Http::HdrType::DATE);
hdr->putTime(Http::HdrType::DATE, squid_curtime);
- } else if (http->getConn() && !http->getConn()->connectionless() && http->getConn()->port->actAsOrigin) {
+ } else if (http->getConn() && http->getConn()->port->actAsOrigin) {
// Swap the Date: header to current time if we are simulating an origin
HttpHeaderEntry *h = hdr->findEntry(Http::HdrType::DATE);
if (h)
request->flags.proxyKeepalive = false;
} else if (http->getConn()) {
ConnStateData * conn = http->getConn();
- if (conn->connectionless()) {
- debugs(88, 3, "connection-less object, close after finished");
- request->flags.proxyKeepalive = false;
- } else if (!Comm::IsConnOpen(conn->port->listenConn)) {
+ if (!Comm::IsConnOpen(conn->port->listenConn)) {
// The listening port closed because of a reconfigure
debugs(88, 3, "listening port closed");
request->flags.proxyKeepalive = false;
StoreEntry *entry = http->storeEntry();
- ConnStateData * conn = http->getConn();
+ if (ConnStateData * conn = http->getConn()) {
+ if (!conn->isOpen()) {
+ debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
+ return;
+ }
+ if (conn->pinning.zeroReply) {
+ debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
+ return;
+ }
- // too late, our conn is closing
- // TODO: should we also quit?
- if (conn == NULL) {
- debugs(33,3, "not sending more data to a closed connection" );
- return;
- }
- if (!conn->isOpen()) {
- debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
- return;
- }
- if (conn->pinning.zeroReply) {
- debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
- return;
+ if (reqofs==0 && !http->logType.isTcpHit() && Comm::IsConnOpen(conn->clientConnection)) {
+ if (Ip::Qos::TheConfig.isHitTosActive()) {
+ Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
+ }
+ if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
+ Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
+ }
+ }
+
+ debugs(88, 5, "clientReplyContext::sendMoreData:" <<
+ conn->clientConnection <<
+ " '" << entry->url() << "'" <<
+ " out.offset=" << http->out.offset);
}
char *buf = next()->readBuffer.data;
memcpy(buf, result.data, result.length);
}
- if (reqofs==0 && !http->logType.isTcpHit() && Comm::IsConnOpen(conn->clientConnection)) {
- if (Ip::Qos::TheConfig.isHitTosActive()) {
- Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
- }
- if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
- Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
- }
- }
-
/* We've got the final data to start pushing... */
flags.storelogiccomplete = 1;
debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
reqofs << " bytes (" << result.length <<
" new bytes)");
- debugs(88, 5, "clientReplyContext::sendMoreData:"
- << conn->clientConnection <<
- " '" << entry->url() << "'" <<
- " out.offset=" << http->out.offset);
/* update size of the request */
reqsize = reqofs;
if (!http_conn)
return;
- // Internal requests such as those from Downloader does not have
- // local port.
- if (!http_conn->port)
- return;
-
request->flags.connectionAuthDisabled = http_conn->port->connection_auth_disabled;
if (!request->flags.connectionAuthDisabled) {
if (Comm::IsConnOpen(http_conn->pinning.serverConnection)) {
Server::Server(const MasterXaction::Pointer &xact) :
AsyncJob("::Server"), // kids overwrite
clientConnection(xact->tcpClient),
+ transferProtocol(xact->squidPort->transport),
port(xact->squidPort),
receivedFirstByte_(false)
-{
- if (xact->squidPort)
- transferProtocol = xact->squidPort->transport;
-}
+{}
bool
Server::doneAll() const
"Ssl::PeerConnector::certDownloadingDone",
PeerConnectorCertDownloaderDialer(&Ssl::PeerConnector::certDownloadingDone, this));
- const Downloader *csd = dynamic_cast<const Downloader*>(request->clientConnectionManager.valid());
- MasterXaction *xaction = new MasterXaction;
- Downloader *dl = new Downloader(url, xaction, certCallback, csd ? csd->nestedLevel() + 1 : 1);
+ // XXX: find a way to link HttpRequest and Downloader, the following always fails.
+ const Downloader *csd = dynamic_cast<const Downloader*>(request->downloader.valid());
+ Downloader *dl = new Downloader(url, certCallback, csd ? csd->nestedLevel() + 1 : 1);
AsyncJob::Start(dl);
}
// Check for nested SSL certificates downloads. For example when the
// certificate located in an SSL site which requires to download a
// a missing certificate (... from an SSL site which requires to ...).
- const Downloader *csd = dynamic_cast<const Downloader*>(request->clientConnectionManager.valid());
+
+ // XXX: find a way to link HttpRequest with Downloader.
+ // The following always fails:
+ const Downloader *csd = dynamic_cast<const Downloader*>(request->downloader.valid());
if (csd && csd->nestedLevel() >= MaxNestedDownloads)
return false;