TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeadersAndBody) {
  setup(false, "");
  ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout())
      .WillByDefault(Return(std::chrono::milliseconds(10)));

  // Codec sends downstream request headers.
  EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status {
    decoder_ = &conn_manager_->newStream(response_encoder_);

    Event::MockTimer* idle_timer = setUpTimer();
    RequestHeaderMapPtr headers{
        new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}};
    EXPECT_CALL(*idle_timer, enableTimer(_, _));
    decoder_->decodeHeaders(std::move(headers), false);

    EXPECT_CALL(*idle_timer, enableTimer(_, _));
    decoder_->decodeData(data, false);

    // Expect resetIdleTimer() to be called for the response
    // encodeHeaders()/encodeData().
    EXPECT_CALL(*idle_timer, enableTimer(_, _)).Times(2);
    EXPECT_CALL(*idle_timer, disableTimer());
    idle_timer->invokeCallback();

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

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

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

  EXPECT_EQ("stream timeout", response_body);
  EXPECT_EQ(1U, stats_.named_.downstream_rq_idle_timeout_.value());
}


TEST_F(HttpConnectionManagerConfigTest, MergeSlashesFalse) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  route_config:
    name: local_route
  merge_slashes: false
  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_FALSE(config.shouldMergeSlashes());
}


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));
}


  Router::RouteConfigProvider* routeConfigProvider() override {
    return route_config_provider_.get();
  }


ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(
    Network::Connection& connection, const Buffer::Instance& data,
    ServerConnectionCallbacks& callbacks, Stats::Scope& scope, Random::RandomGenerator& random,
    Http1::CodecStats::AtomicPtr& http1_codec_stats,
    Http2::CodecStats::AtomicPtr& http2_codec_stats, const Http1Settings& http1_settings,
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
    uint32_t max_request_headers_kb, uint32_t max_request_headers_count,
    envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
        headers_with_underscores_action) {
  if (determineNextProtocol(connection, data) == Utility::AlpnNames::get().Http2) {
    Http2::CodecStats& stats = Http2::CodecStats::atomicGet(http2_codec_stats, scope);
    return std::make_unique<Http2::ServerConnectionImpl>(
        connection, callbacks, stats, random, http2_options, max_request_headers_kb,
        max_request_headers_count, headers_with_underscores_action);
  } else {
    Http1::CodecStats& stats = Http1::CodecStats::atomicGet(http1_codec_stats, scope);
    return std::make_unique<Http1::ServerConnectionImpl>(
        connection, stats, callbacks, http1_settings, max_request_headers_kb,
        max_request_headers_count, headers_with_underscores_action);
  }
}


TEST_F(PathUtilityTest, UnescapeSlashes) {
  using UnescapeResult = std::tuple<std::string, PathUtil::UnescapeSlashesResult>;
  auto unescapeSlashes = [this](const std::string& path_value) {
    auto& path_header = pathHeaderEntry(path_value);
    auto result = PathUtil::unescapeSlashes(headers_);
    auto sanitized_path_value = path_header.value().getStringView();
    return UnescapeResult(std::string(sanitized_path_value), result);
  };
  EXPECT_EQ(UnescapeResult("", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("")); // empty
  EXPECT_EQ(UnescapeResult("//", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%2f%2F")); // case-insensitive
  EXPECT_EQ(UnescapeResult("/a/b/c/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("/a%2Fb%2fc/")); // between other characters
  EXPECT_EQ(UnescapeResult("%2b", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("%2b")); // not %2f
  EXPECT_EQ(UnescapeResult("/a/b/c", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("/a/b/c")); // not %2f
  EXPECT_EQ(UnescapeResult("%2", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("%2")); // incomplete
  EXPECT_EQ(UnescapeResult("%", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("%")); // incomplete
  EXPECT_EQ(UnescapeResult("/abc%2", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("/abc%2")); // incomplete
  EXPECT_EQ(UnescapeResult("foo%", PathUtil::UnescapeSlashesResult::NotFound),
            unescapeSlashes("foo%")); // incomplete
  EXPECT_EQ(UnescapeResult("/a/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("/a%2F")); // prefixed
  EXPECT_EQ(UnescapeResult("/a/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%2fa/")); // suffixed
  EXPECT_EQ(UnescapeResult("%/a/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%%2fa/")); // double escape
  EXPECT_EQ(UnescapeResult("%2/a/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%2%2fa/")); // incomplete escape

  EXPECT_EQ(UnescapeResult("\\\\", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%5c%5C")); // case-insensitive
  EXPECT_EQ(UnescapeResult("/a\\b\\c/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("/a%5Cb%5cc/")); // between other characters
  EXPECT_EQ(UnescapeResult("/a\\", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("/a%5C")); // prefixed
  EXPECT_EQ(UnescapeResult("\\a/", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%5ca/")); // suffixed
  EXPECT_EQ(UnescapeResult("/x/%2E%2e/z//abc\\../def",
                           PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("/x/%2E%2e/z%2f%2Fabc%5C../def"));

  EXPECT_EQ(UnescapeResult("/a\\b/c\\", PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%2fa%5Cb%2fc%5c")); // %5c and %2f together
  EXPECT_EQ(UnescapeResult("/a\\b/c\\?%2fabcd%5C%%2f%",
                           PathUtil::UnescapeSlashesResult::FoundAndUnescaped),
            unescapeSlashes("%2fa%5Cb%2fc%5c?%2fabcd%5C%%2f%")); // query is untouched
}


TEST_P(DownstreamProtocolIntegrationTest, RouterRedirect) {
  auto host = config_helper_.createVirtualHost("www.redirect.com", "/");
  host.set_require_tls(envoy::config::route::v3::VirtualHost::ALL);
  config_helper_.addVirtualHost(host);
  initialize();

  BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest(
      lookupPort("http"), "GET", "/foo", "", downstream_protocol_, version_, "www.redirect.com");
  ASSERT_TRUE(response->complete());
  EXPECT_EQ("301", response->headers().getStatusValue());
  EXPECT_EQ("https://www.redirect.com/foo",
            response->headers().get(Http::Headers::get().Location)[0]->value().getStringView());
}


TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresDropped) {
  config_helper_.addConfigModifier(
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
              hcm) -> void {
        hcm.mutable_common_http_protocol_options()->set_headers_with_underscores_action(
            envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER);
      });
  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(), Not(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"));
  Stats::Store& stats = test_server_->server().stats();
  std::string stat_name;
  switch (downstreamProtocol()) {
  case Http::CodecClient::Type::HTTP1:
    stat_name = "http1.dropped_headers_with_underscores";
    break;
  case Http::CodecClient::Type::HTTP2:
    stat_name = "http2.dropped_headers_with_underscores";
    break;
  case Http::CodecClient::Type::HTTP3:
    stat_name = "http3.dropped_headers_with_underscores";
    break;
  default:
    RELEASE_ASSERT(false, fmt::format("Unknown downstream protocol {}", downstream_protocol_));
  };
  EXPECT_EQ(1L, TestUtility::findCounter(stats, stat_name)->value());
}


TEST_F(HttpConnectionManagerImplTest, PathWithEscapedSlashesRejected) {
  path_with_escaped_slashes_action_ = envoy::extensions::filters::network::http_connection_manager::
      v3::HttpConnectionManager::REJECT_REQUEST;
  testPathNormalization(
      TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/abc%5c../"}, {":method", "GET"}},
      TestResponseHeaderMapImpl{{":status", "400"}, {"connection", "close"}});
  EXPECT_EQ(1U, stats_.named_.downstream_rq_failed_path_normalization_.value());
}


TEST_F(HttpConnectionManagerImplTest, DoNotStartSpanIfTracingIsNotEnabled) {
  setup(false, "");

  // Disable tracing.
  tracing_config_.reset();

  EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)).Times(0);
  ON_CALL(runtime_.snapshot_, featureEnabled("tracing.global_enabled",
                                             An<const envoy::type::v3::FractionalPercent&>(), _))
      .WillByDefault(Return(true));

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

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

  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), true, "details");

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

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


TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAll) {
  config_helper_.addFilter(R"EOF(
name: call-decodedata-once-filter
)EOF");
  config_helper_.addFilter(R"EOF(
name: decode-headers-return-stop-all-filter
)EOF");
  config_helper_.addFilter(R"EOF(
name: passthrough-filter
)EOF");

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

  // Sends a request with headers and data.
  changeHeadersForStopAllTests(default_request_headers_, false);
  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);
  request_encoder_ = &encoder_decoder.first;
  auto response = std::move(encoder_decoder.second);
  for (int i = 0; i < count_ - 1; i++) {
    codec_client_->sendData(*request_encoder_, size_, false);
  }
  // Sleeps for 1s in order to be consistent with testDecodeHeadersReturnsStopAllWatermark.
  absl::SleepFor(absl::Seconds(1));
  codec_client_->sendData(*request_encoder_, size_, true);
  waitForNextUpstreamRequest();

  upstream_request_->encodeHeaders(default_response_headers_, true);
  ASSERT_TRUE(response->waitForEndStream());
  ASSERT_TRUE(response->complete());
  EXPECT_EQ(count_ * size_ + added_decoded_data_size_, upstream_request_->bodyLength());
  EXPECT_EQ(true, upstream_request_->complete());

  // Sends a request with headers, data, and trailers.
  auto encoder_decoder_2 = codec_client_->startRequest(default_request_headers_);
  request_encoder_ = &encoder_decoder_2.first;
  response = std::move(encoder_decoder_2.second);
  for (int i = 0; i < count_; i++) {
    codec_client_->sendData(*request_encoder_, size_, false);
  }
  Http::TestRequestTrailerMapImpl request_trailers{{"trailer", "trailer"}};
  codec_client_->sendTrailers(*request_encoder_, request_trailers);
  waitForNextUpstreamRequest();

  upstream_request_->encodeHeaders(default_response_headers_, true);
  ASSERT_TRUE(response->waitForEndStream());
  verifyUpStreamRequestAfterStopAllFilter();
}


TEST_P(ProtocolIntegrationTest, EnvoyProxyingEarly100Continue) { testEnvoyProxying1xx(true); }


TEST_F(ConnectionManagerUtilityTest, RejectIfEscapedSlashesPresentAndConfiguredToReject) {
  ON_CALL(config_, pathWithEscapedSlashesAction())
      .WillByDefault(Return(envoy::extensions::filters::network::http_connection_manager::v3::
                                HttpConnectionManager::REJECT_REQUEST));
  TestRequestHeaderMapImpl original_headers;
  original_headers.setPath("/xyz%2F..//abc");

  TestRequestHeaderMapImpl header_map(original_headers);
  EXPECT_EQ(ConnectionManagerUtility::NormalizePathAction::Reject,
            ConnectionManagerUtility::maybeNormalizePath(header_map, config_));

  original_headers.setPath("/xyz%5c..//abc");
  header_map = original_headers;
  EXPECT_EQ(ConnectionManagerUtility::NormalizePathAction::Reject,
            ConnectionManagerUtility::maybeNormalizePath(header_map, config_));
}


void ConnectionManagerImpl::ActiveStream::onIdleTimeout() {
  connection_manager_.stats_.named_.downstream_rq_idle_timeout_.inc();
  // If headers have not been sent to the user, send a 408.
  if (responseHeaders().has_value() &&
      !Runtime::runtimeFeatureEnabled("envoy.reloadable_features.allow_response_for_timeout")) {
    // TODO(htuch): We could send trailers here with an x-envoy timeout header
    // or gRPC status code, and/or set H2 RST_STREAM error.
    filter_manager_.streamInfo().setResponseCodeDetails(
        StreamInfo::ResponseCodeDetails::get().StreamIdleTimeout);
    connection_manager_.doEndStream(*this);
  } else {
    // TODO(mattklein) this may result in multiple flags. This Ok?
    filter_manager_.streamInfo().setResponseFlag(StreamInfo::ResponseFlag::StreamIdleTimeout);
    sendLocalReply(request_headers_ != nullptr &&
                       Grpc::Common::isGrpcRequestHeaders(*request_headers_),
                   Http::Code::RequestTimeout, "stream timeout", nullptr, absl::nullopt,
                   StreamInfo::ResponseCodeDetails::get().StreamIdleTimeout);
  }
}


TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionOpenIfCodecStreamErrorIsTrue) {
  sendInvalidRequestAndVerifyConnectionState(true);
}


void HttpConnectionManagerImplTest::sendRequestHeadersAndData() {
  EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false))
      .WillOnce(Return(FilterHeadersStatus::StopIteration));
  auto status = streaming_filter_ ? FilterDataStatus::StopIterationAndWatermark
                                  : FilterDataStatus::StopIterationAndBuffer;
  EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)).WillOnce(Return(status));
  EXPECT_CALL(*decoder_filters_[1], decodeComplete());

  // Kick off the incoming data. |fake_input| is not sent, but instead kicks
  // off sending the headers and |data| queued up in setUpEncoderAndDecoder().
  Buffer::OwnedImpl fake_input("asdf");
  conn_manager_->onData(fake_input, false);
}


TEST_F(ConnectionManagerUtilityTest, UseRemoteAddressWhenNotLocalHostRemoteAddress) {
  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;

  EXPECT_EQ((MutateRequestRet{"12.12.12.12:0", false, Tracing::Reason::NotTraceable}),
            callMutateRequestHeaders(headers, Protocol::Http2));
  EXPECT_EQ(connection_.stream_info_.downstream_address_provider_->remoteAddress()
                ->ip()
                ->addressAsString(),
            headers.get_(Headers::get().ForwardedFor));
}


TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeaderCountConfigurable) {
  const std::string yaml_string = R"EOF(
  stat_prefix: ingress_http
  common_http_protocol_options:
    max_headers_count: 200
  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(200, config.maxRequestHeadersCount());
}


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


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

  auto response = codec_client_->makeRequestWithBody(default_request_headers_, 128);
  waitForNextUpstreamRequest();
  upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true);
  ASSERT_TRUE(response->waitForEndStream());

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


TEST_P(DownstreamProtocolIntegrationTest, ComputedHealthCheck) {
  config_helper_.addFilter(R"EOF(
name: health_check
typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck
    pass_through_mode: false
    cluster_min_healthy_percentages:
        example_cluster_name: { value: 75 }
)EOF");
  initialize();

  codec_client_ = makeHttpConnection(lookupPort("http"));
  auto response = codec_client_->makeHeaderOnlyRequest(Http::TestRequestHeaderMapImpl{
      {":method", "GET"}, {":path", "/healthcheck"}, {":scheme", "http"}, {":authority", "host"}});
  ASSERT_TRUE(response->waitForEndStream());

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


  void TearDown() override {
    if (eds_connection_ != nullptr) {
      AssertionResult result = eds_connection_->close();
      RELEASE_ASSERT(result, result.message());
      result = eds_connection_->waitForDisconnect();
      RELEASE_ASSERT(result, result.message());
      eds_connection_.reset();
    }
  }


TEST_F(HttpConnectionManagerImplTest, RequestTimeoutDisabledByDefault) {
  setup(false, "");

  EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status {
    EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, createTimer_).Times(0);
    conn_manager_->newStream(response_encoder_);
    return Http::okStatus();
  }));

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

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


TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAllWatermark) {
  config_helper_.addFilter(R"EOF(
name: decode-headers-return-stop-all-filter
)EOF");
  config_helper_.addFilter(R"EOF(
name: passthrough-filter
)EOF");

  // Sets initial stream window to min value to make the client sensitive to a low watermark.
  config_helper_.addConfigModifier(
      [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
              hcm) -> void {
        hcm.mutable_http2_protocol_options()->mutable_initial_stream_window_size()->set_value(
            ::Envoy::Http2::Utility::OptionsLimits::MIN_INITIAL_STREAM_WINDOW_SIZE);
      });

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

  // Sends a request with headers and data.
  changeHeadersForStopAllTests(default_request_headers_, true);
  auto encoder_decoder = codec_client_->startRequest(default_request_headers_);
  request_encoder_ = &encoder_decoder.first;
  auto response = std::move(encoder_decoder.second);
  for (int i = 0; i < count_ - 1; i++) {
    codec_client_->sendData(*request_encoder_, size_, false);
  }
  // Gives buffer 1s to react to buffer limit.
  absl::SleepFor(absl::Seconds(1));
  codec_client_->sendData(*request_encoder_, size_, true);
  waitForNextUpstreamRequest();

  upstream_request_->encodeHeaders(default_response_headers_, true);
  ASSERT_TRUE(response->waitForEndStream());
  ASSERT_TRUE(response->complete());
  EXPECT_EQ(count_ * size_ + added_decoded_data_size_, upstream_request_->bodyLength());
  EXPECT_EQ(true, upstream_request_->complete());

  // Sends a request with headers, data, and trailers.
  auto encoder_decoder_2 = codec_client_->startRequest(default_request_headers_);
  request_encoder_ = &encoder_decoder_2.first;
  response = std::move(encoder_decoder_2.second);
  for (int i = 0; i < count_ - 1; i++) {
    codec_client_->sendData(*request_encoder_, size_, false);
  }
  // Gives buffer 1s to react to buffer limit.
  absl::SleepFor(absl::Seconds(1));
  codec_client_->sendData(*request_encoder_, size_, false);
  Http::TestRequestTrailerMapImpl request_trailers{{"trailer", "trailer"}};
  codec_client_->sendTrailers(*request_encoder_, request_trailers);
  waitForNextUpstreamRequest();

  upstream_request_->encodeHeaders(default_response_headers_, true);
  ASSERT_TRUE(response->waitForEndStream());
  verifyUpStreamRequestAfterStopAllFilter();
}


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


absl::optional<std::string> canonicalizePath(absl::string_view original_path) {
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.remove_forked_chromium_url")) {
    std::string canonical_path;
    url::Component in_component(0, original_path.size());
    url::Component out_component;
    url::StdStringCanonOutput output(&canonical_path);
    if (!url::CanonicalizePath(original_path.data(), in_component, &output, &out_component)) {
      return absl::nullopt;
    } else {
      output.Complete();
      return absl::make_optional(std::move(canonical_path));
    }
  }
  return LegacyPathCanonicalizer::canonicalizePath(original_path);
}


TEST_F(ConnectionManagerUtilityTest, DoNotRemoveConnectionUpgradeForWebSocketRequests) {
  TestRequestHeaderMapImpl headers{{"connection", "upgrade"}, {"upgrade", "websocket"}};

  EXPECT_EQ((MutateRequestRet{"10.0.0.3:50000", false, Tracing::Reason::NotTraceable}),
            callMutateRequestHeaders(headers, Protocol::Http11));
  EXPECT_EQ("upgrade", headers.get_("connection"));
  EXPECT_EQ("websocket", headers.get_("upgrade"));
}


void ConnectionManagerImpl::ActiveStream::onResetStream(StreamResetReason reset_reason,
                                                        absl::string_view) {
  // NOTE: This function gets called in all of the following cases:
  //       1) We TX an app level reset
  //       2) The codec TX a codec level reset
  //       3) The codec RX a reset
  //       If we need to differentiate we need to do it inside the codec. Can start with this.
  ENVOY_STREAM_LOG(debug, "stream reset", *this);
  connection_manager_.stats_.named_.downstream_rq_rx_reset_.inc();

  // If the codec sets its responseDetails() for a reason other than peer reset, set a
  // DownstreamProtocolError. Either way, propagate details.
  const absl::string_view encoder_details = response_encoder_->getStream().responseDetails();
  if (!encoder_details.empty() && reset_reason == StreamResetReason::LocalReset) {
    filter_manager_.streamInfo().setResponseFlag(StreamInfo::ResponseFlag::DownstreamProtocolError);
  }
  if (!encoder_details.empty()) {
    filter_manager_.streamInfo().setResponseCodeDetails(encoder_details);
  }

  connection_manager_.doDeferredStreamDestroy(*this);
}


TEST_F(HttpConnectionManagerConfigTest, DynamicFilterDefaultTerminal) {
  const std::string yaml_string = R"EOF(
codec_type: http1
stat_prefix: router
route_config:
  virtual_hosts:
  - name: service
    domains:
    - "*"
    routes:
    - match:
        prefix: "/"
      route:
        cluster: cluster
http_filters:
- name: foo
  config_discovery:
    config_source: { resource_api_version: V3, ads: {} }
    default_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
    type_urls:
    - type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
- name: envoy.filters.http.router
  )EOF";

  EXPECT_THROW_WITH_MESSAGE(createHttpConnectionManagerConfig(yaml_string), EnvoyException,
                            "Error: terminal filter named foo of type envoy.filters.http.router "
                            "must be the last filter in a http filter chain.");
}


TEST_F(ConnectionManagerUtilityTest, ForcedTracedWhenGlobalSet) {
  ON_CALL(config_, useRemoteAddress()).WillByDefault(Return(false));
  // Internal request, make traceable.
  TestRequestHeaderMapImpl headers{{"x-forwarded-for", "10.0.0.1"},
                                   {"x-request-id", "125a4afb-6f55-44ba-ad80-413f09f48a28"},
                                   {"x-envoy-force-trace", "true"}};
  EXPECT_CALL(random_, uuid()).Times(0);
  EXPECT_CALL(
      runtime_.snapshot_,
      featureEnabled("tracing.global_enabled", An<const envoy::type::v3::FractionalPercent&>(), _))
      .WillOnce(Return(true));
  EXPECT_CALL(*request_id_extension_,
              setTraceReason(testing::Ref(headers), Tracing::Reason::ServiceForced));

  EXPECT_EQ((MutateRequestRet{"10.0.0.1:0", true, Tracing::Reason::ServiceForced}),
            callMutateRequestHeaders(headers, Protocol::Http2));
  EXPECT_EQ(Tracing::Reason::ServiceForced, request_id_extension_->getTraceReason(headers));
}


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());
}


Http::StripPortType stripPortType() const override { return Http::StripPortType::None; }


TEST_F(ConnectionManagerUtilityTest, DetermineNextProtocol) {
  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return("hello"));
    Buffer::OwnedImpl data("");
    EXPECT_EQ("hello", ConnectionManagerUtility::determineNextProtocol(connection, data));
  }

  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return(""));
    Buffer::OwnedImpl data("");
    EXPECT_EQ("", ConnectionManagerUtility::determineNextProtocol(connection, data));
  }

  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return(""));
    Buffer::OwnedImpl data("GET / HTTP/1.1");
    EXPECT_EQ("", ConnectionManagerUtility::determineNextProtocol(connection, data));
  }

  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return(""));
    Buffer::OwnedImpl data("PRI * HTTP/2.0\r\n");
    EXPECT_EQ(Utility::AlpnNames::get().Http2,
              ConnectionManagerUtility::determineNextProtocol(connection, data));
  }

  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return(""));
    Buffer::OwnedImpl data("PRI * HTTP/2");
    EXPECT_EQ(Utility::AlpnNames::get().Http2,
              ConnectionManagerUtility::determineNextProtocol(connection, data));
  }

  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return(""));
    Buffer::OwnedImpl data("PRI * HTTP/");
    EXPECT_EQ("", ConnectionManagerUtility::determineNextProtocol(connection, data));
  }

  {
    Network::MockConnection connection;
    EXPECT_CALL(connection, nextProtocol()).WillRepeatedly(Return(""));
    Buffer::OwnedImpl data(" PRI * HTTP/2");
    EXPECT_EQ("", ConnectionManagerUtility::determineNextProtocol(connection, data));
  }
}


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_create_state_control(Slapi_Entry *e, LDAPControl **ctrlp, int type, Sync_Cookie *cookie, PRBool openldap_compat)
{
    int rc;
    BerElement *ber;
    struct berval *bvp;
    char *uuid;
    Slapi_Attr *attr = NULL;
    Slapi_Value *val = NULL;

    if (type == LDAP_SYNC_NONE || ctrlp == NULL || (ber = der_alloc()) == NULL) {
        return (LDAP_OPERATIONS_ERROR);
    }

    *ctrlp = NULL;

    if (openldap_compat) {
        slapi_entry_attr_find(e, SLAPI_ATTR_ENTRYUUID, &attr);
        if (!attr) {
            /*
             * We can't proceed from here. We are in openldap mode, but some entries don't
             * have their UUID. This means that the tree could be corrupted on the openldap
             * server, so we have to stop now.
             */
            slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
                          "sync_create_state_control - Some entries are missing entryUUID. Unable to proceed. You may need to re-run the entryuuid fixup\n");
            return (LDAP_OPERATIONS_ERROR);
        }
        slapi_attr_first_value(attr, &val);
        uuid = sync_entryuuid2uuid(slapi_value_get_string(val));
    } else {
        slapi_entry_attr_find(e, SLAPI_ATTR_UNIQUEID, &attr);
        slapi_attr_first_value(attr, &val);
        if ((attr == NULL) || (val == NULL)) {
            /* It may happen with entries in special backends
             * such like cn=config, cn=shema, cn=monitor...
             */
            slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
                          "sync_create_state_control - Entries are missing nsuniqueid. Unable to proceed.\n");
            return (LDAP_OPERATIONS_ERROR);
        }
        uuid = sync_nsuniqueid2uuid(slapi_value_get_string(val));
    }

    if ((rc = ber_printf(ber, "{eo", type, uuid, 16)) != -1) {
        if (cookie) {
            char *cookiestr = sync_cookie2str(cookie);
            rc = ber_printf(ber, "s}", cookiestr);
            slapi_ch_free((void **)&cookiestr);
        } else {
            rc = ber_printf(ber, "}");
        }
    }
    if (rc != -1) {
        rc = ber_flatten(ber, &bvp);
    }
    ber_free(ber, 1);
    slapi_ch_free((void **)&uuid);

    if (rc == -1) {
        return (LDAP_OPERATIONS_ERROR);
    }

    *ctrlp = (LDAPControl *)slapi_ch_malloc(sizeof(LDAPControl));
    (*ctrlp)->ldctl_iscritical = 0;
    (*ctrlp)->ldctl_oid = slapi_ch_strdup(LDAP_CONTROL_SYNC_STATE);
    (*ctrlp)->ldctl_value = *bvp; /* struct copy */

    bvp->bv_val = NULL;
    ber_bvfree(bvp);

    return (LDAP_SUCCESS);
}


sync_result_err(Slapi_PBlock *pb, int err, char *msg)
{
    int rc = 0;

    slapi_send_ldap_result(pb, err, NULL, msg, 0, NULL);

    return (rc);
}


sync_handle_cnum_entry(Slapi_Entry *e, void *cb_data)
{
    int rc = 0;
    Sync_CallBackData *cb = (Sync_CallBackData *)cb_data;
    Slapi_Value *sval = NULL;
    const struct berval *value;

    cb->changenr = 0;

    if (NULL != e) {
        Slapi_Attr *chattr = NULL;
        sval = NULL;
        value = NULL;
        if (slapi_entry_attr_find(e, CL_ATTR_CHANGENUMBER, &chattr) == 0) {
            slapi_attr_first_value(chattr, &sval);
            if (NULL != sval) {
                value = slapi_value_get_berval(sval);
                if (value && value->bv_val && ('\0' != value->bv_val[0])) {
                    cb->changenr = sync_number2ulong(value->bv_val);
                    if (SYNC_INVALID_CHANGENUM != cb->changenr) {
                        cb->cb_err = 0; /* changenr successfully set */
                    }
                }
            }
        }
    }
    return (rc);
}


static int vvalue_tvb_lpwstr_len(tvbuff_t *tvb, int offset, int length, void *val)
{
	struct data_str *str = (struct data_str*)val;
	const gchar *ptr;
	int len;

	if (length == 0) {
		/* we don't know the length */
		ptr = tvb_get_stringz_enc(wmem_packet_scope(), tvb, offset, &len,
								  ENC_UTF_16|ENC_LITTLE_ENDIAN);
	} else {
		ptr =  tvb_get_string_enc(wmem_packet_scope(), tvb, offset, length,
								  ENC_UTF_16|ENC_LITTLE_ENDIAN);
		len = length;
	}
	str->str = ptr;
	return len;
}


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 void get_name_from_fullpropspec(struct CFullPropSpec *v, char *out, int bufsize)
{
	struct GuidPropertySet *pset = GuidPropertySet_find_guid(&v->guid);
	const char *id_str, *guid_str;
	char *dest = out;
	id_str = pset ? try_val_to_str(v->u.propid, pset->id_map) : NULL;

	if (id_str) {
		g_snprintf(dest, bufsize, "%s", id_str);
	} else {
		guid_str = guids_get_guid_name(&v->guid);
		if (guid_str) {
			g_snprintf(dest, bufsize, "\"%s\"", guid_str);
		} else {
			guid_str = guid_to_str(wmem_packet_scope(), &v->guid);
			g_snprintf(dest, bufsize, "{%s}", guid_str);
		}
		if (v->kind == PRSPEC_LPWSTR) {
			g_snprintf(dest, bufsize, "%s \"%s\"", guid_str, v->u.name);
		} else if (v->kind == PRSPEC_PROPID) {
			g_snprintf(dest, bufsize, "%s 0x%08x", guid_str, v->u.propid);
		} else {
			g_snprintf(dest, bufsize, "%s <INVALID>", dest);
		}
	}
}


static int parse_CSortSet(tvbuff_t *tvb, int offset, proto_tree *parent_tree, proto_tree *pad_tree, const char *fmt, ...)
{
	guint32 count, i;

	proto_item *item;
	proto_tree *tree;
	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_CSortSet, &item, txt);

	count = tvb_get_letohl(tvb, offset);
	proto_tree_add_uint(tree, hf_mswsp_cscortset_count, tvb, offset, 4, count);
	offset += 4;

	for (i=0; i<count; i++) {
		offset = parse_padding(tvb, offset, 4, tree, "padding_sortArray[%u]", i);
		offset = parse_CSort(tvb, offset, tree, pad_tree, "sortArray[%u]", i);
	}

	proto_item_set_end(item, tvb, offset);
	return offset;
}


static int parse_CNodeRestriction(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *parent_tree, proto_tree *pad_tree, struct CNodeRestriction *v, const char *fmt, ...)
{
	proto_tree *tree;
	proto_item *item;
	unsigned i;
	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_CNodeRestriction, &item, txt);

	v->cNode = tvb_get_letohl(tvb, offset);
	proto_tree_add_uint(tree, hf_mswsp_cnoderestrict_cnode, tvb, offset, 4, v->cNode);
	offset += 4;

	for (i=0; i<v->cNode; i++) {
		struct CRestriction r;
		ZERO_STRUCT(r);
		offset = parse_CRestriction(tvb, pinfo, offset, tree, pad_tree, &r, "paNode[%u]", i);
		offset = parse_padding(tvb, offset, 4, tree, "padding_paNode[%u]", i); /*at begin or end of loop ????*/

	}

	proto_item_set_end(item, tvb, offset);
	return offset;
}


static int dissect_CPMSetCatState(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, gboolean in _U_, void *data _U_)
{
	col_append_str(pinfo->cinfo, COL_INFO, "SetCatState");
	return tvb_reported_length(tvb);
}


static void vvalue_strbuf_append_ui4(wmem_strbuf_t *strbuf, void *ptr)
{
	guint32 ui4 = *(guint32*)ptr;
	wmem_strbuf_append_printf(strbuf, "%d", ui4);
}


static int dissect_mswsp_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
	smb2_info_t *si = (smb2_info_t*)data;
	gboolean in;
	char* fid_name = NULL;
	guint32     open_frame = 0, close_frame = 0;

	if (!si) {
		return 0;
	}

	if (si->saved) {
		dcerpc_fetch_polhnd_data(&si->saved->policy_hnd, &fid_name, NULL, &open_frame, &close_frame, pinfo->num);
	}

	if (!fid_name || g_ascii_strcasecmp(fid_name, "File: MsFteWds") != 0) {
		return 0;
	}

	in = !(si->flags & SMB2_FLAGS_RESPONSE);
	p_add_proto_data(wmem_file_scope(), pinfo, proto_mswsp, 0, (void*)&SMB2);
	return dissect_mswsp(tvb, pinfo, tree, in, data);
}


static int parse_VariantCol(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *parent_tree, guint64 base_address, guint32 length _U_, gboolean is_64bit, struct CRowVariant *variant, const char *fmt, ...)
{
	proto_tree *tree;
	proto_item *item, *ti_type;

	va_list ap;
	struct vtype_data *vt_type;
	const char *modifier = "", *txt;
	int size;
	guint16 vtype_high;

	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_CRowVariant, &item, txt);

	variant->vtype = tvb_get_letohs(tvb, offset);
	vt_type = vType_get_type((enum vType)variant->vtype);
	vtype_high = (variant->vtype & 0xFF00);
	if (vtype_high) {
		if (vtype_high == VT_VECTOR) {
			modifier = "|VT_VECTOR";
		} else if (vtype_high == VT_ARRAY) {
			modifier = "|VT_ARRAY";
		} else {
			modifier = "|Unknown, possibly error";
		}
	}

	if (vt_type == NULL) {
		/*
		 * Not a valid type.
		 */
		ti_type = proto_tree_add_string(tree, hf_mswsp_ctablecolumn_vtype, tvb, offset, 4, "Unknown variant column type");
		expert_add_info(pinfo, ti_type, &ei_mswsp_invalid_variant_type);

		THROW_FORMATTED(ReportedBoundsError, "Unknown variant column type%s", modifier);
		return offset;
	}
	proto_tree_add_string_format_value(tree, hf_mswsp_rowvariant_vtype, tvb, offset, 2, vt_type->str, "%s%s", vt_type->str, modifier);
	offset += 2;

	proto_tree_add_item(tree, hf_mswsp_rowvariant_reserved1, tvb, offset, 2, ENC_LITTLE_ENDIAN);
	variant->reserved1 = tvb_get_letohs(tvb, offset);

	offset += 2;
	proto_tree_add_item(tree, hf_mswsp_rowvariant_reserved2, tvb, offset, 4, ENC_LITTLE_ENDIAN);
	variant->reserved2 = tvb_get_letohl(tvb, offset);
	offset += 4;

	size = get_fixed_vtype_dataize((enum vType)(variant->vtype & 0x00FF));

	if (vtype_high == VT_VECTOR || vtype_high == VT_ARRAY) {
		offset = parse_VariantColVector(tvb, offset, tree, base_address,
										is_64bit, variant, vt_type);
	} else {
		wmem_strbuf_t *strbuf = wmem_strbuf_new(wmem_packet_scope(), "");
		if (size != -1) {
			/* single fixed size value type */
			const char* desc = vt_type->str;

			DISSECTOR_ASSERT_HINT(vt_type->tvb_get != 0,
					      "appears fixed type that we don't know yet how to handle, please submit a bug with trace");
			vt_type->tvb_get(tvb, offset, &variant->content);
			vt_type->strbuf_append(strbuf, &variant->content);
			proto_tree_add_string_format_value(tree, hf_mswsp_rowvariant_item_value, tvb, offset, size, desc, "%s: %s", desc, wmem_strbuf_get_str(strbuf));
		} else {
			gint64 value_address;
			int buf_offset = offset;
			int len;
			union vt_single non_fixed_size_val;
			DISSECTOR_ASSERT_HINT(vt_type->tvb_get_value_only != 0, "appears this is a dynamic type that we don't know yet how to handle, please submit a bug with network trace");
			if (is_64bit) {
				variant->content.hyperw = tvb_get_letoh64(tvb, offset);
				offset += 8;
				value_address = variant->content.hyperw;
				proto_tree_add_uint64(tree, hf_mswsp_rowvariant_item_address64, tvb, buf_offset, 8, value_address);
			} else {
				variant->content.longw = tvb_get_letohl(tvb, offset);
				offset += 4;
				value_address = variant->content.longw;
				proto_tree_add_uint(tree, hf_mswsp_rowvariant_item_address32, tvb, buf_offset, 4, (guint32)value_address);
			}

			len = vt_type->tvb_get_value_only(tvb, (int)(value_address - base_address), 0, &non_fixed_size_val);
			vt_type->strbuf_append(strbuf, &non_fixed_size_val);
			proto_tree_add_string(tree, hf_mswsp_rowvariant_item_value, tvb, (gint)(value_address - base_address), len, wmem_strbuf_get_str(strbuf));
		}
	}

	return offset;
}


static const char *str_CBaseStorageVariant(struct CBaseStorageVariant *value, gboolean print_type)
{

	wmem_strbuf_t *strbuf = wmem_strbuf_new(wmem_packet_scope(), "");
	if (value == NULL) {
		return "<NULL>";
	}

	if (value->type == NULL) {
		return "<??""?>";
	}

	if (print_type) {
		wmem_strbuf_append(strbuf, value->type->str);

		if (value->vType & 0xFF00) {
			wmem_strbuf_append_printf(strbuf, "[%d]", value->vValue.vt_vector.len);
		}
		wmem_strbuf_append(strbuf, ": ");
	}

	switch (value->vType & 0xFF00) {
	case 0:
		value->type->strbuf_append(strbuf, &value->vValue);
		break;
	case VT_ARRAY:
		vvalue_strbuf_append_vector(strbuf, value->vValue.vt_array.vData, value->type);
		break;
	case VT_VECTOR:
		vvalue_strbuf_append_vector(strbuf, value->vValue.vt_vector, value->type);
		break;
	default:
		wmem_strbuf_append(strbuf, "Invalid");
	}

	return wmem_strbuf_get_str(strbuf);
}


static int parse_CRowVariantArrayInfo(tvbuff_t *tvb, int offset, proto_tree *tree, gboolean is_64bit, struct CRowVariant *variant)
{
	if (is_64bit) {
		variant->content.array_vector.i64.count =
					tvb_get_letoh64(tvb, offset);
		proto_tree_add_uint64(tree, hf_mswsp_crowvariantinfo_count64, tvb, offset, 8, variant->content.array_vector.i64.count);
		offset += 8;
		variant->content.array_vector.i64.array_address = tvb_get_letoh64(tvb, offset);
		proto_tree_add_uint64(tree, hf_mswsp_arrayvector_address64, tvb, offset, 8, variant->content.array_vector.i64.array_address);
		offset += 8;

	} else {
		variant->content.array_vector.i32.count =
					tvb_get_letohl(tvb, offset);
		proto_tree_add_uint(tree, hf_mswsp_crowvariantinfo_count32, tvb, offset, 4, variant->content.array_vector.i32.count );
		offset += 4;
		variant->content.array_vector.i32.array_address = tvb_get_letohl(tvb, offset);
		proto_tree_add_uint(tree, hf_mswsp_arrayvector_address32, tvb, offset, 4, variant->content.array_vector.i32.array_address);
		offset += 4;
	}
	return offset;
}


static int parse_CDbColId(tvbuff_t *tvb, int offset, proto_tree *parent_tree, proto_tree *pad_tree, const char *text)
{
	guint32 eKind, ulId;
	e_guid_t guid;
	const char *str;
	static const char *KIND[] = {"DBKIND_GUID_NAME", "DBKIND_GUID_PROPID"};

	proto_item *tree_item;
	proto_tree *tree = proto_tree_add_subtree(parent_tree, tvb, offset, 0, ett_CDbColId, &tree_item, text);

	eKind = tvb_get_letohl(tvb, offset);
	str = (eKind < 2 ? KIND[eKind] : "???");
	proto_tree_add_string_format_value(tree, hf_mswsp_cdbcolid_ekind, tvb, offset, 4,  str, "%s (%u)", str, eKind);
	offset += 4;

	offset = parse_padding(tvb, offset, 8, pad_tree, "paddingGuidAlign");

	offset = parse_guid(tvb, offset, tree, &guid, "GUID");

	ulId = tvb_get_letohl(tvb, offset);
	proto_tree_add_uint(tree, hf_mswsp_cdbcolid_ulid, tvb, offset, 4, ulId);
	offset += 4;

	if (eKind == DBKIND_GUID_NAME) {
		char *name;
		int len = ulId;
		name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, len, ENC_LITTLE_ENDIAN | ENC_UCS_2);
		proto_item_append_text(tree_item, " \"%s\"", name);
		proto_tree_add_string_format_value(tree, hf_mswsp_cdbcolid_vstring, tvb, offset, len, name, "\"%s\"", name);
		offset += len;
	} else if (eKind == DBKIND_GUID_PROPID) {
		proto_item_append_text(tree_item, " %08x", ulId);
	} else {
		proto_item_append_text(tree_item, "<INVALID>");
	}

	proto_item_set_end(tree_item, tvb, offset);

	return offset;
}


static void vvalue_strbuf_append_r4(wmem_strbuf_t *strbuf, void *ptr)
{
	float r4 = *(float*)ptr;
	wmem_strbuf_append_printf(strbuf, "%g", (double)r4);
}


static int parse_CBaseStorageVariant(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *parent_tree, proto_tree *pad_tree _U_, struct CBaseStorageVariant *value, const char *text)
{
	int i, len;
	proto_item *ti, *ti_type, *ti_val;
	proto_tree *tree, *tr;
	enum vType highType;

	ZERO_STRUCT(*value);

	tree = proto_tree_add_subtree(parent_tree, tvb, offset, 0, ett_CBaseStorageVariant, &ti, text);

	value->vType = tvb_get_letohs(tvb, offset);
	value->type = vType_get_type(value->vType & 0xFF);
	if (value->type == NULL) {
		/*
		 * Not a valid type.
		 */
		ti_type = proto_tree_add_string(tree, hf_mswsp_cbasestorvariant_vtype, tvb, offset, 2, "Unknown CBaseStorageVariant type");
		expert_add_info(pinfo, ti_type, &ei_mswsp_invalid_variant_type);

		THROW_MESSAGE(ReportedBoundsError, "Unknown CBaseStorageVariant type");
		return offset;
	}

	ti_type = proto_tree_add_string(tree, hf_mswsp_cbasestorvariant_vtype, tvb, offset, 2, value->type->str);
	offset += 2;

	value->vData1 = tvb_get_guint8(tvb, offset);
	proto_tree_add_uint(tree, hf_mswsp_cbasestorvariant_vdata1, tvb, offset, 1, value->vData1);
	offset += 1;

	value->vData2 = tvb_get_guint8(tvb, offset);
	proto_tree_add_uint(tree, hf_mswsp_cbasestorvariant_vdata2, tvb, offset, 1, value->vData2);
	offset += 1;

	highType = (enum vType)(value->vType & 0xFF00);

	ti_val = proto_tree_add_string(tree, hf_mswsp_cbasestorvariant_vvalue, tvb, offset, 0, "");

	switch (highType) {
	case VT_EMPTY:
		DISSECTOR_ASSERT_HINT(value->type->tvb_get != 0,
				      "type that we don't know yet how to handle, please submit a bug with trace");
		len = value->type->tvb_get(tvb, offset, &value->vValue.vt_single);
		offset += len;
		break;
	case VT_VECTOR:
		proto_item_append_text(ti_type, "|VT_VECTOR");
		tr = proto_item_add_subtree(ti_val, ett_CBaseStorageVariant_Vector);

		len = vvalue_tvb_vector(tvb, offset, &value->vValue.vt_vector, value->type);
		proto_tree_add_uint(tr, hf_mswsp_cbasestorvariant_num, tvb, offset, 4, value->vValue.vt_vector.len);
		offset += len;
		break;
	case VT_ARRAY: {
		guint16 cDims, fFeatures;
		guint32 cbElements, cElements, lLbound;
		int num = 1;

		proto_item_append_text(ti_type, "|VT_ARRAY");
		tr = proto_item_add_subtree(ti_val, ett_CBaseStorageVariant_Array);

		cDims = tvb_get_letohs(tvb, offset);
		proto_tree_add_uint(tr, hf_mswsp_cbasestorvariant_cdims, tvb, offset, 2, cDims);
		offset += 2;

		fFeatures = tvb_get_letohs(tvb, offset);
		proto_tree_add_uint(tr, hf_mswsp_cbasestorvariant_ffeatures, tvb, offset, 2, fFeatures);
		offset += 2;

		cbElements = tvb_get_letohl(tvb, offset);
		proto_tree_add_uint(tr, hf_mswsp_cbasestorvariant_cbelements, tvb, offset, 4, cbElements);
		offset += 4;
		for (i=0; i<cDims; i++) {
			cElements = tvb_get_letohl(tvb, offset);
			lLbound =  tvb_get_letohl(tvb, offset + 4);
			proto_tree_add_string_format(tr, hf_mswsp_cbasestorvariant_rgsabound, tvb, offset, 8, "", "Rgsabound[%d]: (%d:%d)", i, cElements, lLbound);
			offset += 8;
			num *= cElements;
		}

		len = vvalue_tvb_vector_internal(tvb, offset, &value->vValue.vt_array.vData, value->type, num);
		offset += len;
		break;
	}
	default:
		proto_item_append_text(ti_type, "|0x%x", highType);
	}
	proto_item_set_end(ti, tvb, offset);
	proto_item_set_end(ti_val, tvb, offset);

	proto_item_append_text(ti_val, " %s", str_CBaseStorageVariant(value, FALSE));
	proto_item_append_text(ti, " %s", str_CBaseStorageVariant(value, TRUE));

	return offset;
}


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

	item = proto_tree_add_item(parent_tree, hf_mswsp_msg, tvb, offset, in ? 0 : -1, ENC_NA);
	tree = proto_item_add_subtree(item, ett_mswsp_msg);

	proto_item_set_text(item, "CompareBmk%s", in ? "In" : "Out");
	col_append_str(pinfo->cinfo, COL_INFO, "CompareBmk");
	if (in) {
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcomparebmk_hcursor, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcomparebmk_chapt, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcomparebmk_bmkfirst, tvb, offset, 4, ENC_LITTLE_ENDIAN);
		offset += 4;
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcomparebmk_bmksecond, tvb, offset, 4, ENC_LITTLE_ENDIAN);
	} else {
		proto_tree_add_item(tree, hf_mswsp_msg_cpmcomparebmk_dwcomparison, tvb, offset, 4, ENC_LITTLE_ENDIAN);
	}
	return tvb_reported_length(tvb);
}


unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
		   const xmlChar *publicId, const xmlChar *systemId,
		   const xmlChar *notationName)
{
const xmlChar *nullstr = BAD_CAST "(null)";

    if (publicId == NULL)
        publicId = nullstr;
    if (systemId == NULL)
        systemId = nullstr;
    if (notationName == NULL)
        notationName = nullstr;
    callbacks++;
    if (noout)
	return;
    fprintf(stdout, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
            (char *) name, (char *) publicId, (char *) systemId,
	    (char *) notationName);
}


startTimer(void)
{
    begin = clock();
}


main(int argc, char **argv) {
    int i, acount;
    int files = 0;
    int version = 0;
    const char* indent;

    if (argc <= 1) {
	usage(stderr, argv[0]);
	return(1);
    }
    LIBXML_TEST_VERSION
    for (i = 1; i < argc ; i++) {
	if (!strcmp(argv[i], "-"))
	    break;

	if (argv[i][0] != '-')
	    continue;
	if ((!strcmp(argv[i], "-debug")) || (!strcmp(argv[i], "--debug")))
	    debug++;
	else
#ifdef LIBXML_DEBUG_ENABLED
	if ((!strcmp(argv[i], "-shell")) ||
	         (!strcmp(argv[i], "--shell"))) {
	    shell++;
            noout = 1;
        } else
#endif
#ifdef LIBXML_TREE_ENABLED
	if ((!strcmp(argv[i], "-copy")) || (!strcmp(argv[i], "--copy")))
	    copy++;
	else
#endif /* LIBXML_TREE_ENABLED */
	if ((!strcmp(argv[i], "-recover")) ||
	         (!strcmp(argv[i], "--recover"))) {
	    recovery++;
	    options |= XML_PARSE_RECOVER;
	} else if ((!strcmp(argv[i], "-huge")) ||
	         (!strcmp(argv[i], "--huge"))) {
	    options |= XML_PARSE_HUGE;
	} else if ((!strcmp(argv[i], "-noent")) ||
	         (!strcmp(argv[i], "--noent"))) {
	    noent++;
	    options |= XML_PARSE_NOENT;
	} else if ((!strcmp(argv[i], "-noenc")) ||
	         (!strcmp(argv[i], "--noenc"))) {
	    noenc++;
	    options |= XML_PARSE_IGNORE_ENC;
	} else if ((!strcmp(argv[i], "-nsclean")) ||
	         (!strcmp(argv[i], "--nsclean"))) {
	    options |= XML_PARSE_NSCLEAN;
	} else if ((!strcmp(argv[i], "-nocdata")) ||
	         (!strcmp(argv[i], "--nocdata"))) {
	    options |= XML_PARSE_NOCDATA;
	} else if ((!strcmp(argv[i], "-nodict")) ||
	         (!strcmp(argv[i], "--nodict"))) {
	    options |= XML_PARSE_NODICT;
	} else if ((!strcmp(argv[i], "-version")) ||
	         (!strcmp(argv[i], "--version"))) {
	    showVersion(argv[0]);
	    version = 1;
	} else if ((!strcmp(argv[i], "-noout")) ||
	         (!strcmp(argv[i], "--noout")))
	    noout++;
#ifdef LIBXML_OUTPUT_ENABLED
	else if ((!strcmp(argv[i], "-o")) ||
	         (!strcmp(argv[i], "-output")) ||
	         (!strcmp(argv[i], "--output"))) {
	    i++;
	    output = argv[i];
	}
#endif /* LIBXML_OUTPUT_ENABLED */
	else if ((!strcmp(argv[i], "-htmlout")) ||
	         (!strcmp(argv[i], "--htmlout")))
	    htmlout++;
	else if ((!strcmp(argv[i], "-nowrap")) ||
	         (!strcmp(argv[i], "--nowrap")))
	    nowrap++;
#ifdef LIBXML_HTML_ENABLED
	else if ((!strcmp(argv[i], "-html")) ||
	         (!strcmp(argv[i], "--html"))) {
	    html++;
        }
	else if ((!strcmp(argv[i], "-xmlout")) ||
	         (!strcmp(argv[i], "--xmlout"))) {
	    xmlout++;
	} else if ((!strcmp(argv[i], "-nodefdtd")) ||
	         (!strcmp(argv[i], "--nodefdtd"))) {
            nodefdtd++;
	    options |= HTML_PARSE_NODEFDTD;
        }
#endif /* LIBXML_HTML_ENABLED */
	else if ((!strcmp(argv[i], "-loaddtd")) ||
	         (!strcmp(argv[i], "--loaddtd"))) {
	    loaddtd++;
	    options |= XML_PARSE_DTDLOAD;
	} else if ((!strcmp(argv[i], "-dtdattr")) ||
	         (!strcmp(argv[i], "--dtdattr"))) {
	    loaddtd++;
	    dtdattrs++;
	    options |= XML_PARSE_DTDATTR;
	}
#ifdef LIBXML_VALID_ENABLED
	else if ((!strcmp(argv[i], "-valid")) ||
	         (!strcmp(argv[i], "--valid"))) {
	    valid++;
	    options |= XML_PARSE_DTDVALID;
	} else if ((!strcmp(argv[i], "-postvalid")) ||
	         (!strcmp(argv[i], "--postvalid"))) {
	    postvalid++;
	    loaddtd++;
	    options |= XML_PARSE_DTDLOAD;
	} else if ((!strcmp(argv[i], "-dtdvalid")) ||
	         (!strcmp(argv[i], "--dtdvalid"))) {
	    i++;
	    dtdvalid = argv[i];
	    loaddtd++;
	    options |= XML_PARSE_DTDLOAD;
	} else if ((!strcmp(argv[i], "-dtdvalidfpi")) ||
	         (!strcmp(argv[i], "--dtdvalidfpi"))) {
	    i++;
	    dtdvalidfpi = argv[i];
	    loaddtd++;
	    options |= XML_PARSE_DTDLOAD;
        }
#endif /* LIBXML_VALID_ENABLED */
	else if ((!strcmp(argv[i], "-dropdtd")) ||
	         (!strcmp(argv[i], "--dropdtd")))
	    dropdtd++;
	else if ((!strcmp(argv[i], "-insert")) ||
	         (!strcmp(argv[i], "--insert")))
	    insert++;
	else if ((!strcmp(argv[i], "-quiet")) ||
	         (!strcmp(argv[i], "--quiet")))
	    quiet++;
	else if ((!strcmp(argv[i], "-timing")) ||
	         (!strcmp(argv[i], "--timing")))
	    timing++;
	else if ((!strcmp(argv[i], "-auto")) ||
	         (!strcmp(argv[i], "--auto")))
	    generate++;
	else if ((!strcmp(argv[i], "-repeat")) ||
	         (!strcmp(argv[i], "--repeat"))) {
	    if (repeat)
	        repeat *= 10;
	    else
	        repeat = 100;
	}
#ifdef LIBXML_PUSH_ENABLED
	else if ((!strcmp(argv[i], "-push")) ||
	         (!strcmp(argv[i], "--push")))
	    push++;
	else if ((!strcmp(argv[i], "-pushsmall")) ||
	         (!strcmp(argv[i], "--pushsmall"))) {
	    push++;
            pushsize = 10;
        }
#endif /* LIBXML_PUSH_ENABLED */
#ifdef HAVE_MMAP
	else if ((!strcmp(argv[i], "-memory")) ||
	         (!strcmp(argv[i], "--memory")))
	    memory++;
#endif
	else if ((!strcmp(argv[i], "-testIO")) ||
	         (!strcmp(argv[i], "--testIO")))
	    testIO++;
#ifdef LIBXML_XINCLUDE_ENABLED
	else if ((!strcmp(argv[i], "-xinclude")) ||
	         (!strcmp(argv[i], "--xinclude"))) {
	    xinclude++;
	    options |= XML_PARSE_XINCLUDE;
	}
	else if ((!strcmp(argv[i], "-noxincludenode")) ||
	         (!strcmp(argv[i], "--noxincludenode"))) {
	    xinclude++;
	    options |= XML_PARSE_XINCLUDE;
	    options |= XML_PARSE_NOXINCNODE;
	}
	else if ((!strcmp(argv[i], "-nofixup-base-uris")) ||
	         (!strcmp(argv[i], "--nofixup-base-uris"))) {
	    xinclude++;
	    options |= XML_PARSE_XINCLUDE;
	    options |= XML_PARSE_NOBASEFIX;
	}
#endif
#ifdef LIBXML_OUTPUT_ENABLED
#ifdef LIBXML_ZLIB_ENABLED
	else if ((!strcmp(argv[i], "-compress")) ||
	         (!strcmp(argv[i], "--compress"))) {
	    compress++;
	    xmlSetCompressMode(9);
        }
#endif
#endif /* LIBXML_OUTPUT_ENABLED */
	else if ((!strcmp(argv[i], "-nowarning")) ||
	         (!strcmp(argv[i], "--nowarning"))) {
	    xmlGetWarningsDefaultValue = 0;
	    xmlPedanticParserDefault(0);
	    options |= XML_PARSE_NOWARNING;
        }
	else if ((!strcmp(argv[i], "-pedantic")) ||
	         (!strcmp(argv[i], "--pedantic"))) {
	    xmlGetWarningsDefaultValue = 1;
	    xmlPedanticParserDefault(1);
	    options |= XML_PARSE_PEDANTIC;
        }
#ifdef LIBXML_DEBUG_ENABLED
	else if ((!strcmp(argv[i], "-debugent")) ||
		 (!strcmp(argv[i], "--debugent"))) {
	    debugent++;
	    xmlParserDebugEntities = 1;
	}
#endif
#ifdef LIBXML_C14N_ENABLED
	else if ((!strcmp(argv[i], "-c14n")) ||
		 (!strcmp(argv[i], "--c14n"))) {
	    canonical++;
	    options |= XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_DTDLOAD;
	}
	else if ((!strcmp(argv[i], "-c14n11")) ||
		 (!strcmp(argv[i], "--c14n11"))) {
	    canonical_11++;
	    options |= XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_DTDLOAD;
	}
	else if ((!strcmp(argv[i], "-exc-c14n")) ||
		 (!strcmp(argv[i], "--exc-c14n"))) {
	    exc_canonical++;
	    options |= XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_DTDLOAD;
	}
#endif
#ifdef LIBXML_CATALOG_ENABLED
	else if ((!strcmp(argv[i], "-catalogs")) ||
		 (!strcmp(argv[i], "--catalogs"))) {
	    catalogs++;
	} else if ((!strcmp(argv[i], "-nocatalogs")) ||
		 (!strcmp(argv[i], "--nocatalogs"))) {
	    nocatalogs++;
	}
#endif
	else if ((!strcmp(argv[i], "-encode")) ||
	         (!strcmp(argv[i], "--encode"))) {
	    i++;
	    encoding = argv[i];
	    /*
	     * OK it's for testing purposes
	     */
	    xmlAddEncodingAlias("UTF-8", "DVEnc");
        }
	else if ((!strcmp(argv[i], "-noblanks")) ||
	         (!strcmp(argv[i], "--noblanks"))) {
	    noblanks++;
	    xmlKeepBlanksDefault(0);
	    options |= XML_PARSE_NOBLANKS;
        }
	else if ((!strcmp(argv[i], "-maxmem")) ||
	         (!strcmp(argv[i], "--maxmem"))) {
	     i++;
	     if (sscanf(argv[i], "%d", &maxmem) == 1) {
	         xmlMemSetup(myFreeFunc, myMallocFunc, myReallocFunc,
		             myStrdupFunc);
	     } else {
	         maxmem = 0;
	     }
        }
	else if ((!strcmp(argv[i], "-format")) ||
	         (!strcmp(argv[i], "--format"))) {
	     noblanks++;
#ifdef LIBXML_OUTPUT_ENABLED
	     format = 1;
#endif /* LIBXML_OUTPUT_ENABLED */
	     xmlKeepBlanksDefault(0);
	}
	else if ((!strcmp(argv[i], "-pretty")) ||
	         (!strcmp(argv[i], "--pretty"))) {
	     i++;
#ifdef LIBXML_OUTPUT_ENABLED
       if (argv[i] != NULL) {
	         format = atoi(argv[i]);
	         if (format == 1) {
	             noblanks++;
	             xmlKeepBlanksDefault(0);
	         }
       }
#endif /* LIBXML_OUTPUT_ENABLED */
	}
#ifdef LIBXML_READER_ENABLED
	else if ((!strcmp(argv[i], "-stream")) ||
	         (!strcmp(argv[i], "--stream"))) {
	     stream++;
	}
	else if ((!strcmp(argv[i], "-walker")) ||
	         (!strcmp(argv[i], "--walker"))) {
	     walker++;
             noout++;
#ifdef LIBXML_PATTERN_ENABLED
        } else if ((!strcmp(argv[i], "-pattern")) ||
                   (!strcmp(argv[i], "--pattern"))) {
	    i++;
	    pattern = argv[i];
#endif
	}
#endif /* LIBXML_READER_ENABLED */
#ifdef LIBXML_SAX1_ENABLED
	else if ((!strcmp(argv[i], "-sax1")) ||
	         (!strcmp(argv[i], "--sax1"))) {
	    sax1++;
	    options |= XML_PARSE_SAX1;
	}
#endif /* LIBXML_SAX1_ENABLED */
	else if ((!strcmp(argv[i], "-sax")) ||
	         (!strcmp(argv[i], "--sax"))) {
	    sax++;
	}
	else if ((!strcmp(argv[i], "-chkregister")) ||
	         (!strcmp(argv[i], "--chkregister"))) {
	    chkregister++;
#ifdef LIBXML_SCHEMAS_ENABLED
	} else if ((!strcmp(argv[i], "-relaxng")) ||
	         (!strcmp(argv[i], "--relaxng"))) {
	    i++;
	    relaxng = argv[i];
	    noent++;
	    options |= XML_PARSE_NOENT;
	} else if ((!strcmp(argv[i], "-schema")) ||
	         (!strcmp(argv[i], "--schema"))) {
	    i++;
	    schema = argv[i];
	    noent++;
#endif
#ifdef LIBXML_SCHEMATRON_ENABLED
	} else if ((!strcmp(argv[i], "-schematron")) ||
	         (!strcmp(argv[i], "--schematron"))) {
	    i++;
	    schematron = argv[i];
	    noent++;
#endif
        } else if ((!strcmp(argv[i], "-nonet")) ||
                   (!strcmp(argv[i], "--nonet"))) {
	    options |= XML_PARSE_NONET;
	    xmlSetExternalEntityLoader(xmlNoNetExternalEntityLoader);
        } else if ((!strcmp(argv[i], "-nocompact")) ||
                   (!strcmp(argv[i], "--nocompact"))) {
	    options &= ~XML_PARSE_COMPACT;
	} else if ((!strcmp(argv[i], "-load-trace")) ||
	           (!strcmp(argv[i], "--load-trace"))) {
	    load_trace++;
        } else if ((!strcmp(argv[i], "-path")) ||
                   (!strcmp(argv[i], "--path"))) {
	    i++;
	    parsePath(BAD_CAST argv[i]);
#ifdef LIBXML_XPATH_ENABLED
        } else if ((!strcmp(argv[i], "-xpath")) ||
                   (!strcmp(argv[i], "--xpath"))) {
	    i++;
	    noout++;
	    xpathquery = argv[i];
#endif
	} else if ((!strcmp(argv[i], "-oldxml10")) ||
	           (!strcmp(argv[i], "--oldxml10"))) {
	    oldxml10++;
	    options |= XML_PARSE_OLD10;
	} else {
	    fprintf(stderr, "Unknown option %s\n", argv[i]);
	    usage(stderr, argv[0]);
	    return(1);
	}
    }

#ifdef LIBXML_CATALOG_ENABLED
    if (nocatalogs == 0) {
	if (catalogs) {
	    const char *catal;

	    catal = getenv("SGML_CATALOG_FILES");
	    if (catal != NULL) {
		xmlLoadCatalogs(catal);
	    } else {
		fprintf(stderr, "Variable $SGML_CATALOG_FILES not set\n");
	    }
	}
    }
#endif

#ifdef LIBXML_SAX1_ENABLED
    if (sax1)
        xmlSAXDefaultVersion(1);
    else
        xmlSAXDefaultVersion(2);
#endif /* LIBXML_SAX1_ENABLED */

    if (chkregister) {
	xmlRegisterNodeDefault(registerNode);
	xmlDeregisterNodeDefault(deregisterNode);
    }

    indent = getenv("XMLLINT_INDENT");
    if(indent != NULL) {
	xmlTreeIndentString = indent;
    }

    defaultEntityLoader = xmlGetExternalEntityLoader();
    xmlSetExternalEntityLoader(xmllintExternalEntityLoader);

    xmlLineNumbersDefault(1);
    if (loaddtd != 0)
	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
    if (dtdattrs)
	xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS;
    if (noent != 0) xmlSubstituteEntitiesDefault(1);
#ifdef LIBXML_VALID_ENABLED
    if (valid != 0) xmlDoValidityCheckingDefaultValue = 1;
#endif /* LIBXML_VALID_ENABLED */
    if ((htmlout) && (!nowrap)) {
	xmlGenericError(xmlGenericErrorContext,
         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n");
	xmlGenericError(xmlGenericErrorContext,
		"\t\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n");
	xmlGenericError(xmlGenericErrorContext,
	 "<title>%s output</title>\n",
		argv[0]);
	xmlGenericError(xmlGenericErrorContext,
	 "
%s output
\n",
		argv[0]);
    }

#ifdef LIBXML_SCHEMATRON_ENABLED
    if ((schematron != NULL) && (sax == 0)
#ifdef LIBXML_READER_ENABLED
        && (stream == 0)
#endif /* LIBXML_READER_ENABLED */
	) {
	xmlSchematronParserCtxtPtr ctxt;

        /* forces loading the DTDs */
        xmlLoadExtDtdDefaultValue |= 1;
	options |= XML_PARSE_DTDLOAD;
	if (timing) {
	    startTimer();
	}
	ctxt = xmlSchematronNewParserCtxt(schematron);
#if 0
	xmlSchematronSetParserErrors(ctxt, xmlGenericError, xmlGenericError,
                NULL);
#endif
	wxschematron = xmlSchematronParse(ctxt);
	if (wxschematron == NULL) {
	    xmlGenericError(xmlGenericErrorContext,
		    "Schematron schema %s failed to compile\n", schematron);
            progresult = XMLLINT_ERR_SCHEMACOMP;
	    schematron = NULL;
	}
	xmlSchematronFreeParserCtxt(ctxt);
	if (timing) {
	    endTimer("Compiling the schemas");
	}
    }
#endif
#ifdef LIBXML_SCHEMAS_ENABLED
    if ((relaxng != NULL) && (sax == 0)
#ifdef LIBXML_READER_ENABLED
        && (stream == 0)
#endif /* LIBXML_READER_ENABLED */
	) {
	xmlRelaxNGParserCtxtPtr ctxt;

        /* forces loading the DTDs */
        xmlLoadExtDtdDefaultValue |= 1;
	options |= XML_PARSE_DTDLOAD;
	if (timing) {
	    startTimer();
	}
	ctxt = xmlRelaxNGNewParserCtxt(relaxng);
	xmlRelaxNGSetParserErrors(ctxt, xmlGenericError, xmlGenericError,
                NULL);
	relaxngschemas = xmlRelaxNGParse(ctxt);
	if (relaxngschemas == NULL) {
	    xmlGenericError(xmlGenericErrorContext,
		    "Relax-NG schema %s failed to compile\n", relaxng);
            progresult = XMLLINT_ERR_SCHEMACOMP;
	    relaxng = NULL;
	}
	xmlRelaxNGFreeParserCtxt(ctxt);
	if (timing) {
	    endTimer("Compiling the schemas");
	}
    } else if ((schema != NULL)
#ifdef LIBXML_READER_ENABLED
		&& (stream == 0)
#endif
	) {
	xmlSchemaParserCtxtPtr ctxt;

	if (timing) {
	    startTimer();
	}
	ctxt = xmlSchemaNewParserCtxt(schema);
	xmlSchemaSetParserErrors(ctxt, xmlGenericError, xmlGenericError, NULL);
	wxschemas = xmlSchemaParse(ctxt);
	if (wxschemas == NULL) {
	    xmlGenericError(xmlGenericErrorContext,
		    "WXS schema %s failed to compile\n", schema);
            progresult = XMLLINT_ERR_SCHEMACOMP;
	    schema = NULL;
	}
	xmlSchemaFreeParserCtxt(ctxt);
	if (timing) {
	    endTimer("Compiling the schemas");
	}
    }
#endif /* LIBXML_SCHEMAS_ENABLED */
#if defined(LIBXML_READER_ENABLED) && defined(LIBXML_PATTERN_ENABLED)
    if ((pattern != NULL) && (walker == 0)) {
        patternc = xmlPatterncompile((const xmlChar *) pattern, NULL, 0, NULL);
	if (patternc == NULL) {
	    xmlGenericError(xmlGenericErrorContext,
		    "Pattern %s failed to compile\n", pattern);
            progresult = XMLLINT_ERR_SCHEMAPAT;
	    pattern = NULL;
	}
    }
#endif /* LIBXML_READER_ENABLED && LIBXML_PATTERN_ENABLED */
    for (i = 1; i < argc ; i++) {
	if ((!strcmp(argv[i], "-encode")) ||
	         (!strcmp(argv[i], "--encode"))) {
	    i++;
	    continue;
        } else if ((!strcmp(argv[i], "-o")) ||
                   (!strcmp(argv[i], "-output")) ||
                   (!strcmp(argv[i], "--output"))) {
            i++;
	    continue;
        }
#ifdef LIBXML_VALID_ENABLED
	if ((!strcmp(argv[i], "-dtdvalid")) ||
	         (!strcmp(argv[i], "--dtdvalid"))) {
	    i++;
	    continue;
        }
	if ((!strcmp(argv[i], "-path")) ||
                   (!strcmp(argv[i], "--path"))) {
            i++;
	    continue;
        }
	if ((!strcmp(argv[i], "-dtdvalidfpi")) ||
	         (!strcmp(argv[i], "--dtdvalidfpi"))) {
	    i++;
	    continue;
        }
#endif /* LIBXML_VALID_ENABLED */
	if ((!strcmp(argv[i], "-relaxng")) ||
	         (!strcmp(argv[i], "--relaxng"))) {
	    i++;
	    continue;
        }
	if ((!strcmp(argv[i], "-maxmem")) ||
	         (!strcmp(argv[i], "--maxmem"))) {
	    i++;
	    continue;
        }
	if ((!strcmp(argv[i], "-pretty")) ||
	         (!strcmp(argv[i], "--pretty"))) {
	    i++;
	    continue;
        }
	if ((!strcmp(argv[i], "-schema")) ||
	         (!strcmp(argv[i], "--schema"))) {
	    i++;
	    continue;
        }
	if ((!strcmp(argv[i], "-schematron")) ||
	         (!strcmp(argv[i], "--schematron"))) {
	    i++;
	    continue;
        }
#if defined(LIBXML_READER_ENABLED) && defined(LIBXML_PATTERN_ENABLED)
        if ((!strcmp(argv[i], "-pattern")) ||
	    (!strcmp(argv[i], "--pattern"))) {
	    i++;
	    continue;
	}
#endif
#ifdef LIBXML_XPATH_ENABLED
        if ((!strcmp(argv[i], "-xpath")) ||
	    (!strcmp(argv[i], "--xpath"))) {
	    i++;
	    continue;
	}
#endif
	if ((timing) && (repeat))
	    startTimer();
	/* Remember file names.  "-" means stdin.  <sven@zen.org> */
	if ((argv[i][0] != '-') || (strcmp(argv[i], "-") == 0)) {
	    if (repeat) {
		xmlParserCtxtPtr ctxt = NULL;

		for (acount = 0;acount < repeat;acount++) {
#ifdef LIBXML_READER_ENABLED
		    if (stream != 0) {
			streamFile(argv[i]);
		    } else {
#endif /* LIBXML_READER_ENABLED */
                        if (sax) {
			    testSAX(argv[i]);
			} else {
			    if (ctxt == NULL)
				ctxt = xmlNewParserCtxt();
			    parseAndPrintFile(argv[i], ctxt);
			}
#ifdef LIBXML_READER_ENABLED
		    }
#endif /* LIBXML_READER_ENABLED */
		}
		if (ctxt != NULL)
		    xmlFreeParserCtxt(ctxt);
	    } else {
		nbregister = 0;

#ifdef LIBXML_READER_ENABLED
		if (stream != 0)
		    streamFile(argv[i]);
		else
#endif /* LIBXML_READER_ENABLED */
                if (sax) {
		    testSAX(argv[i]);
		} else {
		    parseAndPrintFile(argv[i], NULL);
		}

                if ((chkregister) && (nbregister != 0)) {
		    fprintf(stderr, "Registration count off: %d\n", nbregister);
		    progresult = XMLLINT_ERR_RDREGIS;
		}
	    }
	    files ++;
	    if ((timing) && (repeat)) {
		endTimer("%d iterations", repeat);
	    }
	}
    }
    if (generate)
	parseAndPrintFile(NULL, NULL);
    if ((htmlout) && (!nowrap)) {
	xmlGenericError(xmlGenericErrorContext, "\n");
    }
    if ((files == 0) && (!generate) && (version == 0)) {
	usage(stderr, argv[0]);
    }
#ifdef LIBXML_SCHEMATRON_ENABLED
    if (wxschematron != NULL)
	xmlSchematronFree(wxschematron);
#endif
#ifdef LIBXML_SCHEMAS_ENABLED
    if (relaxngschemas != NULL)
	xmlRelaxNGFree(relaxngschemas);
    if (wxschemas != NULL)
	xmlSchemaFree(wxschemas);
    xmlRelaxNGCleanupTypes();
#endif
#if defined(LIBXML_READER_ENABLED) && defined(LIBXML_PATTERN_ENABLED)
    if (patternc != NULL)
        xmlFreePattern(patternc);
#endif
    xmlCleanupParser();
    xmlMemoryDump();

    return(progresult);
}


startTimer(void)
{
    gettimeofday(&begin, NULL);
}


OOM(void)
{
    fprintf(stderr, "Ran out of memory needs > %d bytes\n", maxmem);
    progresult = XMLLINT_ERR_MEM;
}


xmlHTMLValidityError(void *ctx, const char *msg, ...)
{
    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
    xmlParserInputPtr input;
    va_list args;
    int len;

    buffer[0] = 0;
    input = ctxt->input;
    if ((input->filename == NULL) && (ctxt->inputNr > 1))
        input = ctxt->inputTab[ctxt->inputNr - 2];

    xmlHTMLPrintFileInfo(input);

    xmlGenericError(xmlGenericErrorContext, "validity error: ");
    len = strlen(buffer);
    va_start(args, msg);
    vsnprintf(&buffer[len],  sizeof(buffer) - len, msg, args);
    va_end(args);
    xmlHTMLEncodeSend();
    xmlGenericError(xmlGenericErrorContext, "
\n");

    xmlHTMLPrintFileContext(input);
    xmlHTMLEncodeSend();
    progresult = XMLLINT_ERR_VALID;
}


resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId)
{
    callbacks++;
    if (noout)
	return(NULL);
    /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */

    fprintf(stdout, "SAX.resolveEntity(");
    if (publicId != NULL)
	fprintf(stdout, "%s", (char *)publicId);
    else
	fprintf(stdout, " ");
    if (systemId != NULL)
	fprintf(stdout, ", %s)\n", (char *)systemId);
    else
	fprintf(stdout, ", )\n");
    return(NULL);
}


xmlDumpEntityDeclScan(void *ent, void *buf,
                      const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlDumpEntityDecl((xmlBufferPtr) buf, (xmlEntityPtr) ent);
}


xmlFreeEntityWrapper(void *entity, const xmlChar *name ATTRIBUTE_UNUSED) {
    if (entity != NULL)
	xmlFreeEntity((xmlEntityPtr) entity);
}


xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc,
	              const xmlURL url ATTRIBUTE_UNUSED) {
    xmlXIncludeCtxtPtr newctxt;
    int i;

    /*
     * Avoid recursion in already substituted resources
    for (i = 0;i < ctxt->urlNr;i++) {
	if (xmlStrEqual(doc->URL, ctxt->urlTab[i]))
	    return;
    }
     */

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Recursing in doc %s\n", doc->URL);
#endif
    /*
     * Handle recursion here.
     */

    newctxt = xmlXIncludeNewContext(doc);
    if (newctxt != NULL) {
	/*
	 * Copy the private user data
	 */
	newctxt->_private = ctxt->_private;
	/*
	 * Copy the existing document set
	 */
	newctxt->incMax = ctxt->incMax;
	newctxt->incNr = ctxt->incNr;
        newctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(newctxt->incMax *
		                          sizeof(newctxt->incTab[0]));
        if (newctxt->incTab == NULL) {
	    xmlXIncludeErrMemory(ctxt, (xmlNodePtr) doc, "processing doc");
	    xmlFree(newctxt);
	    return;
	}
	/*
	 * copy the urlTab
	 */
	newctxt->urlMax = ctxt->urlMax;
	newctxt->urlNr = ctxt->urlNr;
	newctxt->urlTab = ctxt->urlTab;

	/*
	 * Inherit the existing base
	 */
	newctxt->base = xmlStrdup(ctxt->base);

	/*
	 * Inherit the documents already in use by other includes
	 */
	newctxt->incBase = ctxt->incNr;
	for (i = 0;i < ctxt->incNr;i++) {
	    newctxt->incTab[i] = ctxt->incTab[i];
	    newctxt->incTab[i]->count++; /* prevent the recursion from
					    freeing it */
	}
	/*
	 * The new context should also inherit the Parse Flags
	 * (bug 132597)
	 */
	newctxt->parseFlags = ctxt->parseFlags;
        newctxt->incTotal = ctxt->incTotal;
	xmlXIncludeDoProcess(newctxt, doc, xmlDocGetRootElement(doc), 0);
        ctxt->incTotal = newctxt->incTotal;
	for (i = 0;i < ctxt->incNr;i++) {
	    newctxt->incTab[i]->count--;
	    newctxt->incTab[i] = NULL;
	}

	/* urlTab may have been reallocated */
	ctxt->urlTab = newctxt->urlTab;
	ctxt->urlMax = newctxt->urlMax;

	newctxt->urlMax = 0;
	newctxt->urlNr = 0;
	newctxt->urlTab = NULL;

	xmlXIncludeFreeContext(newctxt);
    }
#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "Done recursing in doc %s\n", url);
#endif
}


xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt, const xmlChar *URI,
	          xmlNodePtr ref) {
    xmlXIncludeRefPtr ret;

#ifdef DEBUG_XINCLUDE
    xmlGenericError(xmlGenericErrorContext, "New ref %s\n", URI);
#endif
    ret = (xmlXIncludeRefPtr) xmlMalloc(sizeof(xmlXIncludeRef));
    if (ret == NULL) {
        xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context");
	return(NULL);
    }
    memset(ret, 0, sizeof(xmlXIncludeRef));
    if (URI == NULL)
	ret->URI = NULL;
    else
	ret->URI = xmlStrdup(URI);
    ret->fragment = NULL;
    ret->ref = ref;
    ret->doc = NULL;
    ret->count = 0;
    ret->xml = 0;
    ret->inc = NULL;
    if (ctxt->incMax == 0) {
	ctxt->incMax = 4;
        ctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(ctxt->incMax *
					      sizeof(ctxt->incTab[0]));
        if (ctxt->incTab == NULL) {
	    xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context");
	    xmlXIncludeFreeRef(ret);
	    return(NULL);
	}
    }
    if (ctxt->incNr >= ctxt->incMax) {
	ctxt->incMax *= 2;
        ctxt->incTab = (xmlXIncludeRefPtr *) xmlRealloc(ctxt->incTab,
	             ctxt->incMax * sizeof(ctxt->incTab[0]));
        if (ctxt->incTab == NULL) {
	    xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context");
	    xmlXIncludeFreeRef(ret);
	    return(NULL);
	}
    }
    ctxt->incTab[ctxt->incNr++] = ret;
    return(ret);
}


regional_create(void)
{
	return regional_create_custom(REGIONAL_CHUNK_SIZE);
}


count_large(struct regional* r)
{
	size_t c = 0;
	char* p = r->large_list;
	while(p) {
		c++;
		p = *(char**)p;
	}
	return c;
}


regional_create_custom(size_t size)
{
	struct regional* r = (struct regional*)malloc(size);
	size = ALIGN_UP(size, ALIGNMENT);
	log_assert(sizeof(struct regional) <= size);
	if(!r) return NULL;
	r->first_size = size;
	regional_init(r);
	return r;
}


dnsc_shared_secrets_deldatafunc(void* d, void* ATTR_UNUSED(arg))
{
    uint8_t* data = (uint8_t*)d;
    free(data);
}


respip_get_funcblock(void)
{
	return &respip_block;
}


respip_set_get_tree(struct respip_set* set)
{
	if(!set)
		return NULL;
	return &set->ip_tree;
}


dnsc_nonces_sizefunc(void *k, void* ATTR_UNUSED(d))
{
    struct nonce_cache_key* nk = (struct nonce_cache_key*)k;
    size_t key_size = sizeof(struct nonce_cache_key)
        + lock_get_mem(&nk->entry.lock);
    (void)nk; /* otherwise ssk is unused if no threading, or fixed locksize */
    return key_size;
}


respip_set_delete(struct respip_set* set)
{
	if(!set)
		return;
	regional_destroy(set->region);
	free(set);
}


int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo,
	struct module_qstate* qstate, struct reply_info* rep, int rcode,
	struct edns_data* edns, struct comm_reply* repinfo, struct regional* region)
{
	return inplace_cb_reply_call_generic(
		env->inplace_cb_lists[inplace_cb_reply], inplace_cb_reply, qinfo,
		qstate, rep, rcode, edns, repinfo, region);
}


query_info_compare(void* m1, void* m2)
{
	struct query_info* msg1 = (struct query_info*)m1;
	struct query_info* msg2 = (struct query_info*)m2;
	int mc;
	/* from most different to least different for speed */
	COMPARE_IT(msg1->qtype, msg2->qtype);
	if((mc = query_dname_compare(msg1->qname, msg2->qname)) != 0)
		return mc;
	log_assert(msg1->qname_len == msg2->qname_len);
	COMPARE_IT(msg1->qclass, msg2->qclass);
	return 0;
#undef COMPARE_IT
}


query_entry_delete(void *k, void* ATTR_UNUSED(arg))
{
	struct msgreply_entry* q = (struct msgreply_entry*)k;
	lock_rw_destroy(&q->entry.lock);
	query_info_clear(&q->key);
	free(q);
}


log_reply_info(enum verbosity_value v, struct query_info *qinf,
	struct sockaddr_storage *addr, socklen_t addrlen, struct timeval dur,
	int cached, struct sldns_buffer *rmsg)
{
	char qname_buf[LDNS_MAX_DOMAINLEN+1];
	char clientip_buf[128];
	char rcode_buf[16];
	char type_buf[16];
	char class_buf[16];
	size_t pktlen;
	uint16_t rcode = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(rmsg, 2));

	if(verbosity < v)
	  return;

	sldns_wire2str_rcode_buf((int)rcode, rcode_buf, sizeof(rcode_buf));
	addr_to_str(addr, addrlen, clientip_buf, sizeof(clientip_buf));
	if(rcode == LDNS_RCODE_FORMERR)
	{
		if(LOG_TAG_QUERYREPLY)
			log_reply("%s - - - %s - - - ", clientip_buf, rcode_buf);
		else	log_info("%s - - - %s - - - ", clientip_buf, rcode_buf);
	} else {
		if(qinf->qname)
			dname_str(qinf->qname, qname_buf);
		else	snprintf(qname_buf, sizeof(qname_buf), "null");
		pktlen = sldns_buffer_limit(rmsg);
		sldns_wire2str_type_buf(qinf->qtype, type_buf, sizeof(type_buf));
		sldns_wire2str_class_buf(qinf->qclass, class_buf, sizeof(class_buf));
		if(LOG_TAG_QUERYREPLY)
		     log_reply("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d",
			clientip_buf, qname_buf, type_buf, class_buf,
			rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen);
		else log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d",
			clientip_buf, qname_buf, type_buf, class_buf,
			rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen);
	}
}


static int inplace_cb_reply_call_generic(
    struct inplace_cb* callback_list, enum inplace_cb_list_type type,
	struct query_info* qinfo, struct module_qstate* qstate,
	struct reply_info* rep, int rcode, struct edns_data* edns,
	struct comm_reply* repinfo, struct regional* region)
{
	struct inplace_cb* cb;
	struct edns_option* opt_list_out = NULL;
#if defined(EXPORT_ALL_SYMBOLS)
	(void)type; /* param not used when fptr_ok disabled */
#endif
	if(qstate)
		opt_list_out = qstate->edns_opts_front_out;
	for(cb=callback_list; cb; cb=cb->next) {
		fptr_ok(fptr_whitelist_inplace_cb_reply_generic(
			(inplace_cb_reply_func_type*)cb->cb, type));
		(void)(*(inplace_cb_reply_func_type*)cb->cb)(qinfo, qstate, rep,
			rcode, edns, &opt_list_out, repinfo, region, cb->id, cb->cb_arg);
	}
	edns->opt_list = opt_list_out;
	return 1;
}


parse_copy_decompress_rrset(sldns_buffer* pkt, struct msg_parse* msg,
	struct rrset_parse *pset, struct regional* region,
	struct ub_packed_rrset_key* pk)
{
	struct packed_rrset_data* data;
	pk->rk.flags = pset->flags;
	pk->rk.dname_len = pset->dname_len;
	if(region)
		pk->rk.dname = (uint8_t*)regional_alloc(
			region, pset->dname_len);
	else	pk->rk.dname =
			(uint8_t*)malloc(pset->dname_len);
	if(!pk->rk.dname)
		return 0;
	/** copy & decompress dname */
	dname_pkt_copy(pkt, pk->rk.dname, pset->dname);
	/** copy over type and class */
	pk->rk.type = htons(pset->type);
	pk->rk.rrset_class = pset->rrset_class;
	/** read data part. */
	if(!parse_create_rrset(pkt, pset, &data, region))
		return 0;
	pk->entry.data = (void*)data;
	pk->entry.key = (void*)pk;
	pk->entry.hash = pset->hash;
	data->trust = get_rrset_trust(msg, pset);
	return 1;
}


RGWOp *RGWHandler_REST_Bucket_SWIFT::op_post()
{
  if (RGWFormPost::is_formpost_req(s)) {
    return new RGWFormPost;
  } else {
    return new RGWPutMetadataBucket_ObjStore_SWIFT;
  }
}


int RGWHandler_REST_SWIFT::init_from_header(struct req_state* const s,
                                            const std::string& frontend_prefix)
{
  string req;
  string first;

  s->prot_flags |= RGW_REST_SWIFT;

  char reqbuf[frontend_prefix.length() + s->decoded_uri.length() + 1];
  sprintf(reqbuf, "%s%s", frontend_prefix.c_str(), s->decoded_uri.c_str());
  const char *req_name = reqbuf;

  const char *p;

  if (*req_name == '?') {
    p = req_name;
  } else {
    p = s->info.request_params.c_str();
  }

  s->info.args.set(p);
  s->info.args.parse();

  /* Skip the leading slash of URL hierarchy. */
  if (req_name[0] != '/') {
    return 0;
  } else {
    req_name++;
  }

  if ('\0' == req_name[0]) {
    return g_conf()->rgw_swift_url_prefix == "/" ? -ERR_BAD_URL : 0;
  }

  req = req_name;

  size_t pos = req.find('/');
  if (std::string::npos != pos && g_conf()->rgw_swift_url_prefix != "/") {
    bool cut_url = g_conf()->rgw_swift_url_prefix.length();
    first = req.substr(0, pos);

    if (first.compare(g_conf()->rgw_swift_url_prefix) == 0) {
      if (cut_url) {
        /* Rewind to the "v1/..." part. */
        next_tok(req, first, '/');
      }
    }
  } else if (req.compare(g_conf()->rgw_swift_url_prefix) == 0) {
    s->formatter = new RGWFormatter_Plain;
    return -ERR_BAD_URL;
  } else {
    first = req;
  }

  std::string tenant_path;
  if (! g_conf()->rgw_swift_tenant_name.empty()) {
    tenant_path = "/AUTH_";
    tenant_path.append(g_conf()->rgw_swift_tenant_name);
  }

  /* verify that the request_uri conforms with what's expected */
  char buf[g_conf()->rgw_swift_url_prefix.length() + 16 + tenant_path.length()];
  int blen;
  if (g_conf()->rgw_swift_url_prefix == "/") {
    blen = sprintf(buf, "/v1%s", tenant_path.c_str());
  } else {
    blen = sprintf(buf, "/%s/v1%s",
                   g_conf()->rgw_swift_url_prefix.c_str(), tenant_path.c_str());
  }

  if (strncmp(reqbuf, buf, blen) != 0) {
    return -ENOENT;
  }

  int ret = allocate_formatter(s, RGW_FORMAT_PLAIN, true);
  if (ret < 0)
    return ret;

  string ver;

  next_tok(req, ver, '/');

  if (!tenant_path.empty() || g_conf()->rgw_swift_account_in_url) {
    string account_name;
    next_tok(req, account_name, '/');

    /* Erase all pre-defined prefixes like "AUTH_" or "KEY_". */
    const vector<string> skipped_prefixes = { "AUTH_", "KEY_" };

    for (const auto pfx : skipped_prefixes) {
      const size_t comp_len = min(account_name.length(), pfx.length());
      if (account_name.compare(0, comp_len, pfx) == 0) {
        /* Prefix is present. Drop it. */
        account_name = account_name.substr(comp_len);
        break;
      }
    }

    if (account_name.empty()) {
      return -ERR_PRECONDITION_FAILED;
    } else {
      s->account_name = account_name;
    }
  }

  next_tok(req, first, '/');

  dout(10) << "ver=" << ver << " first=" << first << " req=" << req << dendl;
  if (first.size() == 0)
    return 0;

  s->info.effective_uri = "/" + first;

  // Save bucket to tide us over until token is parsed.
  s->init_state.url_bucket = first;

  if (req.size()) {
    s->object =
      rgw_obj_key(req, s->info.env->get("HTTP_X_OBJECT_VERSION_ID", "")); /* rgw swift extension */
    s->info.effective_uri.append("/" + s->object.name);
  }

  return 0;
}


int RGWGetObj_ObjStore_SWIFT::send_response_data_error()
{
  std::string error_content;
  op_ret = error_handler(op_ret, &error_content);
  if (! op_ret) {
    /* The error handler has taken care of the error. */
    return 0;
  }

  bufferlist error_bl;
  error_bl.append(error_content);
  return send_response_data(error_bl, 0, error_bl.length());
}


RGWOp *RGWHandler_REST_Bucket_SWIFT::op_put()
{
  if (is_acl_op()) {
    return new RGWPutACLs_ObjStore_SWIFT;
  }
  if(s->info.args.exists("extract-archive")) {
    return new RGWBulkUploadOp_ObjStore_SWIFT;
  }
  return new RGWCreateBucket_ObjStore_SWIFT;
}


RGWOp *RGWHandler_REST_Obj_SWIFT::op_put()
{
  if (is_acl_op()) {
    return new RGWPutACLs_ObjStore_SWIFT;
  }
  if(s->info.args.exists("extract-archive")) {
    return new RGWBulkUploadOp_ObjStore_SWIFT;
  }
  if (s->init_state.src_bucket.empty())
    return new RGWPutObj_ObjStore_SWIFT;
  else
    return new RGWCopyObj_ObjStore_SWIFT;
}


void RGWInfo_ObjStore_SWIFT::send_response()
{
  if (op_ret <  0) {
    op_ret = STATUS_NO_CONTENT;
  }
  set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this);
  rgw_flush_formatter_and_reset(s, s->formatter);
}


RGWOp *RGWHandler_REST_Obj_SWIFT::get_obj_op(bool get_data)
{
  if (is_acl_op()) {
    return new RGWGetACLs_ObjStore_SWIFT;
  }

  RGWGetObj_ObjStore_SWIFT *get_obj_op = new RGWGetObj_ObjStore_SWIFT;
  get_obj_op->set_get_data(get_data);
  return get_obj_op;
}


void RGWInfo_ObjStore_SWIFT::execute()
{
  bool is_admin_info_enabled = false;

  const string& swiftinfo_sig = s->info.args.get("swiftinfo_sig");
  const string& swiftinfo_expires = s->info.args.get("swiftinfo_expires");

  if (!swiftinfo_sig.empty() &&
      !swiftinfo_expires.empty() &&
      !is_expired(swiftinfo_expires, s->cct)) {
    is_admin_info_enabled = true;
  }

  s->formatter->open_object_section("info");

  for (const auto& pair : swift_info) {
    if(!is_admin_info_enabled && pair.second.is_admin_info)
      continue;

    if (!pair.second.list_data) {
      s->formatter->open_object_section((pair.first).c_str());
      s->formatter->close_section();
    }
    else {
      pair.second.list_data(*(s->formatter), s->cct->_conf, *store);
    }
  }

  s->formatter->close_section();
}


int RGWHandler_REST_SWIFT::authorize(const DoutPrefixProvider *dpp)
{
  return rgw::auth::Strategy::apply(dpp, auth_strategy, s);
}


static void next_tok(string& str, string& tok, char delim)
{
  if (str.size() == 0) {
    tok = "";
    return;
  }
  tok = str;
  int pos = str.find(delim);
  if (pos > 0) {
    tok = str.substr(0, pos);
    str = str.substr(pos + 1);
  } else {
    str = "";
  }
}


int RGWPutMetadataBucket_ObjStore_SWIFT::get_params()
{
  if (s->has_bad_meta) {
    return -EINVAL;
  }

  int r = get_swift_container_settings(s, store, &policy, &has_policy,
				       &policy_rw_mask, &cors_config, &has_cors);
  if (r < 0) {
    return r;
  }

  get_rmattrs_from_headers(s, CONT_PUT_ATTR_PREFIX, CONT_REMOVE_ATTR_PREFIX,
			   rmattr_names);
  placement_rule.init(s->info.env->get("HTTP_X_STORAGE_POLICY", ""), s->info.storage_class);

  return get_swift_versioning_settings(s, swift_ver_location);
}


RGWHandler_REST* RGWRESTMgr_SWIFT_Info::get_handler(
  struct req_state* const s,
  const rgw::auth::StrategyRegistry& auth_registry,
  const std::string& frontend_prefix)
{
  s->prot_flags |= RGW_REST_SWIFT;
  const auto& auth_strategy = auth_registry.get_swift();
  return new RGWHandler_REST_SWIFT_Info(auth_strategy);
}


void RGWGetCrossDomainPolicy_ObjStore_SWIFT::send_response()
{
  set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");

  std::stringstream ss;

  ss << R"(<?xml version="1.0"?>)" << "\n"
     << R"(<!DOCTYPE cross-domain-policy SYSTEM )"
     << R"("http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" >)" << "\n"
     << R"(<cross-domain-policy>)" << "\n"
     << g_conf()->rgw_cross_domain_policy << "\n"
     << R"(</cross-domain-policy>)";

  dump_body(s, ss.str());
}


bool RGWFormPost::is_next_file_to_upload()
{
  if (! stream_done) {
    /* We have at least one additional part in the body. */
    struct post_form_part part;
    int r = read_form_part_header(&part, stream_done);
    if (r < 0) {
      return false;
    }

    const auto field_iter = part.fields.find("Content-Disposition");
    if (std::end(part.fields) != field_iter) {
      const auto& params = field_iter->second.params;
      const auto& filename_iter = params.find("filename");

      if (std::end(params) != filename_iter && ! filename_iter->second.empty()) {
        current_data_part = std::move(part);
        return true;
      }
    }
  }

  return false;
}


RGWOp* RGWSwiftWebsiteHandler::get_ws_listing_op()
{
  class RGWWebsiteListing : public RGWListBucket_ObjStore_SWIFT {
    const std::string prefix_override;

    int get_params() override {
      prefix = prefix_override;
      max = default_max;
      delimiter = "/";
      return 0;
    }

    void send_response() override {
      /* Generate the header now. */
      set_req_state_err(s, op_ret);
      dump_errno(s);
      dump_container_metadata(s, bucket, bucket_quota,
                              s->bucket_info.website_conf);
      end_header(s, this, "text/html");
      if (op_ret < 0) {
        return;
      }

      /* Now it's the time to start generating HTML bucket listing.
       * All the crazy stuff with crafting tags will be delegated to
       * RGWSwiftWebsiteListingFormatter. */
      std::stringstream ss;
      RGWSwiftWebsiteListingFormatter htmler(ss, prefix);

      const auto& ws_conf = s->bucket_info.website_conf;
      htmler.generate_header(s->decoded_uri,
                             ws_conf.listing_css_doc);

      for (const auto& pair : common_prefixes) {
        std::string subdir_name = pair.first;
        if (! subdir_name.empty()) {
          /* To be compliant with Swift we need to remove the trailing
           * slash. */
          subdir_name.pop_back();
        }

        htmler.dump_subdir(subdir_name);
      }

      for (const rgw_bucket_dir_entry& obj : objs) {
        if (! common_prefixes.count(obj.key.name + '/')) {
          htmler.dump_object(obj);
        }
      }

      htmler.generate_footer();
      dump_body(s, ss.str());
    }
  public:
    /* Taking prefix_override by value to leverage std::string r-value ref
     * ctor and thus avoid extra memory copying/increasing ref counter. */
    explicit RGWWebsiteListing(std::string prefix_override)
      : prefix_override(std::move(prefix_override)) {
    }
  };

  std::string prefix = std::move(s->object.name);
  s->object = rgw_obj_key();

  return new RGWWebsiteListing(std::move(prefix));
}


void RGWGetHealthCheck_ObjStore_SWIFT::send_response()
{
  set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");

  if (op_ret) {
    static constexpr char DISABLED[] = "DISABLED BY FILE";
    dump_body(s, DISABLED, strlen(DISABLED));
  }
}


static void packet_id_queue_empty(struct PacketIdQueue *q)
{
    USBRedirDevice *dev = q->dev;
    struct PacketIdQueueEntry *e, *next_e;

    DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name);

    QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) {
        QTAILQ_REMOVE(&q->head, e, next);
        g_free(e);
    }
    q->size = 0;
}


static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed)
{
    dev->compatible_speedmask &= ~(1 << speed);
    dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
}


static void usbredir_ep_stopped(USBDevice *udev, USBEndpoint *uep)
{
    USBRedirDevice *dev = USB_REDIRECT(udev);

    usbredir_stop_ep(dev, USBEP2I(uep));
    usbredirparser_do_write(dev->parser);
}


static void usbredir_instance_init(Object *obj)
{
    USBDevice *udev = USB_DEVICE(obj);
    USBRedirDevice *dev = USB_REDIRECT(udev);

    device_add_bootindex_property(obj, &dev->bootindex,
                                  "bootindex", NULL,
                                  &udev->qdev);
}


static void usbredir_create_parser(USBRedirDevice *dev)
{
    uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
    int flags = 0;

    DPRINTF("creating usbredirparser\n");

    dev->parser = qemu_oom_check(usbredirparser_create());
    dev->parser->priv = dev;
    dev->parser->log_func = usbredir_log;
    dev->parser->read_func = usbredir_read;
    dev->parser->write_func = usbredir_write;
    dev->parser->hello_func = usbredir_hello;
    dev->parser->device_connect_func = usbredir_device_connect;
    dev->parser->device_disconnect_func = usbredir_device_disconnect;
    dev->parser->interface_info_func = usbredir_interface_info;
    dev->parser->ep_info_func = usbredir_ep_info;
    dev->parser->configuration_status_func = usbredir_configuration_status;
    dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
    dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
    dev->parser->interrupt_receiving_status_func =
        usbredir_interrupt_receiving_status;
    dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
    dev->parser->bulk_receiving_status_func = usbredir_bulk_receiving_status;
    dev->parser->control_packet_func = usbredir_control_packet;
    dev->parser->bulk_packet_func = usbredir_bulk_packet;
    dev->parser->iso_packet_func = usbredir_iso_packet;
    dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
    dev->parser->buffered_bulk_packet_func = usbredir_buffered_bulk_packet;
    dev->read_buf = NULL;
    dev->read_buf_size = 0;

    usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
    usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
    usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
    usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
    usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
    usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
#if USBREDIR_VERSION >= 0x000700
    if (dev->enable_streams) {
        usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
    }
#endif

    if (runstate_check(RUN_STATE_INMIGRATE)) {
        flags |= usbredirparser_fl_no_hello;
    }
    usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE,
                        flags);
    usbredirparser_do_write(dev->parser);
}


static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
    uint8_t ep, uint64_t id)
{
    USBPacket *p;

    if (usbredir_is_cancelled(dev, id)) {
        return NULL;
    }

    p = usb_ep_find_packet_by_id(&dev->dev,
                            (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT,
                            ep & 0x0f, id);
    if (p == NULL) {
        ERROR("could not find packet with id %"PRIu64"\n", id);
    }
    return p;
}


static void usbredir_do_attach(void *opaque)
{
    USBRedirDevice *dev = opaque;
    Error *local_err = NULL;

    /* In order to work properly with XHCI controllers we need these caps */
    if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !(
        usbredirparser_peer_has_cap(dev->parser,
                                    usb_redir_cap_ep_info_max_packet_size) &&
        usbredirparser_peer_has_cap(dev->parser,
                                    usb_redir_cap_32bits_bulk_length) &&
        usbredirparser_peer_has_cap(dev->parser,
                                    usb_redir_cap_64bits_ids))) {
        ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n");
        usbredir_reject_device(dev);
        return;
    }

    usb_device_attach(&dev->dev, &local_err);
    if (local_err) {
        error_report_err(local_err);
        WARNING("rejecting device due to speed mismatch\n");
        usbredir_reject_device(dev);
    }
}


static void usbredir_chardev_close_bh(void *opaque)
{
    USBRedirDevice *dev = opaque;

    qemu_bh_cancel(dev->device_reject_bh);
    usbredir_device_disconnect(dev);

    if (dev->parser) {
        DPRINTF("destroying usbredirparser\n");
        usbredirparser_destroy(dev->parser);
        dev->parser = NULL;
    }
    if (dev->watch) {
        g_source_remove(dev->watch);
        dev->watch = 0;
    }
}


static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond,
                                         void *opaque)
{
    USBRedirDevice *dev = opaque;

    dev->watch = 0;
    usbredirparser_do_write(dev->parser);

    return FALSE;
}


static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
{
    qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size);
    QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry);
    p->combined = combined;
}


TEST(QueryProjectionTest, InclusionProjectionPreservesChild) {
    auto proj = createProjection("{}", "{a: 1}");
    ASSERT_TRUE(proj.isFieldRetainedExactly("a.b"));
}


TEST(QueryProjectionTest, SortKeyMetaProjectionInInclusionProjection) {
    auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}}");

    ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
    ASSERT_FALSE(proj.requiresMatchDetails());
    ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
    ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]);
    ASSERT_FALSE(proj.requiresDocument());
}


TEST(QueryProjectionTest, MetaProjectionDoesNotPreserveField) {
    auto proj = createProjection("{}", "{a: {$meta: 'textScore'}}");
    ASSERT_FALSE(proj.isFieldRetainedExactly("a"));
}