From 6ad63a06b02c2ffb3fbf770b9c8e74fd3962ba68 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Sun, 8 Mar 2015 17:19:52 +0900
Subject: [PATCH] nghttpx: Support response trailer part handling in h1 backend

---
 src/shrpx_downstream.cc                 | 36 +++++++++++++++++++++++-
 src/shrpx_downstream.h                  |  6 ++++
 src/shrpx_http_downstream_connection.cc | 37 +++++++++++++++----------
 3 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc
index 8480d856..e71893a8 100644
--- a/src/shrpx_downstream.cc
+++ b/src/shrpx_downstream.cc
@@ -122,7 +122,8 @@ Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
       request_connection_close_(false), request_header_key_prev_(false),
       request_http2_expect_body_(false), chunked_response_(false),
       response_connection_close_(false), response_header_key_prev_(false),
-      expect_final_response_(false), request_pending_(false) {
+      response_trailer_key_prev_(false), expect_final_response_(false),
+      request_pending_(false) {
 
   ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
                 get_config()->stream_read_timeout);
@@ -679,6 +680,39 @@ unsigned int Downstream::get_response_http_status() const {
   return response_http_status_;
 }
 
+void Downstream::add_response_trailer(std::string name, std::string value) {
+  response_trailer_key_prev_ = true;
+  response_headers_sum_ += name.size() + value.size();
+  response_trailers_.emplace_back(std::move(name), std::move(value));
+}
+
+bool Downstream::get_response_trailer_key_prev() const {
+  return response_trailer_key_prev_;
+}
+
+void Downstream::append_last_response_trailer_key(const char *data,
+                                                  size_t len) {
+  assert(response_trailer_key_prev_);
+  response_headers_sum_ += len;
+  auto &item = response_trailers_.back();
+  item.name.append(data, len);
+}
+
+void Downstream::append_last_response_trailer_value(const char *data,
+                                                    size_t len) {
+  assert(!response_trailer_key_prev_);
+  response_headers_sum_ += len;
+  auto &item = response_trailers_.back();
+  item.value.append(data, len);
+}
+
+void Downstream::set_last_response_trailer_value(std::string value) {
+  response_trailer_key_prev_ = false;
+  response_headers_sum_ += value.size();
+  auto &item = response_trailers_.back();
+  item.value = std::move(value);
+}
+
 void Downstream::set_response_http_status(unsigned int status) {
   response_http_status_ = status;
 }
diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h
index f138811a..8ffff731 100644
--- a/src/shrpx_downstream.h
+++ b/src/shrpx_downstream.h
@@ -225,6 +225,11 @@ public:
   void add_response_trailer(const uint8_t *name, size_t namelen,
                             const uint8_t *value, size_t valuelen,
                             bool no_index, int16_t token);
+  void add_response_trailer(std::string name, std::string value);
+  bool get_response_trailer_key_prev() const;
+  void append_last_response_trailer_key(const char *data, size_t len);
+  void append_last_response_trailer_value(const char *data, size_t len);
+  void set_last_response_trailer_value(std::string value);
 
   unsigned int get_response_http_status() const;
   void set_response_http_status(unsigned int status);
@@ -405,6 +410,7 @@ private:
   bool chunked_response_;
   bool response_connection_close_;
   bool response_header_key_prev_;
+  bool response_trailer_key_prev_;
   bool expect_final_response_;
   // true if downstream request is pending because backend connection
   // has not been established or should be checked before use;
diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc
index f27e3029..323d2b0e 100644
--- a/src/shrpx_http_downstream_connection.cc
+++ b/src/shrpx_http_downstream_connection.cc
@@ -555,14 +555,19 @@ int htp_hdrs_completecb(http_parser *htp) {
 namespace {
 int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
   auto downstream = static_cast<Downstream *>(htp->data);
-  if (downstream->get_response_state() != Downstream::INITIAL) {
-    // ignore trailers
-    return 0;
-  }
-  if (downstream->get_response_header_key_prev()) {
-    downstream->append_last_response_header_key(data, len);
+  if (downstream->get_response_state() == Downstream::INITIAL) {
+    if (downstream->get_response_header_key_prev()) {
+      downstream->append_last_response_header_key(data, len);
+    } else {
+      downstream->add_response_header(std::string(data, len), "");
+    }
   } else {
-    downstream->add_response_header(std::string(data, len), "");
+    // trailer part
+    if (downstream->get_response_trailer_key_prev()) {
+      downstream->append_last_response_trailer_key(data, len);
+    } else {
+      downstream->add_response_trailer(std::string(data, len), "");
+    }
   }
   if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
     if (LOG_ENABLED(INFO)) {
@@ -578,14 +583,18 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
 namespace {
 int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
   auto downstream = static_cast<Downstream *>(htp->data);
-  if (downstream->get_response_state() != Downstream::INITIAL) {
-    // ignore trailers
-    return 0;
-  }
-  if (downstream->get_response_header_key_prev()) {
-    downstream->set_last_response_header_value(std::string(data, len));
+  if (downstream->get_response_state() == Downstream::INITIAL) {
+    if (downstream->get_response_header_key_prev()) {
+      downstream->set_last_response_header_value(std::string(data, len));
+    } else {
+      downstream->append_last_response_header_value(data, len);
+    }
   } else {
-    downstream->append_last_response_header_value(data, len);
+    if (downstream->get_response_trailer_key_prev()) {
+      downstream->set_last_response_trailer_value(std::string(data, len));
+    } else {
+      downstream->append_last_response_trailer_value(data, len);
+    }
   }
   if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
     if (LOG_ENABLED(INFO)) {