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

#ifndef NET_TEST_EMBEDDED_TEST_SERVER_HTTP_RESPONSE_H_
#define NET_TEST_EMBEDDED_TEST_SERVER_HTTP_RESPONSE_H_

#include <optional>
#include <string>
#include <string_view>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/time/time.h"
#include "net/http/http_status_code.h"

namespace net::test_server {

class HttpResponse;

// Delegate that actually sends the response bytes. Any response created should
// be owned by the delegate that passed in via HttpResponse::SendResponse().
class HttpResponseDelegate {
 public:
  HttpResponseDelegate();
  virtual ~HttpResponseDelegate();
  HttpResponseDelegate(HttpResponseDelegate&) = delete;
  HttpResponseDelegate& operator=(const HttpResponseDelegate&) = delete;

  // The delegate needs to take ownership of the response to ensure the
  // response can stay alive until the delegate has finished sending it.
  virtual void AddResponse(std::unique_ptr<HttpResponse> response) = 0;

  // Builds and sends header block. Should only be called once.
  virtual void SendResponseHeaders(HttpStatusCode status,
                                   const std::string& status_reason,
                                   const base::StringPairs& headers) = 0;
  // Sends a raw header block, in the form of an HTTP1.1 response header block
  // (separated by "\r\n". Best effort will be maintained to preserve the raw
  // headers.
  virtual void SendRawResponseHeaders(const std::string& headers) = 0;

  // Sends a content block, then calls the closure.
  virtual void SendContents(const std::string& contents,
                            base::OnceClosure callback = base::DoNothing()) = 0;

  // Called after the last content block or after the header block. The response
  // will hang until this is called.
  virtual void FinishResponse() = 0;

  // The following functions are essentially shorthand for common combinations
  // of function calls that may have a more efficient layout than just calling
  // one after the other.
  virtual void SendContentsAndFinish(const std::string& contents) = 0;
  virtual void SendHeadersContentAndFinish(HttpStatusCode status,
                                           const std::string& status_reason,
                                           const base::StringPairs& headers,
                                           const std::string& contents) = 0;
};

// Interface for HTTP response implementations. The response should be owned by
// the HttpResponseDelegate passed into SendResponse(), and should stay alive
// until FinishResponse() is called on the delegate (or the owning delegate is
// destroyed).
class HttpResponse {
 public:
  virtual ~HttpResponse();

  // Note that this is a WeakPtr. WeakPtrs can not be dereferenced or
  // invalidated outside of the thread that created them, so any use of the
  // delegate must either be from the same thread or posted to the original
  // task runner
  virtual void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) = 0;
};

// This class is used to handle basic HTTP responses with commonly used
// response headers such as "Content-Type". Sends the response immediately.
class BasicHttpResponse : public HttpResponse {
 public:
  BasicHttpResponse();

  BasicHttpResponse(const BasicHttpResponse&) = delete;
  BasicHttpResponse& operator=(const BasicHttpResponse&) = delete;

  ~BasicHttpResponse() override;

  // The response code.
  HttpStatusCode code() const { return code_; }
  void set_code(HttpStatusCode code) { code_ = code; }

  std::string reason() const {
    return reason_.value_or(GetHttpReasonPhrase(code_));
  }
  void set_reason(std::optional<std::string> reason) {
    reason_ = std::move(reason);
  }

  // The content of the response.
  const std::string& content() const { return content_; }
  void set_content(std::string_view content) {
    content_ = std::string{content};
  }

  // The content type.
  const std::string& content_type() const { return content_type_; }
  void set_content_type(std::string_view content_type) {
    content_type_ = std::string{content_type};
  }

  // Adds a custom header.
  void AddCustomHeader(std::string_view key, std::string_view value) {
    custom_headers_.emplace_back(key, value);
  }

  // Generates and returns a http response string.
  std::string ToResponseString() const;

  base::StringPairs BuildHeaders() const;

  void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) override;

 private:
  HttpStatusCode code_ = HTTP_OK;
  std::optional<std::string> reason_;
  std::string content_;
  std::string content_type_;
  base::StringPairs custom_headers_;
  base::WeakPtrFactory<BasicHttpResponse> weak_factory_{this};
};

class DelayedHttpResponse : public BasicHttpResponse {
 public:
  explicit DelayedHttpResponse(const base::TimeDelta delay);

  DelayedHttpResponse(const DelayedHttpResponse&) = delete;
  DelayedHttpResponse& operator=(const DelayedHttpResponse&) = delete;

  ~DelayedHttpResponse() override;

  // Issues a delayed send to the to the task runner.
  void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) override;

 private:
  // The delay time for the response.
  const base::TimeDelta delay_;
};

class RawHttpResponse : public HttpResponse {
 public:
  RawHttpResponse(const std::string& headers, const std::string& contents);

  RawHttpResponse(const RawHttpResponse&) = delete;
  RawHttpResponse& operator=(const RawHttpResponse&) = delete;

  ~RawHttpResponse() override;

  void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) override;

  void AddHeader(const std::string& key_value_pair);

 private:
  std::string headers_;
  const std::string contents_;
};

// "Response" where the server doesn't actually respond until the server is
// destroyed.
class HungResponse : public HttpResponse {
 public:
  HungResponse() = default;

  HungResponse(const HungResponse&) = delete;
  HungResponse& operator=(const HungResponse&) = delete;

  ~HungResponse() override = default;

  void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) override;
};

// Return headers, then hangs.
class HungAfterHeadersHttpResponse : public HttpResponse {
 public:
  explicit HungAfterHeadersHttpResponse(base::StringPairs headers = {});
  ~HungAfterHeadersHttpResponse() override;

  void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) override;

 private:
  base::StringPairs headers_;
};

}  // namespace net::test_server

#endif  // NET_TEST_EMBEDDED_TEST_SERVER_HTTP_RESPONSE_H_
