text: /**
   *
   *  @file HttpResponseImpl.h
   *  @author An Tao
   *
   *  Copyright 2018, An Tao.  All rights reserved.
   *  https://github.com/an-tao/drogon
   *  Use of this source code is governed by a MIT license
   *  that can be found in the License file.
   *
   *  Drogon
   *
   */
  
  #pragma once
  
  #include "HttpUtils.h"
  #include "HttpMessageBody.h"
  #include <drogon/exports.h>
  #include <drogon/HttpResponse.h>
  #include <drogon/utils/Utilities.h>
  #include <trantor/net/InetAddress.h>
  #include <trantor/utils/Date.h>
  #include <trantor/utils/MsgBuffer.h>
  #include <memory>
  #include <mutex>
  #include <string>
  #include <atomic>
  #include <unordered_map>
  
  namespace drogon
  {
  class DROGON_EXPORT HttpResponseImpl : public HttpResponse
  {
      friend class HttpResponseParser;
  
    public:
      HttpResponseImpl() : creationDate_(trantor::Date::now())
      {
      }
      HttpResponseImpl(HttpStatusCode code, ContentType type)
          : statusCode_(code),
            statusMessage_(statusCodeToString(code)),
            creationDate_(trantor::Date::now()),
            contentType_(type),
            flagForParsingContentType_(true),
            contentTypeString_(webContentTypeToString(type))
      {
      }
      void setPassThrough(bool flag) override
      {
          passThrough_ = flag;
      }
      HttpStatusCode statusCode() const override
      {
          return statusCode_;
      }
  
      const trantor::Date &creationDate() const override
      {
          return creationDate_;
      }
  
      void setStatusCode(HttpStatusCode code) override
      {
          statusCode_ = code;
          setStatusMessage(statusCodeToString(code));
      }
  
      void setVersion(const Version v) override
      {
          version_ = v;
          if (version_ == Version::kHttp10)
          {
              closeConnection_ = true;
          }
      }
  
      Version version() const override
      {
          return version_;
      }
  
      void setCloseConnection(bool on) override
      {
          closeConnection_ = on;
      }
  
      bool ifCloseConnection() const override
      {
          return closeConnection_;
      }
  
      void setContentTypeCode(ContentType type) override
      {
          contentType_ = type;
          setContentType(webContentTypeToString(type));
          flagForParsingContentType_ = true;
      }
  
      //  void setContentTypeCodeAndCharacterSet(ContentType type, const
      // std::string &charSet = "utf-8") override
      // {
      //     contentType_ = type;
      //     setContentType(webContentTypeAndCharsetToString(type, charSet));
      // }
  
      ContentType contentType() const override
      {
          if (!flagForParsingContentType_)
          {
              flagForParsingContentType_ = true;
              auto &contentTypeString = getHeaderBy("content-type");
              if (contentTypeString.empty())
              {
                  contentType_ = CT_NONE;
              }
              else
              {
                  auto pos = contentTypeString.find(';');
                  if (pos != std::string::npos)
                  {
                      contentType_ = parseContentType(
                          string_view(contentTypeString.data(), pos));
                  }
                  else
                  {
                      contentType_ =
                          parseContentType(string_view(contentTypeString));
                  }
              }
          }
          return contentType_;
      }
  
      const std::string &getHeader(std::string key) const override
      {
          transform(key.begin(), key.end(), key.begin(), ::tolower);
          return getHeaderBy(key);
      }
  
      void removeHeader(std::string key) override
      {
          transform(key.begin(), key.end(), key.begin(), ::tolower);
          removeHeaderBy(key);
      }
  
      const std::unordered_map<std::string, std::string> &headers() const override
      {
          return headers_;
      }
  
      const std::string &getHeaderBy(const std::string &lowerKey) const
      {
          const static std::string defaultVal;
          auto iter = headers_.find(lowerKey);
          if (iter == headers_.end())
          {
              return defaultVal;
          }
          return iter->second;
      }
  
      void removeHeaderBy(const std::string &lowerKey)
      {
          fullHeaderString_.reset();
          headers_.erase(lowerKey);
      }
  
      void addHeader(std::string field, const std::string &value) override
      {
          fullHeaderString_.reset();
          transform(field.begin(), field.end(), field.begin(), ::tolower);
          headers_[std::move(field)] = value;
      }
  
      void addHeader(std::string field, std::string &&value) override
      {
          fullHeaderString_.reset();
          transform(field.begin(), field.end(), field.begin(), ::tolower);
          headers_[std::move(field)] = std::move(value);
      }
  
      void addHeader(const char *start, const char *colon, const char *end);
  
      void addCookie(const std::string &key, const std::string &value) override
      {
          cookies_[key] = Cookie(key, value);
      }
  
      void addCookie(const Cookie &cookie) override
      {
          cookies_[cookie.key()] = cookie;
      }
  
      void addCookie(Cookie &&cookie) override
      {
          cookies_[cookie.key()] = std::move(cookie);
      }
  
      const Cookie &getCookie(const std::string &key) const override
      {
          static const Cookie defaultCookie;
          auto it = cookies_.find(key);
          if (it != cookies_.end())
          {
              return it->second;
          }
          return defaultCookie;
      }
  
      const std::unordered_map<std::string, Cookie> &cookies() const override
      {
          return cookies_;
      }
  
      void removeCookie(const std::string &key) override
      {
          cookies_.erase(key);
      }
  
      void setBody(const std::string &body) override
      {
          bodyPtr_ = std::make_shared<HttpMessageStringBody>(body);
          if (passThrough_)
          {
              addHeader("content-length", std::to_string(bodyPtr_->length()));
          }
      }
      void setBody(std::string &&body) override
      {
          bodyPtr_ = std::make_shared<HttpMessageStringBody>(std::move(body));
          if (passThrough_)
          {
              addHeader("content-length", std::to_string(bodyPtr_->length()));
          }
      }
  
      void redirect(const std::string &url)
      {
          headers_["location"] = url;
      }
      std::shared_ptr<trantor::MsgBuffer> renderToBuffer();
      void renderToBuffer(trantor::MsgBuffer &buffer);
      std::shared_ptr<trantor::MsgBuffer> renderHeaderForHeadMethod();
      void clear() override;
  
      void setExpiredTime(ssize_t expiredTime) override
      {
          expriedTime_ = expiredTime;
          datePos_ = std::string::npos;
          if (expriedTime_ < 0 && version_ == Version::kHttp10)
          {
              fullHeaderString_.reset();
          }
      }
  
      ssize_t expiredTime() const override
      {
          return expriedTime_;
      }
  
      const char *getBodyData() const override
      {
          if (!flagForSerializingJson_ && jsonPtr_)
          {
              generateBodyFromJson();
          }
          else if (!bodyPtr_)
          {
              return nullptr;
          }
          return bodyPtr_->data();
      }
      size_t getBodyLength() const override
      {
          if (bodyPtr_)
              return bodyPtr_->length();
          return 0;
      }
  
      void swap(HttpResponseImpl &that) noexcept;
      void parseJson() const;
      const std::shared_ptr<Json::Value> &jsonObject() const override
      {
          // Not multi-thread safe but good, because we basically call this
          // function in a single thread
          if (!flagForParsingJson_)
          {
              flagForParsingJson_ = true;
              parseJson();
          }
          return jsonPtr_;
      }
      const std::string &getJsonError() const override
      {
          const static std::string none;
          if (jsonParsingErrorPtr_)
              return *jsonParsingErrorPtr_;
          return none;
      }
      void setJsonObject(const Json::Value &pJson)
      {
          flagForParsingJson_ = true;
          flagForSerializingJson_ = false;
          jsonPtr_ = std::make_shared<Json::Value>(pJson);
      }
      void setJsonObject(Json::Value &&pJson)
      {
          flagForParsingJson_ = true;
          flagForSerializingJson_ = false;
          jsonPtr_ = std::make_shared<Json::Value>(std::move(pJson));
      }
      bool shouldBeCompressed() const;
      void generateBodyFromJson() const;
      const std::string &sendfileName() const
      {
          return sendfileName_;
      }
      void setSendfile(const std::string &filename)
      {
          sendfileName_ = filename;
      }
      void makeHeaderString()
      {
          fullHeaderString_ = std::make_shared<trantor::MsgBuffer>(128);
          makeHeaderString(*fullHeaderString_);
      }
  
      void gunzip()
      {
          if (bodyPtr_)
          {
              auto gunzipBody =
                  utils::gzipDecompress(bodyPtr_->data(), bodyPtr_->length());
              removeHeaderBy("content-encoding");
              bodyPtr_ =
                  std::make_shared<HttpMessageStringBody>(move(gunzipBody));
              addHeader("content-length", std::to_string(bodyPtr_->length()));
          }
      }
  #ifdef USE_BROTLI
      void brDecompress()
      {
          if (bodyPtr_)
          {
              auto gunzipBody =
                  utils::brotliDecompress(bodyPtr_->data(), bodyPtr_->length());
              removeHeaderBy("content-encoding");
              bodyPtr_ =
                  std::make_shared<HttpMessageStringBody>(move(gunzipBody));
              addHeader("content-length", std::to_string(bodyPtr_->length()));
          }
      }
  #endif
      ~HttpResponseImpl() override = default;
  
    protected:
      void makeHeaderString(trantor::MsgBuffer &headerString);
  
    private:
      void setBody(const char *body, size_t len) override
      {
          bodyPtr_ = std::make_shared<HttpMessageStringViewBody>(body, len);
          if (passThrough_)
          {
              addHeader("content-length", std::to_string(bodyPtr_->length()));
          }
      }
      void setContentTypeCodeAndCustomString(ContentType type,
                                             const char *typeString,
                                             size_t typeStringLength) override
      {
          contentType_ = type;
          flagForParsingContentType_ = true;
          setContentType(string_view{typeString, typeStringLength});
      }
  
      void setCustomStatusCode(int code,
                               const char *message,
                               size_t messageLength) override
      {
          assert(code >= 0);
          customStatusCode_ = code;
          statusMessage_ = string_view{message, messageLength};
      }
  
      std::unordered_map<std::string, std::string> headers_;
      std::unordered_map<std::string, Cookie> cookies_;
  
      int customStatusCode_{-1};
      HttpStatusCode statusCode_{kUnknown};
      string_view statusMessage_;
  
      trantor::Date creationDate_;
      Version version_{Version::kHttp11};
      bool closeConnection_{false};
      mutable std::shared_ptr<HttpMessageBody> bodyPtr_;
      ssize_t expriedTime_{-1};
      std::string sendfileName_;
      mutable std::shared_ptr<Json::Value> jsonPtr_;
  
      std::shared_ptr<trantor::MsgBuffer> fullHeaderString_;
      mutable std::shared_ptr<trantor::MsgBuffer> httpString_;
      mutable size_t datePos_{static_cast<size_t>(-1)};
      mutable int64_t httpStringDate_{-1};
      mutable bool flagForParsingJson_{false};
      mutable bool flagForSerializingJson_{true};
      mutable ContentType contentType_{CT_TEXT_PLAIN};
      mutable bool flagForParsingContentType_{false};
      mutable std::shared_ptr<std::string> jsonParsingErrorPtr_;
      string_view contentTypeString_{
          "content-type: text/html; charset=utf-8\r\n"};
      bool passThrough_{false};
      void setContentType(const string_view &contentType)
      {
          contentTypeString_ = contentType;
      }
      void setStatusMessage(const string_view &message)
      {
          statusMessage_ = message;
      }
  };
  using HttpResponseImplPtr = std::shared_ptr<HttpResponseImpl>;
  
  inline void swap(HttpResponseImpl &one, HttpResponseImpl &two) noexcept
  {
      one.swap(two);
  }
  
  }  // namespace drogon
  