ConnStateData::buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload)
{
    ClientHttpRequest *http = new ClientHttpRequest(this);
    Http::Stream *stream = new Http::Stream(clientConnection, http);

    StoreIOBuffer tempBuffer;
    tempBuffer.data = stream->reqbuf;
    tempBuffer.length = HTTP_REQBUF_SZ;

    ClientStreamData newServer = new clientReplyContext(http);
    ClientStreamData newClient = stream;
    clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
                     clientReplyStatus, newServer, clientSocketRecipient,
                     clientSocketDetach, newClient, tempBuffer);

    stream->flags.parsed_ok = 1; // Do we need it?
    stream->mayUseConnection(true);

    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
                                     CommTimeoutCbPtrFun(clientLifetimeTimeout, stream->http));
    commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);

    stream->registerWithConn();

    MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initClient);
    mx->tcpClient = clientConnection;
    // Setup Http::Request object. Maybe should be replaced by a call to (modified)
    // clientProcessRequest
    HttpRequest::Pointer request = new HttpRequest(mx);
    AnyP::ProtocolType proto = (method == Http::METHOD_NONE) ? AnyP::PROTO_AUTHORITY_FORM : AnyP::PROTO_HTTP;
    request->url.setScheme(proto, nullptr);
    request->method = method;
    request->url.host(useHost.c_str());
    request->url.port(usePort);

    http->uri = SBufToCstring(request->effectiveRequestUri());
    http->initRequest(request.getRaw());

    request->manager(this, http->al);

    if (proto == AnyP::PROTO_HTTP)
        request->header.putStr(Http::HOST, useHost.c_str());

    request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
#if USE_AUTH
    if (getAuth())
        request->auth_user_request = getAuth();
#endif

    inBuf = payload;
    flags.readMore = false;

    return http;
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static CTCB clientLifetimeTimeout;

static int clientIsContentLengthValid(HttpRequest * r);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static bool clientPingHasFinished(ping_data const *aPing);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


readDelayed(void *context, CommRead const &)
{
    HttpStateData *state = static_cast<HttpStateData*>(context);
    state->flags.do_next_read = true;
    state->maybeReadVirginBody();
}

Improve Transfer-Encoding handling (#702)

Reject messages containing Transfer-Encoding header with coding other
than chunked or identity. Squid does not support other codings.

For simplicity and security sake, also reject messages where
Transfer-Encoding contains unnecessary complex values that are
technically equivalent to "chunked" or "identity" (e.g., ",,chunked" or
"identity, chunked").

RFC 7230 formally deprecated and removed identity coding, but it is
still used by some agents.

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static const char *const crlf = "\r\n";

static void httpMaybeRemovePublic(StoreEntry *, Http::StatusCode);

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


ConnStateData::unpinConnection(const bool andClose)
{
    debugs(33, 3, HERE << pinning.serverConnection);

    if (pinning.peer)
        cbdataReferenceDone(pinning.peer);

    if (Comm::IsConnOpen(pinning.serverConnection)) {
        if (pinning.closeHandler != NULL) {
            comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
            pinning.closeHandler = NULL;
        }

        stopPinnedConnectionMonitoring();

        // close the server side socket if requested
        if (andClose)
            pinning.serverConnection->close();
        pinning.serverConnection = NULL;
    }

    safe_free(pinning.host);

    pinning.zeroReply = false;

    /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
     * connection has gone away */
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static bool clientPingHasFinished(ping_data const *aPing);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


HttpStateData::processReplyHeader()
{
    /** Creates a blank header. If this routine is made incremental, this will not do */

    /* NP: all exit points to this function MUST call ctx_exit(ctx) */
    Ctx ctx = ctx_enter(entry->mem_obj->urlXXX());

    debugs(11, 3, "processReplyHeader: key '" << entry->getMD5Text() << "'");

    assert(!flags.headers_parsed);

    if (!inBuf.length()) {
        ctx_exit(ctx);
        return;
    }

    /* Attempt to parse the first line; this will define where the protocol, status, reason-phrase and header begin */
    {
        if (hp == NULL)
            hp = new Http1::ResponseParser;

        bool parsedOk = hp->parse(inBuf);

        // sync the buffers after parsing.
        inBuf = hp->remaining();

        if (hp->needsMoreData()) {
            if (eof) { // no more data coming
                /* Bug 2879: Replies may terminate with \r\n then EOF instead of \r\n\r\n.
                 * We also may receive truncated responses.
                 * Ensure here that we have at minimum two \r\n when EOF is seen.
                 */
                inBuf.append("\r\n\r\n", 4);
                // retry the parse
                parsedOk = hp->parse(inBuf);
                // sync the buffers after parsing.
                inBuf = hp->remaining();
            } else {
                debugs(33, 5, "Incomplete response, waiting for end of response headers");
                ctx_exit(ctx);
                return;
            }
        }

        if (!parsedOk) {
            // unrecoverable parsing error
            // TODO: Use Raw! XXX: inBuf no longer has the [beginning of the] malformed header.
            debugs(11, 3, "Non-HTTP-compliant header:\n---------\n" << inBuf << "\n----------");
            flags.headers_parsed = true;
            HttpReply *newrep = new HttpReply;
            newrep->sline.set(Http::ProtocolVersion(), hp->parseStatusCode);
            setVirginReply(newrep);
            ctx_exit(ctx);
            return;
        }
    }

    /* We know the whole response is in parser now */
    debugs(11, 2, "HTTP Server " << serverConnection);
    debugs(11, 2, "HTTP Server RESPONSE:\n---------\n" <<
           hp->messageProtocol() << " " << hp->messageStatus() << " " << hp->reasonPhrase() << "\n" <<
           hp->mimeHeader() <<
           "----------");

    // reset payload tracking to begin after message headers
    payloadSeen = inBuf.length();

    HttpReply *newrep = new HttpReply;
    // XXX: RFC 7230 indicates we MAY ignore the reason phrase,
    //      and use an empty string on unknown status.
    //      We do that now to avoid performance regression from using SBuf::c_str()
    newrep->sline.set(Http::ProtocolVersion(1,1), hp->messageStatus() /* , hp->reasonPhrase() */);
    newrep->sline.protocol = newrep->sline.version.protocol = hp->messageProtocol().protocol;
    newrep->sline.version.major = hp->messageProtocol().major;
    newrep->sline.version.minor = hp->messageProtocol().minor;

    // parse headers
    if (!newrep->parseHeader(*hp)) {
        // XXX: when Http::ProtocolVersion is a function, remove this hack. just set with messageProtocol()
        newrep->sline.set(Http::ProtocolVersion(), Http::scInvalidHeader);
        newrep->sline.version.protocol = hp->messageProtocol().protocol;
        newrep->sline.version.major = hp->messageProtocol().major;
        newrep->sline.version.minor = hp->messageProtocol().minor;
        debugs(11, 2, "error parsing response headers mime block");
    }

    // done with Parser, now process using the HttpReply
    hp = NULL;

    newrep->sources |= request->url.getScheme() == AnyP::PROTO_HTTPS ? HttpMsg::srcHttps : HttpMsg::srcHttp;

    newrep->removeStaleWarnings();

    if (newrep->sline.protocol == AnyP::PROTO_HTTP && newrep->sline.status() >= 100 && newrep->sline.status() < 200) {
        handle1xx(newrep);
        ctx_exit(ctx);
        return;
    }

    flags.chunked = false;
    if (newrep->sline.protocol == AnyP::PROTO_HTTP && newrep->header.chunked()) {
        flags.chunked = true;
        httpChunkDecoder = new Http1::TeChunkedParser;
    }

    if (!peerSupportsConnectionPinning())
        request->flags.connectionAuthDisabled = true;

    HttpReply *vrep = setVirginReply(newrep);
    flags.headers_parsed = true;

    keepaliveAccounting(vrep);

    checkDateSkew(vrep);

    processSurrogateControl (vrep);

    request->hier.peer_reply_status = newrep->sline.status();

    ctx_exit(ctx);
}

#define SQUID_ENTER_THROWING_CODE() try {

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static void httpMaybeRemovePublic(StoreEntry *, Http::StatusCode);

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


ConnStateData::getSslContextStart()
{
    // If we are called, then CONNECT has succeeded. Finalize it.
    if (auto xact = pipeline.front()) {
        if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
            xact->finished();
        // cannot proceed with encryption if requests wait for plain responses
        Must(pipeline.empty());
    }
    /* careful: finished() above frees request, host, etc. */

    if (port->secure.generateHostCertificates) {
        Ssl::CertificateProperties certProperties;
        buildSslCertGenerationParams(certProperties);

        // Disable caching for bumpPeekAndSplice mode
        if (!(sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare))) {
            sslBumpCertKey.clear();
            Ssl::InRamCertificateDbKey(certProperties, sslBumpCertKey);
            assert(!sslBumpCertKey.isEmpty());

            Security::ContextPointer ctx(getTlsContextFromCache(sslBumpCertKey, certProperties));
            if (ctx) {
                getSslContextDone(ctx);
                return;
            }
        }

#if USE_SSL_CRTD
        try {
            debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
            Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
            request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
            request_message.composeRequest(certProperties);
            debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
            Ssl::Helper::Submit(request_message, sslCrtdHandleReplyWrapper, this);
            return;
        } catch (const std::exception &e) {
            debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
                   "request for " << certProperties.commonName <<
                   " certificate: " << e.what() << "; will now block to " <<
                   "generate that certificate.");
            // fall through to do blocking in-process generation.
        }
#endif // USE_SSL_CRTD

        debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
        if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
            doPeekAndSpliceStep();
            auto ssl = fd_table[clientConnection->fd].ssl.get();
            if (!Ssl::configureSSL(ssl, certProperties, *port))
                debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");

            Security::ContextPointer ctx(Security::GetFrom(fd_table[clientConnection->fd].ssl));
            Ssl::configureUnconfiguredSslContext(ctx, certProperties.signAlgorithm, *port);
        } else {
            Security::ContextPointer dynCtx(Ssl::GenerateSslContext(certProperties, port->secure, (signAlgorithm == Ssl::algSignTrusted)));
            if (dynCtx && !sslBumpCertKey.isEmpty())
                storeTlsContextToCache(sslBumpCertKey, dynCtx);
            getSslContextDone(dynCtx);
        }
        return;
    }

    Security::ContextPointer nil;
    getSslContextDone(nil);
}


HttpStateData::dataConnection() const
{
    return serverConnection;
}

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static const char *const crlf = "\r\n";

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


ConnStateData::whenClientIpKnown()
{
    if (Config.onoff.log_fqdn)
        fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);

#if USE_IDENT
    if (Ident::TheConfig.identLookup) {
        ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
        identChecklist.src_addr = clientConnection->remote;
        identChecklist.my_addr = clientConnection->local;
        if (identChecklist.fastCheck().allowed())
            Ident::Start(clientConnection, clientIdentDone, this);
    }
#endif

    clientdbEstablished(clientConnection->remote, 1);

#if USE_DELAY_POOLS
    fd_table[clientConnection->fd].clientInfo = NULL;

    if (!Config.onoff.client_db)
        return; // client delay pools require client_db

    ClientDelayPools& pools(Config.ClientDelay.pools);
    if (pools.size()) {
        ACLFilledChecklist ch(NULL, NULL, NULL);

        // TODO: we check early to limit error response bandwith but we
        // should recheck when we can honor delay_pool_uses_indirect
        // TODO: we should also pass the port details for myportname here.
        ch.src_addr = clientConnection->remote;
        ch.my_addr = clientConnection->local;

        for (unsigned int pool = 0; pool < pools.size(); ++pool) {

            /* pools require explicit 'allow' to assign a client into them */
            if (pools[pool].access) {
                ch.changeAcl(pools[pool].access);
                allow_t answer = ch.fastCheck();
                if (answer.allowed()) {

                    /*  request client information from db after we did all checks
                        this will save hash lookup if client failed checks */
                    ClientInfo * cli = clientdbGetInfo(clientConnection->remote);
                    assert(cli);

                    /* put client info in FDE */
                    fd_table[clientConnection->fd].clientInfo = cli;

                    /* setup write limiter for this request */
                    const double burst = floor(0.5 +
                                               (pools[pool].highwatermark * Config.ClientDelay.initial)/100.0);
                    cli->setWriteLimiter(pools[pool].rate, burst, pools[pool].highwatermark);
                    break;
                } else {
                    debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
                }
            }
        }
    }
#endif

    // kids must extend to actually start doing something (e.g., reading)
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static IDCB clientIdentDone;

static int clientIsContentLengthValid(HttpRequest * r);

static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static bool clientPingHasFinished(ping_data const *aPing);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


HttpStateData::proceedAfter1xx()
{
    Must(flags.handling1xx);
    debugs(11, 2, "continuing with " << payloadSeen << " bytes in buffer after 1xx");
    CallJobHere(11, 3, this, HttpStateData, HttpStateData::processReply);
}

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }


HttpStateData::readReply(const CommIoCbParams &io)
{
    Must(!flags.do_next_read); // XXX: should have been set false by mayReadVirginBody()
    flags.do_next_read = false;

    debugs(11, 5, io.conn);

    // Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us
    if (io.flag == Comm::ERR_CLOSING) {
        debugs(11, 3, "http socket closing");
        return;
    }

    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
        abortTransaction("store entry aborted while reading reply");
        return;
    }

    Must(Comm::IsConnOpen(serverConnection));
    Must(io.conn->fd == serverConnection->fd);

    /*
     * Don't reset the timeout value here. The value should be
     * counting Config.Timeout.request and applies to the request
     * as a whole, not individual read() calls.
     * Plus, it breaks our lame *HalfClosed() detection
     */

    Must(maybeMakeSpaceAvailable(true));
    CommIoCbParams rd(this); // will be expanded with ReadNow results
    rd.conn = io.conn;
    rd.size = entry->bytesWanted(Range<size_t>(0, inBuf.spaceSize()));

    if (rd.size <= 0) {
        assert(entry->mem_obj);
        AsyncCall::Pointer nilCall;
        entry->mem_obj->delayRead(DeferredRead(readDelayed, this, CommRead(io.conn, NULL, 0, nilCall)));
        return;
    }

    switch (Comm::ReadNow(rd, inBuf)) {
    case Comm::INPROGRESS:
        if (inBuf.isEmpty())
            debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno));
        flags.do_next_read = true;
        maybeReadVirginBody();
        return;

    case Comm::OK:
    {
        payloadSeen += rd.size;
#if USE_DELAY_POOLS
        DelayId delayId = entry->mem_obj->mostBytesAllowed();
        delayId.bytesIn(rd.size);
#endif

        statCounter.server.all.kbytes_in += rd.size;
        statCounter.server.http.kbytes_in += rd.size;
        ++ IOStats.Http.reads;

        int bin = 0;
        for (int clen = rd.size - 1; clen; ++bin)
            clen >>= 1;

        ++ IOStats.Http.read_hist[bin];

        request->hier.notePeerRead();
    }

        /* Continue to process previously read data */
    break;

    case Comm::ENDFILE: // close detected by 0-byte read
        eof = 1;
        flags.do_next_read = false;

        /* Continue to process previously read data */
        break;

    // case Comm::COMM_ERROR:
    default: // no other flags should ever occur
        debugs(11, 2, io.conn << ": read failure: " << xstrerr(rd.xerrno));
        ErrorState *err = new ErrorState(ERR_READ_ERROR, Http::scBadGateway, fwd->request);
        err->xerrno = rd.xerrno;
        fwd->fail(err);
        flags.do_next_read = false;
        closeServer();
        mustStop("HttpStateData::readReply");
        return;
    }

    /* Process next response from buffer */
    processReply();
}


HttpStateData::processReply()
{

    if (flags.handling1xx) { // we came back after handling a 1xx response
        debugs(11, 5, HERE << "done with 1xx handling");
        flags.handling1xx = false;
        Must(!flags.headers_parsed);
    }

    if (!flags.headers_parsed) { // have not parsed headers yet?
        PROF_start(HttpStateData_processReplyHeader);
        processReplyHeader();
        PROF_stop(HttpStateData_processReplyHeader);

        if (!continueAfterParsingHeader()) // parsing error or need more data
            return; // TODO: send errors to ICAP

        adaptOrFinalizeReply(); // may write to, abort, or "close" the entry
    }

    // kick more reads if needed and/or process the response body, if any
    PROF_start(HttpStateData_processReplyBody);
    processReplyBody(); // may call serverComplete()
    PROF_stop(HttpStateData_processReplyBody);
}


bool unsupportedTe() const { return teUnsupported_; }

static HttpHeaderEntry *parse(const char *field_start, const char *field_end, const http_hdr_owner_type msgType);

HttpHeaderEntry *clone() const;

const;
    int64_t getInt64() const;

    Http::HdrType id;

explicit HttpHeader(const http_hdr_owner_type owner);

void append(const HttpHeader * src);

bool update(HttpHeader const *fresh);

int parse(const char *header_start, size_t len);

void packInto(Packable * p, bool mask_sensitive_info=false) const;

HttpHeaderEntry *getEntry(HttpHeaderPos * pos) const;

int delByName(const char *name);

bool hasNamed(const SBuf &s, String *value = 0) const;

bool hasNamed(const char *name, int namelen, String *value = 0) const;

void addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from = 0);

void putStr(Http::HdrType id, const char *str);

void putAuth(const char *auth_scheme, const char *realm);

void putCc(const HttpHdrCc * cc);

void putContRange(const HttpHdrContRange * cr);

void putRange(const HttpHdrRange * range);

void putWarning(const int code, const char *const text);

void putExt(const char *name, const char *value);

const char *getStr(Http::HdrType id) const;

const char *getLastStr(Http::HdrType id) const;

HttpHdrCc *getCc() const;

HttpHdrRange *getRange() const;

HttpHdrSc *getSc() const;

HttpHdrContRange *getContRange() const;

bool unsupportedTe() const { return teUnsupported_;

private:
    HttpHeaderEntry *findLastEntry(Http::HdrType id) const;

bool conflictingContentLength_;

bool teUnsupported_ = false;

int httpHeaderParseQuotedString(const char *start, const int len, String *val);

SBuf httpHeaderQuoteString(const char *raw);


HttpStateData::handleMoreRequestBodyAvailable()
{
    if (eof || !Comm::IsConnOpen(serverConnection)) {
        // XXX: we should check this condition in other callbacks then!
        // TODO: Check whether this can actually happen: We should unsubscribe
        // as a body consumer when the above condition(s) are detected.
        debugs(11, DBG_IMPORTANT, HERE << "Transaction aborted while reading HTTP body");
        return;
    }

    assert(requestBodySource != NULL);

    if (requestBodySource->buf().hasContent()) {
        // XXX: why does not this trigger a debug message on every request?

        if (flags.headers_parsed && !flags.abuse_detected) {
            flags.abuse_detected = true;
            debugs(11, DBG_IMPORTANT, "http handleMoreRequestBodyAvailable: Likely proxy abuse detected '" << request->client_addr << "' -> '" << entry->url() << "'" );

            if (virginReply()->sline.status() == Http::scInvalidHeader) {
                closeServer();
                mustStop("HttpStateData::handleMoreRequestBodyAvailable");
                return;
            }
        }
    }

    HttpStateData::handleMoreRequestBodyAvailable();
}

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static void httpMaybeRemovePublic(StoreEntry *, Http::StatusCode);

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


HttpStateData::statusIfComplete() const
{
    const HttpReply *rep = virginReply();
    /** \par
     * If the reply wants to close the connection, it takes precedence */

    if (httpHeaderHasConnDir(&rep->header, "close"))
        return COMPLETE_NONPERSISTENT_MSG;

    /** \par
     * If we didn't send a keep-alive request header, then this
     * can not be a persistent connection.
     */
    if (!flags.keepalive)
        return COMPLETE_NONPERSISTENT_MSG;

    /** \par
     * If we haven't sent the whole request then this can not be a persistent
     * connection.
     */
    if (!flags.request_sent) {
        debugs(11, 2, "Request not yet fully sent " << request->method << ' ' << entry->url());
        return COMPLETE_NONPERSISTENT_MSG;
    }

    /** \par
     * What does the reply have to say about keep-alive?
     */
    /**
     \bug XXX BUG?
     * If the origin server (HTTP/1.0) does not send a keep-alive
     * header, but keeps the connection open anyway, what happens?
     * We'll return here and http.c waits for an EOF before changing
     * store_status to STORE_OK.   Combine this with ENTRY_FWD_HDR_WAIT
     * and an error status code, and we might have to wait until
     * the server times out the socket.
     */
    if (!rep->keep_alive)
        return COMPLETE_NONPERSISTENT_MSG;

    return COMPLETE_PERSISTENT_MSG;
}


void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
{
    ConnStateData *connState = (ConnStateData *) data;

    // if the connection is closed or closing, just return.
    if (!connState->isOpen())
        return;

    debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
    assert(connState->serverBump());
    Ssl::BumpMode bumpAction;
    if (answer.allowed()) {
        bumpAction = (Ssl::BumpMode)answer.kind;
    } else
        bumpAction = Ssl::bumpSplice;

    connState->serverBump()->act.step2 = bumpAction;
    connState->sslBumpMode = bumpAction;
    Http::StreamPointer context = connState->pipeline.front();
    if (ClientHttpRequest *http = (context ? context->http : nullptr))
        http->al->ssl.bumpMode = bumpAction;

    if (bumpAction == Ssl::bumpTerminate) {
        connState->clientConnection->close();
    } else if (bumpAction != Ssl::bumpSplice) {
        connState->startPeekAndSplice();
    } else if (!connState->splice())
        connState->clientConnection->close();
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static void clientUpdateHierCounters(HierarchyLogEntry *);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);


clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
{
    ConnStateData * conn = http->getConn();
    ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
            cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str);
    ch->al = http->al;
    ch->syncAle(http->request, http->log_uri);
    /*
     * hack for ident ACL. It needs to get full addresses, and a place to store
     * the ident result on persistent connections...
     */
    /* connection oriented auth also needs these two lines for it's operation. */
    return ch;
}


HttpStateData::reusableReply(HttpStateData::ReuseDecision &decision)
{
    HttpReply const *rep = finalReply();
    HttpHeader const *hdr = &rep->header;
    const char *v;
#if USE_HTTP_VIOLATIONS

    const RefreshPattern *R = NULL;

    /* This strange looking define first looks up the refresh pattern
     * and then checks if the specified flag is set. The main purpose
     * of this is to simplify the refresh pattern lookup and USE_HTTP_VIOLATIONS
     * condition
     */
#define REFRESH_OVERRIDE(flag) \
    ((R = (R ? R : refreshLimits(entry->mem_obj->storeId()))) , \
    (R && R->flags.flag))
#else
#define REFRESH_OVERRIDE(flag) 0
#endif

    if (EBIT_TEST(entry->flags, RELEASE_REQUEST))
        return decision.make(ReuseDecision::doNotCacheButShare, "the entry has been released");

    // RFC 7234 section 4: a cache MUST use the most recent response
    // (as determined by the Date header field)
    // TODO: whether such responses could be shareable?
    if (sawDateGoBack)
        return decision.make(ReuseDecision::reuseNot, "the response has an older date header");

    // Check for Surrogate/1.0 protocol conditions
    // NP: reverse-proxy traffic our parent server has instructed us never to cache
    if (surrogateNoStore)
        return decision.make(ReuseDecision::reuseNot, "Surrogate-Control:no-store");

    // RFC 2616: HTTP/1.1 Cache-Control conditions
    if (!ignoreCacheControl) {
        // XXX: check to see if the request headers alone were enough to prevent caching earlier
        // (ie no-store request header) no need to check those all again here if so.
        // for now we are not reliably doing that so we waste CPU re-checking request CC

        // RFC 2616 section 14.9.2 - MUST NOT cache any response with request CC:no-store
        if (request && request->cache_control && request->cache_control->hasNoStore() &&
                !REFRESH_OVERRIDE(ignore_no_store))
            return decision.make(ReuseDecision::reuseNot,
                                 "client request Cache-Control:no-store");

        // NP: request CC:no-cache only means cache READ is forbidden. STORE is permitted.
        if (rep->cache_control && rep->cache_control->hasNoCacheWithParameters()) {
            /* TODO: we are allowed to cache when no-cache= has parameters.
             * Provided we strip away any of the listed headers unless they are revalidated
             * successfully (ie, must revalidate AND these headers are prohibited on stale replies).
             * That is a bit tricky for squid right now so we avoid caching entirely.
             */
            return decision.make(ReuseDecision::reuseNot,
                                 "server reply Cache-Control:no-cache has parameters");
        }

        // NP: request CC:private is undefined. We ignore.
        // NP: other request CC flags are limiters on HIT/MISS. We don't care about here.

        // RFC 2616 section 14.9.2 - MUST NOT cache any response with CC:no-store
        if (rep->cache_control && rep->cache_control->hasNoStore() &&
                !REFRESH_OVERRIDE(ignore_no_store))
            return decision.make(ReuseDecision::reuseNot,
                                 "server reply Cache-Control:no-store");

        // RFC 2616 section 14.9.1 - MUST NOT cache any response with CC:private in a shared cache like Squid.
        // CC:private overrides CC:public when both are present in a response.
        // TODO: add a shared/private cache configuration possibility.
        if (rep->cache_control &&
                rep->cache_control->hasPrivate() &&
                !REFRESH_OVERRIDE(ignore_private)) {
            /* TODO: we are allowed to cache when private= has parameters.
             * Provided we strip away any of the listed headers unless they are revalidated
             * successfully (ie, must revalidate AND these headers are prohibited on stale replies).
             * That is a bit tricky for squid right now so we avoid caching entirely.
             */
            return decision.make(ReuseDecision::reuseNot,
                                 "server reply Cache-Control:private");
        }
    }

    // RFC 2068, sec 14.9.4 - MUST NOT cache any response with Authentication UNLESS certain CC controls are present
    // allow HTTP violations to IGNORE those controls (ie re-block caching Auth)
    if (request && (request->flags.auth || request->flags.authSent)) {
        if (!rep->cache_control)
            return decision.make(ReuseDecision::reuseNot,
                                 "authenticated and server reply missing Cache-Control");

        if (ignoreCacheControl)
            return decision.make(ReuseDecision::reuseNot,
                                 "authenticated and ignoring Cache-Control");

        bool mayStore = false;
        // HTTPbis pt6 section 3.2: a response CC:public is present
        if (rep->cache_control->hasPublic()) {
            debugs(22, 3, HERE << "Authenticated but server reply Cache-Control:public");
            mayStore = true;

            // HTTPbis pt6 section 3.2: a response CC:must-revalidate is present
        } else if (rep->cache_control->hasMustRevalidate()) {
            debugs(22, 3, HERE << "Authenticated but server reply Cache-Control:must-revalidate");
            mayStore = true;

#if USE_HTTP_VIOLATIONS
            // NP: given the must-revalidate exception we should also be able to exempt no-cache.
            // HTTPbis WG verdict on this is that it is omitted from the spec due to being 'unexpected' by
            // some. The caching+revalidate is not exactly unsafe though with Squids interpretation of no-cache
            // (without parameters) as equivalent to must-revalidate in the reply.
        } else if (rep->cache_control->hasNoCacheWithoutParameters()) {
            debugs(22, 3, HERE << "Authenticated but server reply Cache-Control:no-cache (equivalent to must-revalidate)");
            mayStore = true;
#endif

            // HTTPbis pt6 section 3.2: a response CC:s-maxage is present
        } else if (rep->cache_control->hasSMaxAge()) {
            debugs(22, 3, HERE << "Authenticated but server reply Cache-Control:s-maxage");
            mayStore = true;
        }

        if (!mayStore)
            return decision.make(ReuseDecision::reuseNot, "authenticated transaction");

        // NP: response CC:no-cache is equivalent to CC:must-revalidate,max-age=0. We MAY cache, and do so.
        // NP: other request CC flags are limiters on HIT/MISS/REFRESH. We don't care about here.
    }

    /* HACK: The "multipart/x-mixed-replace" content type is used for
     * continuous push replies.  These are generally dynamic and
     * probably should not be cachable
     */
    if ((v = hdr->getStr(Http::HdrType::CONTENT_TYPE)))
        if (!strncasecmp(v, "multipart/x-mixed-replace", 25))
            return decision.make(ReuseDecision::reuseNot, "Content-Type:multipart/x-mixed-replace");

    // TODO: if possible, provide more specific message for each status code
    static const char *shareableError = "shareable error status code";
    static const char *nonShareableError = "non-shareable error status code";
    ReuseDecision::Answers statusAnswer = ReuseDecision::reuseNot;
    const char *statusReason = nonShareableError;

    switch (rep->sline.status()) {

    /* There are several situations when a non-cacheable response may be
     * still shareable (e.g., among collapsed clients). We assume that these
     * are 3xx and 5xx responses, indicating server problems and some of
     * 4xx responses, common for all clients with a given cache key (e.g.,
     * 404 Not Found or 414 URI Too Long). On the other hand, we should not
     * share non-cacheable client-specific errors, such as 400 Bad Request
     * or 406 Not Acceptable.
     */

    /* Responses that are cacheable */

    case Http::scOkay:

    case Http::scNonAuthoritativeInformation:

    case Http::scMultipleChoices:

    case Http::scMovedPermanently:
    case Http::scPermanentRedirect:

    case Http::scGone:
        /*
         * Don't cache objects that need to be refreshed on next request,
         * unless we know how to refresh it.
         */

        if (refreshIsCachable(entry) || REFRESH_OVERRIDE(store_stale))
            decision.make(ReuseDecision::cachePositively, "refresh check returned cacheable");
        else
            decision.make(ReuseDecision::doNotCacheButShare, "refresh check returned non-cacheable");
        break;

    /* Responses that only are cacheable if the server says so */

    case Http::scFound:
    case Http::scTemporaryRedirect:
        if (rep->date <= 0)
            decision.make(ReuseDecision::doNotCacheButShare, "Date is missing/invalid");
        else if (rep->expires > rep->date)
            decision.make(ReuseDecision::cachePositively, "Expires > Date");
        else
            decision.make(ReuseDecision::doNotCacheButShare, "Expires <= Date");
        break;

    /* These responses can be negatively cached. Most can also be shared. */
    case Http::scNoContent:
    case Http::scUseProxy:
    case Http::scForbidden:
    case Http::scNotFound:
    case Http::scMethodNotAllowed:
    case Http::scUriTooLong:
    case Http::scInternalServerError:
    case Http::scNotImplemented:
    case Http::scBadGateway:
    case Http::scServiceUnavailable:
    case Http::scGatewayTimeout:
    case Http::scMisdirectedRequest:
        statusAnswer = ReuseDecision::doNotCacheButShare;
        statusReason = shareableError;
    // fall through to the actual decision making below

    case Http::scBadRequest: // no sharing; perhaps the server did not like something specific to this request
#if USE_HTTP_VIOLATIONS
        if (Config.negativeTtl > 0)
            decision.make(ReuseDecision::cacheNegatively, "Config.negativeTtl > 0");
        else
#endif
            decision.make(statusAnswer, statusReason);
        break;

    /* these responses can never be cached, some
       of them can be shared though */
    case Http::scSeeOther:
    case Http::scNotModified:
    case Http::scUnauthorized:
    case Http::scProxyAuthenticationRequired:
    case Http::scPaymentRequired:
    case Http::scInsufficientStorage:
        // TODO: use more specific reason for non-error status codes
        decision.make(ReuseDecision::doNotCacheButShare, shareableError);
        break;

    case Http::scPartialContent: /* Not yet supported. TODO: make shareable for suitable ranges */
    case Http::scNotAcceptable:
    case Http::scRequestTimeout: // TODO: is this shareable?
    case Http::scConflict: // TODO: is this shareable?
    case Http::scLengthRequired:
    case Http::scPreconditionFailed:
    case Http::scPayloadTooLarge:
    case Http::scUnsupportedMediaType:
    case Http::scUnprocessableEntity:
    case Http::scLocked: // TODO: is this shareable?
    case Http::scFailedDependency:
    case Http::scRequestedRangeNotSatisfied:
    case Http::scExpectationFailed:
    case Http::scInvalidHeader: /* Squid header parsing error */
    case Http::scHeaderTooLarge:
        decision.make(ReuseDecision::reuseNot, nonShareableError);
        break;

    default:
        /* RFC 2616 section 6.1.1: an unrecognized response MUST NOT be cached. */
        decision.make(ReuseDecision::reuseNot, "unknown status code");
        break;
    }

    return decision.answer;
}

#define SQUID_ENTER_THROWING_CODE() try {

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static const char *const crlf = "\r\n";

static void httpMaybeRemovePublic(StoreEntry *, Http::StatusCode);

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


HttpStateData::maybeReadVirginBody()
{
    // too late to read
    if (!Comm::IsConnOpen(serverConnection) || fd_table[serverConnection->fd].closing())
        return;

    if (!maybeMakeSpaceAvailable(false))
        return;

    // XXX: get rid of the do_next_read flag
    // check for the proper reasons preventing read(2)
    if (!flags.do_next_read)
        return;

    flags.do_next_read = false;

    // must not already be waiting for read(2) ...
    assert(!Comm::MonitorsRead(serverConnection->fd));

    // wait for read(2) to be possible.
    typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
    AsyncCall::Pointer call = JobCallback(11, 5, Dialer, this, HttpStateData::readReply);
    Comm::Read(serverConnection, call);
}


tlsAttemptHandshake(ConnStateData *conn, PF *callback)
{
    // TODO: maybe throw instead of returning -1
    // see https://github.com/squid-cache/squid/pull/81#discussion_r153053278
    int fd = conn->clientConnection->fd;
    auto session = fd_table[fd].ssl.get();

    errno = 0;

#if USE_OPENSSL
    const auto ret = SSL_accept(session);
    if (ret > 0)
        return 1;

    const int xerrno = errno;
    const auto ssl_error = SSL_get_error(session, ret);

    switch (ssl_error) {

    case SSL_ERROR_WANT_READ:
        Comm::SetSelect(fd, COMM_SELECT_READ, callback, (callback ? conn : nullptr), 0);
        return 0;

    case SSL_ERROR_WANT_WRITE:
        Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, (callback ? conn : nullptr), 0);
        return 0;

    case SSL_ERROR_SYSCALL:
        if (ret == 0) {
            debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
        } else {
            debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
                   (xerrno == 0 ? Security::ErrorString(ssl_error) : xstrerr(xerrno)));
        }
        break;

    case SSL_ERROR_ZERO_RETURN:
        debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
        break;

    default:
        debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
               fd << ": " << Security::ErrorString(ssl_error) <<
               " (" << ssl_error << "/" << ret << ")");
    }

#elif USE_GNUTLS

    const auto x = gnutls_handshake(session);
    if (x == GNUTLS_E_SUCCESS)
        return 1;

    if (gnutls_error_is_fatal(x)) {
        debugs(83, 2, "Error negotiating TLS on " << conn->clientConnection << ": Aborted by client: " << Security::ErrorString(x));

    } else if (x == GNUTLS_E_INTERRUPTED || x == GNUTLS_E_AGAIN) {
        const auto ioAction = (gnutls_record_get_direction(session)==0 ? COMM_SELECT_READ : COMM_SELECT_WRITE);
        Comm::SetSelect(fd, ioAction, callback, (callback ? conn : nullptr), 0);
        return 0;
    }

#else
    // Performing TLS handshake should never be reachable without a TLS/SSL library.
    (void)session; // avoid compiler and static analysis complaints
    fatal("FATAL: HTTPS not supported by this Squid.");
#endif

    return -1;
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static IOACB httpAccept;

static CTCB clientLifetimeTimeout;

static IDCB clientIdentDone;

static int clientIsContentLengthValid(HttpRequest * r);

static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static void clientUpdateHierCounters(HierarchyLogEntry *);

static bool clientPingHasFinished(ping_data const *aPing);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


HttpStateData::ReuseDecision::ReuseDecision(const StoreEntry *e, const Http::StatusCode code)
    : answer(HttpStateData::ReuseDecision::reuseNot), reason(nullptr), entry(e), statusCode(code) {}


bool ConnStateData::serveDelayedError(Http::Stream *context)
{
    ClientHttpRequest *http = context->http;

    if (!sslServerBump)
        return false;

    assert(sslServerBump->entry);
    // Did we create an error entry while processing CONNECT?
    if (!sslServerBump->entry->isEmpty()) {
        quitAfterError(http->request);

        // Get the saved error entry and send it to the client by replacing the
        // ClientHttpRequest store entry with it.
        clientStreamNode *node = context->getClientReplyContext();
        clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
        assert(repContext);
        debugs(33, 5, "Responding with delated error for " << http->uri);
        repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");

        // Get error details from the fake certificate-peeking request.
        http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
        context->pullData();
        return true;
    }

    // In bump-server-first mode, we have not necessarily seen the intended
    // server name at certificate-peeking time. Check for domain mismatch now,
    // when we can extract the intended name from the bumped HTTP request.
    if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
        HttpRequest *request = http->request;
        if (!Ssl::checkX509ServerValidity(srvCert.get(), request->url.host())) {
            debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
                   "does not match domainname " << request->url.host());

            bool allowDomainMismatch = false;
            if (Config.ssl_client.cert_error) {
                ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
                check.al = http->al;
                check.sslErrors = new Security::CertErrors(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
                check.syncAle(request, http->log_uri);
                allowDomainMismatch = check.fastCheck().allowed();
                delete check.sslErrors;
                check.sslErrors = NULL;
            }

            if (!allowDomainMismatch) {
                quitAfterError(request);

                clientStreamNode *node = context->getClientReplyContext();
                clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
                assert (repContext);

                request->hier = sslServerBump->request->hier;

                // Create an error object and fill it
                ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request);
                err->src_addr = clientConnection->remote;
                Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
                    SQUID_X509_V_ERR_DOMAIN_MISMATCH,
                    srvCert.get(), nullptr);
                err->detail = errDetail;
                repContext->setReplyToError(request->method, err);
                assert(context->http->out.offset == 0);
                context->pullData();
                return true;
            }
        }
    }

    return false;
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static int clientIsContentLengthValid(HttpRequest * r);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static bool clientPingHasFinished(ping_data const *aPing);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


assembleVaryKey(String &vary, SBuf &vstr, const HttpRequest &request)
{
    static const SBuf asterisk("*");
    const char *pos = nullptr;
    const char *item = nullptr;
    int ilen = 0;

    while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
        SBuf name(item, ilen);
        if (name == asterisk) {
            vstr = asterisk;
            break;
        }
        name.toLower();
        if (!vstr.isEmpty())
            vstr.append(", ", 2);
        vstr.append(name);
        String hdr(request.header.getByName(name));
        const char *value = hdr.termedBuf();
        if (value) {
            value = rfc1738_escape_part(value);
            vstr.append("=\"", 2);
            vstr.append(value);
            vstr.append("\"", 1);
        }

        hdr.clean();
    }
}


ConnStateData::checkLogging()
{
    // if we are parsing request body, its request is responsible for logging
    if (bodyPipe)
        return;

    // a request currently using this connection is responsible for logging
    if (!pipeline.empty() && pipeline.back()->mayUseConnection())
        return;

    /* Either we are waiting for the very first transaction, or
     * we are done with the Nth transaction and are waiting for N+1st.
     * XXX: We assume that if anything was added to inBuf, then it could
     * only be consumed by actions already covered by the above checks.
     */

    // do not log connections that closed after a transaction (it is normal)
    // TODO: access_log needs ACLs to match received-no-bytes connections
    if (pipeline.nrequests && inBuf.isEmpty())
        return;

    /* Create a temporary ClientHttpRequest object. Its destructor will log. */
    ClientHttpRequest http(this);
    http.req_sz = inBuf.length();
    // XXX: Or we died while waiting for the pinned connection to become idle.
    http.setErrorUri("error:transaction-end-before-headers");
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);


HttpStateData::finishingBrokenPost()
{
#if USE_HTTP_VIOLATIONS
    if (!Config.accessList.brokenPosts) {
        debugs(11, 5, HERE << "No brokenPosts list");
        return false;
    }

    ACLFilledChecklist ch(Config.accessList.brokenPosts, originalRequest(), NULL);
    ch.al = fwd->al;
    ch.syncAle(originalRequest(), nullptr);
    if (!ch.fastCheck().allowed()) {
        debugs(11, 5, HERE << "didn't match brokenPosts");
        return false;
    }

    if (!Comm::IsConnOpen(serverConnection)) {
        debugs(11, 3, HERE << "ignoring broken POST for closed " << serverConnection);
        assert(closeHandler != NULL);
        return true; // prevent caller from proceeding as if nothing happened
    }

    debugs(11, 3, "finishingBrokenPost: fixing broken POST");
    typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
    requestSender = JobCallback(11,5,
                                Dialer, this, HttpStateData::wroteLast);
    Comm::Write(serverConnection, "\r\n", 2, requestSender, NULL);
    return true;
#else
    return false;
#endif /* USE_HTTP_VIOLATIONS */
}


HttpStateData::continueAfterParsingHeader()
{
    if (flags.handling1xx) {
        debugs(11, 5, HERE << "wait for 1xx handling");
        Must(!flags.headers_parsed);
        return false;
    }

    if (!flags.headers_parsed && !eof) {
        debugs(11, 9, "needs more at " << inBuf.length());
        flags.do_next_read = true;
        /** \retval false If we have not finished parsing the headers and may get more data.
         *                Schedules more reads to retrieve the missing data.
         */
        maybeReadVirginBody(); // schedules all kinds of reads; TODO: rename
        return false;
    }

    /** If we are done with parsing, check for errors */

    err_type error = ERR_NONE;

    if (flags.headers_parsed) { // parsed headers, possibly with errors
        // check for header parsing errors
        if (HttpReply *vrep = virginReply()) {
            const Http::StatusCode s = vrep->sline.status();
            const AnyP::ProtocolVersion &v = vrep->sline.version;
            if (s == Http::scInvalidHeader && v != Http::ProtocolVersion(0,9)) {
                debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Bad header encountered from " << entry->url() << " AKA " << request->url);
                error = ERR_INVALID_RESP;
            } else if (s == Http::scHeaderTooLarge) {
                fwd->dontRetry(true);
                error = ERR_TOO_BIG;
            } else if (vrep->header.conflictingContentLength()) {
                fwd->dontRetry(true);
                error = ERR_INVALID_RESP;
            } else if (vrep->header.unsupportedTe()) {
                fwd->dontRetry(true);
                error = ERR_INVALID_RESP;
            } else {
                return true; // done parsing, got reply, and no error
            }
        } else {
            // parsed headers but got no reply
            debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: No reply at all for " << entry->url() << " AKA " << request->url);
            error = ERR_INVALID_RESP;
        }
    } else {
        assert(eof);
        if (inBuf.length()) {
            error = ERR_INVALID_RESP;
            debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Headers did not parse at all for " << entry->url() << " AKA " << request->url);
        } else {
            error = ERR_ZERO_SIZE_OBJECT;
            debugs(11, (request->flags.accelerated?DBG_IMPORTANT:2), "WARNING: HTTP: Invalid Response: No object data received for " << entry->url() << " AKA " << request->url);
        }
    }

    assert(error != ERR_NONE);
    entry->reset();
    fwd->fail(new ErrorState(error, Http::scBadGateway, fwd->request));
    flags.do_next_read = false;
    closeServer();
    mustStop("HttpStateData::continueAfterParsingHeader");
    return false; // quit on error
}


HttpStateData::finishingChunkedRequest()
{
    if (flags.sentLastChunk) {
        debugs(11, 5, HERE << "already sent last-chunk");
        return false;
    }

    Must(receivedWholeRequestBody); // or we should not be sending last-chunk
    flags.sentLastChunk = true;

    typedef CommCbMemFunT<HttpStateData, CommIoCbParams> Dialer;
    requestSender = JobCallback(11,5, Dialer, this, HttpStateData::wroteLast);
    Comm::Write(serverConnection, "0\r\n\r\n", 5, requestSender, NULL);
    return true;
}


HttpStateData::haveParsedReplyHeaders()
{
    Client::haveParsedReplyHeaders();

    Ctx ctx = ctx_enter(entry->mem_obj->urlXXX());
    HttpReply *rep = finalReply();
    const Http::StatusCode statusCode = rep->sline.status();

    entry->timestampsSet();

    /* Check if object is cacheable or not based on reply code */
    debugs(11, 3, "HTTP CODE: " << statusCode);

    if (StoreEntry *oldEntry = findPreviouslyCachedEntry(entry)) {
        oldEntry->lock("HttpStateData::haveParsedReplyHeaders");
        sawDateGoBack = rep->olderThan(oldEntry->getReply());
        oldEntry->unlock("HttpStateData::haveParsedReplyHeaders");
    }

    if (neighbors_do_private_keys && !sawDateGoBack)
        httpMaybeRemovePublic(entry, rep->sline.status());

    bool varyFailure = false;
    if (rep->header.has(Http::HdrType::VARY)
#if X_ACCELERATOR_VARY
            || rep->header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY)
#endif
       ) {
        const SBuf vary(httpMakeVaryMark(request, rep));

        if (vary.isEmpty()) {
            // TODO: check whether such responses are shareable.
            // Do not share for now.
            entry->makePrivate(false);
            if (fwd->reforwardableStatus(rep->sline.status()))
                EBIT_SET(entry->flags, ENTRY_FWD_HDR_WAIT);
            varyFailure = true;
        } else {
            entry->mem_obj->vary_headers = vary;

            // RFC 7231 section 7.1.4
            // Vary:* can be cached, but has mandatory revalidation
            static const SBuf asterisk("*");
            if (vary == asterisk)
                EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
        }
    }

    if (!varyFailure) {
        /*
         * If its not a reply that we will re-forward, then
         * allow the client to get it.
         */
        if (fwd->reforwardableStatus(rep->sline.status()))
            EBIT_SET(entry->flags, ENTRY_FWD_HDR_WAIT);

        ReuseDecision decision(entry, statusCode);

        switch (reusableReply(decision)) {

        case ReuseDecision::reuseNot:
            entry->makePrivate(false);
            break;

        case ReuseDecision::cachePositively:
            if (!entry->makePublic()) {
                decision.make(ReuseDecision::doNotCacheButShare, "public key creation error");
                entry->makePrivate(true);
            }
            break;

        case ReuseDecision::cacheNegatively:
            if (!entry->cacheNegatively()) {
                decision.make(ReuseDecision::doNotCacheButShare, "public key creation error");
                entry->makePrivate(true);
            }
            break;

        case ReuseDecision::doNotCacheButShare:
            entry->makePrivate(true);
            break;

        default:
            assert(0);
            break;
        }
        debugs(11, 3, "decided: " << decision);
    }

    if (!ignoreCacheControl) {
        if (rep->cache_control) {
            // We are required to revalidate on many conditions.
            // For security reasons we do so even if storage was caused by refresh_pattern ignore-* option

            // CC:must-revalidate or CC:proxy-revalidate
            const bool ccMustRevalidate = (rep->cache_control->hasProxyRevalidate() || rep->cache_control->hasMustRevalidate());

            // CC:no-cache (only if there are no parameters)
            const bool ccNoCacheNoParams = rep->cache_control->hasNoCacheWithoutParameters();

            // CC:s-maxage=N
            const bool ccSMaxAge = rep->cache_control->hasSMaxAge();

            // CC:private (yes, these can sometimes be stored)
            const bool ccPrivate = rep->cache_control->hasPrivate();

            if (ccNoCacheNoParams || ccPrivate)
                EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
            else if (ccMustRevalidate || ccSMaxAge)
                EBIT_SET(entry->flags, ENTRY_REVALIDATE_STALE);
        }
#if USE_HTTP_VIOLATIONS // response header Pragma::no-cache is undefined in HTTP
        else {
            // Expensive calculation. So only do it IF the CC: header is not present.

            /* HACK: Pragma: no-cache in _replies_ is not documented in HTTP,
             * but servers like "Active Imaging Webcast/2.0" sure do use it */
            if (rep->header.has(Http::HdrType::PRAGMA) &&
                    rep->header.hasListMember(Http::HdrType::PRAGMA,"no-cache",','))
                EBIT_SET(entry->flags, ENTRY_REVALIDATE_ALWAYS);
        }
#endif
    }

#if HEADERS_LOG
    headersLog(1, 0, request->method, rep);

#endif

    ctx_exit(ctx);
}


varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
{
    SBuf vary(request->vary_headers);
    int has_vary = entry->getReply()->header.has(Http::HdrType::VARY);
#if X_ACCELERATOR_VARY

    has_vary |=
        entry->getReply()->header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
#endif

    if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
        if (!vary.isEmpty()) {
            /* Oops... something odd is going on here.. */
            debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
                   entry->mem_obj->urlXXX() << "' '" << vary << "'");
            request->vary_headers.clear();
            return VARY_CANCEL;
        }

        if (!has_vary) {
            /* This is not a varying object */
            return VARY_NONE;
        }

        /* virtual "vary" object found. Calculate the vary key and
         * continue the search
         */
        vary = httpMakeVaryMark(request, entry->getReply());

        if (!vary.isEmpty()) {
            request->vary_headers = vary;
            return VARY_OTHER;
        } else {
            /* Ouch.. we cannot handle this kind of variance */
            /* XXX This cannot really happen, but just to be complete */
            return VARY_CANCEL;
        }
    } else {
        if (vary.isEmpty()) {
            vary = httpMakeVaryMark(request, entry->getReply());

            if (!vary.isEmpty())
                request->vary_headers = vary;
        }

        if (vary.isEmpty()) {
            /* Ouch.. we cannot handle this kind of variance */
            /* XXX This cannot really happen, but just to be complete */
            return VARY_CANCEL;
        } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
            return VARY_MATCH;
        } else {
            /* Oops.. we have already been here and still haven't
             * found the requested variant. Bail out
             */
            debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
                   entry->mem_obj->urlXXX() << "' '" << vary << "'");
            return VARY_CANCEL;
        }
    }
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static int clientIsContentLengthValid(HttpRequest * r);

static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


HttpHeader::clean()
{

    assert(owner > hoNone && owner < hoEnd);
    debugs(55, 7, "cleaning hdr: " << this << " owner: " << owner);

    PROF_start(HttpHeaderClean);

    if (owner <= hoReply) {
        /*
         * An unfortunate bug.  The entries array is initialized
         * such that count is set to zero.  httpHeaderClean() seems to
         * be called both when 'hdr' is created, and destroyed.  Thus,
         * we accumulate a large number of zero counts for 'hdr' before
         * it is ever used.  Can't think of a good way to fix it, except
         * adding a state variable that indicates whether or not 'hdr'
         * has been used.  As a hack, just never count zero-sized header
         * arrays.
         */
        if (!entries.empty())
            HttpHeaderStats[owner].hdrUCountDistr.count(entries.size());

        ++ HttpHeaderStats[owner].destroyedCount;

        HttpHeaderStats[owner].busyDestroyedCount += entries.size() > 0;
    } // if (owner <= hoReply)

    for (HttpHeaderEntry *e : entries) {
        if (e == nullptr)
            continue;
        if (!Http::any_valid_header(e->id)) {
            debugs(55, DBG_CRITICAL, "BUG: invalid entry (" << e->id << "). Ignored.");
        } else {
            if (owner <= hoReply)
                HttpHeaderStats[owner].fieldTypeDistr.count(e->id);
            delete e;
        }
    }

    entries.clear();
    httpHeaderMaskInit(&mask, 0);
    len = 0;
    conflictingContentLength_ = false;
    teUnsupported_ = false;
    PROF_stop(HttpHeaderClean);
}

std::vector<HttpHeaderFieldStat> headerStatsTable(Http::HdrType::enumEnd_);

static HttpHeaderStat HttpHeaderStats[] = {
    HttpHeaderStat(/*hoNone*/ "all", NULL),
#if USE_HTCP
    HttpHeaderStat(/*hoHtcpReply*/ "HTCP reply", &ReplyHeadersMask),
#endif
    HttpHeaderStat(/*hoRequest*/ "request", &RequestHeadersMask),
    HttpHeaderStat(/*hoReply*/ "reply", &ReplyHeadersMask)
#if USE_OPENSSL
    /* hoErrorDetail */
#endif
    /* hoEnd */
};

static int HttpHeaderStatCount = countof(HttpHeaderStats);

static void httpHeaderNoteParsedEntry(Http::HdrType id, String const &value, bool error);


HttpStateData::ReuseDecision::make(const HttpStateData::ReuseDecision::Answers ans, const char *why)
{
    answer = ans;
    reason = why;
    return answer;
}


httpsEstablish(ConnStateData *connState, const Security::ContextPointer &ctx)
{
    assert(connState);
    const Comm::ConnectionPointer &details = connState->clientConnection;

    if (!ctx || !httpsCreate(connState, ctx))
        return;

    typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
    AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
                                     connState, ConnStateData::requestTimeout);
    commSetConnTimeout(details, Config.Timeout.request, timeoutCall);

    Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static bool clientPingHasFinished(ping_data const *aPing);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload)
{
    // fake a CONNECT request to force connState to tunnel
    SBuf connectHost;
    unsigned short connectPort = 0;

    if (pinning.serverConnection != nullptr) {
        static char ip[MAX_IPSTRLEN];
        connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
        connectPort = pinning.serverConnection->remote.port();
    } else if (cause) {
        connectHost = cause->url.hostOrIp();
        connectPort = cause->url.port();
#if USE_OPENSSL
    } else if (!tlsConnectHostOrIp.isEmpty()) {
        connectHost = tlsConnectHostOrIp;
        connectPort = tlsConnectPort;
#endif
    } else if (transparent()) {
        static char ip[MAX_IPSTRLEN];
        connectHost = clientConnection->local.toStr(ip, sizeof(ip));
        connectPort = clientConnection->local.port();
    } else {
        debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
        return false;
    }

    debugs(33, 2, "Request tunneling for " << reason);
    ClientHttpRequest *http = buildFakeRequest(method, connectHost, connectPort, payload);
    HttpRequest::Pointer request = http->request;
    request->flags.forceTunnel = true;
    http->calloutContext = new ClientRequestContext(http);
    http->doCallouts();
    clientProcessRequestFinished(this, request);
    return true;
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static IOACB httpAccept;

static CTCB clientLifetimeTimeout;

static IDCB clientIdentDone;

static int clientIsContentLengthValid(HttpRequest * r);

static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static void clientUpdateHierCounters(HierarchyLogEntry *);

static bool clientPingHasFinished(ping_data const *aPing);

void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

char *skipLeadingSpace(char *aString);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


HttpHeader::parse(const char *header_start, size_t hdrLen)
{
    const char *field_ptr = header_start;
    const char *header_end = header_start + hdrLen; // XXX: remove
    int warnOnError = (Config.onoff.relaxed_header_parser <= 0 ? DBG_IMPORTANT : 2);

    PROF_start(HttpHeaderParse);

    assert(header_start && header_end);
    debugs(55, 7, "parsing hdr: (" << this << ")" << std::endl << getStringPrefix(header_start, hdrLen));
    ++ HttpHeaderStats[owner].parsedCount;

    char *nulpos;
    if ((nulpos = (char*)memchr(header_start, '\0', hdrLen))) {
        debugs(55, DBG_IMPORTANT, "WARNING: HTTP header contains NULL characters {" <<
               getStringPrefix(header_start, nulpos-header_start) << "}\nNULL\n{" << getStringPrefix(nulpos+1, hdrLen-(nulpos-header_start)-1));
        PROF_stop(HttpHeaderParse);
        clean();
        return 0;
    }

    Http::ContentLengthInterpreter clen(warnOnError);
    /* common format headers are "<name>:[ws]<value>" lines delimited by <CRLF>.
     * continuation lines start with a (single) space or tab */
    while (field_ptr < header_end) {
        const char *field_start = field_ptr;
        const char *field_end;

        const char *hasBareCr = nullptr;
        size_t lines = 0;
        do {
            const char *this_line = field_ptr;
            field_ptr = (const char *)memchr(field_ptr, '\n', header_end - field_ptr);
            ++lines;

            if (!field_ptr) {
                // missing <LF>
                PROF_stop(HttpHeaderParse);
                clean();
                return 0;
            }

            field_end = field_ptr;

            ++field_ptr;    /* Move to next line */

            if (field_end > this_line && field_end[-1] == '\r') {
                --field_end;    /* Ignore CR LF */

                if (owner == hoRequest && field_end > this_line) {
                    bool cr_only = true;
                    for (const char *p = this_line; p < field_end && cr_only; ++p) {
                        if (*p != '\r')
                            cr_only = false;
                    }
                    if (cr_only) {
                        debugs(55, DBG_IMPORTANT, "SECURITY WARNING: Rejecting HTTP request with a CR+ "
                               "header field to prevent request smuggling attacks: {" <<
                               getStringPrefix(header_start, hdrLen) << "}");
                        PROF_stop(HttpHeaderParse);
                        clean();
                        return 0;
                    }
                }
            }

            /* Barf on stray CR characters */
            if (memchr(this_line, '\r', field_end - this_line)) {
                hasBareCr = "bare CR";
                debugs(55, warnOnError, "WARNING: suspicious CR characters in HTTP header {" <<
                       getStringPrefix(field_start, field_end-field_start) << "}");

                if (Config.onoff.relaxed_header_parser) {
                    char *p = (char *) this_line;   /* XXX Warning! This destroys original header content and violates specifications somewhat */

                    while ((p = (char *)memchr(p, '\r', field_end - p)) != NULL) {
                        *p = ' ';
                        ++p;
                    }
                } else {
                    PROF_stop(HttpHeaderParse);
                    clean();
                    return 0;
                }
            }

            if (this_line + 1 == field_end && this_line > field_start) {
                debugs(55, warnOnError, "WARNING: Blank continuation line in HTTP header {" <<
                       getStringPrefix(header_start, hdrLen) << "}");
                PROF_stop(HttpHeaderParse);
                clean();
                return 0;
            }
        } while (field_ptr < header_end && (*field_ptr == ' ' || *field_ptr == '\t'));

        if (field_start == field_end) {
            if (field_ptr < header_end) {
                debugs(55, warnOnError, "WARNING: unparseable HTTP header field near {" <<
                       getStringPrefix(field_start, hdrLen-(field_start-header_start)) << "}");
                PROF_stop(HttpHeaderParse);
                clean();
                return 0;
            }

            break;      /* terminating blank line */
        }

        const auto e = HttpHeaderEntry::parse(field_start, field_end, owner);
        if (!e) {
            debugs(55, warnOnError, "WARNING: unparseable HTTP header field {" <<
                   getStringPrefix(field_start, field_end-field_start) << "}");
            debugs(55, warnOnError, " in {" << getStringPrefix(header_start, hdrLen) << "}");

            PROF_stop(HttpHeaderParse);
            clean();
            return 0;
        }

        if (lines > 1 || hasBareCr) {
            const auto framingHeader = (e->id == Http::HdrType::CONTENT_LENGTH || e->id == Http::HdrType::TRANSFER_ENCODING);
            if (framingHeader) {
                if (!hasBareCr) // already warned about bare CRs
                    debugs(55, warnOnError, "WARNING: obs-fold in framing-sensitive " << e->name << ": " << e->value);
                delete e;
                PROF_stop(HttpHeaderParse);
                clean();
                return 0;
            }
        }

        if (e->id == Http::HdrType::CONTENT_LENGTH && !clen.checkField(e->value)) {
            delete e;

            if (Config.onoff.relaxed_header_parser)
                continue; // clen has printed any necessary warnings

            PROF_stop(HttpHeaderParse);
            clean();
            return 0;
        }

        addEntry(e);
    }

    if (clen.headerWideProblem) {
        debugs(55, warnOnError, "WARNING: " << clen.headerWideProblem <<
               " Content-Length field values in" <<
               Raw("header", header_start, hdrLen));
    }

    String rawTe;
    if (getByIdIfPresent(Http::HdrType::TRANSFER_ENCODING, &rawTe)) {
        // RFC 2616 section 4.4: ignore Content-Length with Transfer-Encoding
        // RFC 7230 section 3.3.3 #3: Transfer-Encoding overwrites Content-Length
        delById(Http::HdrType::CONTENT_LENGTH);
        // and clen state becomes irrelevant

        if (rawTe == "chunked") {
            ; // leave header present for chunked() method
        } else if (rawTe == "identity") { // deprecated. no coding
            delById(Http::HdrType::TRANSFER_ENCODING);
        } else {
            // This also rejects multiple encodings until we support them properly.
            debugs(55, warnOnError, "WARNING: unsupported Transfer-Encoding used by client: " << rawTe);
            teUnsupported_ = true;
        }

    } else if (clen.sawBad) {
        // ensure our callers do not accidentally see bad Content-Length values
        delById(Http::HdrType::CONTENT_LENGTH);
        conflictingContentLength_ = true; // TODO: Rename to badContentLength_.
    } else if (clen.needsSanitizing) {
        // RFC 7230 section 3.3.2: MUST either reject or ... [sanitize];
        // ensure our callers see a clean Content-Length value or none at all
        delById(Http::HdrType::CONTENT_LENGTH);
        if (clen.sawGood) {
            putInt64(Http::HdrType::CONTENT_LENGTH, clen.value);
            debugs(55, 5, "sanitized Content-Length to be " << clen.value);
        }
    }

    PROF_stop(HttpHeaderParse);
    return 1;           /* even if no fields where found, it is a valid header */
}

std::vector<HttpHeaderFieldStat> headerStatsTable(Http::HdrType::enumEnd_);

static HttpHeaderStat HttpHeaderStats[] = {
    HttpHeaderStat(/*hoNone*/ "all", NULL),
#if USE_HTCP
    HttpHeaderStat(/*hoHtcpReply*/ "HTCP reply", &ReplyHeadersMask),
#endif
    HttpHeaderStat(/*hoRequest*/ "request", &RequestHeadersMask),
    HttpHeaderStat(/*hoReply*/ "reply", &ReplyHeadersMask)
#if USE_OPENSSL
    /* hoErrorDetail */
#endif
    /* hoEnd */
};

static int HttpHeaderStatCount = countof(HttpHeaderStats);

static int HeaderEntryParsedCount = 0;

static void httpHeaderNoteParsedEntry(Http::HdrType id, String const &value, bool error);

static void httpHeaderStatDump(const HttpHeaderStat * hs, StoreEntry * e);

extern const HttpHeaderStat *dump_stat;

const HttpHeaderStat *dump_stat = NULL;


ConnStateData::parseProxy1p0()
{
    ::Parser::Tokenizer tok(inBuf);
    tok.skip(Proxy1p0magic);

    // skip to first LF (assumes it is part of CRLF)
    static const CharacterSet lineContent = CharacterSet::LF.complement("non-LF");
    SBuf line;
    if (tok.prefix(line, lineContent, 107-Proxy1p0magic.length())) {
        if (tok.skip('\n')) {
            // found valid header
            inBuf = tok.remaining();
            needProxyProtocolHeader_ = false;
            // reset the tokenizer to work on found line only.
            tok.reset(line);
        } else
            return false; // no LF yet

    } else // protocol error only if there are more than 107 bytes prefix header
        return proxyProtocolError(inBuf.length() > 107? "PROXY/1.0 error: missing CRLF" : NULL);

    static const SBuf unknown("UNKNOWN"), tcpName("TCP");
    if (tok.skip(tcpName)) {

        // skip TCP/IP version number
        static const CharacterSet tcpVersions("TCP-version","46");
        if (!tok.skipOne(tcpVersions))
            return proxyProtocolError("PROXY/1.0 error: missing TCP version");

        // skip SP after protocol version
        if (!tok.skip(' '))
            return proxyProtocolError("PROXY/1.0 error: missing SP");

        SBuf ipa, ipb;
        int64_t porta, portb;
        static const CharacterSet ipChars = CharacterSet("IP Address",".:") + CharacterSet::HEXDIG;

        // parse:  src-IP SP dst-IP SP src-port SP dst-port CR
        // leave the LF until later.
        const bool correct = tok.prefix(ipa, ipChars) && tok.skip(' ') &&
                             tok.prefix(ipb, ipChars) && tok.skip(' ') &&
                             tok.int64(porta) && tok.skip(' ') &&
                             tok.int64(portb) &&
                             tok.skip('\r');
        if (!correct)
            return proxyProtocolError("PROXY/1.0 error: invalid syntax");

        // parse IP and port strings
        Ip::Address originalClient, originalDest;

        if (!originalClient.GetHostByName(ipa.c_str()))
            return proxyProtocolError("PROXY/1.0 error: invalid src-IP address");

        if (!originalDest.GetHostByName(ipb.c_str()))
            return proxyProtocolError("PROXY/1.0 error: invalid dst-IP address");

        if (porta > 0 && porta <= 0xFFFF) // max uint16_t
            originalClient.port(static_cast<uint16_t>(porta));
        else
            return proxyProtocolError("PROXY/1.0 error: invalid src port");

        if (portb > 0 && portb <= 0xFFFF) // max uint16_t
            originalDest.port(static_cast<uint16_t>(portb));
        else
            return proxyProtocolError("PROXY/1.0 error: invalid dst port");

        // we have original client and destination details now
        // replace the client connection values
        debugs(33, 5, "PROXY/1.0 protocol on connection " << clientConnection);
        clientConnection->local = originalDest;
        clientConnection->remote = originalClient;
        if ((clientConnection->flags & COMM_TRANSPARENT))
            clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
        debugs(33, 5, "PROXY/1.0 upgrade: " << clientConnection);
        return true;

    } else if (tok.skip(unknown)) {
        // found valid but unusable header
        return true;

    } else
        return proxyProtocolError("PROXY/1.0 error: invalid protocol family");

    return false;
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static IOACB httpAccept;

static CTCB clientLifetimeTimeout;

static IDCB clientIdentDone;

static int clientIsContentLengthValid(HttpRequest * r);

static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static void clientUpdateHierCounters(HierarchyLogEntry *);

static bool clientPingHasFinished(ping_data const *aPing);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
{
    if (!Comm::IsConnOpen(io.conn))
        return;

    const err_type error = receivedFirstByte_ ? ERR_REQUEST_PARSE_TIMEOUT : ERR_REQUEST_START_TIMEOUT;
    if (tunnelOnError(HttpRequestMethod(), error))
        return;

    /*
    * Just close the connection to not confuse browsers
    * using persistent connections. Some browsers open
    * a connection and then do not use it until much
    * later (presumeably because the request triggering
    * the open has already been completed on another
    * connection)
    */
    debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
    io.conn->close();
}

class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
{
public:
    typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
    ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
        handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}

    virtual void print(std::ostream &os) const {
        startPrint(os) <<
                       ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
    }

    virtual bool canDial(AsyncCall &) const { return true; }
    virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }

public:
    Handler handler;

private:
    AnyP::PortCfgPointer portCfg;   ///< from HttpPortList
    Ipc::FdNoteId portTypeNote;    ///< Type of IPC socket being opened
    Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
}

static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);

static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);

static void clientUpdateStatCounters(const LogTags &logType);

static bool clientPingHasFinished(ping_data const *aPing);

static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData * conn);

static const SBuf Proxy1p0magic("PROXY ", 6);

static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);


HttpStateData::doneWithServer() const
{
    return !Comm::IsConnOpen(serverConnection);
}

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static const char *const crlf = "\r\n";

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


HttpStateData::HttpStateData(FwdState *theFwdState) :
    AsyncJob("HttpStateData"),
    Client(theFwdState),
    lastChunk(0),
    httpChunkDecoder(NULL),
    payloadSeen(0),
    payloadTruncated(0),
    sawDateGoBack(false)
{
    debugs(11,5,HERE << "HttpStateData " << this << " created");
    ignoreCacheControl = false;
    surrogateNoStore = false;
    serverConnection = fwd->serverConnection();

    if (fwd->serverConnection() != NULL)
        _peer = cbdataReference(fwd->serverConnection()->getPeer());         /* might be NULL */

    if (_peer) {
        request->flags.proxying = true;
        /*
         * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here.
         * We might end up getting the object from somewhere else if,
         * for example, the request to this neighbor fails.
         */
        if (_peer->options.proxy_only)
            entry->releaseRequest(true);

#if USE_DELAY_POOLS
        entry->setNoDelay(_peer->options.no_delay);
#endif
    }

    /*
     * register the handler to free HTTP state data when the FD closes
     */
    typedef CommCbMemFunT<HttpStateData, CommCloseCbParams> Dialer;
    closeHandler = JobCallback(9, 5, Dialer, this, HttpStateData::httpStateConnClosed);
    comm_add_close_handler(serverConnection->fd, closeHandler);
}

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }

static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);


HttpStateData::~HttpStateData()
{
    /*
     * don't forget that ~Client() gets called automatically
     */

    if (httpChunkDecoder)
        delete httpChunkDecoder;

    cbdataReferenceDone(_peer);

    debugs(11,5, HERE << "HttpStateData " << this << " destroyed; " << serverConnection);
}

#define SQUID_EXIT_THROWING_CODE(status) \
    status = true; \
    } \
    catch (const std::exception &e) { \
    debugs (11, 1, "Exception error:" << e.what()); \
    status = false; \
    }


HttpStateData::decideIfWeDoRanges (HttpRequest * request)
{
    bool result = true;
    /* decide if we want to do Ranges ourselves
     * and fetch the whole object now)
     * We want to handle Ranges ourselves iff
     *    - we can actually parse client Range specs
     *    - the specs are expected to be simple enough (e.g. no out-of-order ranges)
     *    - reply will be cachable
     * (If the reply will be uncachable we have to throw it away after
     *  serving this request, so it is better to forward ranges to
     *  the server and fetch only the requested content)
     */

    int64_t roffLimit = request->getRangeOffsetLimit();

    if (NULL == request->range || !request->flags.cachable
            || request->range->offsetLimitExceeded(roffLimit) || request->flags.connectionAuth)
        result = false;

    debugs(11, 8, "decideIfWeDoRanges: range specs: " <<
           request->range << ", cachable: " <<
           request->flags.cachable << "; we_do_ranges: " << result);

    return result;
}


HttpStateData::sentRequestBody(const CommIoCbParams &io)
{
    if (io.size > 0)
        statCounter.server.http.kbytes_out += io.size;

    Client::sentRequestBody(io);
}


static void hci_req_clear_event_filter(struct hci_request *req)
{
	struct hci_cp_set_event_filter f;

	memset(&f, 0, sizeof(f));
	f.flt_type = HCI_FLT_CLEAR_ALL;
	hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &f);

	/* Update page scan state (since we may have modified it when setting
	 * the event filter).
	 */
	__hci_req_update_scan(req);
}

bluetooth: eliminate the potential race condition when removing the HCI controller

There is a possible race condition vulnerability between issuing a HCI
command and removing the cont.  Specifically, functions hci_req_sync()
and hci_dev_do_close() can race each other like below:

thread-A in hci_req_sync()      |   thread-B in hci_dev_do_close()
                                |   hci_req_sync_lock(hdev);
test_bit(HCI_UP, &hdev->flags); |
...                             |   test_and_clear_bit(HCI_UP, &hdev->flags)
hci_req_sync_lock(hdev);        |
                                |
In this commit we alter the sequence in function hci_req_sync(). Hence,
the thread-A cannot issue th.

Signed-off-by: Lin Ma <linma@zju.edu.cn>
Cc: Marcel Holtmann <marcel@holtmann.org>
Fixes: 7c6a329e4447 ("[Bluetooth] Fix regression from using default link policy")
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>