aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/http/http_transport_fake.h
blob: ac8bc2a2be3a30e40e0a4b92a783719100d8b691 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_
#define LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_

#include <map>
#include <queue>
#include <string>
#include <type_traits>
#include <vector>

#include <base/callback.h>
#include <base/values.h>
#include <brillo/http/http_transport.h>
#include <brillo/http/http_utils.h>

namespace brillo {
namespace http {
namespace fake {

class ServerRequest;
class ServerResponse;
class Connection;

///////////////////////////////////////////////////////////////////////////////
// A fake implementation of http::Transport that simulates HTTP communication
// with a server.
///////////////////////////////////////////////////////////////////////////////
class Transport : public http::Transport {
 public:
  Transport();
  ~Transport() override;

  // Server handler callback signature.
  using HandlerCallback =
      base::Callback<void(const ServerRequest&, ServerResponse*)>;

  // This method allows the test code to provide a callback to handle requests
  // for specific URL/HTTP-verb combination. When a specific |method| request
  // is made on the given |url|, the |handler| will be invoked and all the
  // request data will be filled in the |ServerRequest| parameter. Any server
  // response should be returned through the |ServerResponse| parameter.
  // Either |method| or |url| (or both) can be specified as "*" to handle
  // any requests. So, ("http://localhost","*") will handle any request type
  // on that URL and ("*","GET") will handle any GET requests.
  // The lookup starts with the most specific data pair to the catch-all (*,*).
  void AddHandler(const std::string& url,
                  const std::string& method,
                  const HandlerCallback& handler);
  // Simple version of AddHandler. AddSimpleReplyHandler just returns the
  // specified text response of given MIME type.
  void AddSimpleReplyHandler(const std::string& url,
                             const std::string& method,
                             int status_code,
                             const std::string& reply_text,
                             const std::string& mime_type);
  // Retrieve a handler for specific |url| and request |method|.
  HandlerCallback GetHandler(const std::string& url,
                             const std::string& method) const;

  // For tests that want to assert on the number of HTTP requests sent,
  // these methods can be used to do just that.
  int GetRequestCount() const { return request_count_; }
  void ResetRequestCount() { request_count_ = 0; }

  // For tests that wish to simulate critical transport errors, this method
  // can be used to specify the error to be returned when creating a connection.
  void SetCreateConnectionError(brillo::ErrorPtr create_connection_error) {
    create_connection_error_ = std::move(create_connection_error);
  }

  // For tests that really need async operations with message loop, call this
  // function with true.
  void SetAsyncMode(bool async) { async_ = async; }

  // Pops one callback from the top of |async_callback_queue_| and invokes it.
  // Returns false if the queue is empty.
  bool HandleOneAsyncRequest();

  // Invokes all the callbacks currently queued in |async_callback_queue_|.
  void HandleAllAsyncRequests();

  // Overrides from http::Transport.
  std::shared_ptr<http::Connection> CreateConnection(
      const std::string& url,
      const std::string& method,
      const HeaderList& headers,
      const std::string& user_agent,
      const std::string& referer,
      brillo::ErrorPtr* error) override;

  void RunCallbackAsync(const tracked_objects::Location& from_here,
                        const base::Closure& callback) override;

  RequestID StartAsyncTransfer(http::Connection* connection,
                               const SuccessCallback& success_callback,
                               const ErrorCallback& error_callback) override;

  bool CancelRequest(RequestID request_id) override;

  void SetDefaultTimeout(base::TimeDelta timeout) override;

 private:
  // A list of user-supplied request handlers.
  std::map<std::string, HandlerCallback> handlers_;
  // Counter incremented each time a request is made.
  int request_count_{0};
  bool async_{false};
  // A list of queued callbacks that need to be called at some point.
  // Call HandleOneAsyncRequest() or HandleAllAsyncRequests() to invoke them.
  std::queue<base::Closure> async_callback_queue_;

  // Fake error to be returned from CreateConnection method.
  brillo::ErrorPtr create_connection_error_;

  DISALLOW_COPY_AND_ASSIGN(Transport);
};

///////////////////////////////////////////////////////////////////////////////
// A base class for ServerRequest and ServerResponse. It provides common
// functionality to work with request/response HTTP headers and data.
///////////////////////////////////////////////////////////////////////////////
class ServerRequestResponseBase {
 public:
  ServerRequestResponseBase() = default;

  // Add/retrieve request/response body data.
  void SetData(StreamPtr stream);
  const std::vector<uint8_t>& GetData() const { return data_; }
  std::string GetDataAsString() const;
  std::unique_ptr<base::DictionaryValue> GetDataAsJson() const;
  // Parses the data into a JSON object and writes it back to JSON to normalize
  // its string representation (no pretty print, extra spaces, etc).
  std::string GetDataAsNormalizedJsonString() const;

  // Add/retrieve request/response HTTP headers.
  void AddHeaders(const HeaderList& headers);
  std::string GetHeader(const std::string& header_name) const;
  const std::multimap<std::string, std::string>& GetHeaders() const {
    return headers_;
  }

 protected:
  // Data buffer.
  std::vector<uint8_t> data_;
  // Header map.
  std::multimap<std::string, std::string> headers_;

 private:
  DISALLOW_COPY_AND_ASSIGN(ServerRequestResponseBase);
};

///////////////////////////////////////////////////////////////////////////////
// A container class that encapsulates all the HTTP server request information.
///////////////////////////////////////////////////////////////////////////////
class ServerRequest : public ServerRequestResponseBase {
 public:
  ServerRequest(const std::string& url, const std::string& method);

  // Get the actual request URL. Does not include the query string or fragment.
  const std::string& GetURL() const { return url_; }
  // Get the request method.
  const std::string& GetMethod() const { return method_; }
  // Get the POST/GET request parameters. These are parsed query string
  // parameters from the URL. In addition, for POST requests with
  // application/x-www-form-urlencoded content type, the request body is also
  // parsed and individual fields can be accessed through this method.
  std::string GetFormField(const std::string& field_name) const;

 private:
  // Request URL (without query string or URL fragment).
  std::string url_;
  // Request method
  std::string method_;
  // List of available request data form fields.
  mutable std::map<std::string, std::string> form_fields_;
  // Flag used on first request to GetFormField to parse the body of HTTP POST
  // request with application/x-www-form-urlencoded content.
  mutable bool form_fields_parsed_ = false;

  DISALLOW_COPY_AND_ASSIGN(ServerRequest);
};

///////////////////////////////////////////////////////////////////////////////
// A container class that encapsulates all the HTTP server response information.
// The request handler will use this class to provide a response to the caller.
// Call the Reply() or the appropriate ReplyNNN() specialization to provide
// the response data. Additional calls to AddHeaders() can be made to provide
// custom response headers. The Reply-methods will already provide the
// following response headers:
//    Content-Length
//    Content-Type
///////////////////////////////////////////////////////////////////////////////
class ServerResponse : public ServerRequestResponseBase {
 public:
  ServerResponse() = default;

  // Generic reply method.
  void Reply(int status_code,
             const void* data,
             size_t data_size,
             const std::string& mime_type);
  // Reply with text body.
  void ReplyText(int status_code,
                 const std::string& text,
                 const std::string& mime_type);
  // Reply with JSON object. The content type will be "application/json".
  void ReplyJson(int status_code, const base::Value* json);
  // Special form for JSON response for simple objects that have a flat
  // list of key-value pairs of string type.
  void ReplyJson(int status_code, const FormFieldList& fields);

  // Specialized overload to send the binary data as an array of simple
  // data elements. Only trivial data types (scalars, POD structures, etc)
  // can be used.
  template<typename T>
  void Reply(int status_code,
             const std::vector<T>& data,
             const std::string& mime_type) {
    // Make sure T doesn't have virtual functions, custom constructors, etc.
    static_assert(std::is_trivial<T>::value, "Only simple data is supported");
    Reply(status_code, data.data(), data.size() * sizeof(T), mime_type);
  }

  // Specialized overload to send the binary data.
  // Only trivial data types (scalars, POD structures, etc) can be used.
  template<typename T>
  void Reply(int status_code, const T& data, const std::string& mime_type) {
    // Make sure T doesn't have virtual functions, custom constructors, etc.
    static_assert(std::is_trivial<T>::value, "Only simple data is supported");
    Reply(status_code, &data, sizeof(T), mime_type);
  }

  // For handlers that want to simulate versions of HTTP protocol other
  // than HTTP/1.1, call this method with the custom version string,
  // for example "HTTP/1.0".
  void SetProtocolVersion(const std::string& protocol_version) {
    protocol_version_ = protocol_version;
  }

 protected:
  // These methods are helpers to implement corresponding functionality
  // of fake::Connection.
  friend class Connection;
  // Helper for fake::Connection::GetResponseStatusCode().
  int GetStatusCode() const { return status_code_; }
  // Helper for fake::Connection::GetResponseStatusText().
  std::string GetStatusText() const;
  // Helper for fake::Connection::GetProtocolVersion().
  std::string GetProtocolVersion() const { return protocol_version_; }

 private:
  int status_code_ = 0;
  std::string protocol_version_ = "HTTP/1.1";

  DISALLOW_COPY_AND_ASSIGN(ServerResponse);
};

}  // namespace fake
}  // namespace http
}  // namespace brillo

#endif  // LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_