proxygen
ResponseBuilder.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
10 #pragma once
11 
12 #include <folly/ScopeGuard.h>
14 
15 namespace proxygen {
16 
65  public:
66  explicit ResponseBuilder(ResponseHandler* txn): txn_(txn) {
67  }
68 
69  ResponseBuilder& promise(const std::string& url, const std::string& host) {
70  headers_ = std::make_unique<HTTPMessage>();
71  headers_->setHTTPVersion(1, 1);
72  headers_->setURL(url);
73  headers_->getHeaders().set(HTTP_HEADER_HOST, host);
74  return *this;
75  }
76 
78  headers_ = std::make_unique<HTTPMessage>();
79  headers_->setHTTPVersion(1, 1);
80  headers_->setStatusCode(code);
81  headers_->setStatusMessage(message);
82  return *this;
83  }
84 
85  template <typename T>
86  ResponseBuilder& header(const std::string& headerIn, const T& value) {
87  CHECK(headers_) << "You need to call `status` before adding headers";
88  headers_->getHeaders().add(headerIn, value);
89  return *this;
90  }
91 
92  template <typename T>
94  CHECK(headers_) << "You need to call `status` before adding headers";
95  headers_->getHeaders().add(code, value);
96  return *this;
97  }
98 
99  ResponseBuilder& body(std::unique_ptr<folly::IOBuf> bodyIn) {
100  if (bodyIn) {
101  if (body_) {
102  body_->prependChain(std::move(bodyIn));
103  } else {
104  body_ = std::move(bodyIn);
105  }
106  }
107 
108  return *this;
109  }
110 
111  template <typename T>
114  folly::to<std::string>(std::forward<T>(t))));
115  }
116 
118  return header(HTTP_HEADER_CONNECTION, "close");
119  }
120 
122  trailers_.reset(new HTTPHeaders(trailers));
123  return *this;
124  }
125 
126  void sendWithEOM() {
127  sendEOM_ = true;
128  send();
129  }
130 
131  void send() {
132  // Once we send them, we don't want to send them again
133  SCOPE_EXIT { headers_.reset(); };
134 
135  // By default, chunked
136  bool chunked = true;
137 
138  // If we have complete response, we can use Content-Length and get done
139  if (headers_ && sendEOM_) {
140  chunked = false;
141  }
142 
143  if (headers_) {
144  // We don't need to add Content-Length or Encoding for 1xx responses
145  if (headers_->isResponse() && headers_->getStatusCode() >= 200) {
146  if (chunked) {
147  headers_->setIsChunked(true);
148  } else {
149  const auto len = body_ ? body_->computeChainDataLength() : 0;
150  headers_->getHeaders().add(
152  folly::to<std::string>(len));
153  }
154  }
155 
157  }
158 
159  if (body_) {
160  if (chunked) {
161  txn_->sendChunkHeader(body_->computeChainDataLength());
164  } else {
166  }
167  }
168 
169  if (sendEOM_) {
170  if (trailers_) {
171  auto txn = txn_->getTransaction();
172  if (txn) {
173  txn->sendTrailers(*trailers_);
174  }
175  trailers_.reset();
176  }
177  txn_->sendEOM();
178  }
179  }
180 
181  enum class UpgradeType {
182  CONNECT_REQUEST = 0,
183  HTTP_UPGRADE,
184  };
185 
187  const std::string upgradeProtocol = "") {
188  headers_ = std::make_unique<HTTPMessage>();
189  if (upgradeType == UpgradeType::CONNECT_REQUEST) {
190  headers_->constructDirectResponse({1, 1}, 200, "OK");
191  } else {
192  CHECK(!upgradeProtocol.empty());
193  headers_->constructDirectResponse({1, 1}, 101, "Switching Protocols");
194  headers_->getHeaders().add(HTTP_HEADER_UPGRADE, upgradeProtocol);
195  headers_->getHeaders().add(HTTP_HEADER_CONNECTION, "Upgrade");
196  }
198  }
199 
201  headers_ = std::make_unique<HTTPMessage>();
202  headers_->constructDirectResponse({1, 1}, 400, "Bad Request");
204  txn_->sendEOM();
205  }
206 
208  headers_->setEgressWebsocketUpgrade();
209  return *this;
210  }
211 
212  private:
213  ResponseHandler* const txn_{nullptr};
214 
215  std::unique_ptr<HTTPMessage> headers_;
216  std::unique_ptr<folly::IOBuf> body_;
217  std::unique_ptr<HTTPHeaders> trailers_;
218 
219  // If true, sends EOM.
220  bool sendEOM_{false};
221 };
222 
223 }
#define T(v)
Definition: http_parser.c:233
Definition: test.c:42
virtual void sendChunkTerminator() noexcept=0
ResponseBuilder & status(uint16_t code, const std::string &message)
void acceptUpgradeRequest(UpgradeType upgradeType, const std::string upgradeProtocol="")
virtual void sendEOM() noexcept=0
virtual void sendTrailers(const HTTPHeaders &trailers)
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
HTTPTransaction * getTransaction() const noexcept
std::unique_ptr< folly::IOBuf > body_
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
virtual void sendChunkHeader(size_t len) noexcept=0
ResponseBuilder & body(std::unique_ptr< folly::IOBuf > bodyIn)
ResponseBuilder & setEgressWebsocketHeaders()
ResponseBuilder & header(HTTPHeaderCode code, const T &value)
ResponseHandler *const txn_
virtual void sendHeaders(HTTPMessage &msg) noexcept=0
ResponseBuilder & header(const std::string &headerIn, const T &value)
ResponseBuilder & trailers(const HTTPHeaders &trailers)
ResponseBuilder & promise(const std::string &url, const std::string &host)
virtual void sendBody(std::unique_ptr< folly::IOBuf > body) noexcept=0
static const char *const value
Definition: Conv.cpp:50
ResponseBuilder(ResponseHandler *txn)
ResponseBuilder & closeConnection()
const char * string
Definition: Conv.cpp:212
std::unique_ptr< HTTPMessage > headers_
std::unique_ptr< HTTPHeaders > trailers_
ResponseBuilder & body(T &&t)
Chunked chunked(const Container &container, int chunkSize=256)
Definition: Parallel.h:49
static std::unique_ptr< IOBuf > maybeCopyBuffer(const std::string &buf, std::size_t headroom=0, std::size_t minTailroom=0)
Definition: IOBuf.h:1609