// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fuzzer/FuzzedDataProvider.h>

#include <cstddef>
#include <cstdint>
#include <limits>
#include <utility>

#include "absl/strings/string_view.h"
#include "quiche/quic/core/qpack/qpack_decoder.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/test_tools/qpack/qpack_decoder_test_utils.h"
#include "quiche/quic/test_tools/qpack/qpack_test_utils.h"

namespace quic {
namespace test {

struct DecoderAndHandler {
  std::unique_ptr<QpackProgressiveDecoder> decoder;
  std::unique_ptr<QpackProgressiveDecoder::HeadersHandlerInterface> handler;
};

using DecoderAndHandlerMap = std::map<QuicStreamId, DecoderAndHandler>;

// Class that sets externally owned |error_detected| to true
// on encoder stream error.
class ErrorDelegate : public QpackDecoder::EncoderStreamErrorDelegate {
 public:
  ErrorDelegate(bool* error_detected) : error_detected_(error_detected) {}
  ~ErrorDelegate() override = default;

  void OnEncoderStreamError(QuicErrorCode /*error_code*/,
                            absl::string_view /*error_message*/) override {
    *error_detected_ = true;
  }

 private:
  bool* const error_detected_;
};

// Class that destroys DecoderAndHandler when decoding completes, and sets
// externally owned |error_detected| to true on encoder stream error.
class HeadersHandler : public QpackProgressiveDecoder::HeadersHandlerInterface {
 public:
  HeadersHandler(QuicStreamId stream_id,
                 DecoderAndHandlerMap* processing_decoders,
                 bool* error_detected)
      : stream_id_(stream_id),
        processing_decoders_(processing_decoders),
        error_detected_(error_detected) {}
  ~HeadersHandler() override = default;

  void OnHeaderDecoded(absl::string_view /*name*/,
                       absl::string_view /*value*/) override {}

  // Remove DecoderAndHandler from |*processing_decoders|.
  void OnDecodingCompleted() override {
    // Will delete |this|.
    size_t result = processing_decoders_->erase(stream_id_);
    QUICHE_CHECK_EQ(1u, result);
  }

  void OnDecodingErrorDetected(QuicErrorCode /*error_code*/,
                               absl::string_view /*error_message*/) override {
    *error_detected_ = true;
  }

 private:
  const QuicStreamId stream_id_;
  DecoderAndHandlerMap* const processing_decoders_;
  bool* const error_detected_;
};

// This fuzzer exercises QpackDecoder.  It should be able to cover all possible
// code paths.  There is no point in encoding QpackDecoder's output to turn this
// into a roundtrip test, because the same header list can be encoded in many
// different ways, so the output could not be expected to match the original
// input.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  FuzzedDataProvider provider(data, size);

  // Maximum 256 byte dynamic table.  Such a small size helps test draining
  // entries and eviction.
  const uint64_t maximum_dynamic_table_capacity =
      provider.ConsumeIntegral<uint8_t>();
  // Maximum 256 blocked streams.
  const uint64_t maximum_blocked_streams = provider.ConsumeIntegral<uint8_t>();

  // |error_detected| will be set to true if an error is encountered either in a
  // header block or on the encoder stream.
  bool error_detected = false;

  ErrorDelegate encoder_stream_error_delegate(&error_detected);
  QpackDecoder decoder(maximum_dynamic_table_capacity, maximum_blocked_streams,
                       &encoder_stream_error_delegate);

  NoopQpackStreamSenderDelegate decoder_stream_sender_delegate;
  decoder.set_qpack_stream_sender_delegate(&decoder_stream_sender_delegate);

  // Decoders still reading the header block, with corresponding handlers.
  DecoderAndHandlerMap reading_decoders;

  // Decoders still processing the completely read header block,
  // with corresponding handlers.
  DecoderAndHandlerMap processing_decoders;

  // Maximum 256 data fragments to limit runtime and memory usage.
  auto fragment_count = provider.ConsumeIntegral<uint8_t>();
  while (fragment_count > 0 && !error_detected &&
         provider.remaining_bytes() > 0) {
    --fragment_count;
    switch (provider.ConsumeIntegralInRange<uint8_t>(0, 3)) {
      // Feed encoder stream data to QpackDecoder.
      case 0: {
        size_t fragment_size = provider.ConsumeIntegral<uint8_t>();
        std::string encoded_data =
            provider.ConsumeRandomLengthString(fragment_size);
        decoder.encoder_stream_receiver()->Decode(encoded_data);

        continue;
      }

      // Create new progressive decoder.
      case 1: {
        QuicStreamId stream_id = provider.ConsumeIntegral<uint8_t>();
        if (reading_decoders.find(stream_id) != reading_decoders.end() ||
            processing_decoders.find(stream_id) != processing_decoders.end()) {
          continue;
        }

        DecoderAndHandler decoder_and_handler;
        decoder_and_handler.handler = std::make_unique<HeadersHandler>(
            stream_id, &processing_decoders, &error_detected);
        decoder_and_handler.decoder = decoder.CreateProgressiveDecoder(
            stream_id, decoder_and_handler.handler.get());
        reading_decoders.insert({stream_id, std::move(decoder_and_handler)});

        continue;
      }

      // Feed header block data to existing decoder.
      case 2: {
        if (reading_decoders.empty()) {
          continue;
        }

        auto it = reading_decoders.begin();
        auto distance = provider.ConsumeIntegralInRange<uint8_t>(
            0, reading_decoders.size() - 1);
        std::advance(it, distance);

        size_t fragment_size = provider.ConsumeIntegral<uint8_t>();
        std::string encoded_data =
            provider.ConsumeRandomLengthString(fragment_size);
        it->second.decoder->Decode(encoded_data);

        continue;
      }

      // End header block.
      case 3: {
        if (reading_decoders.empty()) {
          continue;
        }

        auto it = reading_decoders.begin();
        auto distance = provider.ConsumeIntegralInRange<uint8_t>(
            0, reading_decoders.size() - 1);
        std::advance(it, distance);

        QpackProgressiveDecoder* reading_decoder = it->second.decoder.get();

        // Move DecoderAndHandler to |processing_decoders| first, because
        // EndHeaderBlock() might synchronously call OnDecodingCompleted().
        QuicStreamId stream_id = it->first;
        processing_decoders.insert({stream_id, std::move(it->second)});
        reading_decoders.erase(it);

        reading_decoder->EndHeaderBlock();

        continue;
      }
    }
  }

  return 0;
}

}  // namespace test
}  // namespace quic
