=== 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 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 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(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(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(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(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 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(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(in.notYetUsed); + const int64_t haveAvailable = static_cast(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(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(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 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