xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) {
    xmlChar limit = 0;
    xmlChar *buf = NULL;
    xmlChar *rep = NULL;
    size_t len = 0;
    size_t buf_size = 0;
    int c, l, in_space = 0;
    xmlChar *current = NULL;
    xmlEntityPtr ent;

    if (NXT(0) == '"') {
	ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE;
	limit = '"';
        NEXT;
    } else if (NXT(0) == '\'') {
	limit = '\'';
	ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE;
        NEXT;
    } else {
	xmlFatalErr(ctxt, XML_ERR_ATTRIBUTE_NOT_STARTED, NULL);
	return(NULL);
    }

    /*
     * allocate a translation buffer.
     */
    buf_size = XML_PARSER_BUFFER_SIZE;
    buf = (xmlChar *) xmlMallocAtomic(buf_size);
    if (buf == NULL) goto mem_error;

    /*
     * OK loop until we reach one of the ending char or a size limit.
     */
    c = CUR_CHAR(l);
    while (((NXT(0) != limit) && /* checked */
            (IS_CHAR(c)) && (c != '<')) &&
            (ctxt->instate != XML_PARSER_EOF)) {
        /*
         * Impose a reasonable limit on attribute size, unless XML_PARSE_HUGE
         * special option is given
         */
        if ((len > XML_MAX_TEXT_LENGTH) &&
            ((ctxt->options & XML_PARSE_HUGE) == 0)) {
            xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED,
                           "AttValue length too long\n");
            goto mem_error;
        }
	if (c == '&') {
	    in_space = 0;
	    if (NXT(1) == '#') {
		int val = xmlParseCharRef(ctxt);

		if (val == '&') {
		    if (ctxt->replaceEntities) {
			if (len + 10 > buf_size) {
			    growBuffer(buf, 10);
			}
			buf[len++] = '&';
		    } else {
			/*
			 * The reparsing will be done in xmlStringGetNodeList()
			 * called by the attribute() function in SAX.c
			 */
			if (len + 10 > buf_size) {
			    growBuffer(buf, 10);
			}
			buf[len++] = '&';
			buf[len++] = '#';
			buf[len++] = '3';
			buf[len++] = '8';
			buf[len++] = ';';
		    }
		} else if (val != 0) {
		    if (len + 10 > buf_size) {
			growBuffer(buf, 10);
		    }
		    len += xmlCopyChar(0, &buf[len], val);
		}
	    } else {
		ent = xmlParseEntityRef(ctxt);
		ctxt->nbentities++;
		if (ent != NULL)
		    ctxt->nbentities += ent->owner;
		if ((ent != NULL) &&
		    (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) {
		    if (len + 10 > buf_size) {
			growBuffer(buf, 10);
		    }
		    if ((ctxt->replaceEntities == 0) &&
		        (ent->content[0] == '&')) {
			buf[len++] = '&';
			buf[len++] = '#';
			buf[len++] = '3';
			buf[len++] = '8';
			buf[len++] = ';';
		    } else {
			buf[len++] = ent->content[0];
		    }
		} else if ((ent != NULL) &&
		           (ctxt->replaceEntities != 0)) {
		    if (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) {
			++ctxt->depth;
			rep = xmlStringDecodeEntities(ctxt, ent->content,
						      XML_SUBSTITUTE_REF,
						      0, 0, 0);
			--ctxt->depth;
			if (rep != NULL) {
			    current = rep;
			    while (*current != 0) { /* non input consuming */
                                if ((*current == 0xD) || (*current == 0xA) ||
                                    (*current == 0x9)) {
                                    buf[len++] = 0x20;
                                    current++;
                                } else
                                    buf[len++] = *current++;
				if (len + 10 > buf_size) {
				    growBuffer(buf, 10);
				}
			    }
			    xmlFree(rep);
			    rep = NULL;
			}
		    } else {
			if (len + 10 > buf_size) {
			    growBuffer(buf, 10);
			}
			if (ent->content != NULL)
			    buf[len++] = ent->content[0];
		    }
		} else if (ent != NULL) {
		    int i = xmlStrlen(ent->name);
		    const xmlChar *cur = ent->name;

		    /*
		     * This may look absurd but is needed to detect
		     * entities problems
		     */
		    if ((ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) &&
			(ent->content != NULL) && (ent->checked == 0)) {
			unsigned long oldnbent = ctxt->nbentities, diff;

			++ctxt->depth;
			rep = xmlStringDecodeEntities(ctxt, ent->content,
						  XML_SUBSTITUTE_REF, 0, 0, 0);
			--ctxt->depth;

                        diff = ctxt->nbentities - oldnbent + 1;
                        if (diff > INT_MAX / 2)
                            diff = INT_MAX / 2;
                        ent->checked = diff * 2;
			if (rep != NULL) {
			    if (xmlStrchr(rep, '<'))
			        ent->checked |= 1;
			    xmlFree(rep);
			    rep = NULL;
			} else {
                            ent->content[0] = 0;
                        }
		    }

		    /*
		     * Just output the reference
		     */
		    buf[len++] = '&';
		    while (len + i + 10 > buf_size) {
			growBuffer(buf, i + 10);
		    }
		    for (;i > 0;i--)
			buf[len++] = *cur++;
		    buf[len++] = ';';
		}
	    }
	} else {
	    if ((c == 0x20) || (c == 0xD) || (c == 0xA) || (c == 0x9)) {
	        if ((len != 0) || (!normalize)) {
		    if ((!normalize) || (!in_space)) {
			COPY_BUF(l,buf,len,0x20);
			while (len + 10 > buf_size) {
			    growBuffer(buf, 10);
			}
		    }
		    in_space = 1;
		}
	    } else {
	        in_space = 0;
		COPY_BUF(l,buf,len,c);
		if (len + 10 > buf_size) {
		    growBuffer(buf, 10);
		}
	    }
	    NEXTL(l);
	}
	GROW;
	c = CUR_CHAR(l);
    }
    if (ctxt->instate == XML_PARSER_EOF)
        goto error;

    if ((in_space) && (normalize)) {
        while ((len > 0) && (buf[len - 1] == 0x20)) len--;
    }
    buf[len] = 0;
    if (RAW == '<') {
	xmlFatalErr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, NULL);
    } else if (RAW != limit) {
	if ((c != 0) && (!IS_CHAR(c))) {
	    xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR,
			   "invalid character in attribute value\n");
	} else {
	    xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED,
			   "AttValue: ' expected\n");
        }
    } else
	NEXT;

    /*
     * There we potentially risk an overflow, don't allow attribute value of
     * length more than INT_MAX it is a very reasonable assumption !
     */
    if (len >= INT_MAX) {
        xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED,
                       "AttValue length too long\n");
        goto mem_error;
    }

    if (attlen != NULL) *attlen = (int) len;
    return(buf);

mem_error:
    xmlErrMemory(ctxt, NULL);
error:
    if (buf != NULL)
        xmlFree(buf);
    if (rep != NULL)
        xmlFree(rep);
    return(NULL);
}


xmlParsePI(xmlParserCtxtPtr ctxt) {
    xmlChar *buf = NULL;
    size_t len = 0;
    size_t size = XML_PARSER_BUFFER_SIZE;
    int cur, l;
    const xmlChar *target;
    xmlParserInputState state;
    int count = 0;

    if ((RAW == '<') && (NXT(1) == '?')) {
	int inputid = ctxt->input->id;
	state = ctxt->instate;
        ctxt->instate = XML_PARSER_PI;
	/*
	 * this is a Processing Instruction.
	 */
	SKIP(2);
	SHRINK;

	/*
	 * Parse the target name and check for special support like
	 * namespace.
	 */
        target = xmlParsePITarget(ctxt);
	if (target != NULL) {
	    if ((RAW == '?') && (NXT(1) == '>')) {
		if (inputid != ctxt->input->id) {
		    xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY,
	                           "PI declaration doesn't start and stop in"
                                   " the same entity\n");
		}
		SKIP(2);

		/*
		 * SAX: PI detected.
		 */
		if ((ctxt->sax) && (!ctxt->disableSAX) &&
		    (ctxt->sax->processingInstruction != NULL))
		    ctxt->sax->processingInstruction(ctxt->userData,
		                                     target, NULL);
		if (ctxt->instate != XML_PARSER_EOF)
		    ctxt->instate = state;
		return;
	    }
	    buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
	    if (buf == NULL) {
		xmlErrMemory(ctxt, NULL);
		ctxt->instate = state;
		return;
	    }
	    if (SKIP_BLANKS == 0) {
		xmlFatalErrMsgStr(ctxt, XML_ERR_SPACE_REQUIRED,
			  "ParsePI: PI %s space expected\n", target);
	    }
	    cur = CUR_CHAR(l);
	    while (IS_CHAR(cur) && /* checked */
		   ((cur != '?') || (NXT(1) != '>'))) {
		if (len + 5 >= size) {
		    xmlChar *tmp;
                    size_t new_size = size * 2;
		    tmp = (xmlChar *) xmlRealloc(buf, new_size);
		    if (tmp == NULL) {
			xmlErrMemory(ctxt, NULL);
			xmlFree(buf);
			ctxt->instate = state;
			return;
		    }
		    buf = tmp;
                    size = new_size;
		}
		count++;
		if (count > 50) {
		    SHRINK;
		    GROW;
                    if (ctxt->instate == XML_PARSER_EOF) {
                        xmlFree(buf);
                        return;
                    }
		    count = 0;
                    if ((len > XML_MAX_TEXT_LENGTH) &&
                        ((ctxt->options & XML_PARSE_HUGE) == 0)) {
                        xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED,
                                          "PI %s too big found", target);
                        xmlFree(buf);
                        ctxt->instate = state;
                        return;
                    }
		}
		COPY_BUF(l,buf,len,cur);
		NEXTL(l);
		cur = CUR_CHAR(l);
		if (cur == 0) {
		    SHRINK;
		    GROW;
		    cur = CUR_CHAR(l);
		}
	    }
            if ((len > XML_MAX_TEXT_LENGTH) &&
                ((ctxt->options & XML_PARSE_HUGE) == 0)) {
                xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED,
                                  "PI %s too big found", target);
                xmlFree(buf);
                ctxt->instate = state;
                return;
            }
	    buf[len] = 0;
	    if (cur != '?') {
		xmlFatalErrMsgStr(ctxt, XML_ERR_PI_NOT_FINISHED,
		      "ParsePI: PI %s never end ...\n", target);
	    } else {
		if (inputid != ctxt->input->id) {
		    xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY,
	                           "PI declaration doesn't start and stop in"
                                   " the same entity\n");
		}
		SKIP(2);

#ifdef LIBXML_CATALOG_ENABLED
		if (((state == XML_PARSER_MISC) ||
	             (state == XML_PARSER_START)) &&
		    (xmlStrEqual(target, XML_CATALOG_PI))) {
		    xmlCatalogAllow allow = xmlCatalogGetDefaults();
		    if ((allow == XML_CATA_ALLOW_DOCUMENT) ||
			(allow == XML_CATA_ALLOW_ALL))
			xmlParseCatalogPI(ctxt, buf);
		}
#endif

		/*
		 * SAX: PI detected.
		 */
		if ((ctxt->sax) && (!ctxt->disableSAX) &&
		    (ctxt->sax->processingInstruction != NULL))
		    ctxt->sax->processingInstruction(ctxt->userData,
		                                     target, buf);
	    }
	    xmlFree(buf);
	} else {
	    xmlFatalErr(ctxt, XML_ERR_PI_NOT_STARTED, NULL);
	}
	if (ctxt->instate != XML_PARSER_EOF)
	    ctxt->instate = state;
    }
}


xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) {
    xmlChar *buf = NULL;
    int len = 0;
    int size = XML_PARSER_BUFFER_SIZE;
    int cur, l;
    xmlChar stop;
    int state = ctxt->instate;
    int count = 0;

    SHRINK;
    if (RAW == '"') {
        NEXT;
	stop = '"';
    } else if (RAW == '\'') {
        NEXT;
	stop = '\'';
    } else {
	xmlFatalErr(ctxt, XML_ERR_LITERAL_NOT_STARTED, NULL);
	return(NULL);
    }

    buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
    if (buf == NULL) {
        xmlErrMemory(ctxt, NULL);
	return(NULL);
    }
    ctxt->instate = XML_PARSER_SYSTEM_LITERAL;
    cur = CUR_CHAR(l);
    while ((IS_CHAR(cur)) && (cur != stop)) { /* checked */
	if (len + 5 >= size) {
	    xmlChar *tmp;

            if ((size > XML_MAX_NAME_LENGTH) &&
                ((ctxt->options & XML_PARSE_HUGE) == 0)) {
                xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "SystemLiteral");
                xmlFree(buf);
		ctxt->instate = (xmlParserInputState) state;
                return(NULL);
            }
	    size *= 2;
	    tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
	    if (tmp == NULL) {
	        xmlFree(buf);
		xmlErrMemory(ctxt, NULL);
		ctxt->instate = (xmlParserInputState) state;
		return(NULL);
	    }
	    buf = tmp;
	}
	count++;
	if (count > 50) {
	    SHRINK;
	    GROW;
	    count = 0;
            if (ctxt->instate == XML_PARSER_EOF) {
	        xmlFree(buf);
		return(NULL);
            }
	}
	COPY_BUF(l,buf,len,cur);
	NEXTL(l);
	cur = CUR_CHAR(l);
	if (cur == 0) {
	    GROW;
	    SHRINK;
	    cur = CUR_CHAR(l);
	}
    }
    buf[len] = 0;
    ctxt->instate = (xmlParserInputState) state;
    if (!IS_CHAR(cur)) {
	xmlFatalErr(ctxt, XML_ERR_LITERAL_NOT_FINISHED, NULL);
    } else {
	NEXT;
    }
    return(buf);
}


xmlParsePEReference(xmlParserCtxtPtr ctxt)
{
    const xmlChar *name;
    xmlEntityPtr entity = NULL;
    xmlParserInputPtr input;

    if (RAW != '%')
        return;
    NEXT;
    name = xmlParseName(ctxt);
    if (name == NULL) {
	xmlFatalErrMsg(ctxt, XML_ERR_PEREF_NO_NAME, "PEReference: no name\n");
	return;
    }
    if (xmlParserDebugEntities)
	xmlGenericError(xmlGenericErrorContext,
		"PEReference: %s\n", name);
    if (RAW != ';') {
	xmlFatalErr(ctxt, XML_ERR_PEREF_SEMICOL_MISSING, NULL);
        return;
    }

    NEXT;

    /*
     * Increase the number of entity references parsed
     */
    ctxt->nbentities++;

    /*
     * Request the entity from SAX
     */
    if ((ctxt->sax != NULL) &&
	(ctxt->sax->getParameterEntity != NULL))
	entity = ctxt->sax->getParameterEntity(ctxt->userData, name);
    if (ctxt->instate == XML_PARSER_EOF)
	return;
    if (entity == NULL) {
	/*
	 * [ WFC: Entity Declared ]
	 * In a document without any DTD, a document with only an
	 * internal DTD subset which contains no parameter entity
	 * references, or a document with "standalone='yes'", ...
	 * ... The declaration of a parameter entity must precede
	 * any reference to it...
	 */
	if ((ctxt->standalone == 1) ||
	    ((ctxt->hasExternalSubset == 0) &&
	     (ctxt->hasPErefs == 0))) {
	    xmlFatalErrMsgStr(ctxt, XML_ERR_UNDECLARED_ENTITY,
			      "PEReference: %%%s; not found\n",
			      name);
	} else {
	    /*
	     * [ VC: Entity Declared ]
	     * In a document with an external subset or external
	     * parameter entities with "standalone='no'", ...
	     * ... The declaration of a parameter entity must
	     * precede any reference to it...
	     */
            if ((ctxt->validate) && (ctxt->vctxt.error != NULL)) {
                xmlValidityError(ctxt, XML_WAR_UNDECLARED_ENTITY,
                                 "PEReference: %%%s; not found\n",
                                 name, NULL);
            } else
                xmlWarningMsg(ctxt, XML_WAR_UNDECLARED_ENTITY,
                              "PEReference: %%%s; not found\n",
                              name, NULL);
            ctxt->valid = 0;
	}
	xmlParserEntityCheck(ctxt, 0, NULL, 0);
    } else {
	/*
	 * Internal checking in case the entity quest barfed
	 */
	if ((entity->etype != XML_INTERNAL_PARAMETER_ENTITY) &&
	    (entity->etype != XML_EXTERNAL_PARAMETER_ENTITY)) {
	    xmlWarningMsg(ctxt, XML_WAR_UNDECLARED_ENTITY,
		  "Internal: %%%s; is not a parameter entity\n",
			  name, NULL);
	} else {
            xmlChar start[4];
            xmlCharEncoding enc;

	    if (xmlParserEntityCheck(ctxt, 0, entity, 0))
	        return;

	    if ((entity->etype == XML_EXTERNAL_PARAMETER_ENTITY) &&
	        ((ctxt->options & XML_PARSE_NOENT) == 0) &&
		((ctxt->options & XML_PARSE_DTDVALID) == 0) &&
		((ctxt->options & XML_PARSE_DTDLOAD) == 0) &&
		((ctxt->options & XML_PARSE_DTDATTR) == 0) &&
		(ctxt->replaceEntities == 0) &&
		(ctxt->validate == 0))
		return;

	    input = xmlNewEntityInputStream(ctxt, entity);
	    if (xmlPushInput(ctxt, input) < 0) {
                xmlFreeInputStream(input);
		return;
            }

	    if (entity->etype == XML_EXTERNAL_PARAMETER_ENTITY) {
                /*
                 * Get the 4 first bytes and decode the charset
                 * if enc != XML_CHAR_ENCODING_NONE
                 * plug some encoding conversion routines.
                 * Note that, since we may have some non-UTF8
                 * encoding (like UTF16, bug 135229), the 'length'
                 * is not known, but we can calculate based upon
                 * the amount of data in the buffer.
                 */
                GROW
                if (ctxt->instate == XML_PARSER_EOF)
                    return;
                if ((ctxt->input->end - ctxt->input->cur)>=4) {
                    start[0] = RAW;
                    start[1] = NXT(1);
                    start[2] = NXT(2);
                    start[3] = NXT(3);
                    enc = xmlDetectCharEncoding(start, 4);
                    if (enc != XML_CHAR_ENCODING_NONE) {
                        xmlSwitchEncoding(ctxt, enc);
                    }
                }

                if ((CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) &&
                    (IS_BLANK_CH(NXT(5)))) {
                    xmlParseTextDecl(ctxt);
                }
            }
	}
    }
    ctxt->hasPErefs = 1;
}


xmlSkipBlankChars(xmlParserCtxtPtr ctxt) {
    int res = 0;

    /*
     * It's Okay to use CUR/NEXT here since all the blanks are on
     * the ASCII range.
     */
    if (ctxt->instate != XML_PARSER_DTD) {
	const xmlChar *cur;
	/*
	 * if we are in the document content, go really fast
	 */
	cur = ctxt->input->cur;
	while (IS_BLANK_CH(*cur)) {
	    if (*cur == '\n') {
		ctxt->input->line++; ctxt->input->col = 1;
	    } else {
		ctxt->input->col++;
	    }
	    cur++;
	    res++;
	    if (*cur == 0) {
		ctxt->input->cur = cur;
		xmlParserInputGrow(ctxt->input, INPUT_CHUNK);
		cur = ctxt->input->cur;
	    }
	}
	ctxt->input->cur = cur;
    } else {
        int expandPE = ((ctxt->external != 0) || (ctxt->inputNr != 1));

	while (1) {
            if (IS_BLANK_CH(CUR)) { /* CHECKED tstblanks.xml */
		NEXT;
	    } else if (CUR == '%') {
                /*
                 * Need to handle support of entities branching here
                 */
	        if ((expandPE == 0) || (IS_BLANK_CH(NXT(1))) || (NXT(1) == 0))
                    break;
	        xmlParsePEReference(ctxt);
            } else if (CUR == 0) {
                if (ctxt->inputNr <= 1)
                    break;
                xmlPopInput(ctxt);
            } else {
                break;
            }

            /*
             * Also increase the counter when entering or exiting a PERef.
             * The spec says: "When a parameter-entity reference is recognized
             * in the DTD and included, its replacement text MUST be enlarged
             * by the attachment of one leading and one following space (#x20)
             * character."
             */
	    res++;
        }
    }
    return(res);
}


xmlParseContentInternal(xmlParserCtxtPtr ctxt) {
    int nameNr = ctxt->nameNr;

    GROW;
    while ((RAW != 0) &&
	   (ctxt->instate != XML_PARSER_EOF)) {
	const xmlChar *test = CUR_PTR;
	unsigned int cons = ctxt->input->consumed;
	const xmlChar *cur = ctxt->input->cur;

	/*
	 * First case : a Processing Instruction.
	 */
	if ((*cur == '<') && (cur[1] == '?')) {
	    xmlParsePI(ctxt);
	}

	/*
	 * Second case : a CDSection
	 */
	/* 2.6.0 test was *cur not RAW */
	else if (CMP9(CUR_PTR, '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[')) {
	    xmlParseCDSect(ctxt);
	}

	/*
	 * Third case :  a comment
	 */
	else if ((*cur == '<') && (NXT(1) == '!') &&
		 (NXT(2) == '-') && (NXT(3) == '-')) {
	    xmlParseComment(ctxt);
	    ctxt->instate = XML_PARSER_CONTENT;
	}

	/*
	 * Fourth case :  a sub-element.
	 */
	else if (*cur == '<') {
            if (NXT(1) == '/') {
                if (ctxt->nameNr <= nameNr)
                    break;
	        xmlParseElementEnd(ctxt);
            } else {
	        xmlParseElementStart(ctxt);
            }
	}

	/*
	 * Fifth case : a reference. If if has not been resolved,
	 *    parsing returns it's Name, create the node
	 */

	else if (*cur == '&') {
	    xmlParseReference(ctxt);
	}

	/*
	 * Last case, text. Note that References are handled directly.
	 */
	else {
	    xmlParseCharData(ctxt, 0);
	}

	GROW;
	SHRINK;

	if ((cons == ctxt->input->consumed) && (test == CUR_PTR)) {
	    xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR,
	                "detected an error in element content\n");
	    xmlHaltParser(ctxt);
            break;
	}
    }
}


TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(RequestHeaderForTagsConfig)) {
  TestDeprecatedV2Api _deprecated_v2_api;
  const std::string yaml_string = R"EOF(
stat_prefix: router
route_config:
  name: local_route
tracing:
  request_headers_for_tags:
  - foo
  )EOF";
  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string, false),
                                     context_, date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);

  const Tracing::CustomTagMap& custom_tag_map = config.tracingConfig()->custom_tags_;
  const Tracing::RequestHeaderCustomTag* foo = dynamic_cast<const Tracing::RequestHeaderCustomTag*>(
      custom_tag_map.find("foo")->second.get());
  EXPECT_NE(foo, nullptr);
  EXPECT_EQ(foo->tag(), "foo");
}


TEST_F(ConnectionManagerUtilityTest, TlsSanitizeClientCertWhenForward) {
  auto ssl = std::make_shared<NiceMock<Ssl::MockConnectionInfo>>();
  ON_CALL(*ssl, peerCertificatePresented()).WillByDefault(Return(false));
  ON_CALL(connection_, ssl()).WillByDefault(Return(ssl));
  ON_CALL(config_, forwardClientCert())
      .WillByDefault(Return(Http::ForwardClientCertType::ForwardOnly));
  std::vector<Http::ClientCertDetailsType> details;
  ON_CALL(config_, setCurrentClientCertDetails()).WillByDefault(ReturnRef(details));
  TestRequestHeaderMapImpl headers{{"x-forwarded-client-cert", "By=test;URI=abc"}};

  EXPECT_EQ((MutateRequestRet{"10.0.0.3:50000", false, Tracing::Reason::NotTraceable}),
            callMutateRequestHeaders(headers, Protocol::Http2));
  EXPECT_FALSE(headers.has("x-forwarded-client-cert"));
}


TEST_P(HeaderIntegrationTest, TestTeHeaderSanitized) {
  initializeFilter(HeaderMode::Append, false);
  performRequest(
      Http::TestRequestHeaderMapImpl{
          {":method", "GET"},
          {":path", "/"},
          {":scheme", "http"},
          {":authority", "no-headers.com"},
          {"x-request-foo", "downstram"},
          {"connection", "te, mike, sam, will, close"},
          {"te", "gzip"},
          {"mike", "foo"},
          {"sam", "bar"},
          {"will", "baz"},
      },
      Http::TestRequestHeaderMapImpl{
          {":authority", "no-headers.com"},
          {":path", "/"},
          {":method", "GET"},
          {"x-request-foo", "downstram"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"content-length", "0"},
          {":status", "200"},
          {"x-return-foo", "upstream"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"x-return-foo", "upstream"},
          {":status", "200"},
          {"connection", "close"},
      });
}


TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) {
  if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) {
    // QUICHE Qpack splits concatenated cookies into crumbs to increase
    // compression ratio. On the receiver side, the total size of these crumbs
    // may be larger than coalesced cookie headers. Increase the max request
    // header size to avoid QUIC_HEADERS_TOO_LARGE stream error.
    config_helper_.addConfigModifier(
        [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
                hcm) -> void {
          hcm.mutable_max_request_headers_kb()->set_value(96);
          hcm.mutable_common_http_protocol_options()->mutable_max_headers_count()->set_value(8000);
        });
  }
  if (upstreamProtocol() == FakeHttpConnection::Type::HTTP3) {
    setMaxRequestHeadersKb(96);
    setMaxRequestHeadersCount(8000);
  }
  initialize();

  codec_client_ = makeHttpConnection(lookupPort("http"));
  Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"},
                                                 {":path", "/test/long/url"},
                                                 {":scheme", "http"},
                                                 {":authority", "host"},
                                                 {"content-length", "0"}};
  std::vector<std::string> cookie_pieces;
  cookie_pieces.reserve(7000);
  for (int i = 0; i < 7000; i++) {
    cookie_pieces.push_back(fmt::sprintf("a%x=b", i));
  }
  request_headers.addCopy("cookie", absl::StrJoin(cookie_pieces, "; "));
  auto response = sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 0);

  ASSERT_TRUE(response->complete());
  EXPECT_EQ("200", response->headers().getStatusValue());
}


TEST_P(DownstreamProtocolIntegrationTest, ManyLargeRequestHeadersAccepted) {
  // Fail under TSAN. Quic blackhole detection fired and closed the connection with
  // QUIC_TOO_MANY_RTOS while waiting for upstream finishing transferring the large header. Observed
  // long event loop.
  EXCLUDE_DOWNSTREAM_HTTP3;
  // Send 70 headers each of size 100 kB with limit 8192 kB (8 MB) and 100 headers.
  testLargeRequestHeaders(100, 70, 8192, 100, TSAN_TIMEOUT_FACTOR * TestUtility::DefaultTimeout);
}


TEST_F(ConnectionManagerUtilityTest, EdgeRequestRegenerateRequestIdAndWipeDownstream) {
  connection_.stream_info_.downstream_address_provider_->setRemoteAddress(
      std::make_shared<Network::Address::Ipv4Instance>("34.0.0.1"));
  ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true));
  ON_CALL(runtime_.snapshot_, featureEnabled("tracing.global_enabled",
                                             An<const envoy::type::v3::FractionalPercent&>(), _))
      .WillByDefault(Return(true));

  {
    TestRequestHeaderMapImpl headers{{"x-envoy-downstream-service-cluster", "foo"},
                                     {"x-request-id", "will_be_regenerated"}};
    EXPECT_CALL(random_, uuid());

    EXPECT_CALL(runtime_.snapshot_,
                featureEnabled("tracing.client_enabled", testing::Matcher<uint64_t>(_)))
        .Times(0);
    EXPECT_EQ((MutateRequestRet{"34.0.0.1:0", false, Tracing::Reason::NotTraceable}),
              callMutateRequestHeaders(headers, Protocol::Http2));
    EXPECT_FALSE(headers.has(Headers::get().EnvoyDownstreamServiceCluster));
    EXPECT_FALSE(headers.has(Headers::get().EnvoyDownstreamServiceNode));
    // No changes to uuid as x-client-trace-id is missing.
    EXPECT_EQ(random_.uuid_, headers.get_(Headers::get().RequestId));
  }

  {
    // Runtime does not allow to make request traceable even though x-client-trace-id set.
    TestRequestHeaderMapImpl headers{{"x-envoy-downstream-service-cluster", "foo"},
                                     {"x-request-id", "will_be_regenerated"},
                                     {"x-client-trace-id", "trace-id"}};
    EXPECT_CALL(random_, uuid());
    EXPECT_CALL(runtime_.snapshot_, featureEnabled("tracing.client_enabled",
                                                   An<const envoy::type::v3::FractionalPercent&>()))
        .WillOnce(Return(false));

    EXPECT_EQ((MutateRequestRet{"34.0.0.1:0", false, Tracing::Reason::NotTraceable}),
              callMutateRequestHeaders(headers, Protocol::Http2));
    EXPECT_FALSE(headers.has(Headers::get().EnvoyDownstreamServiceCluster));
    EXPECT_EQ(random_.uuid_, headers.get_(Headers::get().RequestId));
  }

  {
    // Runtime is enabled for tracing and x-client-trace-id set.
    TestRequestHeaderMapImpl headers{{"x-envoy-downstream-service-cluster", "foo"},
                                     {"x-request-id", "will_be_regenerated"},
                                     {"x-client-trace-id", "trace-id"}};
    EXPECT_CALL(random_, uuid());
    EXPECT_CALL(runtime_.snapshot_, featureEnabled("tracing.client_enabled",
                                                   An<const envoy::type::v3::FractionalPercent&>()))
        .WillOnce(Return(true));

    EXPECT_EQ((MutateRequestRet{"34.0.0.1:0", false, Tracing::Reason::ClientForced}),
              callMutateRequestHeaders(headers, Protocol::Http2));
    EXPECT_FALSE(headers.has(Headers::get().EnvoyDownstreamServiceCluster));
    // Traceable (client trace) variant of random_.uuid_
    EXPECT_EQ("a121e9e1-feae-b136-9e0e-6fac343d56c9", headers.get_(Headers::get().RequestId));
  }
}


TEST_F(HttpConnectionManagerImplTest, EscapedSlashesRedirectedAfterOtherNormalizations) {
  normalize_path_ = true;
  merge_slashes_ = true;
  path_with_escaped_slashes_action_ = envoy::extensions::filters::network::http_connection_manager::
      v3::HttpConnectionManager::UNESCAPE_AND_REDIRECT;
  // Both Chromium URL normalization and merge slashes should happen if request is redirected
  // due to escaped slash sequences.
  testPathNormalization(TestRequestHeaderMapImpl{{":authority", "host"},
                                                 {":path", "/abc%2f../%5cdef//"},
                                                 {":method", "GET"}},
                        TestResponseHeaderMapImpl{{":status", "307"}, {"location", "/def/"}});
  EXPECT_EQ(1U, stats_.named_.downstream_rq_redirected_with_normalized_path_.value());
}


    bool operator==(const MutateRequestRet& rhs) const {
      return downstream_address_ == rhs.downstream_address_ && internal_ == rhs.internal_ &&
             trace_reason_ == rhs.trace_reason_;
    }


TEST_P(ProtocolIntegrationTest, TestDownstreamResetIdleTimeout) {
  useAccessLog("%RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS%");
  config_helper_.setDownstreamHttpIdleTimeout(std::chrono::milliseconds(100));

  initialize();
  codec_client_ = makeHttpConnection(lookupPort("http"));

  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);

  EXPECT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));

  EXPECT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));

  if (downstreamProtocol() == Http::CodecClient::Type::HTTP1) {
    codec_client_->close();
  } else {
    codec_client_->sendReset(encoder_decoder.first);
  }

  if (upstreamProtocol() == FakeHttpConnection::Type::HTTP1) {
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
  } else {
    ASSERT_TRUE(upstream_request_->waitForReset());
    ASSERT_TRUE(fake_upstream_connection_->close());
    ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
  }

  ASSERT_TRUE(codec_client_->waitForDisconnect());
  EXPECT_THAT(waitForAccessLog(access_log_name_), Not(HasSubstr("DPE")));
}


TEST_P(ProtocolIntegrationTest, AddBodyToRequestAndWaitForIt) {
  // filters are prepended, so add them in reverse order
  config_helper_.addFilter(R"EOF(
  name: wait-for-whole-request-and-response-filter
  )EOF");
  config_helper_.addFilter(R"EOF(
  name: add-body-filter
  )EOF");
  initialize();
  codec_client_ = makeHttpConnection(lookupPort("http"));

  auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
  waitForNextUpstreamRequest();
  EXPECT_EQ("body", upstream_request_->body().toString());
  upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false);
  // encode data, as we have a separate test for the transforming header only response.
  upstream_request_->encodeData(128, true);
  ASSERT_TRUE(response->waitForEndStream());

  EXPECT_TRUE(upstream_request_->complete());
  EXPECT_TRUE(response->complete());
  EXPECT_EQ("200", response->headers().getStatusValue());
}


void ConnectionManagerImpl::ActiveStream::clearRouteCache() {
  cached_route_ = absl::optional<Router::RouteConstSharedPtr>();
  cached_cluster_info_ = absl::optional<Upstream::ClusterInfoConstSharedPtr>();
  if (tracing_custom_tags_) {
    tracing_custom_tags_->clear();
  }
}


TEST_F(ConnectionManagerUtilityTest, SkipMutateResponseHeadersReturnXRequestId) {
  TestResponseHeaderMapImpl response_headers;
  TestRequestHeaderMapImpl request_headers{{"x-request-id", "request-id"}};

  EXPECT_CALL(*request_id_extension_,
              setInResponse(testing::Ref(response_headers), testing::Ref(request_headers)))
      .Times(0);
  ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_, "");
  EXPECT_EQ("", response_headers.get_("x-request-id"));
}


    std::chrono::milliseconds transportSocketConnectTimeout() const override {
      return std::chrono::milliseconds::zero();
    }


TEST_F(ConnectionManagerUtilityTest, PreserveForwardedProtoWhenInternal) {
  TestScopedRuntime scoped_runtime;

  ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true));
  ON_CALL(config_, xffNumTrustedHops()).WillByDefault(Return(1));
  EXPECT_CALL(config_, skipXffAppend()).WillOnce(Return(true));
  connection_.stream_info_.downstream_address_provider_->setRemoteAddress(
      std::make_shared<Network::Address::Ipv4Instance>("12.12.12.12"));
  ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true));
  TestRequestHeaderMapImpl headers{{"x-forwarded-proto", "https"}};

  callMutateRequestHeaders(headers, Protocol::Http2);
  EXPECT_EQ("https", headers.getForwardedProtoValue());
  // Given :scheme was not set, it will be set to X-Forwarded-Proto
  EXPECT_EQ("https", headers.getSchemeValue());

  // Make sure if x-forwarded-proto changes it doesn't cause problems.
  headers.setForwardedProto("ftp");
  EXPECT_EQ("https", headers.getSchemeValue());
}


TEST_F(HttpConnectionManagerConfigTest, PathWithEscapedSlashesActionDefaultOverridenAndDisabled) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  route_config:
    name: local_route
  http_filters:
  - name: envoy.filters.http.router
  )EOF";

  EXPECT_CALL(context_.runtime_loader_.snapshot_, featureEnabled(_, An<uint64_t>()))
      .WillRepeatedly(Invoke(&context_.runtime_loader_.snapshot_,
                             &Runtime::MockSnapshot::featureEnabledDefault));
  EXPECT_CALL(context_.runtime_loader_.snapshot_,
              featureEnabled("http_connection_manager.path_with_escaped_slashes_action_enabled",
                             An<const envoy::type::v3::FractionalPercent&>()))
      .WillOnce(Return(false));
  EXPECT_CALL(context_.runtime_loader_.snapshot_, getInteger(_, _)).Times(AnyNumber());
  EXPECT_CALL(context_.runtime_loader_.snapshot_,
              getInteger("http_connection_manager.path_with_escaped_slashes_action", 0))
      .Times(0);
  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_,
                                     date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);
  EXPECT_EQ(envoy::extensions::filters::network::http_connection_manager::v3::
                HttpConnectionManager::KEEP_UNCHANGED,
            config.pathWithEscapedSlashesAction());
}


TEST_F(HttpConnectionManagerImplTest,
       StartAndFinishSpanNormalFlowEgressDecoratorOverrideOpNoActiveSpan) {
  setup(false, "");
  envoy::type::v3::FractionalPercent percent1;
  percent1.set_numerator(100);
  envoy::type::v3::FractionalPercent percent2;
  percent2.set_numerator(10000);
  percent2.set_denominator(envoy::type::v3::FractionalPercent::TEN_THOUSAND);
  tracing_config_ = std::make_unique<TracingConnectionManagerConfig>(
      TracingConnectionManagerConfig{Tracing::OperationName::Egress,
                                     {{":method", requestHeaderCustomTag(":method")}},
                                     percent1,
                                     percent2,
                                     percent1,
                                     false,
                                     256});

  EXPECT_CALL(
      runtime_.snapshot_,
      featureEnabled("tracing.global_enabled", An<const envoy::type::v3::FractionalPercent&>(), _))
      .WillOnce(Return(false));
  std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());

  EXPECT_CALL(filter_factory_, createFilterChain(_))
      .WillRepeatedly(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void {
        callbacks.addStreamDecoderFilter(filter);
      }));

  // Treat request as internal, otherwise x-request-id header will be overwritten.
  use_remote_address_ = false;
  EXPECT_CALL(random_, uuid()).Times(0);

  EXPECT_CALL(*codec_, dispatch(_))
      .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status {
        decoder_ = &conn_manager_->newStream(response_encoder_);

        RequestHeaderMapPtr headers{
            new TestRequestHeaderMapImpl{{":method", "GET"},
                                         {":authority", "host"},
                                         {":path", "/"},
                                         {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}};
        decoder_->decodeHeaders(std::move(headers), true);

        filter->callbacks_->streamInfo().setResponseCodeDetails("");
        ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{
            {":status", "200"}, {"x-envoy-decorator-operation", "testOp"}}};
        filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details");

        data.drain(4);
        return Http::okStatus();
      }));

  Buffer::OwnedImpl fake_input("1234");
  conn_manager_->onData(fake_input, false);
}


TEST_F(ConnectionManagerUtilityTest, NoPreserveExternalRequestIdEdgeRequestGenerateRequestId) {
  ON_CALL(config_, preserveExternalRequestId()).WillByDefault(Return(false));
  connection_.stream_info_.downstream_address_provider_->setRemoteAddress(
      std::make_shared<Network::Address::Ipv4Instance>("134.2.2.11"));

  // with request id
  {
    ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(true));
    TestRequestHeaderMapImpl headers{{"x-forwarded-for", "198.51.100.1"},
                                     {"x-request-id", "my-request-id"}};
    EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), true));
    EXPECT_CALL(*request_id_extension_, set(_, false)).Times(0);
    EXPECT_EQ((MutateRequestRet{"134.2.2.11:0", false, Tracing::Reason::NotTraceable}),
              callMutateRequestHeaders(headers, Protocol::Http2));
    EXPECT_EQ(random_.uuid_, headers.get_(Headers::get().RequestId));
  }

  // with no request id
  {
    TestRequestHeaderMapImpl headers{{"x-forwarded-for", "198.51.100.1"}};
    EXPECT_CALL(*request_id_extension_, set(testing::Ref(headers), true));
    EXPECT_CALL(*request_id_extension_, set(_, false)).Times(0);
    EXPECT_EQ((MutateRequestRet{"134.2.2.11:0", false, Tracing::Reason::NotTraceable}),
              callMutateRequestHeaders(headers, Protocol::Http2));
    EXPECT_EQ(random_.uuid_, headers.get_(Headers::get().RequestId));
  }
}


TEST_P(DownstreamProtocolIntegrationTest, LocalReplyDuringEncoding) {
  config_helper_.addFilter(R"EOF(
name: local-reply-during-encode
)EOF");
  initialize();

  codec_client_ = makeHttpConnection(lookupPort("http"));

  auto response = codec_client_->makeHeaderOnlyRequest(
      Http::TestRequestHeaderMapImpl{{":method", "GET"},
                                     {":path", "/test/long/url"},
                                     {":scheme", "http"},
                                     {":authority", "host"}});

  // Wait for the upstream request and begin sending a response with end_stream = false.
  waitForNextUpstreamRequest();
  upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "503"}}, true);

  ASSERT_TRUE(response->waitForEndStream());
  EXPECT_TRUE(response->complete());
  EXPECT_EQ("500", response->headers().getStatusValue());
  EXPECT_EQ(0, upstream_request_->body().length());
}


TEST_F(HttpConnectionManagerConfigTest, DEPRECATED_FEATURE_TEST(IdleTimeout)) {
  TestDeprecatedV2Api _deprecated_v2_api;
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  idle_timeout: 1s
  route_config:
    name: local_route
  http_filters:
  - name: envoy.filters.http.router
  )EOF";

  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string, false),
                                     context_, date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);
  EXPECT_EQ(1000, config.idleTimeout().value().count());
}


TEST_F(HttpConnectionManagerConfigTest, ServerOverwrite) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  server_header_transformation: OVERWRITE
  route_config:
    name: local_route
  http_filters:
  - name: envoy.filters.http.router
  )EOF";

  EXPECT_CALL(context_.runtime_loader_.snapshot_, featureEnabled(_, An<uint64_t>()))
      .WillRepeatedly(Invoke(&context_.runtime_loader_.snapshot_,
                             &Runtime::MockSnapshot::featureEnabledDefault));
  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_,
                                     date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);
  EXPECT_EQ(HttpConnectionManagerConfig::HttpConnectionManagerProto::OVERWRITE,
            config.serverHeaderTransformation());
}


TEST_P(ProtocolIntegrationTest, EnvoyProxyingLateMultiple1xx) {
  testEnvoyProxying1xx(false, false, true);
}


std::string testField() { return config_.test_field(); }


TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresRemainByDefault) {
  initialize();
  codec_client_ = makeHttpConnection(lookupPort("http"));
  auto response = codec_client_->makeHeaderOnlyRequest(
      Http::TestRequestHeaderMapImpl{{":method", "GET"},
                                     {":path", "/test/long/url"},
                                     {":scheme", "http"},
                                     {":authority", "host"},
                                     {"foo_bar", "baz"}});
  waitForNextUpstreamRequest();

  EXPECT_THAT(upstream_request_->headers(), HeaderHasValueRef("foo_bar", "baz"));
  upstream_request_->encodeHeaders(
      Http::TestResponseHeaderMapImpl{{":status", "200"}, {"bar_baz", "fooz"}}, true);
  ASSERT_TRUE(response->waitForEndStream());
  EXPECT_TRUE(response->complete());
  EXPECT_EQ("200", response->headers().getStatusValue());
  EXPECT_THAT(response->headers(), HeaderHasValueRef("bar_baz", "fooz"));
}


Network::Socket& mutableSocket() { return *socket_; }


TEST_F(ConnectionManagerUtilityTest, MutateResponseHeaders) {
  TestResponseHeaderMapImpl response_headers{
      {"connection", "foo"}, {"transfer-encoding", "foo"}, {"custom_header", "custom_value"}};
  TestRequestHeaderMapImpl request_headers{{"x-request-id", "request-id"}};

  ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_, "");

  EXPECT_EQ(1UL, response_headers.size());
  EXPECT_EQ("custom_value", response_headers.get_("custom_header"));
  EXPECT_FALSE(response_headers.has("x-request-id"));
  EXPECT_FALSE(response_headers.has(Headers::get().Via));
}


  void decoderFilterCallbackAction(
      const test::common::http::DecoderFilterCallbackAction& decoder_filter_callback_action) {
    switch (decoder_filter_callback_action.decoder_filter_callback_action_selector_case()) {
    case test::common::http::DecoderFilterCallbackAction::kAddDecodedData: {
      if (request_state_ == StreamState::PendingDataOrTrailers) {
        Buffer::OwnedImpl buf(std::string(
            decoder_filter_callback_action.add_decoded_data().size() % (1024 * 1024), 'a'));
        decoder_filter_->callbacks_->addDecodedData(
            buf, decoder_filter_callback_action.add_decoded_data().streaming());
      }
      break;
    }
    default:
      // Maybe nothing is set?
      break;
    }
  }


TEST_F(HttpConnectionManagerConfigTest, UnknownRequestIDExtension) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  route_config:
    name: local_route
  request_id_extension:
    typed_config:
      "@type": type.googleapis.com/test.http_connection_manager.UnknownRequestIDExtension
  http_filters:
  - name: envoy.filters.http.router
  )EOF";

  EXPECT_THROW_WITH_REGEX(createHttpConnectionManagerConfig(yaml_string), EnvoyException,
                          "Didn't find a registered implementation for type");
}


void ConnectionManagerImpl::ActiveStream::onRequestTimeout() {
  connection_manager_.stats_.named_.downstream_rq_timeout_.inc();
  sendLocalReply(request_headers_ != nullptr &&
                     Grpc::Common::isGrpcRequestHeaders(*request_headers_),
                 Http::Code::RequestTimeout, "request timeout", nullptr, absl::nullopt,
                 StreamInfo::ResponseCodeDetails::get().RequestOverallTimeout);
}


TEST_F(HttpConnectionManagerImplTest, DrainCloseRaceWithClose) {
  InSequence s;
  setup(false, "");

  EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status {
    decoder_ = &conn_manager_->newStream(response_encoder_);
    RequestHeaderMapPtr headers{
        new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}};
    decoder_->decodeHeaders(std::move(headers), true);
    return Http::okStatus();
  }));

  setupFilterChain(1, 0);

  EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true))
      .WillOnce(Return(FilterHeadersStatus::StopIteration));
  EXPECT_CALL(*decoder_filters_[0], decodeComplete());

  Buffer::OwnedImpl fake_input;
  conn_manager_->onData(fake_input, false);

  ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}};
  EXPECT_CALL(drain_close_, drainClose()).WillOnce(Return(true));
  EXPECT_CALL(*codec_, shutdownNotice());
  Event::MockTimer* drain_timer = setUpTimer();
  EXPECT_CALL(*drain_timer, enableTimer(_, _));
  expectOnDestroy();
  decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails("");
  decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), true, "details");

  // Fake a protocol error that races with the drain timeout. This will cause a local close.
  // Also fake the local close not closing immediately.
  EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Return(codecProtocolError("protocol error")));
  EXPECT_CALL(*drain_timer, disableTimer());
  EXPECT_CALL(filter_callbacks_.connection_,
              close(Network::ConnectionCloseType::FlushWriteAndDelay))
      .WillOnce(Return());
  conn_manager_->onData(fake_input, false);

  // Now fire the close event which should have no effect as all close work has already been done.
  filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::LocalClose);
}


void ConnectionManagerImpl::ActiveStream::encodeTrailers(ResponseTrailerMap& trailers) {
  ENVOY_STREAM_LOG(debug, "encoding trailers via codec:\n{}", *this, trailers);

  response_encoder_->encodeTrailers(trailers);
}


  HttpConnectionManagerFilterConfigFactory()
      : FactoryBase(NetworkFilterNames::get().HttpConnectionManager, true) {}


TEST_P(HeaderIntegrationTest, TestDynamicHeaders) {
  prepareEDS();
  initializeFilter(HeaderMode::Replace, true);
  performRequest(
      Http::TestRequestHeaderMapImpl{
          {":method", "GET"},
          {":path", "/vhost-route-and-weighted-clusters"},
          {":scheme", "http"},
          {":authority", "vhost-headers.com"},
          {"x-routeconfig-request", "downstream"},
          {"x-vhost-request", "downstream"},
          {"x-route-request", "downstream"},
          {"x-weighted-cluster-request", "downstream"},
          {"x-unmodified", "request"},
      },
      Http::TestRequestHeaderMapImpl{
          {":authority", "vhost-headers.com"},
          {"x-unmodified", "request"},
          {"x-weighted-cluster-request", "weighted-cluster-1"},
          {"x-route-request", "route"},
          {"x-vhost-request", "vhost"},
          {"x-routeconfig-request", "routeconfig"},
          {":path", "/vhost-route-and-weighted-clusters"},
          {":method", "GET"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"content-length", "0"},
          {":status", "200"},
          {"x-routeconfig-response", "upstream"},
          {"x-vhost-response", "upstream"},
          {"x-route-response", "upstream"},
          {"x-weighted-cluster-response", "upstream"},
          {"x-unmodified", "response"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"x-unmodified", "response"},
          {"x-weighted-cluster-response", "weighted-cluster-1"},
          {"x-weighted-cluster-dynamic", "weighted:metadata-value"},
          {"x-route-response", "route"},
          {"x-route-dynamic", "route:metadata-value"},
          {"x-vhost-response", "vhost"},
          {"x-vhost-dynamic", "vhost:metadata-value"},
          {"x-routeconfig-response", "routeconfig"},
          {"x-routeconfig-dynamic", "metadata-value"},
          {":status", "200"},
      });
}


TEST_F(HttpConnectionManagerImplTest, DelegatingRouteEntryAllCalls) {
  setup(false, "");
  setupFilterChain(2, 0);

  // Cluster mock: foo
  // For when decoder_filters_[0] invokes setRoute
  const std::string foo_cluster_name = "foo";
  std::shared_ptr<Upstream::MockThreadLocalCluster> foo_cluster =
      std::make_shared<NiceMock<Upstream::MockThreadLocalCluster>>();
  EXPECT_CALL(cluster_manager_, getThreadLocalCluster(absl::string_view{foo_cluster_name}))
      .Times(1)
      .WillRepeatedly(Return(foo_cluster.get()));

  // Cluster mock: default
  // For decoder_filters_[0] invokes decodeHeaders and subsequently refreshCachedRoute
  const std::string default_cluster_name = "default";
  std::shared_ptr<Upstream::MockThreadLocalCluster> default_cluster =
      std::make_shared<NiceMock<Upstream::MockThreadLocalCluster>>();
  EXPECT_CALL(cluster_manager_, getThreadLocalCluster(absl::string_view{default_cluster_name}))
      .Times(1)
      .WillRepeatedly(Return(default_cluster.get()));

  // Route mock: default
  // For when decoder_filters_[0] invokes decodeHeaders and subsequently refreshCachedRoute
  std::shared_ptr<Router::MockRoute> default_route =
      std::make_shared<NiceMock<Router::MockRoute>>();
  EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _))
      .Times(1)
      .WillRepeatedly(Return(default_route));
  EXPECT_CALL(default_route->route_entry_, clusterName())
      .Times(1)
      .WillRepeatedly(ReturnRef(default_cluster_name));

  // DelegatingRoute: foo
  std::shared_ptr<const Router::ExampleDerivedDelegatingRoute> delegating_route_foo(nullptr);

  EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true))
      .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus {
        // Instantiate a DelegatingRoute child class object and invoke setRoute from
        // StreamFilterCallbacks to manually override the cached route for the current request.
        delegating_route_foo = std::make_shared<Router::ExampleDerivedDelegatingRoute>(
            default_route, foo_cluster_name);
        decoder_filters_[0]->callbacks_->setRoute(delegating_route_foo);

        return FilterHeadersStatus::Continue;
      }));
  EXPECT_CALL(*decoder_filters_[0], decodeComplete());

  EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true))
      .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus {
        // Check that cached_route was correctly set to the delegating route.
        EXPECT_EQ(delegating_route_foo, decoder_filters_[1]->callbacks_->route());

        // Check that delegating route correctly overrides the routeEntry()->clusterName()
        EXPECT_EQ(foo_cluster_name, delegating_route_foo->routeEntry()->clusterName());

        // Check that all other routeEntry calls are delegated correctly
        EXPECT_EQ(default_route->routeEntry()->clusterNotFoundResponseCode(),
                  delegating_route_foo->routeEntry()->clusterNotFoundResponseCode());
        EXPECT_EQ(default_route->routeEntry()->corsPolicy(),
                  delegating_route_foo->routeEntry()->corsPolicy());

        auto test_req_headers = Http::TestRequestHeaderMapImpl{{":authority", "www.choice.com"},
                                                               {":path", "/new_endpoint/foo"},
                                                               {":method", "GET"},
                                                               {"x-forwarded-proto", "http"}};
        EXPECT_EQ(default_route->routeEntry()->currentUrlPathAfterRewrite(test_req_headers),
                  delegating_route_foo->routeEntry()->currentUrlPathAfterRewrite(test_req_headers));

        EXPECT_EQ(default_route->routeEntry()->hashPolicy(),
                  delegating_route_foo->routeEntry()->hashPolicy());

        // HedgePolicy objects don't have valid comparison operator, so compare 2 attributes at
        // random. Will apply this strategy for all objects that don't have a valid comparison
        // operator (next example being RetryPolicy).
        EXPECT_EQ(default_route->routeEntry()->hedgePolicy().additionalRequestChance().numerator(),
                  delegating_route_foo->routeEntry()
                      ->hedgePolicy()
                      .additionalRequestChance()
                      .numerator());
        EXPECT_EQ(default_route->routeEntry()->hedgePolicy().initialRequests(),
                  delegating_route_foo->routeEntry()->hedgePolicy().initialRequests());

        EXPECT_EQ(default_route->routeEntry()->priority(),
                  delegating_route_foo->routeEntry()->priority());

        EXPECT_EQ(default_route->routeEntry()->rateLimitPolicy().empty(),
                  delegating_route_foo->routeEntry()->rateLimitPolicy().empty());
        EXPECT_EQ(default_route->routeEntry()->rateLimitPolicy().getApplicableRateLimit(0).empty(),
                  delegating_route_foo->routeEntry()
                      ->rateLimitPolicy()
                      .getApplicableRateLimit(0)
                      .empty());

        EXPECT_EQ(default_route->routeEntry()->retryPolicy().numRetries(),
                  delegating_route_foo->routeEntry()->retryPolicy().numRetries());
        EXPECT_EQ(default_route->routeEntry()->retryPolicy().retryOn(),
                  delegating_route_foo->routeEntry()->retryPolicy().retryOn());

        EXPECT_EQ(default_route->routeEntry()->internalRedirectPolicy().enabled(),
                  delegating_route_foo->routeEntry()->internalRedirectPolicy().enabled());
        EXPECT_EQ(
            default_route->routeEntry()->internalRedirectPolicy().shouldRedirectForResponseCode(
                Code::OK),
            delegating_route_foo->routeEntry()
                ->internalRedirectPolicy()
                .shouldRedirectForResponseCode(Code::OK));

        EXPECT_EQ(default_route->routeEntry()->retryShadowBufferLimit(),
                  delegating_route_foo->routeEntry()->retryShadowBufferLimit());
        EXPECT_EQ(default_route->routeEntry()->shadowPolicies().empty(),
                  delegating_route_foo->routeEntry()->shadowPolicies().empty());
        EXPECT_EQ(default_route->routeEntry()->timeout(),
                  delegating_route_foo->routeEntry()->timeout());
        EXPECT_EQ(default_route->routeEntry()->idleTimeout(),
                  delegating_route_foo->routeEntry()->idleTimeout());
        EXPECT_EQ(default_route->routeEntry()->usingNewTimeouts(),
                  delegating_route_foo->routeEntry()->usingNewTimeouts());
        EXPECT_EQ(default_route->routeEntry()->maxStreamDuration(),
                  delegating_route_foo->routeEntry()->maxStreamDuration());
        EXPECT_EQ(default_route->routeEntry()->grpcTimeoutHeaderMax(),
                  delegating_route_foo->routeEntry()->grpcTimeoutHeaderMax());
        EXPECT_EQ(default_route->routeEntry()->grpcTimeoutHeaderOffset(),
                  delegating_route_foo->routeEntry()->grpcTimeoutHeaderOffset());
        EXPECT_EQ(default_route->routeEntry()->maxGrpcTimeout(),
                  delegating_route_foo->routeEntry()->maxGrpcTimeout());
        EXPECT_EQ(default_route->routeEntry()->grpcTimeoutOffset(),
                  delegating_route_foo->routeEntry()->grpcTimeoutOffset());
        EXPECT_EQ(default_route->routeEntry()->virtualCluster(test_req_headers),
                  delegating_route_foo->routeEntry()->virtualCluster(test_req_headers));

        EXPECT_EQ(default_route->routeEntry()->virtualHost().corsPolicy(),
                  delegating_route_foo->routeEntry()->virtualHost().corsPolicy());
        EXPECT_EQ(default_route->routeEntry()->virtualHost().rateLimitPolicy().empty(),
                  delegating_route_foo->routeEntry()->virtualHost().rateLimitPolicy().empty());

        EXPECT_EQ(default_route->routeEntry()->autoHostRewrite(),
                  delegating_route_foo->routeEntry()->autoHostRewrite());
        EXPECT_EQ(default_route->routeEntry()->metadataMatchCriteria(),
                  delegating_route_foo->routeEntry()->metadataMatchCriteria());
        EXPECT_EQ(default_route->routeEntry()->opaqueConfig(),
                  delegating_route_foo->routeEntry()->opaqueConfig());
        EXPECT_EQ(default_route->routeEntry()->includeVirtualHostRateLimits(),
                  delegating_route_foo->routeEntry()->includeVirtualHostRateLimits());

        // NOTE: no coverage for routeEntry()->typedMetadata()
        // "The mock function has no default action set, and its return type has no default value
        // set"

        EXPECT_EQ(default_route->routeEntry()->metadata().filter_metadata().size(),
                  delegating_route_foo->routeEntry()->metadata().filter_metadata().size());
        EXPECT_EQ(default_route->routeEntry()->tlsContextMatchCriteria(),
                  delegating_route_foo->routeEntry()->tlsContextMatchCriteria());

        EXPECT_EQ(default_route->routeEntry()->pathMatchCriterion().matcher(),
                  delegating_route_foo->routeEntry()->pathMatchCriterion().matcher());
        EXPECT_EQ(default_route->routeEntry()->pathMatchCriterion().matchType(),
                  delegating_route_foo->routeEntry()->pathMatchCriterion().matchType());

        EXPECT_EQ(default_route->routeEntry()->perFilterConfig("bar"),
                  delegating_route_foo->routeEntry()->perFilterConfig("bar"));
        EXPECT_EQ(default_route->routeEntry()->includeAttemptCountInRequest(),
                  delegating_route_foo->routeEntry()->includeAttemptCountInRequest());
        EXPECT_EQ(default_route->routeEntry()->includeAttemptCountInResponse(),
                  delegating_route_foo->routeEntry()->includeAttemptCountInResponse());
        EXPECT_EQ(default_route->routeEntry()->upgradeMap(),
                  delegating_route_foo->routeEntry()->upgradeMap());

        EXPECT_EQ(default_route->routeEntry()->connectConfig().has_value(),
                  delegating_route_foo->routeEntry()->connectConfig().has_value());
        if (default_route->routeEntry()->connectConfig().has_value()) {
          EXPECT_EQ(default_route->routeEntry()->connectConfig().value().allow_post(),
                    delegating_route_foo->routeEntry()->connectConfig().value().allow_post());
        }

        EXPECT_EQ(default_route->routeEntry()->routeName(),
                  delegating_route_foo->routeEntry()->routeName());

        // Coverage for finalizeRequestHeaders
        NiceMock<Envoy::StreamInfo::MockStreamInfo> stream_info;
        delegating_route_foo->routeEntry()->finalizeRequestHeaders(test_req_headers, stream_info,
                                                                   true);
        EXPECT_EQ("/new_endpoint/foo", test_req_headers.get_(Http::Headers::get().Path));

        // Coverage for finalizeResponseHeaders
        Http::TestResponseHeaderMapImpl test_resp_headers;
        delegating_route_foo->routeEntry()->finalizeResponseHeaders(test_resp_headers, stream_info);
        EXPECT_EQ(test_resp_headers, Http::TestResponseHeaderMapImpl{});

        return FilterHeadersStatus::StopIteration;
      }));
  EXPECT_CALL(*decoder_filters_[1], decodeComplete());

  // Kick off the incoming data. end_stream set to true to indicate this is a header only request.
  startRequest(true);

  // Clean up.
  expectOnDestroy();
  filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose);
}


TEST_F(ConnectionManagerUtilityTest, MtlsForwardOnlyClientCert) {
  auto ssl = std::make_shared<NiceMock<Ssl::MockConnectionInfo>>();
  ON_CALL(*ssl, peerCertificatePresented()).WillByDefault(Return(true));
  ON_CALL(connection_, ssl()).WillByDefault(Return(ssl));
  ON_CALL(config_, forwardClientCert())
      .WillByDefault(Return(Http::ForwardClientCertType::ForwardOnly));
  std::vector<Http::ClientCertDetailsType> details;
  ON_CALL(config_, setCurrentClientCertDetails()).WillByDefault(ReturnRef(details));
  TestRequestHeaderMapImpl headers{
      {"x-forwarded-client-cert", "By=test://foo.com/fe;URI=test://bar.com/be;DNS=example.com"}};

  EXPECT_EQ((MutateRequestRet{"10.0.0.3:50000", false, Tracing::Reason::NotTraceable}),
            callMutateRequestHeaders(headers, Protocol::Http2));
  EXPECT_TRUE(headers.has("x-forwarded-client-cert"));
  EXPECT_EQ("By=test://foo.com/fe;URI=test://bar.com/be;DNS=example.com",
            headers.get_("x-forwarded-client-cert"));
}


TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReplyWithBody) {
  useAccessLog("%RESPONSE_CODE_DETAILS%");
  config_helper_.addFilter("{ name: invalid-header-filter, typed_config: { \"@type\": "
                           "type.googleapis.com/google.protobuf.Empty } }");
  initialize();
  codec_client_ = makeHttpConnection(lookupPort("http"));

  // Missing method
  auto response =
      codec_client_->makeRequestWithBody(Http::TestRequestHeaderMapImpl{{":method", "GET"},
                                                                        {":path", "/test/long/url"},
                                                                        {":scheme", "http"},
                                                                        {":authority", "host"},
                                                                        {"remove-method", "yes"},
                                                                        {"send-reply", "yes"}},
                                         1024);
  ASSERT_TRUE(response->waitForEndStream());
  EXPECT_TRUE(response->complete());
  EXPECT_EQ("200", response->headers().getStatusValue());
  EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("InvalidHeaderFilter_ready\n"));
}


TEST_F(HttpConnectionManagerConfigTest, CommonHttpProtocolIdleTimeout) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  common_http_protocol_options:
    idle_timeout: 1s
  route_config:
    name: local_route
  http_filters:
  - name: envoy.filters.http.router
  )EOF";

  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_,
                                     date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);
  EXPECT_EQ(1000, config.idleTimeout().value().count());
}


Tracing::Span& ConnectionManagerImpl::ActiveStream::activeSpan() {
  if (active_span_) {
    return *active_span_;
  } else {
    return Tracing::NullSpan::instance();
  }
}


void HttpConnectionManagerConfig::createFilterChainForFactories(
    Http::FilterChainFactoryCallbacks& callbacks, const FilterFactoriesList& filter_factories) {
  bool added_missing_config_filter = false;
  for (const auto& filter_config_provider : filter_factories) {
    auto config = filter_config_provider->config();
    if (config.has_value()) {
      config.value()(callbacks);
      continue;
    }

    // If a filter config is missing after warming, inject a local reply with status 500.
    if (!added_missing_config_filter) {
      ENVOY_LOG(trace, "Missing filter config for a provider {}", filter_config_provider->name());
      callbacks.addStreamDecoderFilter(
          Http::StreamDecoderFilterSharedPtr{std::make_shared<MissingConfigFilter>()});
      added_missing_config_filter = true;
    } else {
      ENVOY_LOG(trace, "Provider {} missing a filter config", filter_config_provider->name());
    }
  }
}


TEST_F(HttpConnectionManagerImplTest, 100ContinueResponseWithEncoderFiltersProxyingDisabled) {
  proxy_100_continue_ = false;
  setup(false, "envoy-custom-server", false);
  setUpEncoderAndDecoder(false, false);
  sendRequestHeadersAndData();

  // Akin to 100ContinueResponseWithEncoderFilters below, but with
  // proxy_100_continue_ false. Verify the filters do not get the 100 continue
  // headers.
  EXPECT_CALL(*encoder_filters_[0], encode100ContinueHeaders(_)).Times(0);
  EXPECT_CALL(*encoder_filters_[1], encode100ContinueHeaders(_)).Times(0);
  EXPECT_CALL(response_encoder_, encode100ContinueHeaders(_)).Times(0);
  ResponseHeaderMapPtr continue_headers{new TestResponseHeaderMapImpl{{":status", "100"}}};
  decoder_filters_[0]->callbacks_->encode100ContinueHeaders(std::move(continue_headers));

  EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false))
      .WillOnce(Return(FilterHeadersStatus::Continue));
  EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false))
      .WillOnce(Return(FilterHeadersStatus::Continue));
  EXPECT_CALL(response_encoder_, encodeHeaders(_, false));
  ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}};
  decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails("");
  decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details");

  doRemoteClose();
}


TEST_F(ConnectionManagerUtilityTest, ClearUpgradeHeadersForNonUpgradeRequests) {
  // Test clearing non-upgrade request and response headers
  {
    TestRequestHeaderMapImpl request_headers{{"x-request-id", "request-id"}};
    TestResponseHeaderMapImpl response_headers{
        {"connection", "foo"}, {"transfer-encoding", "bar"}, {"custom_header", "custom_value"}};
    EXPECT_FALSE(Utility::isUpgrade(request_headers));
    EXPECT_FALSE(Utility::isUpgrade(response_headers));
    ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_,
                                                    "");

    EXPECT_EQ(1UL, response_headers.size()) << response_headers;
    EXPECT_EQ("custom_value", response_headers.get_("custom_header"));
  }

  // Test with the request headers not valid upgrade headers
  {
    TestRequestHeaderMapImpl request_headers{{"upgrade", "foo"}};
    TestResponseHeaderMapImpl response_headers{{"connection", "upgrade"},
                                               {"transfer-encoding", "eep"},
                                               {"upgrade", "foo"},
                                               {"custom_header", "custom_value"}};
    EXPECT_FALSE(Utility::isUpgrade(request_headers));
    EXPECT_TRUE(Utility::isUpgrade(response_headers));
    ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_,
                                                    "");

    EXPECT_EQ(1UL, response_headers.size()) << response_headers;
    EXPECT_EQ("custom_value", response_headers.get_("custom_header"));
  }

  // Test with the response headers not valid upgrade headers
  {
    TestRequestHeaderMapImpl request_headers{{"connection", "UpGrAdE"}, {"upgrade", "foo"}};
    TestResponseHeaderMapImpl response_headers{{"transfer-encoding", "foo"}, {"upgrade", "bar"}};
    EXPECT_TRUE(Utility::isUpgrade(request_headers));
    EXPECT_FALSE(Utility::isUpgrade(response_headers));
    ConnectionManagerUtility::mutateResponseHeaders(response_headers, &request_headers, config_,
                                                    "");

    EXPECT_EQ(0UL, response_headers.size()) << response_headers;
  }
}


const Http::LowerCaseString& traceStatusHeader() {
  static Http::LowerCaseString header("x-trace-status");
  return header;
}


TEST_P(HeaderIntegrationTest, TestTeHeaderPassthrough) {
  initializeFilter(HeaderMode::Append, false);
  performRequest(
      Http::TestRequestHeaderMapImpl{
          {":method", "GET"},
          {":path", "/"},
          {":scheme", "http"},
          {":authority", "no-headers.com"},
          {"x-request-foo", "downstram"},
          {"connection", "te, close"},
          {"te", "trailers"},
      },
      Http::TestRequestHeaderMapImpl{
          {":authority", "no-headers.com"},
          {":path", "/"},
          {":method", "GET"},
          {"x-request-foo", "downstram"},
          {"te", "trailers"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"content-length", "0"},
          {":status", "200"},
          {"x-return-foo", "upstream"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"x-return-foo", "upstream"},
          {":status", "200"},
          {"connection", "close"},
      });
}


TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecoratorOverrideOp) {
  setup(false, "");
  envoy::type::v3::FractionalPercent percent1;
  percent1.set_numerator(100);
  envoy::type::v3::FractionalPercent percent2;
  percent2.set_numerator(10000);
  percent2.set_denominator(envoy::type::v3::FractionalPercent::TEN_THOUSAND);
  tracing_config_ = std::make_unique<TracingConnectionManagerConfig>(
      TracingConnectionManagerConfig{Tracing::OperationName::Egress,
                                     {{":method", requestHeaderCustomTag(":method")}},
                                     percent1,
                                     percent2,
                                     percent1,
                                     false,
                                     256});

  auto* span = new NiceMock<Tracing::MockSpan>();
  EXPECT_CALL(*tracer_, startSpan_(_, _, _, _))
      .WillOnce(
          Invoke([&](const Tracing::Config& config, const HeaderMap&, const StreamInfo::StreamInfo&,
                     const Tracing::Decision) -> Tracing::Span* {
            EXPECT_EQ(Tracing::OperationName::Egress, config.operationName());

            return span;
          }));
  route_config_provider_.route_config_->route_->decorator_.operation_ = "initOp";
  EXPECT_CALL(*route_config_provider_.route_config_->route_, decorator()).Times(2);
  EXPECT_CALL(route_config_provider_.route_config_->route_->decorator_, apply(_))
      .WillOnce(Invoke(
          [&](const Tracing::Span& apply_to_span) -> void { EXPECT_EQ(span, &apply_to_span); }));
  EXPECT_CALL(*span, finishSpan());
  EXPECT_CALL(*span, setTag(_, _)).Times(testing::AnyNumber());
  EXPECT_CALL(
      runtime_.snapshot_,
      featureEnabled("tracing.global_enabled", An<const envoy::type::v3::FractionalPercent&>(), _))
      .WillOnce(Return(true));
  // Verify that span operation overridden by value supplied in response header.
  EXPECT_CALL(*span, setOperation(Eq("testOp")));

  std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());

  EXPECT_CALL(filter_factory_, createFilterChain(_))
      .WillRepeatedly(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void {
        callbacks.addStreamDecoderFilter(filter);
      }));

  // Treat request as internal, otherwise x-request-id header will be overwritten.
  use_remote_address_ = false;
  EXPECT_CALL(random_, uuid()).Times(0);

  EXPECT_CALL(*codec_, dispatch(_))
      .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status {
        decoder_ = &conn_manager_->newStream(response_encoder_);

        RequestHeaderMapPtr headers{
            new TestRequestHeaderMapImpl{{":method", "GET"},
                                         {":authority", "host"},
                                         {":path", "/"},
                                         {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}};
        decoder_->decodeHeaders(std::move(headers), true);

        filter->callbacks_->streamInfo().setResponseCodeDetails("");
        ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{
            {":status", "200"}, {"x-envoy-decorator-operation", "testOp"}}};
        filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details");
        filter->callbacks_->activeSpan().setTag("service-cluster", "scoobydoo");

        data.drain(4);
        return Http::okStatus();
      }));

  Buffer::OwnedImpl fake_input("1234");
  conn_manager_->onData(fake_input, false);
}


TEST_F(HttpConnectionManagerConfigTest, Http3Configured) {
  const std::string yaml_string = R"EOF(
codec_type: http3
server_name: foo
stat_prefix: router
route_config:
  virtual_hosts:
  - name: service
    domains:
    - "*"
    routes:
    - match:
        prefix: "/"
      route:
        cluster: cluster
http_filters:
- name: envoy.filters.http.router
  )EOF";

#ifdef ENVOY_ENABLE_QUIC
  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_,
                                     date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);
#else
  EXPECT_THROW_WITH_MESSAGE(
      HttpConnectionManagerConfig(parseHttpConnectionManagerFromYaml(yaml_string), context_,
                                  date_provider_, route_config_provider_manager_,
                                  scoped_routes_config_provider_manager_, http_tracer_manager_,
                                  filter_config_provider_manager_),
      EnvoyException, "HTTP3 configured but not enabled in the build.");
#endif
}


TEST_F(HttpConnectionManagerImplTest, RequestTimeoutCallbackDisarmsAndReturns408) {
  request_timeout_ = std::chrono::milliseconds(10);
  setup(false, "");

  std::string response_body;
  EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status {
    Event::MockTimer* request_timer = setUpTimer();
    EXPECT_CALL(*request_timer, enableTimer(request_timeout_, _));
    EXPECT_CALL(*request_timer, disableTimer()).Times(AtLeast(1));

    EXPECT_CALL(response_encoder_, encodeHeaders(_, false))
        .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void {
          EXPECT_EQ("408", headers.getStatusValue());
        }));
    EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body));

    conn_manager_->newStream(response_encoder_);
    EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, pushTrackedObject(_));
    EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_));
    request_timer->invokeCallback();
    return Http::okStatus();
  }));

  Buffer::OwnedImpl fake_input("1234");
  conn_manager_->onData(fake_input, false); // kick off request

  EXPECT_EQ(1U, stats_.named_.downstream_rq_timeout_.value());
  EXPECT_EQ("request timeout", response_body);
}


TEST_F(HttpConnectionManagerImplTest, TestAccessLogSsl) {
  setup(true, "");

  std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());
  std::shared_ptr<AccessLog::MockInstance> handler(new NiceMock<AccessLog::MockInstance>());

  EXPECT_CALL(filter_factory_, createFilterChain(_))
      .WillOnce(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void {
        callbacks.addStreamDecoderFilter(filter);
        callbacks.addAccessLogHandler(handler);
      }));

  EXPECT_CALL(*handler, log(_, _, _, _))
      .WillOnce(Invoke([](const HeaderMap*, const HeaderMap*, const HeaderMap*,
                          const StreamInfo::StreamInfo& stream_info) {
        EXPECT_TRUE(stream_info.responseCode());
        EXPECT_EQ(stream_info.responseCode().value(), uint32_t(200));
        EXPECT_NE(nullptr, stream_info.downstreamAddressProvider().localAddress());
        EXPECT_NE(nullptr, stream_info.downstreamAddressProvider().remoteAddress());
        EXPECT_NE(nullptr, stream_info.downstreamAddressProvider().directRemoteAddress());
        EXPECT_NE(nullptr, stream_info.downstreamSslConnection());
        EXPECT_NE(nullptr, stream_info.routeEntry());
      }));

  EXPECT_CALL(*codec_, dispatch(_))
      .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status {
        decoder_ = &conn_manager_->newStream(response_encoder_);

        RequestHeaderMapPtr headers{
            new TestRequestHeaderMapImpl{{":method", "GET"},
                                         {":authority", "host"},
                                         {":path", "/"},
                                         {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}};
        decoder_->decodeHeaders(std::move(headers), true);

        filter->callbacks_->streamInfo().setResponseCodeDetails("");
        ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}};
        filter->callbacks_->encodeHeaders(std::move(response_headers), false, "details");

        ResponseTrailerMapPtr response_trailers{new TestResponseTrailerMapImpl{{"x-trailer", "1"}}};
        filter->callbacks_->encodeTrailers(std::move(response_trailers));

        data.drain(4);
        return Http::okStatus();
      }));

  Buffer::OwnedImpl fake_input("1234");
  conn_manager_->onData(fake_input, false);
}


TEST_P(HeaderIntegrationTest, TestVirtualHostAppendHeaderManipulation) {
  initializeFilter(HeaderMode::Append, false);
  performRequest(
      Http::TestRequestHeaderMapImpl{
          {":method", "GET"},
          {":path", "/vhost-only"},
          {":scheme", "http"},
          {":authority", "vhost-headers.com"},
          {"x-vhost-request", "downstream"},
          {"x-vhost-request-remove", "downstream"},
      },
      Http::TestRequestHeaderMapImpl{
          {":authority", "vhost-headers.com"},
          {"x-vhost-request", "downstream"},
          {"x-vhost-request", "vhost"},
          {":path", "/vhost-only"},
          {":method", "GET"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"content-length", "0"},
          {":status", "200"},
          {"x-vhost-response", "upstream"},
          {"x-vhost-response-remove", "upstream"},
      },
      Http::TestResponseHeaderMapImpl{
          {"server", "envoy"},
          {"x-vhost-response", "upstream"},
          {"x-vhost-response", "vhost"},
          {":status", "200"},
      });
}


TEST_F(HttpConnectionManagerImplTest, RejectWebSocketOnNonWebSocketRoute) {
  setup(false, "");
  EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status {
    decoder_ = &conn_manager_->newStream(response_encoder_);
    RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"},
                                                             {":method", "GET"},
                                                             {":path", "/"},
                                                             {"connection", "Upgrade"},
                                                             {"upgrade", "websocket"}}};
    decoder_->decodeHeaders(std::move(headers), false);
    // Try sending trailers after the headers which will be rejected, just to
    // test the HCM logic that further decoding will not be passed to the
    // filters once the early response path is kicked off.
    RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"bazzz", "bar"}}};
    decoder_->decodeTrailers(std::move(trailers));
    data.drain(4);
    return Http::okStatus();
  }));

  EXPECT_CALL(response_encoder_, encodeHeaders(_, true))
      .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void {
        EXPECT_EQ("403", headers.getStatusValue());
      }));

  Buffer::OwnedImpl fake_input("1234");
  conn_manager_->onData(fake_input, false);

  EXPECT_EQ(1U, stats_.named_.downstream_rq_ws_on_non_ws_route_.value());
}


TEST_F(ConnectionManagerUtilityTest, RemovePort) {
  ON_CALL(config_, stripPortType()).WillByDefault(Return(Http::StripPortType::MatchingHost));
  TestRequestHeaderMapImpl original_headers;
  original_headers.setHost("host:443");

  TestRequestHeaderMapImpl header_map(original_headers);
  ConnectionManagerUtility::maybeNormalizeHost(header_map, config_, 443);
  EXPECT_EQ(header_map.getHostValue(), "host");

  ON_CALL(config_, stripPortType()).WillByDefault(Return(Http::StripPortType::Any));
  TestRequestHeaderMapImpl original_headers_any;
  original_headers_any.setHost("host:9999");

  TestRequestHeaderMapImpl header_map_any(original_headers_any);
  ConnectionManagerUtility::maybeNormalizeHost(header_map_any, config_, 7777);
  EXPECT_EQ(header_map_any.getHostValue(), "host");

  ON_CALL(config_, stripPortType()).WillByDefault(Return(Http::StripPortType::None));
  TestRequestHeaderMapImpl original_headers_none;
  original_headers_none.setHost("host:9999");

  TestRequestHeaderMapImpl header_map_none(original_headers_none);
  ConnectionManagerUtility::maybeNormalizeHost(header_map_none, config_, 0);
  EXPECT_EQ(header_map_none.getHostValue(), "host:9999");
}


  explicit MockRequestIDExtension(Random::RandomGenerator& random)
      : real_(Extensions::RequestId::UUIDRequestIDExtension::defaultInstance(random)) {
    ON_CALL(*this, set(_, _))
        .WillByDefault([this](Http::RequestHeaderMap& request_headers, bool force) {
          return real_->set(request_headers, force);
        });
    ON_CALL(*this, setInResponse(_, _))
        .WillByDefault([this](Http::ResponseHeaderMap& response_headers,
                              const Http::RequestHeaderMap& request_headers) {
          return real_->setInResponse(response_headers, request_headers);
        });
    ON_CALL(*this, toInteger(_))
        .WillByDefault([this](const Http::RequestHeaderMap& request_headers) {
          return real_->toInteger(request_headers);
        });
    ON_CALL(*this, getTraceReason(_))
        .WillByDefault([this](const Http::RequestHeaderMap& request_headers) {
          return real_->getTraceReason(request_headers);
        });
    ON_CALL(*this, setTraceReason(_, _))
        .WillByDefault(
            [this](Http::RequestHeaderMap& request_headers, Tracing::Reason trace_status) {
              real_->setTraceReason(request_headers, trace_status);
            });
  }


TEST_F(HttpConnectionManagerConfigTest, NormalizePathTrue) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  route_config:
    name: local_route
  normalize_path: true
  http_filters:
  - name: envoy.filters.http.router
  )EOF";

  EXPECT_CALL(context_.runtime_loader_.snapshot_, featureEnabled(_, An<uint64_t>()))
      .WillRepeatedly(Invoke(&context_.runtime_loader_.snapshot_,
                             &Runtime::MockSnapshot::featureEnabledDefault));
  EXPECT_CALL(context_.runtime_loader_.snapshot_,
              featureEnabled("http_connection_manager.normalize_path", An<uint64_t>()))
      .Times(0);
  HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_,
                                     date_provider_, route_config_provider_manager_,
                                     scoped_routes_config_provider_manager_, http_tracer_manager_,
                                     filter_config_provider_manager_);
  EXPECT_TRUE(config.shouldNormalizePath());
}


absl::string_view name() const override { return "admin"; }


sync_intermediate_msg(Slapi_PBlock *pb, int tag, Sync_Cookie *cookie, char **uuids)
{
    int rc;
    struct berval *syncInfo;
    LDAPControl *ctrlp = NULL;
    char *cookiestr = sync_cookie2str(cookie);

    syncInfo = create_syncinfo_value(tag, cookiestr, (const char **)uuids);

    rc = slapi_send_ldap_intermediate(pb, &ctrlp, LDAP_SYNC_INFO, syncInfo);
    slapi_ch_free((void **)&cookiestr);
    ber_bvfree(syncInfo);
    return (rc);
}


sync_nsuniqueid2uuid(const char *nsuniqueid)
{
    char *uuid;
    char u[17];

    u[0] = slapi_str_to_u8(nsuniqueid);
    u[1] = slapi_str_to_u8(nsuniqueid + 2);
    u[2] = slapi_str_to_u8(nsuniqueid + 4);
    u[3] = slapi_str_to_u8(nsuniqueid + 6);

    u[4] = slapi_str_to_u8(nsuniqueid + 9);
    u[5] = slapi_str_to_u8(nsuniqueid + 11);
    u[6] = slapi_str_to_u8(nsuniqueid + 13);
    u[7] = slapi_str_to_u8(nsuniqueid + 15);

    u[8] = slapi_str_to_u8(nsuniqueid + 18);
    u[9] = slapi_str_to_u8(nsuniqueid + 20);
    u[10] = slapi_str_to_u8(nsuniqueid + 22);
    u[11] = slapi_str_to_u8(nsuniqueid + 24);

    u[12] = slapi_str_to_u8(nsuniqueid + 27);
    u[13] = slapi_str_to_u8(nsuniqueid + 29);
    u[14] = slapi_str_to_u8(nsuniqueid + 31);
    u[15] = slapi_str_to_u8(nsuniqueid + 33);

    u[16] = '\0';

    uuid = slapi_ch_malloc(sizeof(u));
    memcpy(uuid, u, sizeof(u));

    return (uuid);
}


sync_is_active_scope(const Slapi_DN *dn, Slapi_PBlock *pb)
{
    int rc;
    char *origbase = NULL;
    Slapi_DN *base = NULL;
    int scope;

    slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET_DN, &origbase);
    slapi_pblock_get(pb, SLAPI_SEARCH_TARGET_SDN, &base);
    slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
    if (NULL == base) {
        base = slapi_sdn_new_dn_byref(origbase);
        slapi_pblock_set(pb, SLAPI_SEARCH_TARGET_SDN, base);
    }
    if (slapi_sdn_scope_test(dn, base, scope)) {
        rc = 1;
    } else {
        rc = 0;
    }

    return (rc);
}


static int vvalue_tvb_lpstr(tvbuff_t *tvb, int offset, void *val)
{
	struct data_str *str = (struct data_str*)val;
	gint len;

	str->len = tvb_get_letohl(tvb, offset);
	str->str = tvb_get_stringz_enc(wmem_packet_scope(), tvb, offset + 4, &len,
								   ENC_ASCII|ENC_LITTLE_ENDIAN);
	/* XXX test str->len == len */
	return 4 + len;
}


static int dissect_CPMCiState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolean in, void *data _U_)
{
	gint offset = 16;
	proto_item *item;
	proto_tree *tree;

	col_append_str(pinfo->cinfo, COL_INFO, "CiState");

	if (!in) {
		item = proto_tree_add_item(parent_tree, hf_mswsp_msg, tvb, offset, -1, ENC_NA);
		tree = proto_item_add_subtree(item, ett_mswsp_msg);
		proto_item_set_text(item, "CiStateOut");
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cbstruct, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cwordlist, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cpersistindex, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cqueries, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cfreshtest, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cfreshtest, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_dwmergeprogress, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_estate, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cfiltereddocs, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_ctotaldocs, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cpendingscans, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_dwindexsize, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_cuniquekeys, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_csecqdocuments, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcistate_dwpropcachesize, tvb, offset, 4, ENC_LITTLE_ENDIAN);
	}
	return tvb_reported_length(tvb);
}


static int vvalue_tvb_get4(tvbuff_t *tvb, int offset, void *val)
{
	guint32 *ui4 = (guint32*)val;
	*ui4 = tvb_get_letohl(tvb, offset);
	return 4;
}


static int parse_PropertySetArray(tvbuff_t *tvb, packet_info *pinfo, int offset, int size_offset, proto_tree *parent_tree, proto_tree *pad_tree, const char *fmt, ...)
{
	const int offset_in = offset;
	guint32 size, num;
	int i;
	proto_tree *tree;
	proto_item *item;
	const char *txt;
	va_list ap;

	va_start(ap, fmt);
	txt = wmem_strdup_vprintf(wmem_packet_scope(), fmt, ap);
	va_end(ap);

	tree = proto_tree_add_subtree(parent_tree, tvb, offset, 0, ett_CDbPropSet_Array, &item, txt);

	size = tvb_get_letohl(tvb, size_offset);
	proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_Blob1, tvb,
						size_offset, 4, ENC_LITTLE_ENDIAN);

	num = tvb_get_letohl(tvb, offset);
	proto_tree_add_item(tree, hf_mswsp_msg_ConnectIn_PropSets_num, tvb,
						offset, 4, ENC_LITTLE_ENDIAN);
	offset += 4;

	for (i = 0; i < (int)num; i++) {
		offset = parse_CDbPropSet(tvb, pinfo, offset, tree, pad_tree, "PropertySet[%d]", i);
	}

	proto_item_set_end(item, tvb, offset);
	DISSECTOR_ASSERT(offset - offset_in == (int)size);
	return offset;
}


static gboolean get_fid_and_frame(packet_info *pinfo, guint32 *fid, guint *frame,
							  void *data)
{
	gboolean result = TRUE;
	int *p_smb_level = (int*)p_get_proto_data(wmem_file_scope(), pinfo, proto_mswsp, 0);
	if (!p_smb_level) {
		return FALSE;
	}
	*frame = pinfo->num;
	if (*p_smb_level == SMB1) {
		smb_info_t *si = (smb_info_t*)data;
		smb_fid_info_t *info;
		info = find_fid_info(si);
		if (!info) {
			return FALSE;
		}
		*fid = info->fid;
	} else {
		smb2_info_t *si2 = (smb2_info_t*)data;
		guint32     open_frame = 0, close_frame = 0;
		char       *fid_name = NULL;
		if (si2->saved) {
			dcerpc_fetch_polhnd_data(&si2->saved->policy_hnd, &fid_name, NULL, &open_frame, &close_frame, pinfo->num);
			*fid = open_frame;
		} else {
			result = FALSE;
		}
	}
	return result;
}