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