On 9/03/2014 7:34 p.m., Alex Rousskov wrote:
> On 03/08/2014 04:13 AM, Amos Jeffries wrote:
>> Prepare the way to efficiently parse client requests using SBuf based
>> parser-ng.
>>
<snip>
>
> Please redesign to avoid these problems. Please let me know if you need
> help with that -- I have a couple of ideas how this can be done, but I
> suspect yours are going to be better since you have spent a lot more
> time with this code.
>
Good points.
Attached patch extends the earlier one so IoCallback stores a
raw-pointer to the ConnStateData::In::buf.
This is now specifically to the SBuf member object rather than its
MemBlob or char* backing stores. So only the short (blocking)
FD_READ_METHOD() call needs to provide any synchronous guarantees.
We particularly need a raw-pointer to the ConnStateData member to
prevent the same possible read/consume collisions causing problems when
it comes to merging the two separate SBuf later (by keeping only one SBuf).
>
>> - comm_read(clientConnection, in.addressToReadInto(),
>> getAvailableBufferLength(), reader);
>> + comm_read(clientConnection, in.buf.rawSpace(in.buf.spaceSize()),
>> in.buf.spaceSize()-1, reader);
>
> If this code survives, please allocate the buffer outside the comm_read
> call. There was already an ugly dependency on the right parameter
> evaluation order, and your changes make it look even worse.
>
It is moved inside the deep read operations now, just before
FD_READ_METHOD is used. If I am understanding what you meant then that
should fix it properly.
Amos
=== modified file 'src/CommCalls.h'
--- src/CommCalls.h 2014-01-28 19:28:23 +0000
+++ src/CommCalls.h 2014-03-15 06:36:56 +0000
@@ -89,52 +89,55 @@
class CommAcceptCbParams: public CommCommonCbParams
{
public:
CommAcceptCbParams(void *aData);
void print(std::ostream &os) const;
/// Transaction which this call is part of.
MasterXaction::Pointer xaction;
};
// connect parameters
class CommConnectCbParams: public CommCommonCbParams
{
public:
CommConnectCbParams(void *aData);
bool syncWithComm(); // see CommCommonCbParams::syncWithComm
};
+class SBuf;
+
// read/write (I/O) parameters
class CommIoCbParams: public CommCommonCbParams
{
public:
CommIoCbParams(void *aData);
void print(std::ostream &os) const;
bool syncWithComm(); // see CommCommonCbParams::syncWithComm
public:
char *buf;
size_t size;
+ SBuf *buf2; // alternative buffer for use when buf is unset
};
// close parameters
class CommCloseCbParams: public CommCommonCbParams
{
public:
CommCloseCbParams(void *aData);
};
class CommTimeoutCbParams: public CommCommonCbParams
{
public:
CommTimeoutCbParams(void *aData);
};
/// Special Calls parameter, for direct use of an FD without a controlling
Comm::Connection
/// This is used for pipe() FD with helpers, and internally by Comm when
handling some special FD actions.
class FdeCbParams: public CommCommonCbParams
{
public:
=== modified file 'src/client_side.cc'
--- src/client_side.cc 2014-03-08 17:28:23 +0000
+++ src/client_side.cc 2014-03-15 10:48:51 +0000
@@ -231,46 +231,46 @@
}
clientStreamNode *
ClientSocketContext::getClientReplyContext() const
{
return (clientStreamNode *)http->client_stream.tail->prev->data;
}
/**
* This routine should be called to grow the inbuf and then
* call comm_read().
*/
void
ConnStateData::readSomeData()
{
if (reading())
return;
debugs(33, 4, HERE << clientConnection << ": reading request...");
- if (!maybeMakeSpaceAvailable())
+ if (!in.maybeMakeSpaceAvailable())
return;
typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
reader = JobCallback(33, 5, Dialer, this,
ConnStateData::clientReadRequest);
- comm_read(clientConnection, in.addressToReadInto(),
getAvailableBufferLength(), reader);
+ comm_read(clientConnection, in.buf, reader);
}
void
ClientSocketContext::removeFromConnectionList(ConnStateData * conn)
{
ClientSocketContext::Pointer *tempContextPointer;
assert(conn != NULL && cbdataReferenceValid(conn));
assert(conn->getCurrentContext() != NULL);
/* Unlink us from the connection request list */
tempContextPointer = & conn->currentobject;
while (tempContextPointer->getRaw()) {
if (*tempContextPointer == this)
break;
tempContextPointer = &(*tempContextPointer)->next;
}
assert(tempContextPointer->getRaw() != NULL);
*tempContextPointer = next;
@@ -1544,41 +1544,41 @@
/* We are only called when the client socket shutsdown.
* Tell the prev pipeline member we're finished
*/
clientStreamDetach(node, http);
}
static void
clientWriteBodyComplete(const Comm::ConnectionPointer &conn, char *buf, size_t
size, comm_err_t errflag, int xerrno, void *data)
{
debugs(33,7, HERE << "clientWriteBodyComplete schedules
clientWriteComplete");
clientWriteComplete(conn, NULL, size, errflag, xerrno, data);
}
void
ConnStateData::readNextRequest()
{
debugs(33, 5, HERE << clientConnection << " reading next req");
fd_note(clientConnection->fd, "Idle client: Waiting for next request");
/**
- * Set the timeout BEFORE calling clientReadRequest().
+ * Set the timeout BEFORE calling readSomeData().
*/
typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
TimeoutDialer, this,
ConnStateData::requestTimeout);
commSetConnTimeout(clientConnection, Config.Timeout.clientIdlePconn,
timeoutCall);
readSomeData();
/** Please don't do anything with the FD past here! */
}
static void
ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer
deferredRequest, ConnStateData * conn)
{
debugs(33, 2, HERE << conn->clientConnection << " Sending next");
/** If the client stream is waiting on a socket write to occur, then */
if (deferredRequest->flags.deferred) {
/** NO data is allowed to have been sent. */
assert(deferredRequest->http->out.size == 0);
@@ -1872,41 +1872,41 @@
{
http->getConn()->stopSending(reason); // closes ASAP
}
void
ConnStateData::stopSending(const char *error)
{
debugs(33, 4, HERE << "sending error (" << clientConnection << "): " <<
error <<
"; old receiving error: " <<
(stoppedReceiving() ? stoppedReceiving_ : "none"));
if (const char *oldError = stoppedSending()) {
debugs(33, 3, HERE << "already stopped sending: " << oldError);
return; // nothing has changed as far as this connection is concerned
}
stoppedSending_ = error;
if (!stoppedReceiving()) {
if (const int64_t expecting = mayNeedToReadMoreBody()) {
debugs(33, 5, HERE << "must still read " << expecting <<
- " request body bytes with " << in.notYetUsed << " unused");
+ " request body bytes with " << in.buf.length() << "
unused");
return; // wait for the request receiver to finish reading
}
}
clientConnection->close();
}
void
ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char
*bufnotused, size_t size, comm_err_t errflag)
{
const StoreEntry *entry = http->storeEntry();
http->out.size += size;
debugs(33, 5, HERE << conn << ", sz " << size <<
", err " << errflag << ", off " << http->out.size << ", len " <<
(entry ? entry->objectLen() : 0));
clientUpdateSocketStats(http->logType, size);
/* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
if (errflag == COMM_ERR_CLOSING || !Comm::IsConnOpen(conn))
@@ -1939,41 +1939,41 @@
case STREAM_FAILED:
initiateClose("STREAM_FAILED");
return;
default:
fatal("Hit unreachable code in clientWriteComplete\n");
}
}
SQUIDCEXTERN CSR clientGetMoreData;
SQUIDCEXTERN CSS clientReplyStatus;
SQUIDCEXTERN CSD clientReplyDetach;
static ClientSocketContext *
parseHttpRequestAbort(ConnStateData * csd, const char *uri)
{
ClientHttpRequest *http;
ClientSocketContext *context;
StoreIOBuffer tempBuffer;
http = new ClientHttpRequest(csd);
- http->req_sz = csd->in.notYetUsed;
+ http->req_sz = csd->in.buf.length();
http->uri = xstrdup(uri);
setLogUri (http, uri);
context = new ClientSocketContext(csd->clientConnection, http);
tempBuffer.data = context->reqbuf;
tempBuffer.length = HTTP_REQBUF_SZ;
clientStreamInit(&http->client_stream, clientGetMoreData,
clientReplyDetach,
clientReplyStatus, new clientReplyContext(http),
clientSocketRecipient,
clientSocketDetach, context, tempBuffer);
return context;
}
char *
skipLeadingSpace(char *aString)
{
char *result = aString;
while (xisspace(*aString))
++aString;
return result;
@@ -2362,153 +2362,134 @@
if (!http->uri) {
/* No special rewrites have been applied above, use the
* requested url. may be rewritten later, so make extra room */
int url_sz = strlen(url) + Config.appendDomainLen + 5;
http->uri = (char *)xcalloc(url_sz, 1);
strcpy(http->uri, url);
}
debugs(33, 5, "parseHttpRequest: Complete request received");
// XXX: crop this dump at the end of headers. No need for extras
debugs(11, 2, "HTTP Client " << csd->clientConnection);
debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) +
hp->req.m_start << "\n----------");
result->flags.parsed_ok = 1;
xfree(url);
return result;
}
-int
-ConnStateData::getAvailableBufferLength() const
-{
- assert (in.allocatedSize > in.notYetUsed); // allocated more than used
- const size_t result = in.allocatedSize - in.notYetUsed - 1;
- // huge request_header_max_size may lead to more than INT_MAX unused space
- assert (static_cast<ssize_t>(result) <= INT_MAX);
- return result;
-}
-
bool
-ConnStateData::maybeMakeSpaceAvailable()
+ConnStateData::In::maybeMakeSpaceAvailable()
{
- if (getAvailableBufferLength() < 2) {
- size_t newSize;
- if (in.allocatedSize >= Config.maxRequestBufferSize) {
+ if (buf.spaceSize() < 2) {
+ const SBuf::size_type haveCapacity = buf.length() + buf.spaceSize();
+ if (haveCapacity >= Config.maxRequestBufferSize) {
debugs(33, 4, "request buffer full:
client_request_buffer_max_size=" << Config.maxRequestBufferSize);
return false;
}
- if ((newSize=in.allocatedSize * 2) > Config.maxRequestBufferSize) {
- newSize=Config.maxRequestBufferSize;
- }
- in.buf = (char *)memReallocBuf(in.buf, newSize, &in.allocatedSize);
- debugs(33, 2, "growing request buffer: notYetUsed=" << in.notYetUsed
<< " size=" << in.allocatedSize);
+ const SBuf::size_type wantCapacity = min(Config.maxRequestBufferSize,
haveCapacity*2);
+ buf.reserveCapacity(wantCapacity);
+ debugs(33, 2, "growing request buffer: available=" << buf.spaceSize()
<< " used=" << buf.length());
}
- return true;
+ return (buf.spaceSize() >= 2);
}
void
ConnStateData::addContextToQueue(ClientSocketContext * context)
{
ClientSocketContext::Pointer *S;
for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
S = &(*S)->next);
*S = context;
++nrequests;
}
int
ConnStateData::getConcurrentRequestCount() const
{
int result = 0;
ClientSocketContext::Pointer *T;
for (T = (ClientSocketContext::Pointer *) ¤tobject;
T->getRaw(); T = &(*T)->next, ++result);
return result;
}
int
ConnStateData::connReadWasError(comm_err_t flag, int size, int xerrno)
{
if (flag != COMM_OK) {
debugs(33, 2, "connReadWasError: FD " << clientConnection << ": got
flag " << flag);
return 1;
}
if (size < 0) {
if (!ignoreErrno(xerrno)) {
debugs(33, 2, "connReadWasError: FD " << clientConnection << ": "
<< xstrerr(xerrno));
return 1;
- } else if (in.notYetUsed == 0) {
+ } else if (in.buf.isEmpty()) {
debugs(33, 2, "connReadWasError: FD " << clientConnection << ": no
data to process (" << xstrerr(xerrno) << ")");
}
}
return 0;
}
int
ConnStateData::connFinishedWithConn(int size)
{
if (size == 0) {
- if (getConcurrentRequestCount() == 0 && in.notYetUsed == 0) {
+ if (getConcurrentRequestCount() == 0 && in.buf.isEmpty()) {
/* no current or pending requests */
debugs(33, 4, HERE << clientConnection << " closed");
return 1;
} else if (!Config.onoff.half_closed_clients) {
/* admin doesn't want to support half-closed client sockets */
debugs(33, 3, HERE << clientConnection << " aborted
(half_closed_clients disabled)");
notifyAllContexts(0); // no specific error implies abort
return 1;
}
}
return 0;
}
void
connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
{
- assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
- conn->in.notYetUsed -= byteCount;
- debugs(33, 5, HERE << "conn->in.notYetUsed = " << conn->in.notYetUsed);
- /*
- * If there is still data that will be used,
- * move it to the beginning.
- */
-
- if (conn->in.notYetUsed > 0)
- memmove(conn->in.buf, conn->in.buf + byteCount, conn->in.notYetUsed);
+ assert(byteCount > 0 && byteCount <= conn->in.buf.length());
+ conn->in.buf.consume(byteCount);
+ debugs(33, 5, "conn->in.buf has " << conn->in.buf.length() << " bytes
unused.");
}
/// respond with ERR_TOO_BIG if request header exceeds request_header_max_size
void
ConnStateData::checkHeaderLimits()
{
- if (in.notYetUsed < Config.maxRequestHeaderSize)
+ if (in.buf.length() < Config.maxRequestHeaderSize)
return; // can accumulte more header data
- debugs(33, 3, "Request header is too large (" << in.notYetUsed << " > " <<
+ debugs(33, 3, "Request header is too large (" << in.buf.length() << " > "
<<
Config.maxRequestHeaderSize << " bytes)");
ClientSocketContext *context = parseHttpRequestAbort(this,
"error:request-too-large");
clientStreamNode *node = context->getClientReplyContext();
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_TOO_BIG,
Http::scBadRequest, Http::METHOD_NONE, NULL,
clientConnection->remote, NULL, NULL, NULL);
context->registerWithConn();
context->pullData();
}
void
ConnStateData::clientAfterReadingRequests()
{
// Were we expecting to read more request body from half-closed connection?
if (mayNeedToReadMoreBody() && commIsHalfClosed(clientConnection->fd)) {
debugs(33, 3, HERE << "truncated body: closing half-closed " <<
clientConnection);
clientConnection->close();
@@ -2625,49 +2606,49 @@
bool notedUseOfBuffer = false;
bool chunked = false;
bool mustReplyToOptions = false;
bool unsupportedTe = false;
bool expectBody = false;
/* We have an initial client stream in place should it be needed */
/* setup our private context */
context->registerWithConn();
if (context->flags.parsed_ok == 0) {
clientStreamNode *node = context->getClientReplyContext();
debugs(33, 2, "clientProcessRequest: Invalid Request");
conn->quitAfterError(NULL);
// setLogUri should called before repContext->setReplyToError
setLogUri(http, http->uri, true);
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
switch (hp->request_parse_status) {
case Http::scHeaderTooLarge:
- repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest,
method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL);
+ repContext->setReplyToError(ERR_TOO_BIG, Http::scBadRequest,
method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf.c_str(),
NULL);
break;
case Http::scMethodNotAllowed:
repContext->setReplyToError(ERR_UNSUP_REQ,
Http::scMethodNotAllowed, method, http->uri,
- conn->clientConnection->remote, NULL,
conn->in.buf, NULL);
+ conn->clientConnection->remote, NULL,
conn->in.buf.c_str(), NULL);
break;
default:
repContext->setReplyToError(ERR_INVALID_REQ,
hp->request_parse_status, method, http->uri,
- conn->clientConnection->remote, NULL,
conn->in.buf, NULL);
+ conn->clientConnection->remote, NULL,
conn->in.buf.c_str(), NULL);
}
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
}
if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) ==
NULL) {
clientStreamNode *node = context->getClientReplyContext();
debugs(33, 5, "Invalid URL: " << http->uri);
conn->quitAfterError(request.getRaw());
// setLogUri should called before repContext->setReplyToError
setLogUri(http, http->uri, true);
clientReplyContext *repContext = dynamic_cast<clientReplyContext
*>(node->data.getRaw());
assert (repContext);
repContext->setReplyToError(ERR_INVALID_URL, Http::scBadRequest,
method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
assert(context->http->out.offset == 0);
context->pullData();
goto finish;
}
@@ -2883,43 +2864,43 @@
if (!notedUseOfBuffer)
connNoteUseOfBuffer(conn, http->req_sz);
/*
* DPW 2007-05-18
* Moved the TCP_RESET feature from clientReplyContext::sendMoreData
* to here because calling comm_reset_close() causes http to
* be freed and the above connNoteUseOfBuffer() would hit an
* assertion, not to mention that we were accessing freed memory.
*/
if (request != NULL && request->flags.resetTcp &&
Comm::IsConnOpen(conn->clientConnection)) {
debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
conn->flags.readMore = false;
comm_reset_close(conn->clientConnection);
}
}
static void
connStripBufferWhitespace (ConnStateData * conn)
{
- while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
- memmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
- -- conn->in.notYetUsed;
+ // XXX: kill this whole function.
+ while (!conn->in.buf.isEmpty() && xisspace(conn->in.buf.at(0))) {
+ conn->in.buf.consume(1);
}
}
/**
* Limit the number of concurrent requests.
* \return true when there are available position(s) in the pipeline queue
for another request.
* \return false when the pipeline queue is full or disabled.
*/
bool
ConnStateData::concurrentRequestQueueFilled() const
{
const int existingRequestCount = getConcurrentRequestCount();
// default to the configured pipeline size.
// add 1 because the head of pipeline is counted in concurrent requests
and not prefetch queue
const int concurrentRequestLimit = Config.pipeline_max_prefetch + 1;
// when queue filled already we cant add more.
if (existingRequestCount >= concurrentRequestLimit) {
debugs(33, 3, clientConnection << " max concurrent requests reached ("
<< concurrentRequestLimit << ")");
@@ -2928,58 +2909,54 @@
}
return false;
}
/**
* Attempt to parse one or more requests from the input buffer.
* If a request is successfully parsed, even if the next request
* is only partially parsed, it will return TRUE.
*/
bool
ConnStateData::clientParseRequests()
{
HttpRequestMethod method;
bool parsed_req = false;
debugs(33, 5, HERE << clientConnection << ": attempting to parse");
// Loop while we have read bytes that are not needed for producing the body
// On errors, bodyPipe may become nil, but readMore will be cleared
- while (in.notYetUsed > 0 && !bodyPipe && flags.readMore) {
+ while (!in.buf.isEmpty() && !bodyPipe && flags.readMore) {
connStripBufferWhitespace(this);
/* Don't try to parse if the buffer is empty */
- if (in.notYetUsed == 0)
+ if (in.buf.isEmpty())
break;
/* Limit the number of concurrent requests */
if (concurrentRequestQueueFilled())
break;
- /* Should not be needed anymore */
- /* Terminate the string */
- in.buf[in.notYetUsed] = '\0';
-
/* Begin the parsing */
PROF_start(parseHttpRequest);
- HttpParserInit(&parser_, in.buf, in.notYetUsed);
+ HttpParserInit(&parser_, in.buf.c_str(), in.buf.length());
/* Process request */
Http::ProtocolVersion http_ver;
ClientSocketContext *context = parseHttpRequest(this, &parser_,
&method, &http_ver);
PROF_stop(parseHttpRequest);
/* partial or incomplete request */
if (!context) {
// TODO: why parseHttpRequest can just return parseHttpRequestAbort
// (which becomes context) but checkHeaderLimits cannot?
checkHeaderLimits();
break;
}
/* status -1 or 1 */
if (context) {
debugs(33, 5, HERE << clientConnection << ": parsed a request");
AsyncCall::Pointer timeoutCall = commCbCall(5, 4,
"clientLifetimeTimeout",
CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
commSetConnTimeout(clientConnection, Config.Timeout.lifetime,
timeoutCall);
@@ -3017,41 +2994,41 @@
assert(io.conn->fd == clientConnection->fd);
/*
* Don't reset the timeout value here. The timeout value will be
* set to Config.Timeout.request by httpAccept() and
* clientWriteComplete(), and should apply to the request as a
* whole, not individual read() calls. Plus, it breaks our
* lame half-close detection
*/
if (connReadWasError(io.flag, io.size, io.xerrno)) {
notifyAllContexts(io.xerrno);
io.conn->close();
return;
}
if (io.flag == COMM_OK) {
if (io.size > 0) {
kb_incr(&(statCounter.client_http.kbytes_in), io.size);
// may comm_close or setReplyToError
- if (!handleReadData(io.buf, io.size))
+ if (!handleReadData(io.buf2))
return;
} else if (io.size == 0) {
debugs(33, 5, HERE << io.conn << " closed?");
if (connFinishedWithConn(io.size)) {
clientConnection->close();
return;
}
/* It might be half-closed, we can't tell */
fd_table[io.conn->fd].flags.socket_eof = true;
commMarkHalfClosed(io.conn->fd);
fd_note(io.conn->fd, "half-closed");
/* There is one more close check at the end, to detect aborted
* (partial) requests. At this point we can't tell if the request
* is partial.
@@ -3078,119 +3055,112 @@
if (getConcurrentRequestCount() == 0 && commIsHalfClosed(io.fd)) {
debugs(33, 5, HERE << io.conn << ": half-closed connection, no
completed request parsed, connection closing.");
clientConnection->close();
return;
}
}
if (!isOpen())
return;
clientAfterReadingRequests();
}
/**
* called when new request data has been read from the socket
*
* \retval false called comm_close or setReplyToError (the caller should bail)
* \retval true we did not call comm_close or setReplyToError
*/
bool
-ConnStateData::handleReadData(char *buf, size_t size)
+ConnStateData::handleReadData(SBuf *buf)
{
- char *current_buf = in.addressToReadInto();
-
- if (buf != current_buf)
- memmove(current_buf, buf, size);
-
- in.notYetUsed += size;
-
- in.buf[in.notYetUsed] = '\0'; /* Terminate the string */
+ assert(buf == &in.buf); // XXX: make this abort the transaction if this
fails
// if we are reading a body, stuff data into the body pipe
if (bodyPipe != NULL)
return handleRequestBodyData();
return true;
}
/**
* called when new request body data has been buffered in in.buf
* may close the connection if we were closing and piped everything out
*
* \retval false called comm_close or setReplyToError (the caller should bail)
* \retval true we did not call comm_close or setReplyToError
*/
bool
ConnStateData::handleRequestBodyData()
{
assert(bodyPipe != NULL);
size_t putSize = 0;
if (in.bodyParser) { // chunked encoding
if (const err_type error = handleChunkedRequestBody(putSize)) {
abortChunkedRequestBody(error);
return false;
}
} else { // identity encoding
debugs(33,5, HERE << "handling plain request body for " <<
clientConnection);
- putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed);
+ putSize = bodyPipe->putMoreData(in.buf.c_str(), in.buf.length());
if (!bodyPipe->mayNeedMoreData()) {
// BodyPipe will clear us automagically when we produced everything
bodyPipe = NULL;
}
}
if (putSize > 0)
connNoteUseOfBuffer(this, putSize);
if (!bodyPipe) {
debugs(33,5, HERE << "produced entire request body for " <<
clientConnection);
if (const char *reason = stoppedSending()) {
/* we've finished reading like good clients,
* now do the close that initiateClose initiated.
*/
debugs(33, 3, HERE << "closing for earlier sending error: " <<
reason);
clientConnection->close();
return false;
}
}
return true;
}
/// parses available chunked encoded body bytes, checks size, returns errors
err_type
ConnStateData::handleChunkedRequestBody(size_t &putSize)
{
- debugs(33,7, HERE << "chunked from " << clientConnection << ": " <<
in.notYetUsed);
+ debugs(33, 7, "chunked from " << clientConnection << ": " <<
in.buf.length());
try { // the parser will throw on errors
- if (!in.notYetUsed) // nothing to do (MemBuf::init requires this check)
+ if (in.buf.isEmpty()) // nothing to do
return ERR_NONE;
MemBuf raw; // ChunkedCodingParser only works with MemBufs
// add one because MemBuf will assert if it cannot 0-terminate
- raw.init(in.notYetUsed, in.notYetUsed+1);
- raw.append(in.buf, in.notYetUsed);
+ raw.init(in.buf.length(), in.buf.length()+1);
+ raw.append(in.buf.c_str(), in.buf.length());
const mb_size_t wasContentSize = raw.contentSize();
BodyPipeCheckout bpc(*bodyPipe);
const bool parsed = in.bodyParser->parse(&raw, &bpc.buf);
bpc.checkIn();
putSize = wasContentSize - raw.contentSize();
// dechunk then check: the size limit applies to _dechunked_ content
if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
return ERR_TOO_BIG;
if (parsed) {
finishDechunkingRequest(true);
Must(!bodyPipe);
return ERR_NONE; // nil bodyPipe implies body end for the caller
}
// if chunk parser needs data, then the body pipe must need it too
Must(!in.bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
@@ -3296,41 +3266,41 @@
sslBumpMode(Ssl::bumpEnd),
switchedToHttps_(false),
sslServerBump(NULL),
#endif
stoppedSending_(NULL),
stoppedReceiving_(NULL)
{
pinning.host = NULL;
pinning.port = -1;
pinning.pinned = false;
pinning.auth = false;
pinning.zeroReply = false;
pinning.peer = NULL;
// store the details required for creating more MasterXaction objects as
new requests come in
clientConnection = xact->tcpClient;
port = cbdataReference(xact->squidPort.get());
log_addr = xact->tcpClient->remote;
log_addr.applyMask(Config.Addrs.client_netmask);
- in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &in.allocatedSize);
+ in.buf.reserveCapacity(CLIENT_REQ_BUF_SZ);
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)
int i = IP_PMTUDISC_DONT;
if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i,
sizeof(i)) < 0)
debugs(33, 2, "WARNING: Path MTU discovery disabling failed on "
<< clientConnection << " : " << xstrerror());
#else
static bool reported = false;
if (!reported) {
debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is
not supported on your platform.");
reported = true;
}
#endif
}
typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this,
ConnStateData::connStateClosed);
comm_add_close_handler(clientConnection->fd, call);
@@ -3638,44 +3608,44 @@
httpsSslBumpAccessCheckDone(allow_t answer, void *data)
{
ConnStateData *connState = (ConnStateData *) data;
// if the connection is closed or closing, just return.
if (!connState->isOpen())
return;
// Require both a match and a positive bump mode to work around exceptional
// cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
if (answer == ACCESS_ALLOWED && answer.kind != Ssl::bumpNone) {
debugs(33, 2, HERE << "sslBump needed for " <<
connState->clientConnection);
connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
httpsEstablish(connState, NULL, (Ssl::BumpMode)answer.kind);
} else {
debugs(33, 2, HERE << "sslBump not needed for " <<
connState->clientConnection);
connState->sslBumpMode = Ssl::bumpNone;
// fake a CONNECT request to force connState to tunnel
static char ip[MAX_IPSTRLEN];
- static char reqStr[MAX_IPSTRLEN + 80];
connState->clientConnection->local.toUrl(ip, sizeof(ip));
- snprintf(reqStr, sizeof(reqStr), "CONNECT %s HTTP/1.1\r\nHost:
%s\r\n\r\n", ip, ip);
- bool ret = connState->handleReadData(reqStr, strlen(reqStr));
+ SBuf reqStr;
+ reqStr.append("CONNECT ").append(ip).append(" HTTP/1.1\r\nHost:
").append(ip).append("\r\n\r\n");
+ bool ret = connState->handleReadData(&reqStr);
if (ret)
ret = connState->clientParseRequests();
if (!ret) {
debugs(33, 2, HERE << "Failed to start fake CONNECT request for
ssl bumped connection: " << connState->clientConnection);
connState->clientConnection->close();
}
}
}
/** handle a new HTTPS connection */
static void
httpsAccept(const CommAcceptCbParams ¶ms)
{
MasterXaction::Pointer xact = params.xaction;
const AnyP::PortCfgPointer s = xact->squidPort;
if (!s.valid()) {
// it is possible the call or accept() was still queued when the port
was reconfigured
debugs(33, 2, "HTTPS accept failure: port reconfigured.");
@@ -4333,41 +4303,41 @@
ConnStateData::expectRequestBody(int64_t size)
{
bodyPipe = new BodyPipe(this);
if (size >= 0)
bodyPipe->setBodySize(size);
else
startDechunkingRequest();
return bodyPipe;
}
int64_t
ConnStateData::mayNeedToReadMoreBody() const
{
if (!bodyPipe)
return 0; // request without a body or read/produced all body bytes
if (!bodyPipe->bodySizeKnown())
return -1; // probably need to read more, but we cannot be sure
const int64_t needToProduce = bodyPipe->unproducedSize();
- const int64_t haveAvailable = static_cast<int64_t>(in.notYetUsed);
+ const int64_t haveAvailable = static_cast<int64_t>(in.buf.length());
if (needToProduce <= haveAvailable)
return 0; // we have read what we need (but are waiting for pipe space)
return needToProduce - haveAvailable;
}
void
ConnStateData::stopReceiving(const char *error)
{
debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " <<
error <<
"; old sending error: " <<
(stoppedSending() ? stoppedSending_ : "none"));
if (const char *oldError = stoppedReceiving()) {
debugs(33, 3, HERE << "already stopped receiving: " << oldError);
return; // nothing has changed as far as this connection is concerned
}
stoppedReceiving_ = error;
@@ -4403,54 +4373,47 @@
{
debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
if (bodyPipe != NULL) {
debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
BodyPipe::Pointer myPipe = bodyPipe;
stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
Must(!bodyPipe); // we rely on it being nil after we are done with body
if (withSuccess) {
Must(myPipe->bodySizeKnown());
ClientSocketContext::Pointer context = getCurrentContext();
if (context != NULL && context->http && context->http->request)
context->http->request->setContentLength(myPipe->bodySize());
}
}
delete in.bodyParser;
in.bodyParser = NULL;
}
-char *
-ConnStateData::In::addressToReadInto() const
-{
- return buf + notYetUsed;
-}
-
-ConnStateData::In::In() : bodyParser(NULL),
- buf (NULL), notYetUsed (0), allocatedSize (0)
+ConnStateData::In::In() :
+ bodyParser(NULL),
+ buf()
{}
ConnStateData::In::~In()
{
- if (allocatedSize)
- memFreeBuf(allocatedSize, buf);
delete bodyParser; // TODO: pool
}
void
ConnStateData::sendControlMsg(HttpControlMsg msg)
{
if (!isOpen()) {
debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
return;
}
ClientSocketContext::Pointer context = getCurrentContext();
if (context != NULL) {
context->writeControlMsg(msg); // will call msg.cbSuccess
return;
}
debugs(33, 3, HERE << " closing due to missing context for 1xx");
clientConnection->close();
}
=== modified file 'src/client_side.h'
--- src/client_side.h 2014-01-05 19:49:23 +0000
+++ src/client_side.h 2014-03-14 07:43:27 +0000
@@ -172,69 +172,65 @@
*
* Multiple requests (up to pipeline_prefetch) can be pipelined. This object
is responsible for managing
* which one is currently being fulfilled and what happens to the queue if the
current one
* causes the client connection to be closed early.
*
* Act as a manager for the connection and passes data in buffer to the
current parser.
* the parser has ambiguous scope at present due to being made from global
functions
* I believe this object uses the parser to identify boundaries and kick off
the
* actual HTTP request handling objects (ClientSocketContext,
ClientHttpRequest, HttpRequest)
*
* If the above can be confirmed accurate we can call this object
PipelineManager or similar
*/
class ConnStateData : public BodyProducer, public HttpControlMsgSink
{
public:
explicit ConnStateData(const MasterXaction::Pointer &xact);
~ConnStateData();
void readSomeData();
- int getAvailableBufferLength() const;
bool areAllContextsForThisConnection() const;
void freeAllContexts();
void notifyAllContexts(const int xerrno); ///< tell everybody about the err
/// Traffic parsing
bool clientParseRequests();
void readNextRequest();
- bool maybeMakeSpaceAvailable();
ClientSocketContext::Pointer getCurrentContext() const;
void addContextToQueue(ClientSocketContext * context);
int getConcurrentRequestCount() const;
bool isOpen() const;
void checkHeaderLimits();
// HttpControlMsgSink API
virtual void sendControlMsg(HttpControlMsg msg);
// Client TCP connection details from comm layer.
Comm::ConnectionPointer clientConnection;
struct In {
In();
~In();
- char *addressToReadInto() const;
+ bool maybeMakeSpaceAvailable();
ChunkedCodingParser *bodyParser; ///< parses chunked request body
- char *buf;
- size_t notYetUsed;
- size_t allocatedSize;
+ SBuf buf;
} in;
/** number of body bytes we need to comm_read for the "current" request
*
* \retval 0 We do not need to read any [more] body bytes
* \retval negative May need more but do not know how many; could be zero!
* \retval positive Need to read exactly that many more body bytes
*/
int64_t mayNeedToReadMoreBody() const;
#if USE_AUTH
/**
* Fetch the user details for connection based authentication
* NOTE: this is ONLY connection based because NTLM and Negotiate is
against HTTP spec.
*/
const Auth::UserRequest::Pointer &getAuth() const { return auth_; }
/**
* Set the user details for connection-based authentication to use from
now until connection closure.
*
@@ -276,41 +272,41 @@
bool transparent() const;
bool reading() const;
void stopReading(); ///< cancels comm_read if it is scheduled
/// true if we stopped receiving the request
const char *stoppedReceiving() const { return stoppedReceiving_; }
/// true if we stopped sending the response
const char *stoppedSending() const { return stoppedSending_; }
/// note request receiving error and close as soon as we write the response
void stopReceiving(const char *error);
/// note response sending error and close as soon as we read the request
void stopSending(const char *error);
void expectNoForwarding(); ///< cleans up virgin request [body] forwarding
state
BodyPipe::Pointer expectRequestBody(int64_t size);
virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer);
virtual void noteBodyConsumerAborted(BodyPipe::Pointer);
- bool handleReadData(char *buf, size_t size);
+ bool handleReadData(SBuf *buf);
bool handleRequestBodyData();
/**
* Correlate the current ConnStateData object with the pinning_fd socket
descriptor.
*/
void pinConnection(const Comm::ConnectionPointer &pinServerConn,
HttpRequest *request, CachePeer *peer, bool auth);
/**
* Decorrelate the ConnStateData object from its pinned CachePeer
*/
void unpinConnection();
/**
* Checks if there is pinning info if it is valid. It can close the server
side connection
* if pinned info is not valid.
\param request if it is not NULL also checks if the pinning info refers
to the request client side HttpRequest
\param CachePeer if it is not NULL also check if the CachePeer is
the pinning CachePeer
\return The details of the server side connection (may be closed
if failures were present).
*/
const Comm::ConnectionPointer validatePinnedConnection(HttpRequest
*request, const CachePeer *peer);
/**
* returts the pinned CachePeer if exists, NULL otherwise
=== modified file 'src/comm.cc'
--- src/comm.cc 2014-02-21 10:46:19 +0000
+++ src/comm.cc 2014-03-15 02:26:21 +0000
@@ -113,42 +113,53 @@
return fd >= 0 && fd_table && fd_table[fd].flags.open != 0;
}
/**
* Attempt a read
*
* If the read attempt succeeds or fails, call the callback.
* Else, wait for another IO notification.
*/
void
commHandleRead(int fd, void *data)
{
Comm::IoCallback *ccb = (Comm::IoCallback *) data;
assert(data == COMMIO_FD_READCB(fd));
assert(ccb->active());
/* Attempt a read */
++ statCounter.syscalls.sock.reads;
errno = 0;
int retval;
- retval = FD_READ_METHOD(fd, ccb->buf, ccb->size);
- debugs(5, 3, "comm_read_try: FD " << fd << ", size " << ccb->size << ",
retval " << retval << ", errno " << errno);
+ if (ccb->buf) {
+ retval = FD_READ_METHOD(fd, ccb->buf, ccb->size);
+ debugs(5, 3, "char FD " << fd << ", size " << ccb->size << ", retval "
<< retval << ", errno " << errno);
+ } else {
+ assert(ccb->buf2 != NULL);
+ SBuf::size_type sz = ccb->buf2->spaceSize();
+ char *buf = ccb->buf2->rawSpace(sz);
+ retval = FD_READ_METHOD(fd, buf, sz-1); // blocking synchronous read(2)
+ if (retval > 0) {
+ ccb->buf2->append(buf, retval);
+ }
+ debugs(5, 3, "SBuf FD " << fd << ", size " << sz << ", retval " <<
retval << ", errno " << errno);
+ }
if (retval < 0 && !ignoreErrno(errno)) {
debugs(5, 3, "comm_read_try: scheduling COMM_ERROR");
ccb->offset = 0;
ccb->finish(COMM_ERROR, errno);
return;
};
/* See if we read anything */
/* Note - read 0 == socket EOF, which is a valid read */
if (retval >= 0) {
fd_bytes(fd, retval, FD_READ);
ccb->offset = retval;
ccb->finish(COMM_OK, errno);
return;
}
/* Nope, register for some more IO */
Comm::SetSelect(fd, COMM_SELECT_READ, commHandleRead, data, 0);
}
@@ -166,40 +177,70 @@
assert(Comm::IsConnOpen(conn));
assert(!fd_table[conn->fd].closing());
Comm::IoCallback *ccb = COMMIO_FD_READCB(conn->fd);
// Make sure we are either not reading or just passively monitoring.
// Active/passive conflicts are OK and simply cancel passive monitoring.
if (ccb->active()) {
// if the assertion below fails, we have an active comm_read conflict
assert(fd_table[conn->fd].halfClosedReader != NULL);
commStopHalfClosedMonitor(conn->fd);
assert(!ccb->active());
}
ccb->conn = conn;
/* Queue the read */
ccb->setCallback(Comm::IOCB_READ, callback, (char *)buf, NULL, size);
Comm::SetSelect(conn->fd, COMM_SELECT_READ, commHandleRead, ccb, 0);
}
/**
+ * Queue a read. handler/handler_data are called when the read
+ * completes, on error, or on file descriptor close.
+ */
+void
+comm_read(const Comm::ConnectionPointer &conn, SBuf &buf, AsyncCall::Pointer
&callback)
+{
+ debugs(5, 5, "comm_read, queueing read for " << conn << "; asynCall " <<
callback);
+
+ /* Make sure we are open and not closing */
+ assert(Comm::IsConnOpen(conn));
+ assert(!fd_table[conn->fd].closing());
+ Comm::IoCallback *ccb = COMMIO_FD_READCB(conn->fd);
+
+ // Make sure we are either not reading or just passively monitoring.
+ // Active/passive conflicts are OK and simply cancel passive monitoring.
+ if (ccb->active()) {
+ // if the assertion below fails, we have an active comm_read conflict
+ assert(fd_table[conn->fd].halfClosedReader != NULL);
+ commStopHalfClosedMonitor(conn->fd);
+ assert(!ccb->active());
+ }
+ ccb->conn = conn;
+ ccb->buf2 = &buf;
+
+ /* Queue the read */
+ ccb->setCallback(Comm::IOCB_READ, callback, NULL, NULL, buf.spaceSize());
+ Comm::SetSelect(conn->fd, COMM_SELECT_READ, commHandleRead, ccb, 0);
+}
+
+/**
* Empty the read buffers
*
* This is a magical routine that empties the read buffers.
* Under some platforms (Linux) if a buffer has data in it before
* you call close(), the socket will hang and take quite a while
* to timeout.
*/
static void
comm_empty_os_read_buffers(int fd)
{
#if _SQUID_LINUX_
/* prevent those nasty RST packets */
char buf[SQUID_TCP_SO_RCVBUF];
if (fd_table[fd].flags.nonblocking) {
while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0) {};
}
#endif
}
=== modified file 'src/comm.h'
--- src/comm.h 2014-01-19 05:39:55 +0000
+++ src/comm.h 2014-03-15 02:29:55 +0000
@@ -62,42 +62,42 @@
/**
* Set or clear the timeout for some action on an active connection.
* API to replace commSetTimeout() when a Comm::ConnectionPointer is available.
*/
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int seconds,
AsyncCall::Pointer &callback);
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn);
int ignoreErrno(int);
void commCloseAllSockets(void);
void checkTimeouts(void);
//typedef void IOACB(int fd, int nfd, Comm::ConnectionPointer details,
comm_err_t flag, int xerrno, void *data);
void comm_add_close_handler(int fd, CLCB *, void *);
void comm_add_close_handler(int fd, AsyncCall::Pointer &);
void comm_remove_close_handler(int fd, CLCB *, void *);
void comm_remove_close_handler(int fd, AsyncCall::Pointer &);
int comm_has_pending_read_callback(int fd);
bool comm_monitors_read(int fd);
-//void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, IOCB
*handler, void *data);
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len,
AsyncCall::Pointer &callback);
+void comm_read(const Comm::ConnectionPointer &conn, SBuf &buf,
AsyncCall::Pointer &callback);
void comm_read_cancel(int fd, IOCB *callback, void *data);
void comm_read_cancel(int fd, AsyncCall::Pointer &callback);
int comm_udp_recvfrom(int fd, void *buf, size_t len, int flags, Ip::Address
&from);
int comm_udp_recv(int fd, void *buf, size_t len, int flags);
ssize_t comm_udp_send(int s, const void *buf, size_t len, int flags);
bool comm_has_incomplete_write(int);
/** The read channel has closed and the caller does not expect more data
* but needs to detect connection aborts. The current detection method uses
* 0-length reads: We read until the error occurs or the writer closes
* the connection. If there is a read error, we close the connection.
*/
void commStartHalfClosedMonitor(int fd);
bool commHasHalfClosedMonitor(int fd);
// XXX: remove these wrappers which minimize client_side.cc changes in a commit
inline void commMarkHalfClosed(int fd) { commStartHalfClosedMonitor(fd); }
inline bool commIsHalfClosed(int fd) { return commHasHalfClosedMonitor(fd); }
/* A comm engine that calls comm_select */
=== modified file 'src/comm/IoCallback.cc'
--- src/comm/IoCallback.cc 2012-08-14 11:53:07 +0000
+++ src/comm/IoCallback.cc 2014-03-13 12:28:22 +0000
@@ -72,62 +72,64 @@
#endif
SetSelect(conn->fd, COMM_SELECT_WRITE, Comm::HandleWrite, this, 0);
}
void
Comm::IoCallback::cancel(const char *reason)
{
if (!active())
return;
callback->cancel(reason);
callback = NULL;
reset();
}
void
Comm::IoCallback::reset()
{
conn = NULL;
+ buf2 = NULL; // we do not own this buffer.
if (freefunc) {
freefunc(buf);
buf = NULL;
freefunc = NULL;
}
xerrno = 0;
#if USE_DELAY_POOLS
quotaQueueReserv = 0;
#endif
}
// Schedule the callback call and clear the callback
void
Comm::IoCallback::finish(comm_err_t code, int xerrn)
{
debugs(5, 3, HERE << "called for " << conn << " (" << code << ", " <<
xerrno << ")");
assert(active());
/* free data */
- if (freefunc) {
+ if (freefunc && buf) {
freefunc(buf);
buf = NULL;
freefunc = NULL;
}
if (callback != NULL) {
typedef CommIoCbParams Params;
Params ¶ms = GetCommParams<Params>(callback);
if (conn != NULL) params.fd = conn->fd; // for legacy write handlers...
params.conn = conn;
+ params.buf2 = buf2;
params.buf = buf;
params.size = offset;
params.flag = code;
params.xerrno = xerrn;
ScheduleCallHere(callback);
callback = NULL;
}
/* Reset for next round. */
reset();
}
=== modified file 'src/comm/IoCallback.h'
--- src/comm/IoCallback.h 2012-09-21 14:57:30 +0000
+++ src/comm/IoCallback.h 2014-03-15 10:48:29 +0000
@@ -1,45 +1,55 @@
#ifndef _SQUID_COMM_IOCALLBACK_H
#define _SQUID_COMM_IOCALLBACK_H
#include "base/AsyncCall.h"
#include "comm/forward.h"
#include "comm_err_t.h"
#include "typedefs.h"
+class SBuf;
+
namespace Comm
{
/// Type of IO callbacks the Comm layer deals with.
typedef enum {
IOCB_NONE,
IOCB_READ,
IOCB_WRITE
} iocb_type;
/// Details about a particular Comm IO callback event.
class IoCallback
{
public:
iocb_type type;
Comm::ConnectionPointer conn;
AsyncCall::Pointer callback;
+
+ /// Buffer to store read(2) into when set.
+ // This is a pointer to the Jobs buffer rather than an SBuf using
+ // the same store since we cannot know when or how the Job will
+ // alter its SBuf while we are reading.
+ SBuf *buf2;
+
+ // Legacy c-string buffers used when buf2 is unset.
char *buf;
FREE *freefunc;
int size;
int offset;
comm_err_t errcode;
int xerrno;
#if USE_DELAY_POOLS
unsigned int quotaQueueReserv; ///< reservation ID from CommQuotaQueue
#endif
bool active() const { return callback != NULL; }
void setCallback(iocb_type type, AsyncCall::Pointer &cb, char *buf, FREE
*func, int sz);
/// called when fd needs to write but may need to wait in line for its
quota
void selectOrQueueWrite();
/// Actively cancel the given callback
void cancel(const char *reason);
/// finish the IO operation imediately and schedule the callback with the
current state.
=== modified file 'src/stat.cc'
--- src/stat.cc 2014-01-24 01:57:15 +0000
+++ src/stat.cc 2014-03-07 12:47:12 +0000
@@ -2006,42 +2006,42 @@
static void
statClientRequests(StoreEntry * s)
{
dlink_node *i;
ClientHttpRequest *http;
StoreEntry *e;
char buf[MAX_IPSTRLEN];
for (i = ClientActiveRequests.head; i; i = i->next) {
const char *p = NULL;
http = static_cast<ClientHttpRequest *>(i->data);
assert(http);
ConnStateData * conn = http->getConn();
storeAppendPrintf(s, "Connection: %p\n", conn);
if (conn != NULL) {
const int fd = conn->clientConnection->fd;
storeAppendPrintf(s, "\tFD %d, read %" PRId64 ", wrote %" PRId64
"\n", fd,
fd_table[fd].bytes_read,
fd_table[fd].bytes_written);
storeAppendPrintf(s, "\tFD desc: %s\n", fd_table[fd].desc);
- storeAppendPrintf(s, "\tin: buf %p, offset %ld, size %ld\n",
- conn->in.buf, (long int) conn->in.notYetUsed,
(long int) conn->in.allocatedSize);
+ storeAppendPrintf(s, "\tin: buf %p, used %ld, free %ld\n",
+ conn->in.buf.c_str(), (long int)
conn->in.buf.length(), (long int) conn->in.buf.spaceSize());
storeAppendPrintf(s, "\tremote: %s\n",
conn->clientConnection->remote.toUrl(buf,MAX_IPSTRLEN));
storeAppendPrintf(s, "\tlocal: %s\n",
conn->clientConnection->local.toUrl(buf,MAX_IPSTRLEN));
storeAppendPrintf(s, "\tnrequests: %d\n",
conn->nrequests);
}
storeAppendPrintf(s, "uri %s\n", http->uri);
storeAppendPrintf(s, "logType %s\n", LogTags_str[http->logType]);
storeAppendPrintf(s, "out.offset %ld, out.size %lu\n",
(long int) http->out.offset, (unsigned long int)
http->out.size);
storeAppendPrintf(s, "req_sz %ld\n", (long int) http->req_sz);
e = http->storeEntry();
storeAppendPrintf(s, "entry %p/%s\n", e, e ? e->getMD5Text() : "N/A");
storeAppendPrintf(s, "start %ld.%06d (%f seconds ago)\n",
(long int) http->al->cache.start_time.tv_sec,
(int) http->al->cache.start_time.tv_usec,
tvSubDsec(http->al->cache.start_time, current_time));
#if USE_AUTH
=== modified file 'src/tests/stub_client_side.cc'
--- src/tests/stub_client_side.cc 2014-01-05 19:49:23 +0000
+++ src/tests/stub_client_side.cc 2014-03-15 00:05:44 +0000
@@ -12,73 +12,72 @@
void ClientSocketContext::pullData() STUB
int64_t ClientSocketContext::getNextRangeOffset() const STUB_RETVAL(0)
bool ClientSocketContext::canPackMoreRanges() const STUB_RETVAL(false)
clientStream_status_t ClientSocketContext::socketState()
STUB_RETVAL(STREAM_NONE)
void ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData)
STUB
void ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer
bodyData) STUB
size_t ClientSocketContext::lengthToSend(Range<int64_t> const &available)
STUB_RETVAL(0)
void ClientSocketContext::noteSentBodyBytes(size_t) STUB
void ClientSocketContext::buildRangeHeader(HttpReply * rep) STUB
clientStreamNode * ClientSocketContext::getTail() const STUB_RETVAL(NULL)
clientStreamNode * ClientSocketContext::getClientReplyContext() const
STUB_RETVAL(NULL)
void ClientSocketContext::connIsFinished() STUB
void ClientSocketContext::removeFromConnectionList(ConnStateData * conn) STUB
void ClientSocketContext::deferRecipientForLater(clientStreamNode * node,
HttpReply * rep, StoreIOBuffer receivedData) STUB
bool ClientSocketContext::multipartRangeRequest() const STUB_RETVAL(false)
void ClientSocketContext::registerWithConn() STUB
void ClientSocketContext::noteIoError(const int xerrno) STUB
void ClientSocketContext::writeControlMsg(HttpControlMsg &msg) STUB
void ConnStateData::readSomeData() STUB
-int ConnStateData::getAvailableBufferLength() const STUB_RETVAL(0)
bool ConnStateData::areAllContextsForThisConnection() const STUB_RETVAL(false)
void ConnStateData::freeAllContexts() STUB
void ConnStateData::notifyAllContexts(const int xerrno) STUB
bool ConnStateData::clientParseRequests() STUB_RETVAL(false)
void ConnStateData::readNextRequest() STUB
-bool ConnStateData::maybeMakeSpaceAvailable() STUB_RETVAL(false)
void ConnStateData::addContextToQueue(ClientSocketContext * context) STUB
int ConnStateData::getConcurrentRequestCount() const STUB_RETVAL(0)
bool ConnStateData::isOpen() const STUB_RETVAL(false)
void ConnStateData::checkHeaderLimits() STUB
void ConnStateData::sendControlMsg(HttpControlMsg msg) STUB
-char *ConnStateData::In::addressToReadInto() const STUB_RETVAL(NULL)
int64_t ConnStateData::mayNeedToReadMoreBody() const STUB_RETVAL(0)
#if USE_AUTH
void ConnStateData::setAuth(const Auth::UserRequest::Pointer &aur, const char
*cause) STUB
#endif
bool ConnStateData::transparent() const STUB_RETVAL(false)
bool ConnStateData::reading() const STUB_RETVAL(false)
void ConnStateData::stopReading() STUB
void ConnStateData::stopReceiving(const char *error) STUB
void ConnStateData::stopSending(const char *error) STUB
void ConnStateData::expectNoForwarding() STUB
void ConnStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer) STUB
void ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer) STUB
-bool ConnStateData::handleReadData(char *buf, size_t size) STUB_RETVAL(false)
+bool ConnStateData::handleReadData(SBuf *buf) STUB_RETVAL(false)
bool ConnStateData::handleRequestBodyData() STUB_RETVAL(false)
void ConnStateData::pinConnection(const Comm::ConnectionPointer
&pinServerConn, HttpRequest *request, CachePeer *peer, bool auth) STUB
void ConnStateData::unpinConnection() STUB
const Comm::ConnectionPointer
ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer
*peer) STUB_RETVAL(NULL)
void ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
STUB
void ConnStateData::clientReadRequest(const CommIoCbParams &io) STUB
void ConnStateData::connStateClosed(const CommCloseCbParams &io) STUB
void ConnStateData::requestTimeout(const CommTimeoutCbParams ¶ms) STUB
void ConnStateData::swanSong() STUB
void ConnStateData::quitAfterError(HttpRequest *request) STUB
#if USE_SSL
void ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection) STUB
void ConnStateData::getSslContextStart() STUB
void ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew) STUB
void ConnStateData::sslCrtdHandleReplyWrapper(void *data, const HelperReply
&reply) STUB
void ConnStateData::sslCrtdHandleReply(const HelperReply &reply) STUB
void ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode
bumpServerMode) STUB
void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties
&certProperties) STUB
bool ConnStateData::serveDelayedError(ClientSocketContext *context)
STUB_RETVAL(false)
#endif
+bool ConnStateData::In::maybeMakeSpaceAvailable() STUB_RETVAL(false)
+
void setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl) STUB
const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char
*end) STUB_RETVAL(NULL)
int varyEvaluateMatch(StoreEntry * entry, HttpRequest * req) STUB_RETVAL(0)
void clientOpenListenSockets(void) STUB
void clientHttpConnectionsClose(void) STUB
void httpRequestFree(void *) STUB