1// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_
6#define LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_
7
8#include <map>
9#include <queue>
10#include <string>
11#include <type_traits>
12#include <vector>
13
14#include <base/callback.h>
15#include <base/values.h>
16#include <brillo/http/http_transport.h>
17#include <brillo/http/http_utils.h>
18
19namespace brillo {
20namespace http {
21namespace fake {
22
23class ServerRequest;
24class ServerResponse;
25class Connection;
26
27///////////////////////////////////////////////////////////////////////////////
28// A fake implementation of http::Transport that simulates HTTP communication
29// with a server.
30///////////////////////////////////////////////////////////////////////////////
31class Transport : public http::Transport {
32 public:
33  Transport();
34  ~Transport() override;
35
36  // Server handler callback signature.
37  using HandlerCallback =
38      base::Callback<void(const ServerRequest&, ServerResponse*)>;
39
40  // This method allows the test code to provide a callback to handle requests
41  // for specific URL/HTTP-verb combination. When a specific |method| request
42  // is made on the given |url|, the |handler| will be invoked and all the
43  // request data will be filled in the |ServerRequest| parameter. Any server
44  // response should be returned through the |ServerResponse| parameter.
45  // Either |method| or |url| (or both) can be specified as "*" to handle
46  // any requests. So, ("http://localhost","*") will handle any request type
47  // on that URL and ("*","GET") will handle any GET requests.
48  // The lookup starts with the most specific data pair to the catch-all (*,*).
49  void AddHandler(const std::string& url,
50                  const std::string& method,
51                  const HandlerCallback& handler);
52  // Simple version of AddHandler. AddSimpleReplyHandler just returns the
53  // specified text response of given MIME type.
54  void AddSimpleReplyHandler(const std::string& url,
55                             const std::string& method,
56                             int status_code,
57                             const std::string& reply_text,
58                             const std::string& mime_type);
59  // Retrieve a handler for specific |url| and request |method|.
60  HandlerCallback GetHandler(const std::string& url,
61                             const std::string& method) const;
62
63  // For tests that want to assert on the number of HTTP requests sent,
64  // these methods can be used to do just that.
65  int GetRequestCount() const { return request_count_; }
66  void ResetRequestCount() { request_count_ = 0; }
67
68  // For tests that wish to simulate critical transport errors, this method
69  // can be used to specify the error to be returned when creating a connection.
70  void SetCreateConnectionError(brillo::ErrorPtr create_connection_error) {
71    create_connection_error_ = std::move(create_connection_error);
72  }
73
74  // For tests that really need async operations with message loop, call this
75  // function with true.
76  void SetAsyncMode(bool async) { async_ = async; }
77
78  // Pops one callback from the top of |async_callback_queue_| and invokes it.
79  // Returns false if the queue is empty.
80  bool HandleOneAsyncRequest();
81
82  // Invokes all the callbacks currently queued in |async_callback_queue_|.
83  void HandleAllAsyncRequests();
84
85  // Overrides from http::Transport.
86  std::shared_ptr<http::Connection> CreateConnection(
87      const std::string& url,
88      const std::string& method,
89      const HeaderList& headers,
90      const std::string& user_agent,
91      const std::string& referer,
92      brillo::ErrorPtr* error) override;
93
94  void RunCallbackAsync(const tracked_objects::Location& from_here,
95                        const base::Closure& callback) override;
96
97  RequestID StartAsyncTransfer(http::Connection* connection,
98                               const SuccessCallback& success_callback,
99                               const ErrorCallback& error_callback) override;
100
101  bool CancelRequest(RequestID request_id) override;
102
103  void SetDefaultTimeout(base::TimeDelta timeout) override;
104
105 private:
106  // A list of user-supplied request handlers.
107  std::map<std::string, HandlerCallback> handlers_;
108  // Counter incremented each time a request is made.
109  int request_count_{0};
110  bool async_{false};
111  // A list of queued callbacks that need to be called at some point.
112  // Call HandleOneAsyncRequest() or HandleAllAsyncRequests() to invoke them.
113  std::queue<base::Closure> async_callback_queue_;
114
115  // Fake error to be returned from CreateConnection method.
116  brillo::ErrorPtr create_connection_error_;
117
118  DISALLOW_COPY_AND_ASSIGN(Transport);
119};
120
121///////////////////////////////////////////////////////////////////////////////
122// A base class for ServerRequest and ServerResponse. It provides common
123// functionality to work with request/response HTTP headers and data.
124///////////////////////////////////////////////////////////////////////////////
125class ServerRequestResponseBase {
126 public:
127  ServerRequestResponseBase() = default;
128
129  // Add/retrieve request/response body data.
130  void SetData(StreamPtr stream);
131  const std::vector<uint8_t>& GetData() const { return data_; }
132  std::string GetDataAsString() const;
133  std::unique_ptr<base::DictionaryValue> GetDataAsJson() const;
134  // Parses the data into a JSON object and writes it back to JSON to normalize
135  // its string representation (no pretty print, extra spaces, etc).
136  std::string GetDataAsNormalizedJsonString() const;
137
138  // Add/retrieve request/response HTTP headers.
139  void AddHeaders(const HeaderList& headers);
140  std::string GetHeader(const std::string& header_name) const;
141  const std::multimap<std::string, std::string>& GetHeaders() const {
142    return headers_;
143  }
144
145 protected:
146  // Data buffer.
147  std::vector<uint8_t> data_;
148  // Header map.
149  std::multimap<std::string, std::string> headers_;
150
151 private:
152  DISALLOW_COPY_AND_ASSIGN(ServerRequestResponseBase);
153};
154
155///////////////////////////////////////////////////////////////////////////////
156// A container class that encapsulates all the HTTP server request information.
157///////////////////////////////////////////////////////////////////////////////
158class ServerRequest : public ServerRequestResponseBase {
159 public:
160  ServerRequest(const std::string& url, const std::string& method);
161
162  // Get the actual request URL. Does not include the query string or fragment.
163  const std::string& GetURL() const { return url_; }
164  // Get the request method.
165  const std::string& GetMethod() const { return method_; }
166  // Get the POST/GET request parameters. These are parsed query string
167  // parameters from the URL. In addition, for POST requests with
168  // application/x-www-form-urlencoded content type, the request body is also
169  // parsed and individual fields can be accessed through this method.
170  std::string GetFormField(const std::string& field_name) const;
171
172 private:
173  // Request URL (without query string or URL fragment).
174  std::string url_;
175  // Request method
176  std::string method_;
177  // List of available request data form fields.
178  mutable std::map<std::string, std::string> form_fields_;
179  // Flag used on first request to GetFormField to parse the body of HTTP POST
180  // request with application/x-www-form-urlencoded content.
181  mutable bool form_fields_parsed_ = false;
182
183  DISALLOW_COPY_AND_ASSIGN(ServerRequest);
184};
185
186///////////////////////////////////////////////////////////////////////////////
187// A container class that encapsulates all the HTTP server response information.
188// The request handler will use this class to provide a response to the caller.
189// Call the Reply() or the appropriate ReplyNNN() specialization to provide
190// the response data. Additional calls to AddHeaders() can be made to provide
191// custom response headers. The Reply-methods will already provide the
192// following response headers:
193//    Content-Length
194//    Content-Type
195///////////////////////////////////////////////////////////////////////////////
196class ServerResponse : public ServerRequestResponseBase {
197 public:
198  ServerResponse() = default;
199
200  // Generic reply method.
201  void Reply(int status_code,
202             const void* data,
203             size_t data_size,
204             const std::string& mime_type);
205  // Reply with text body.
206  void ReplyText(int status_code,
207                 const std::string& text,
208                 const std::string& mime_type);
209  // Reply with JSON object. The content type will be "application/json".
210  void ReplyJson(int status_code, const base::Value* json);
211  // Special form for JSON response for simple objects that have a flat
212  // list of key-value pairs of string type.
213  void ReplyJson(int status_code, const FormFieldList& fields);
214
215  // Specialized overload to send the binary data as an array of simple
216  // data elements. Only trivial data types (scalars, POD structures, etc)
217  // can be used.
218  template<typename T>
219  void Reply(int status_code,
220             const std::vector<T>& data,
221             const std::string& mime_type) {
222    // Make sure T doesn't have virtual functions, custom constructors, etc.
223    static_assert(std::is_trivial<T>::value, "Only simple data is supported");
224    Reply(status_code, data.data(), data.size() * sizeof(T), mime_type);
225  }
226
227  // Specialized overload to send the binary data.
228  // Only trivial data types (scalars, POD structures, etc) can be used.
229  template<typename T>
230  void Reply(int status_code, const T& data, const std::string& mime_type) {
231    // Make sure T doesn't have virtual functions, custom constructors, etc.
232    static_assert(std::is_trivial<T>::value, "Only simple data is supported");
233    Reply(status_code, &data, sizeof(T), mime_type);
234  }
235
236  // For handlers that want to simulate versions of HTTP protocol other
237  // than HTTP/1.1, call this method with the custom version string,
238  // for example "HTTP/1.0".
239  void SetProtocolVersion(const std::string& protocol_version) {
240    protocol_version_ = protocol_version;
241  }
242
243 protected:
244  // These methods are helpers to implement corresponding functionality
245  // of fake::Connection.
246  friend class Connection;
247  // Helper for fake::Connection::GetResponseStatusCode().
248  int GetStatusCode() const { return status_code_; }
249  // Helper for fake::Connection::GetResponseStatusText().
250  std::string GetStatusText() const;
251  // Helper for fake::Connection::GetProtocolVersion().
252  std::string GetProtocolVersion() const { return protocol_version_; }
253
254 private:
255  int status_code_ = 0;
256  std::string protocol_version_ = "HTTP/1.1";
257
258  DISALLOW_COPY_AND_ASSIGN(ServerResponse);
259};
260
261}  // namespace fake
262}  // namespace http
263}  // namespace brillo
264
265#endif  // LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_
266