--- /dev/null
+
+
+#include "squid.h"
+#include "MemBuf.h"
+#include "BodyReader.h"
+
+BodyReader::BodyReader(size_t len, BodyReadFunc *r, BodyAbortFunc *a, BodyKickFunc *k, void *d) :
+ _remaining(len), _available(0),
+ read_func(r), abort_func(a), kick_func(k), read_func_data(d),
+ read_callback(NULL), read_callback_data(NULL)
+{
+ theBuf.init(4096, 65536);
+ debugs(32,3,HERE << this << " " << "created new BodyReader for content-length " << len);
+ bytes_read = 0;
+}
+
+BodyReader::~BodyReader()
+{
+ if (_remaining && abort_func)
+ abort_func(read_func_data, _remaining);
+
+ if (callbackPending())
+ doCallback();
+
+}
+
+void
+BodyReader::read(CBCB *callback, void *cbdata)
+{
+ assert(_remaining || theBuf.contentSize());
+ debugs(32,3,HERE << this << " " << "remaining = " << _remaining);
+ debugs(32,3,HERE << this << " " << "available = " << _available);
+
+ if (read_callback == NULL) {
+ read_callback = callback;
+ read_callback_data = cbdataReference(cbdata);
+ } else {
+ assert(read_callback == callback);
+ assert(read_callback_data == cbdata);
+ }
+
+ if ((_available == 0) && (theBuf.contentSize() == 0)) {
+ debugs(32,3,HERE << this << " " << "read: no body data available, saving callback pointers");
+
+ if (kick_func)
+ kick_func(read_func_data);
+
+ return;
+ }
+
+ debugs(32,3,HERE << this << " " << "read_func=" << (int) read_func);
+ debugs(32,3,HERE << this << " " << "data=" << read_func_data);
+ size_t size = theBuf.potentialSpaceSize();
+
+ if (size > _available)
+ size = _available;
+
+ if (size > 0) {
+ debugs(32,3,HERE << this << " " << "calling read_func for " << size << " bytes");
+
+ size_t nread = read_func(read_func_data, theBuf, size);
+
+ if (nread > 0) {
+ _available -= nread;
+ _remaining -= nread;
+ } else {
+ debugs(32,3,HERE << this << " " << "Help, read_func() ret " << nread);
+ }
+ }
+
+ if (theBuf.contentSize() > 0) {
+ debugs(32,3,HERE << this << " have " << theBuf.contentSize() << " bytes in theBuf, calling back");
+ doCallback();
+ }
+}
+
+void
+BodyReader::notify(size_t now_available)
+{
+ debugs(32,3,HERE << this << " " << "old available = " << _available);
+ debugs(32,3,HERE << this << " " << "now_available = " << now_available);
+ _available = now_available;
+
+ if (!callbackPending()) {
+ debugs(32,3,HERE << this << " " << "no callback pending, nothing to do");
+ return;
+ }
+
+ debugs(32,3,HERE << this << " " << "have data and pending callback, calling read()");
+
+ read(read_callback, read_callback_data);
+}
+
+bool
+BodyReader::callbackPending()
+{
+ return read_callback ? true : false;
+}
+
+/*
+ * doCallback
+ *
+ * Execute the read callback if there is a function registered
+ * and the read_callback_data is still valid.
+ */
+bool
+BodyReader::doCallback()
+{
+ CBCB *t_callback = read_callback;
+ void *t_cbdata;
+
+ if (t_callback == NULL)
+ return false;
+
+ read_callback = NULL;
+
+ if (!cbdataReferenceValidDone(read_callback_data, &t_cbdata))
+ return false;
+
+ debugs(32,3,HERE << this << " doing callback, theBuf size = " << theBuf.contentSize());
+
+ t_callback(theBuf, t_cbdata);
+
+ return true;
+}
+
+bool
+BodyReader::consume(size_t size)
+{
+ debugs(32,3,HERE << this << " BodyReader::consume consuming " << size);
+
+ if (theBuf.contentSize() < (mb_size_t) size) {
+ debugs(0,0,HERE << this << "BodyReader::consume failed");
+ debugs(0,0,HERE << this << "BodyReader::consume size = " << size);
+ debugs(0,0,HERE << this << "BodyReader::consume contentSize() = " << theBuf.contentSize());
+ return false;
+ }
+
+ theBuf.consume(size);
+
+ if (callbackPending() && _available > 0) {
+ debugs(32,3,HERE << this << " " << "data avail and pending callback, calling read()");
+ read(read_callback, read_callback_data);
+ }
+
+ return true;
+}
--- /dev/null
+
+#ifndef SQUID_BODY_READER_H
+#define SQUID_BODY_READER_H
+
+typedef void CBCB (MemBuf &mb, void *data);
+typedef size_t BodyReadFunc (void *, MemBuf &mb, size_t size);
+typedef void BodyAbortFunc (void *, size_t);
+typedef void BodyKickFunc (void *);
+
+class BodyReader : public RefCountable
+{
+
+public:
+ typedef RefCount<BodyReader> Pointer;
+ BodyReader(size_t len, BodyReadFunc *r, BodyAbortFunc *a, BodyKickFunc *k, void *d);
+ ~BodyReader();
+ void read(CBCB *, void *);
+ void notify(size_t now_available);
+ size_t remaining() { return _remaining; }
+
+ bool callbackPending();
+ bool consume(size_t size);
+
+ int bytes_read;
+
+private:
+ size_t _remaining;
+ size_t _available;
+ MemBuf theBuf;
+
+ /*
+ * These are for interacting with things that
+ * "provide" body content. ie, ConnStateData and
+ * ICAPReqMod after adapation.
+ */
+ BodyReadFunc *read_func;
+ BodyAbortFunc *abort_func;
+ BodyKickFunc *kick_func;
+ void *read_func_data;
+
+ /*
+ * These are for interacting with things that
+ * "consume" body content. ie, HttpStateData and
+ * ICAPReqMod before adaptation.
+ */
+ CBCB *read_callback;
+ void *read_callback_data;
+ bool doCallback();
+};
+
+#endif
/*
- * $Id: HttpRequest.cc,v 1.61 2006/04/22 05:29:18 robertc Exp $
+ * $Id: HttpRequest.cc,v 1.62 2006/04/27 19:27:37 wessels Exp $
*
* DEBUG: section 73 HTTP Request
* AUTHOR: Duane Wessels
my_addr = no_addr;
my_port = 0;
client_port = 0;
- body_connection = NULL;
+ body_reader = NULL;
// hier
errType = ERR_NONE;
peer_login = NULL; // not allocated/deallocated by this class
void
HttpRequest::clean()
{
- if (body_connection.getRaw() != NULL)
- fatal ("request being destroyed with body connection intact\n");
+ if (body_reader != NULL)
+ fatal ("request being destroyed with body reader intact\n");
if (auth_user_request) {
auth_user_request->unlock();
/*
- * $Id: HttpRequest.h,v 1.19 2006/02/17 18:10:59 wessels Exp $
+ * $Id: HttpRequest.h,v 1.20 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#include "HttpMsg.h"
#include "client_side.h"
#include "HierarchyLogEntry.h"
+#include "BodyReader.h"
/* Http Request */
extern int httpRequestHdrAllowed(const HttpHeaderEntry * e, String * strConnection);
unsigned short client_port;
- ConnStateData::Pointer body_connection; /* used by clientReadBody() */
+ BodyReader::Pointer body_reader;
HierarchyLogEntry hier;
if (virgin != NULL)
freeVirgin();
- if (adapted != NULL)
+ if (adapted != NULL) {
+ /*
+ * adapted->sink is equal to this. Remove the pointer since
+ * we are deleting this.
+ */
+
+ if (adapted->sink)
+ adapted->sink = NULL;
+
freeAdapted();
+ }
}
void ICAPClientReqmodPrecache::startReqMod(ClientHttpRequest *aHttp, HttpRequest *request)
* tell the other side to use the original request/response
* headers.
*/
+ HttpRequest *req = dynamic_cast<HttpRequest*>(adapted->data->header);
+
+ if (req && req->content_length > 0) {
+ assert(req->body_reader == NULL);
+ req->body_reader = new BodyReader(req->content_length, readBody, abortBody, kickBody, this);
+ }
+
http->takeAdaptedHeaders(adapted->data->header);
noteSourceProgress(p);
}
-// ICAP client sends more data
+/*
+ * This is where we receive a notification from the other
+ * side of the MsgPipe that new adapted data is available.
+ * We, in turn, tell whoever is reading from the request's
+ * body_reader about the new data.
+ */
void ICAPClientReqmodPrecache::noteSourceProgress(MsgPipe *p)
{
debug(93,3)("ICAPClientReqmodPrecache::noteSourceProgress() called\n");
//tell ClientHttpRequest to store a fresh portion of the adapted response
if (p->data->body->hasContent()) {
- http->takeAdaptedBody(p->data->body);
+ HttpRequest *req = dynamic_cast<HttpRequest*>(adapted->data->header);
+ assert(req);
+ debugs(32,3,HERE << "notifying body_reader, contentSize() = " << p->data->body->contentSize());
+ req->body_reader->notify(p->data->body->contentSize());
}
}
freeVirgin();
}
+#if DONT_FREE_ADAPTED
+ /*
+ * NOTE: We do not clean up "adapted->sink" here because it may
+ * have an HTTP message body that needs to stay around a little
+ * while longer so that the HTTP server-side can forward it on.
+ */
if (adapted != NULL) {
if (notify == notifyIcap)
adapted->sendSinkAbort();
freeAdapted();
}
+#endif
+
if (http) {
if (notify == notifyOwner)
// tell ClientHttpRequest that we are aborting prematurely
{
adapted = NULL; // refcounted
}
+
+/*
+ * Something that needs to read the adapated request body
+ * calls this function, via the BodyReader class. We copy
+ * the body data from our bodybuf object to the BodyReader
+ * MemBuf, which was passed as a reference to this function.
+ */
+size_t
+ICAPClientReqmodPrecache::readBody(void *data, MemBuf &mb, size_t size)
+{
+ ICAPClientReqmodPrecache *icap = static_cast<ICAPClientReqmodPrecache *>(data);
+ assert(icap != NULL);
+ assert(icap->adapted != NULL);
+ assert(icap->adapted->data != NULL);
+ MemBuf *bodybuf = icap->adapted->data->body;
+ assert(bodybuf != NULL);
+ debugs(32,3,HERE << "readBody requested size " << size);
+ debugs(32,3,HERE << "readBody bodybuf size " << bodybuf->contentSize());
+
+ if ((mb_size_t) size > bodybuf->contentSize())
+ size = bodybuf->contentSize();
+
+ debugs(32,3,HERE << "readBody actual size " << size);
+
+ assert(size);
+
+ mb.append(bodybuf->content(), size);
+
+ bodybuf->consume(size);
+
+ return size;
+}
+
+void
+ICAPClientReqmodPrecache::abortBody(void *data, size_t remaining)
+{
+ if (remaining >= 0) {
+ debugs(0,0,HERE << "ICAPClientReqmodPrecache::abortBody size " << remaining);
+ // more?
+ }
+
+ ICAPClientReqmodPrecache *icap = static_cast<ICAPClientReqmodPrecache *>(data);
+ icap->stop(notifyIcap);
+}
+
+/*
+ * Restart reading the adapted response from the ICAP server in case
+ * the body buffer became full and we stopped reading.
+ */
+void
+ICAPClientReqmodPrecache::kickBody(void *data)
+{
+ debugs(32,3,HERE << "ICAPClientReqmodPrecache::kickBody");
+ ICAPClientReqmodPrecache *icap = static_cast<ICAPClientReqmodPrecache *>(data);
+ assert(icap->adapted != NULL);
+ icap->adapted->sendSinkNeed();
+}
/*
- * $Id: ICAPClientReqmodPrecache.h,v 1.2 2005/12/22 22:26:31 wessels Exp $
+ * $Id: ICAPClientReqmodPrecache.h,v 1.3 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
ClientHttpRequest *http;
MsgPipe::Pointer virgin;
MsgPipe::Pointer adapted;
+ BodyReader::Pointer body_reader;
private:
typedef enum { notifyNone, notifyOwner, notifyIcap } Notify;
void freeVirgin();
void freeAdapted();
CBDATA_CLASS2(ICAPClientReqmodPrecache);
+
+ // Hooks to BodyReader so HttpStateData can get the
+ // adapted request body
+ static BodyReadFunc readBody;
+ static BodyAbortFunc abortBody;
+ static BodyKickFunc kickBody;
};
#endif /* SQUID_ICAPCLIENTSIDEHOOK_H */
/*
- * $Id: ICAPConfig.cc,v 1.8 2006/04/27 19:07:16 wessels Exp $
+ * $Id: ICAPConfig.cc,v 1.9 2006/04/27 19:27:37 wessels Exp $
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
* ----------------------------------------------------------
extern ConfigParser LegacyParser; // from cache_cf.cc
ICAPConfig TheICAPConfig;
+extern ConfigParser LegacyParser; // found in cache_cf.cc
ICAPServiceRep::Pointer
ICAPConfig::findService(const String& key)
requestBuf.init();
makeRequestHeaders(requestBuf);
- debugs(93, 9, "ICAPModXact ICAP request prefix " << status() << ":\n" <<
+ debugs(93, 9, "ICAPModXact ICAP status " << status() << " will write:\n" <<
(requestBuf.terminate(), requestBuf.content()));
// write headers
state.writing = State::writingHeaders;
scheduleWrite(requestBuf);
+ virgin->sendSinkNeed();
}
void ICAPModXact::handleCommWrote(size_t sz)
Must(state.writing == State::writingHeaders);
if (virginBody.expected()) {
- debugs(98, 5, HERE);
state.writing = preview.enabled() ?
State::writingPreview : State::writingPrime;
virginWriteClaim.protectAll();
writeMore();
} else {
- debugs(98, 5, HERE);
stopWriting();
}
}
void ICAPModXact::readMore()
{
- if (reader || doneReading())
+ if (reader || doneReading()) {
+ debugs(32,3,HERE << "returning from readMore because reader or doneReading()");
return;
+ }
// do not fill readBuf if we have no space to store the result
- if (!adapted->data->body->hasPotentialSpace())
+ if (!adapted->data->body->hasPotentialSpace()) {
+ debugs(93,1,HERE << "Not reading because ICAP reply buffer is full");
return;
+ }
if (readBuf.hasSpace())
scheduleRead();
+ else
+ debugs(93,1,HERE << "nothing to do because !readBuf.hasSpace()");
}
// comm module read a portion of the ICAP response for us
if (parsed)
return true;
- if (bodyParser->needsMoreData())
+ debugs(32,3,HERE << this << " needsMoreData = " << bodyParser->needsMoreData());
+
+ if (bodyParser->needsMoreData()) {
+ debugs(32,3,HERE << this);
Must(mayReadMore());
+ readMore();
+ }
if (bodyParser->needsMoreSpace()) {
Must(!doneSending()); // can hope for more space
if (state.sending == State::sendingVirgin)
echoMore();
+ else if (state.sending == State::sendingAdapted)
+ parseMore();
else
- if (state.sending == State::sendingAdapted)
- parseMore();
- else
- Must(state.sending == State::sendingUndecided);
+ Must(state.sending == State::sendingUndecided);
ICAPXaction_Exit();
}
static
void ICAPXaction_noteCommRead(int, char *, size_t size, comm_err_t status, int xerrno, void *data)
{
+ debugs(93,3,HERE << data << " read returned " << size);
ICAPXaction_fromData(data).noteCommRead(status, size);
}
bool ICAPXaction::done() const
{
- if (stopReason != NULL) // mustStop() has been called
+ if (stopReason != NULL) { // mustStop() has been called
+ debugs(93,1,HERE << "ICAPXaction is done() because " << stopReason);
return true;
+ }
return doneAll();
}
Must(commStatus == COMM_OK);
Must(sz >= 0);
- debugs(93, 5, HERE << "read " << sz << " bytes");
+ debugs(93, 3, HERE << "read " << sz << " bytes");
/*
* See comments in ICAPXaction.h about why we use commBuf
void ICAPXaction::callEnd()
{
if (done()) {
- debugs(93, 5, "ICAPXaction::" << inCall << " ends xaction " <<
+ debugs(93, 5, HERE << "ICAPXaction::" << inCall << " ends xaction " <<
status());
doStop(); // may delete us
return;
MsgPipe::~MsgPipe()
{
delete data;
- delete source;
- delete sink;
+ assert(source == NULL);
+ assert(sink == NULL);
};
void MsgPipe::sendSourceStart()
#
# Makefile for the Squid Object Cache server
#
-# $Id: Makefile.am,v 1.138 2006/04/27 19:04:15 wessels Exp $
+# $Id: Makefile.am,v 1.139 2006/04/27 19:27:37 wessels Exp $
#
# Uncomment and customize the following to suit your needs:
#
client_side_reply.h \
client_side_request.cc \
client_side_request.h \
- ClientBody.cc \
- ClientBody.h \
+ BodyReader.cc \
+ BodyReader.h \
ClientRequestContext.h \
clientStream.cc \
clientStream.h \
## SwapDir wants ConfigOption
ufsdump_SOURCES = \
- ClientBody.cc \
+ BodyReader.cc \
ConfigParser.cc \
debug.cc \
int.cc \
#
# Makefile for the Squid Object Cache server
#
-# $Id: Makefile.in,v 1.376 2006/04/27 19:04:15 wessels Exp $
+# $Id: Makefile.in,v 1.377 2006/04/27 19:27:37 wessels Exp $
#
# Uncomment and customize the following to suit your needs:
#
AuthUser.cc AuthUserRequest.cc cache_cf.cc CacheDigest.cc \
cache_manager.cc carp.cc cbdata.cc client_db.cc client_side.cc \
client_side.h client_side_reply.cc client_side_reply.h \
- client_side_request.cc client_side_request.h ClientBody.cc \
- ClientBody.h ClientRequestContext.h clientStream.cc \
+ client_side_request.cc client_side_request.h BodyReader.cc \
+ BodyReader.h ClientRequestContext.h clientStream.cc \
clientStream.h comm.cc comm.h CommIO.h comm_select.cc \
comm_poll.cc comm_epoll.cc comm_kqueue.cc CommRead.h \
ConfigOption.cc ConfigParser.cc ConfigParser.h \
CacheDigest.$(OBJEXT) cache_manager.$(OBJEXT) carp.$(OBJEXT) \
cbdata.$(OBJEXT) client_db.$(OBJEXT) client_side.$(OBJEXT) \
client_side_reply.$(OBJEXT) client_side_request.$(OBJEXT) \
- ClientBody.$(OBJEXT) clientStream.$(OBJEXT) comm.$(OBJEXT) \
+ BodyReader.$(OBJEXT) clientStream.$(OBJEXT) comm.$(OBJEXT) \
comm_select.$(OBJEXT) comm_poll.$(OBJEXT) comm_epoll.$(OBJEXT) \
comm_kqueue.$(OBJEXT) ConfigOption.$(OBJEXT) \
ConfigParser.$(OBJEXT) debug.$(OBJEXT) $(am__objects_5) \
am_tests_testUfs_OBJECTS = tests/testUfs.$(OBJEXT) \
tests/testMain.$(OBJEXT) $(am__objects_27)
tests_testUfs_OBJECTS = $(am_tests_testUfs_OBJECTS)
-am__ufsdump_SOURCES_DIST = ClientBody.cc ConfigParser.cc debug.cc \
+am__ufsdump_SOURCES_DIST = BodyReader.cc ConfigParser.cc debug.cc \
int.cc ufsdump.cc store.cc StoreFileSystem.cc StoreMeta.cc \
StoreMeta.h StoreMetaMD5.cc StoreMetaMD5.h StoreMetaSTD.cc \
StoreMetaSTD.h StoreMetaUnpacker.cc StoreMetaUnpacker.h \
store_swapmeta.cc store_swapout.cc structs.h SwapDir.cc \
tools.cc typedefs.h unlinkd.cc url.cc urn.cc useragent.cc \
wais.cc wccp.cc whois.cc wordlist.cc win32.cc
-am_ufsdump_OBJECTS = ClientBody.$(OBJEXT) ConfigParser.$(OBJEXT) \
+am_ufsdump_OBJECTS = BodyReader.$(OBJEXT) ConfigParser.$(OBJEXT) \
debug.$(OBJEXT) int.$(OBJEXT) ufsdump.$(OBJEXT) \
store.$(OBJEXT) StoreFileSystem.$(OBJEXT) StoreMeta.$(OBJEXT) \
StoreMetaMD5.$(OBJEXT) StoreMetaSTD.$(OBJEXT) \
client_side_reply.h \
client_side_request.cc \
client_side_request.h \
- ClientBody.cc \
- ClientBody.h \
+ BodyReader.cc \
+ BodyReader.h \
ClientRequestContext.h \
clientStream.cc \
clientStream.h \
dnsserver_SOURCES = dnsserver.cc SquidNew.cc
recv_announce_SOURCES = recv-announce.cc SquidNew.cc
ufsdump_SOURCES = \
- ClientBody.cc \
+ BodyReader.cc \
ConfigParser.cc \
debug.cc \
int.cc \
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthUser.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthUserRequest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CacheDigest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ClientBody.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BodyReader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConfigOption.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConfigParser.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DelayBucket.Po@am__quote@
/*
- * $Id: client_side.cc,v 1.718 2006/04/22 05:29:19 robertc Exp $
+ * $Id: client_side.cc,v 1.719 2006/04/27 19:27:37 wessels Exp $
*
* DEBUG: section 33 Client-side Routines
* AUTHOR: Duane Wessels
#include "client_side_reply.h"
#include "ClientRequestContext.h"
#include "MemBuf.h"
-#include "ClientBody.h"
#if LINGERING_CLOSE
#define comm_close comm_lingering_close
#if USE_IDENT
static IDCB clientIdentDone;
#endif
+static BodyReadFunc clientReadBody;
+static BodyAbortFunc clientAbortBody;
static CSCB clientSocketRecipient;
static CSD clientSocketDetach;
static void clientSetKeepaliveFlag(ClientHttpRequest *);
static ClientSocketContext *parseURIandHTTPVersion(char **url_p, HttpVersion * http_ver_p, ConnStateData::Pointer& conn, char *http_version_str);
static int connReadWasError(ConnStateData::Pointer& conn, comm_err_t, int size, int xerrno);
static int connFinishedWithConn(ConnStateData::Pointer& conn, int size);
-static void connNoteUseOfBuffer(ConnStateData::Pointer & conn, size_t byteCount);
+static void connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount);
static int connKeepReadingIncompleteRequest(ConnStateData::Pointer & conn);
static void connCancelIncompleteRequests(ConnStateData::Pointer & conn);
auth_user_request,this);
auth_user_request->onConnectionClose(this);
}
-
- /*
- * This is awkward: body has a RefCount::Pointer to this. We must
- * destroy body so that our own reference count will go to zero.
- * Furthermore, there currently exists a potential loop because
- * ~ConnStateData() will delete body if it is not NULL.
- */
- if (body) {
- ClientBody *tmp = body;
- body = NULL;
- delete tmp;
- }
}
bool
cbdataReferenceDone(port);
- if (body)
- delete body;
+ body_reader = NULL; // refcounted
}
/*
void
ClientSocketContext::initiateClose()
{
- if (!http || !http->getConn().getRaw()) {
- doClose();
- return;
- }
-
- if (http->getConn()->body_size_left > 0) {
- debug(33, 5) ("ClientSocketContext::initiateClose: closing, but first we need to read the rest of the request\n");
- /* XXX We assumes the reply does fit in the TCP transmit window.
- * If not the connection may stall while sending the reply
- * (before reaching here) if the client does not try to read the
- * response while sending the request body. As of yet we have
- * not received any complaints indicating this may be an issue.
+ if (http != NULL) {
+ ConnStateData::Pointer conn = http->getConn();
+
+ if (conn != NULL) {
+ if (conn->bodySizeLeft() > 0) {
+ debug(33, 5) ("ClientSocketContext::initiateClose: closing, but first we need to read the rest of the request\n");
+ /*
+ * XXX We assume the reply fits in the TCP transmit
+ * window. If not the connection may stall while sending
+ * the reply (before reaching here) if the client does not
+ * try to read the response while sending the request body.
+ * As of yet we have not received any complaints indicating
+ * this may be an issue.
*/
- http->getConn()->closing(true);
- clientAbortBody(http->request);
- return;
+ conn->closing(true);
+ /*
+ * Trigger the BodyReader abort handler, if necessary,
+ * by destroying it. It is a refcounted pointer, so
+ * set it to NULL and let the destructor be called when
+ * all references are gone.
+ */
+ http->request->body_reader = NULL; // refcounted
+ return;
+ }
+ }
}
doClose();
}
void
-connNoteUseOfBuffer(ConnStateData::Pointer & conn, size_t byteCount)
+connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
{
assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
conn->in.notYetUsed -= byteCount;
static void
clientAfterReadingRequests(int fd, ConnStateData::Pointer &conn, int do_next_read)
{
- fde *F = &fd_table[fd];
-
- /* Check if a half-closed connection was aborted in the middle */
+ /*
+ * If (1) we are reading a message body, (2) and the connection
+ * is half-closed, and (3) we didn't get the entire HTTP request
+ * yet, then close this connection.
+ */
- if (F->flags.socket_eof) {
- if (conn->in.notYetUsed != conn->body_size_left) {
- /* != 0 when no request body */
+ if (fd_table[fd].flags.socket_eof) {
+ if ((ssize_t) conn->in.notYetUsed < conn->bodySizeLeft()) {
/* Partial request received. Abort client connection! */
debug(33, 3) ("clientAfterReadingRequests: FD %d aborted, partial request\n", fd);
comm_close(fd);
HttpRequest *request = NULL;
/* We have an initial client stream in place should it be needed */
/* setup our private context */
- connNoteUseOfBuffer(conn, http->req_sz);
+ connNoteUseOfBuffer(conn.getRaw(), http->req_sz);
context->registerWithConn();
/* Do we expect a request-body? */
if (request->content_length > 0) {
- conn->body = new ClientBody(conn, request);
- conn->body_size_left = request->content_length;
- request->body_connection = conn;
+ request->body_reader = new BodyReader(request->content_length,
+ clientReadBody,
+ clientAbortBody,
+ NULL,
+ conn.getRaw());
+ conn->body_reader = request->body_reader;
+ request->body_reader->notify(conn->in.notYetUsed);
+
+ if (request->body_reader->remaining())
+ conn->readSomeData();
+
/* Is it too large? */
if (!clientIsRequestBodyValid(request->content_length) ||
return result;
}
+/*
+ * bodySizeLeft
+ *
+ * Report on the number of bytes of body content that we
+ * know are yet to be read on this connection.
+ */
+ssize_t
+ConnStateData::bodySizeLeft()
+{
+ if (body_reader != NULL)
+ return body_reader->remaining();
+
+ return 0;
+}
+
/*
* Attempt to parse one or more requests from the input buffer.
* If a request is successfully parsed, even if the next request
debug(33, 5) ("clientParseRequest: FD %d: attempting to parse\n", conn->fd);
- while (conn->in.notYetUsed > 0 && conn->body_size_left == 0) {
+ while (conn->in.notYetUsed > 0 && conn->bodySizeLeft() == 0) {
size_t req_line_sz;
connStripBufferWhitespace (conn);
parsed_req = true;
if (context->mayUseConnection()) {
- debug (33, 3) ("clientReadRequest: Not reading, as this request may need the connection\n");
+ debug (33, 3) ("clientParseRequest: Not reading, as this request may need the connection\n");
do_next_read = 0;
break;
}
break;
}
- continue; /* while offset > 0 && body_size_left == 0 */
+ continue; /* while offset > 0 && conn->bodySizeLeft() == 0 */
}
- } /* while offset > 0 && conn->body_size_left == 0 */
+ } /* while offset > 0 && conn->bodySizeLeft() == 0 */
return parsed_req;
}
clientReadRequest(int fd, char *buf, size_t size, comm_err_t flag, int xerrno,
void *data)
{
+ debugs(33,5,HERE << "clientReadRequest FD " << fd << " size " << size);
ConnStateData::Pointer conn ((ConnStateData *)data);
conn->reading(false);
bool do_next_read = 1; /* the default _is_ to read data! - adrian */
if (flag == COMM_OK) {
if (size > 0) {
+ /*
+ * If this assertion fails, we need to handle the case
+ * where a persistent connection has some leftover body
+ * request data from a previous, aborted transaction.
+ * Probably just decrement size and adjust/memmove the
+ * 'buf' pointer accordingly.
+ */
+ assert(conn->in.abortedSize == 0);
+
char *current_buf = conn->in.addressToReadInto();
kb_incr(&statCounter.client_http.kbytes_in, size);
conn->in.notYetUsed += size;
conn->in.buf[conn->in.notYetUsed] = '\0'; /* Terminate the string */
+
+ if (conn->body_reader != NULL)
+ conn->body_reader->notify(conn->in.notYetUsed);
} else if (size == 0) {
debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
}
}
-
- /* Process request body if any */
- if (conn->in.notYetUsed > 0 && conn->body && conn->body->hasCallback()) {
- conn->body->process();
- }
-
/* Process next request */
if (conn->getConcurrentRequestCount() == 0)
fd_note(conn->fd, "Reading next request");
}
}
-/* file_read like function, for reading body content */
-void
-clientReadBody(HttpRequest * request, char *buf, size_t size, CBCB * callback,
- void *cbdata)
+/*
+ * clientReadBody
+ *
+ * A request to receive some HTTP request body data. This is a
+ * 'read_func' of BodyReader class. Feels to me like this function
+ * belongs to ConnStateData class.
+ *
+ * clientReadBody is of type 'BodyReadFunc'
+ */
+size_t
+clientReadBody(void *data, MemBuf &mb, size_t size)
{
- ConnStateData::Pointer conn = request->body_connection;
+ ConnStateData *conn = (ConnStateData *) data;
+ assert(conn);
+ debugs(32,3,HERE << "clientReadBody requested size " << size);
+ debugs(32,3,HERE << "clientReadBody FD " << conn->fd);
+ debugs(32,3,HERE << "clientReadBody in.notYetUsed " << conn->in.notYetUsed);
- if (conn.getRaw() == NULL) {
- debug(33, 5) ("clientReadBody: no body to read, request=%p\n", request);
- callback(buf, 0, cbdata); /* Signal end of body */
- return;
- }
+ if (size > conn->in.notYetUsed)
+ size = conn->in.notYetUsed;
+
+ debugs(32,3,HERE << "clientReadBody actual size " << size);
+
+ assert(size);
+
+ mb.append(conn->in.buf, size);
- debug(33, 2) ("clientReadBody: start FD %d body_size=%lu in.notYetUsed=%ld cb=%p req=%p\n",
- conn->fd, (unsigned long int) conn->body_size_left,
- (unsigned long int) conn->in.notYetUsed, callback, request);
- conn->body->init(buf, size, callback, cbdata);
- conn->body->process();
+ connNoteUseOfBuffer(conn, size);
+
+ return size;
}
-/* A dummy handler that throws away a request-body */
+/*
+ * clientAbortBody
+ *
+ * A dummy callback that consumes the remains of a request
+ * body for an aborted transaction.
+ *
+ * clientAbortBody is of type 'BodyAbortFunc'
+ */
static void
-clientReadBodyAbortHandler(char *buf, ssize_t size, void *data)
+clientAbortBody(void *data, size_t remaining)
{
- static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
ConnStateData *conn = (ConnStateData *) data;
- debug(33, 2) ("clientReadBodyAbortHandler: FD %d body_size=%lu in.notYetUsed=%lu\n",
- conn->fd, (unsigned long int) conn->body_size_left,
- (unsigned long) conn->in.notYetUsed);
+ debugs(32,3,HERE << "clientAbortBody FD " << conn->fd);
+ debugs(32,3,HERE << "clientAbortBody in.notYetUsed " << conn->in.notYetUsed);
+ debugs(32,3,HERE << "clientAbortBody remaining " << remaining);
+ conn->in.abortedSize += remaining;
- if (size != 0 && conn->body_size_left != 0) {
- debug(33, 3) ("clientReadBodyAbortHandler: FD %d shedule next read\n",
- conn->fd);
- conn->body->init(bodyAbortBuf, sizeof(bodyAbortBuf), clientReadBodyAbortHandler, data);
+ if (conn->in.notYetUsed) {
+ size_t to_discard = XMIN(conn->in.notYetUsed, conn->in.abortedSize);
+ debugs(32,3,HERE << "to_discard " << to_discard);
+ conn->in.abortedSize -= to_discard;
+ connNoteUseOfBuffer(conn, to_discard);
}
if (conn->closing())
comm_close(conn->fd);
}
-/* Abort a body request */
-int
-clientAbortBody(HttpRequest * request)
-{
- ConnStateData::Pointer conn = request->body_connection;
-
- if (conn.getRaw() == NULL || conn->body_size_left <= 0 || conn->body->getRequest() != request)
- return 0; /* No body to abort */
-
- if (conn->body->hasCallback())
- conn->body->negativeCallback();
-
- clientReadBodyAbortHandler(NULL, -1, conn.getRaw()); /* Install abort handler */
-
- /* ClientBody::process() */
- return 1; /* Aborted */
-}
-
/* general lifetime handler for HTTP requests */
static void
requestTimeout(int fd, void *data)
cbdataFree(t);
}
-ConnStateData::ConnStateData() : body_size_left(0), transparent_ (false), reading_ (false), closing_ (false)
+ConnStateData::ConnStateData() : transparent_ (false), reading_ (false), closing_ (false)
{
openReference = this;
}
/*
- * $Id: client_side.h,v 1.14 2006/03/04 05:38:34 wessels Exp $
+ * $Id: client_side.h,v 1.15 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#define SQUID_CLIENTSIDE_H
#include "StoreIOBuffer.h"
+#include "BodyReader.h"
#include "RefCount.h"
class ConnStateData;
class clientStreamNode;
-class ClientBody;
-;
-
template <class T>
class Range;
char *buf;
size_t notYetUsed;
size_t allocatedSize;
+ /*
+ * abortedSize is the amount of data that should be read
+ * from the socket and immediately discarded. It may be
+ * set when there is a request body and that transaction
+ * gets aborted. The client side should read the remaining
+ * body content and just discard it, if the connection
+ * will be staying open.
+ */
+ size_t abortedSize;
}
in;
- ClientBody *body;
- size_t body_size_left;
-
- auth_type_t auth_type; /* Is this connection based authentication? if so what type it is. */
- /* note this is ONLY connection based because NTLM is against HTTP spec */
- /* the user details for connection based authentication */
+ BodyReader::Pointer body_reader;
+ ssize_t bodySizeLeft();
+
+ /*
+ * Is this connection based authentication? if so what type it
+ * is.
+ */
+ auth_type_t auth_type;
+ /*
+ * note this is ONLY connection based because NTLM is against HTTP spec.
+ * the user details for connection based authentication
+ */
auth_user_request_t *auth_user_request;
- /* TODO: generalise the connection owner concept */
- ClientSocketContext::Pointer currentobject; /* used by the owner of the connection. Opaque otherwise */
+ /*
+ * used by the owner of the connection, opaque otherwise
+ * TODO: generalise the connection owner concept.
+ */
+ ClientSocketContext::Pointer currentobject;
struct sockaddr_in peer;
/*
- * $Id: client_side_request.cc,v 1.61 2006/04/23 11:10:31 robertc Exp $
+ * $Id: client_side_request.cc,v 1.62 2006/04/27 19:27:37 wessels Exp $
*
* DEBUG: section 85 Client-side Request Routines
* AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
* found the end of the body yet
*/
- if (request && request->body_connection.getRaw() != NULL) {
- clientAbortBody(request); /* abort body transter */
- request->body_connection = NULL;
+ if (request && request->body_reader != NULL) {
+ request->body_reader = NULL; // refcounted
+ debugs(32,3,HERE << "setting body_reader = NULL for request " << request);
}
/* the ICP check here was erroneous
;
}
- if (old_request->body_connection.getRaw() != NULL) {
- new_request->body_connection = old_request->body_connection;
- old_request->body_connection = NULL;
+ if (old_request->body_reader != NULL) {
+ new_request->body_reader = old_request->body_reader;
+ old_request->body_reader = NULL;
+ debugs(0,0,HERE << "setting body_reader = NULL for request " << old_request);
}
new_request->content_length = old_request->content_length;
icap = new ICAPClientReqmodPrecache(service);
(void) cbdataReference(icap);
icap->startReqMod(this, request);
- icap->doneSending();
+
+ if (request->body_reader == NULL) {
+ debugs(32,3,HERE << "client request hasnt body...");
+ icap->doneSending();
+
+ }
+
return 0;
}
+/*
+ * icapSendRequestBodyWrapper
+ *
+ * A callback wrapper for ::icapSendRequestBody()
+ *
+ * icapSendRequestBodyWrapper is of type CBCB
+ */
+void
+ClientHttpRequest::icapSendRequestBodyWrapper(MemBuf &mb, void *data)
+{
+ ClientHttpRequest *chr = static_cast<ClientHttpRequest*>(data);
+ chr->icapSendRequestBody(mb);
+}
+
+
+/*
+ * icapSendRequestBody
+ *
+ * Sends some chunk of a request body to the ICAP side. Must make sure
+ * that the ICAP-side can accept the data we have. If there is more
+ * body data to read, then schedule another BodyReader callback.
+ */
+void
+ClientHttpRequest::icapSendRequestBody(MemBuf &mb)
+{
+ ssize_t size_to_send = mb.contentSize();
+ debugs(32,3,HERE << "have " << mb.contentSize() << " bytes in mb");
+
+ if (size_to_send == 0) {
+ /*
+ * An error occurred during this transaction. Tell ICAP that we're done.
+ */
+
+ if (icap)
+ icap->doneSending();
+
+ return;
+ }
+
+ debugs(32,3,HERE << "icap->potentialSpaceSize() = " << icap->potentialSpaceSize());
+
+ if (size_to_send > icap->potentialSpaceSize())
+ size_to_send = icap->potentialSpaceSize();
+
+ if (size_to_send) {
+ debugs(32,3,HERE << "sending " << size_to_send << " body bytes to ICAP");
+ StoreIOBuffer sbuf(size_to_send, 0, mb.content());
+ icap->sendMoreData(sbuf);
+ icap->body_reader->consume(size_to_send);
+ icap->body_reader->bytes_read += size_to_send;
+ debugs(32,3," HTTP client body bytes_read=" << icap->body_reader->bytes_read);
+ } else {
+ debugs(32,0,HERE << "cannot send body data to ICAP");
+ debugs(32,0,HERE << "\tBodyReader MemBuf has " << mb.contentSize());
+ debugs(32,0,HERE << "\tbut icap->potentialSpaceSize() is " << icap->potentialSpaceSize());
+ return;
+ }
+
+ /*
+ * If we sent some data this time, and there is more data to
+ * read, then schedule another read request via BodyReader.
+ */
+ if (size_to_send && icap->body_reader->remaining()) {
+ debugs(32,3,HERE << "calling body_reader->read()");
+ icap->body_reader->read(icapSendRequestBodyWrapper, this);
+ } else {
+ debugs(32,3,HERE << "No more request body bytes to send");
+ icap->doneSending();
+ }
+}
+
/*
* Called by ICAPAnchor when it has space available for us.
*/
void
ClientHttpRequest::icapSpaceAvailable()
{
- debug(85,3)("ClientHttpRequest::icapSpaceAvailable() called\n");
+ debugs(85,3,HERE << this << " ClientHttpRequest::icapSpaceAvailable() called\n");
+
+ if (request->body_reader != NULL && icap->body_reader == NULL) {
+ debugs(32,3,HERE << "reassigning HttpRequest->body_reader to ICAP");
+ /*
+ * ICAP hooks on to the BodyReader that gets data from
+ * ConnStateData. We'll make a new BodyReader that
+ * HttpStateData can use if the adapted response has a
+ * request body. See ICAPClientReqmodPrecache::noteSourceStart()
+ */
+ icap->body_reader = request->body_reader;
+ request->body_reader = NULL;
+ }
+
+ if (icap->body_reader == NULL)
+ return;
+
+ if (icap->body_reader->callbackPending())
+ return;
+
+ debugs(32,3,HERE << "Calling read() for body data");
+
+ icap->body_reader->read(icapSendRequestBodyWrapper, this);
}
void
* Move the "body_connection" over, then unlink old and
* link new to the http state.
*/
- new_req->body_connection = request->body_connection;
- request->body_connection = NULL;
HTTPMSGUNLOCK(request);
request = HTTPMSGLOCK(new_req);
/*
if ((NULL == storeEntry()) || storeEntry()->isEmpty()) {
debug(85,3)("WARNING: ICAP REQMOD callout failed, proceeding with original request\n");
- doCallouts();
+
+ if (calloutContext)
+ doCallouts();
+
#if ICAP_HARD_ERROR
clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
+
clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+
assert (repContext);
+
// Note if this code is ever used, clientBuildError() should be modified to
// accept an errno arg
repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
NULL, getConn().getRaw() != NULL
&& getConn()->auth_user_request ? getConn()->
auth_user_request : request->auth_user_request, errno);
+
node = (clientStreamNode *)client_stream.tail->data;
+
clientStreamRead(node, this, node->readBuffer);
+
#endif
return;
/*
- * $Id: client_side_request.h,v 1.25 2006/04/25 07:13:33 robertc Exp $
+ * $Id: client_side_request.h,v 1.26 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
public:
ICAPClientReqmodPrecache *icap;
int doIcap(ICAPServiceRep::Pointer);
+ void icapSendRequestBody(MemBuf&);
+ static void icapSendRequestBodyWrapper(MemBuf&, void*);
void icapSpaceAvailable();
void takeAdaptedHeaders(HttpMsg *);
void takeAdaptedBody(MemBuf *);
/*
- * $Id: forward.cc,v 1.137 2006/04/02 14:32:35 serassio Exp $
+ * $Id: forward.cc,v 1.138 2006/04/27 19:27:37 wessels Exp $
*
* DEBUG: section 17 Request Forwarding
* AUTHOR: Duane Wessels
* even if the method is indempotent
*/
- if (request->body_connection.getRaw() != NULL)
+ if (request->body_reader != NULL)
return false;
/* RFC2616 9.1 Safe and Idempotent Methods */
/*
- * $Id: ftp.cc,v 1.391 2006/04/23 11:10:31 robertc Exp $
+ * $Id: ftp.cc,v 1.392 2006/04/27 19:27:37 wessels Exp $
*
* DEBUG: section 9 File Transfer Protocol (FTP)
* AUTHOR: Harvest Derived
static IOCB ftpReadControlReply;
static IOWCB ftpWriteCommandCallback;
static HttpReply *ftpAuthRequired(HttpRequest * request, const char *realm);
- static void ftpRequestBody(char *buf, ssize_t size, void *data);
+ static CBCB ftpRequestBody;
static wordlist *ftpParseControlReply(char *, size_t, int *, int *);
#if ICAP_CLIENT
/* This will be called when there is data available to put */
void
-FtpStateData::ftpRequestBody(char *buf, ssize_t size, void *data)
+FtpStateData::ftpRequestBody(MemBuf &mb, void *data)
{
FtpStateData *ftpState = (FtpStateData *) data;
- debug(9, 3) ("ftpRequestBody: buf=%p size=%d ftpState=%p\n", buf, (int) size, data);
+ debugs(9, 3, HERE << "ftpRequestBody: size=" << mb.contentSize() << " ftpState=%p" << data);
- if (size > 0) {
+ if (mb.contentSize() > 0) {
/* DataWrite */
- comm_write(ftpState->data.fd, buf, size, FtpStateData::ftpDataWriteCallback, ftpState);
- } else if (size < 0) {
+ comm_write(ftpState->data.fd, mb.content(), mb.contentSize(), FtpStateData::ftpDataWriteCallback, ftpState);
+ } else if (mb.contentSize() < 0) {
/* Error */
debug(9, 1) ("ftpRequestBody: request aborted");
ftpState->failed(ERR_READ_ERROR, 0);
- } else if (size == 0) {
+ } else if (mb.contentSize() == 0) {
/* End of transfer */
ftpState->dataComplete();
}
FtpStateData *ftpState = (FtpStateData *) data;
debug(9, 3) ("ftpDataWrite\n");
/* This starts the body transfer */
- clientReadBody(ftpState->request,
- ftpState->data.readBuf->content(),
- ftpState->data.readBuf->contentSize(),
- ftpRequestBody,
- ftpState);
+ ftpState->request->body_reader->read(ftpRequestBody, ftpState);
}
static void
/*
- * $Id: http.cc,v 1.492 2006/04/22 09:02:44 robertc Exp $
+ * $Id: http.cc,v 1.493 2006/04/27 19:27:37 wessels Exp $
*
* DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
* AUTHOR: Harvest Derived
HttpStateData::~HttpStateData()
{
/*
- * don't forget about ~ServerStateData()
+ * don't forget that ~ServerStateData() gets called automatically
*/
- if (request_body_buf) {
- if (orig_request->body_connection != NULL) {
- clientAbortBody(orig_request);
- }
-
- if (request_body_buf) {
- memFree(request_body_buf, MEM_8K_BUF);
- request_body_buf = NULL;
- }
+ if (orig_request->body_reader != NULL) {
+ orig_request->body_reader = NULL;
+ debugs(32,3,HERE << "setting body_reader = NULL for request " << orig_request);
}
if (!readBuf->isNull())
flags.do_next_read = 1;
maybeReadData();
- if (orig_request->body_connection != NULL)
+ debugs(32,3,HERE<< "request " << request << " body_reader = " << orig_request->body_reader.getRaw());
+
+ if (orig_request->body_reader != NULL)
sendHeaderDone = HttpStateData::SendRequestEntityWrapper;
else
sendHeaderDone = HttpStateData::SendComplete;
}
}
+/*
+ * RequestBodyHandlerWrapper
+ *
+ * BodyReader calls this when it has some body data for us.
+ * It is of type CBCB.
+ */
void
-HttpStateData::RequestBodyHandlerWrapper(char *buf, ssize_t size, void *data)
+HttpStateData::RequestBodyHandlerWrapper(MemBuf &mb, void *data)
{
HttpStateData *httpState = static_cast<HttpStateData *>(data);
- httpState->requestBodyHandler(buf, size);
+ httpState->requestBodyHandler(mb);
}
void
-HttpStateData::requestBodyHandler(char *buf, ssize_t size)
+HttpStateData::requestBodyHandler(MemBuf &mb)
{
- request_body_buf = NULL;
-
if (eof || fd < 0) {
debugs(11, 1, HERE << "Transaction aborted while reading HTTP body");
- memFree8K(buf);
return;
}
- if (size > 0) {
+ if (mb.contentSize() > 0) {
if (flags.headers_parsed && !flags.abuse_detected) {
flags.abuse_detected = 1;
debug(11, 1) ("httpSendRequestEntryDone: Likely proxy abuse detected '%s' -> '%s'\n",
storeUrl(entry));
if (getReply()->sline.status == HTTP_INVALID_HEADER) {
- memFree8K(buf);
comm_close(fd);
return;
}
}
- comm_old_write(fd, buf, size, SendRequestEntityWrapper, this, memFree8K);
- } else if (size == 0) {
+ /*
+ * mb's content will be consumed in the SendRequestEntityWrapper
+ * callback after comm_write is done.
+ */
+ flags.consume_body_data = 1;
+
+ comm_old_write(fd, mb.content(), mb.contentSize(), SendRequestEntityWrapper, this, NULL);
+ } else if (orig_request->body_reader == NULL) {
+ /* Failed to get whole body, probably aborted */
+ SendComplete(fd, NULL, 0, COMM_ERR_CLOSING, this);
+ } else if (orig_request->body_reader->remaining() == 0) {
/* End of body */
- memFree8K(buf);
sendRequestEntityDone();
} else {
/* Failed to get whole body, probably aborted */
- memFree8K(buf);
- HttpStateData::SendComplete(fd, NULL, 0, COMM_ERR_CLOSING, this);
+ SendComplete(fd, NULL, 0, COMM_ERR_CLOSING, this);
}
}
{
debug(11, 5) ("httpSendRequestEntity: FD %d: size %d: errflag %d.\n",
fd, (int) size, errflag);
+ debugs(32,3,HERE << "httpSendRequestEntity called");
+ assert(orig_request->body_reader != NULL);
if (size > 0) {
fd_bytes(fd, size, FD_WRITE);
kb_incr(&statCounter.server.all.kbytes_out, size);
kb_incr(&statCounter.server.http.kbytes_out, size);
+
+ if (flags.consume_body_data) {
+ orig_request->body_reader->consume(size);
+ orig_request->body_reader->bytes_read += size;
+ debugs(32,3," HTTP server body bytes_read=" << orig_request->body_reader->bytes_read);
+ }
}
if (errflag == COMM_ERR_CLOSING)
return;
}
- request_body_buf = (char *)memAllocate(MEM_8K_BUF);
- clientReadBody(orig_request, request_body_buf, 8192, RequestBodyHandlerWrapper, this);
+ size_t r = orig_request->body_reader->remaining();
+ debugs(32,3,HERE << "body remaining = " << r);
+
+ if (r) {
+ debugs(32,3,HERE << "reading more body data");
+ orig_request->body_reader->read(RequestBodyHandlerWrapper, this);
+ } else {
+ debugs(32,3,HERE << "done reading body data");
+ sendRequestEntityDone();
+ }
}
void
/*
- * $Id: http.h,v 1.23 2006/03/07 18:47:48 wessels Exp $
+ * $Id: http.h,v 1.24 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
#include "comm.h"
#include "forward.h"
#include "Server.h"
+#include "BodyReader.h"
#if ICAP_CLIENT
#include "ICAP/ICAPServiceRep.h"
HttpRequest *orig_request;
int fd;
http_state_flags flags;
- char *request_body_buf;
off_t currentOffset;
size_t read_sz;
int body_bytes_read; /* to find end of response, independent of StoreEntry */
void transactionComplete();
void writeReplyBody(const char *data, int len);
void sendRequestEntityDone();
- void requestBodyHandler(char *buf, ssize_t size);
+ void requestBodyHandler(MemBuf &);
void sendRequestEntity(int fd, size_t size, comm_err_t errflag);
mb_size_t buildRequestPrefix(HttpRequest * request,
HttpRequest * orig_request,
/*
- * $Id: protos.h,v 1.522 2006/04/23 11:10:32 robertc Exp $
+ * $Id: protos.h,v 1.523 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
SQUIDCEXTERN int clientdbEstablished(struct IN_ADDR, int);
SQUIDCEXTERN void clientOpenListenSockets(void);
SQUIDCEXTERN void clientHttpConnectionsClose(void);
-SQUIDCEXTERN void clientReadBody(HttpRequest * req, char *buf, size_t size, CBCB * callback, void *data);
-SQUIDCEXTERN int clientAbortBody(HttpRequest * req);
SQUIDCEXTERN void httpRequestFree(void *);
extern void clientAccessCheck(void *);
/*
- * $Id: structs.h,v 1.539 2006/04/23 11:10:32 robertc Exp $
+ * $Id: structs.h,v 1.540 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
unsigned int do_next_read:
1;
+
+unsigned int consume_body_data:
+ 1;
};
struct _ipcache_addrs
/*
- * $Id: typedefs.h,v 1.181 2006/04/23 11:10:32 robertc Exp $
+ * $Id: typedefs.h,v 1.182 2006/04/27 19:27:37 wessels Exp $
*
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
typedef void UH(void *data, wordlist *);
typedef int READ_HANDLER(int, char *, int);
typedef int WRITE_HANDLER(int, const char *, int);
-typedef void CBCB(char *buf, ssize_t size, void *data);
typedef void STIOCB(void *their_data, int errflag, storeIOState *);
typedef void STFNCB(void *their_data, int errflag, storeIOState *);