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 19 namespace brillo { 20 namespace http { 21 namespace fake { 22 23 class ServerRequest; 24 class ServerResponse; 25 class Connection; 26 27 /////////////////////////////////////////////////////////////////////////////// 28 // A fake implementation of http::Transport that simulates HTTP communication 29 // with a server. 30 /////////////////////////////////////////////////////////////////////////////// 31 class 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. GetRequestCount()65 int GetRequestCount() const { return request_count_; } ResetRequestCount()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. SetCreateConnectionError(brillo::ErrorPtr create_connection_error)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. SetAsyncMode(bool async)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 base::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 SetLocalIpAddress(const std::string &)105 void SetLocalIpAddress(const std::string& /* ip_address */) override {} 106 107 private: 108 // A list of user-supplied request handlers. 109 std::map<std::string, HandlerCallback> handlers_; 110 // Counter incremented each time a request is made. 111 int request_count_{0}; 112 bool async_{false}; 113 // A list of queued callbacks that need to be called at some point. 114 // Call HandleOneAsyncRequest() or HandleAllAsyncRequests() to invoke them. 115 std::queue<base::Closure> async_callback_queue_; 116 117 // Fake error to be returned from CreateConnection method. 118 brillo::ErrorPtr create_connection_error_; 119 120 DISALLOW_COPY_AND_ASSIGN(Transport); 121 }; 122 123 /////////////////////////////////////////////////////////////////////////////// 124 // A base class for ServerRequest and ServerResponse. It provides common 125 // functionality to work with request/response HTTP headers and data. 126 /////////////////////////////////////////////////////////////////////////////// 127 class ServerRequestResponseBase { 128 public: 129 ServerRequestResponseBase() = default; 130 131 // Add/retrieve request/response body data. 132 void SetData(StreamPtr stream); GetData()133 const std::vector<uint8_t>& GetData() const { return data_; } 134 std::string GetDataAsString() const; 135 std::unique_ptr<base::DictionaryValue> GetDataAsJson() const; 136 // Parses the data into a JSON object and writes it back to JSON to normalize 137 // its string representation (no pretty print, extra spaces, etc). 138 std::string GetDataAsNormalizedJsonString() const; 139 140 // Add/retrieve request/response HTTP headers. 141 void AddHeaders(const HeaderList& headers); 142 std::string GetHeader(const std::string& header_name) const; GetHeaders()143 const std::multimap<std::string, std::string>& GetHeaders() const { 144 return headers_; 145 } 146 147 protected: 148 // Data buffer. 149 std::vector<uint8_t> data_; 150 // Header map. 151 std::multimap<std::string, std::string> headers_; 152 153 private: 154 DISALLOW_COPY_AND_ASSIGN(ServerRequestResponseBase); 155 }; 156 157 /////////////////////////////////////////////////////////////////////////////// 158 // A container class that encapsulates all the HTTP server request information. 159 /////////////////////////////////////////////////////////////////////////////// 160 class ServerRequest : public ServerRequestResponseBase { 161 public: 162 ServerRequest(const std::string& url, const std::string& method); 163 164 // Get the actual request URL. Does not include the query string or fragment. GetURL()165 const std::string& GetURL() const { return url_; } 166 // Get the request method. GetMethod()167 const std::string& GetMethod() const { return method_; } 168 // Get the POST/GET request parameters. These are parsed query string 169 // parameters from the URL. In addition, for POST requests with 170 // application/x-www-form-urlencoded content type, the request body is also 171 // parsed and individual fields can be accessed through this method. 172 std::string GetFormField(const std::string& field_name) const; 173 174 private: 175 // Request URL (without query string or URL fragment). 176 std::string url_; 177 // Request method 178 std::string method_; 179 // List of available request data form fields. 180 mutable std::map<std::string, std::string> form_fields_; 181 // Flag used on first request to GetFormField to parse the body of HTTP POST 182 // request with application/x-www-form-urlencoded content. 183 mutable bool form_fields_parsed_ = false; 184 185 DISALLOW_COPY_AND_ASSIGN(ServerRequest); 186 }; 187 188 /////////////////////////////////////////////////////////////////////////////// 189 // A container class that encapsulates all the HTTP server response information. 190 // The request handler will use this class to provide a response to the caller. 191 // Call the Reply() or the appropriate ReplyNNN() specialization to provide 192 // the response data. Additional calls to AddHeaders() can be made to provide 193 // custom response headers. The Reply-methods will already provide the 194 // following response headers: 195 // Content-Length 196 // Content-Type 197 /////////////////////////////////////////////////////////////////////////////// 198 class ServerResponse : public ServerRequestResponseBase { 199 public: 200 ServerResponse() = default; 201 202 // Generic reply method. 203 void Reply(int status_code, 204 const void* data, 205 size_t data_size, 206 const std::string& mime_type); 207 // Reply with text body. 208 void ReplyText(int status_code, 209 const std::string& text, 210 const std::string& mime_type); 211 // Reply with JSON object. The content type will be "application/json". 212 void ReplyJson(int status_code, const base::Value* json); 213 // Special form for JSON response for simple objects that have a flat 214 // list of key-value pairs of string type. 215 void ReplyJson(int status_code, const FormFieldList& fields); 216 217 // Specialized overload to send the binary data as an array of simple 218 // data elements. Only trivial data types (scalars, POD structures, etc) 219 // can be used. 220 template<typename T> Reply(int status_code,const std::vector<T> & data,const std::string & mime_type)221 void Reply(int status_code, 222 const std::vector<T>& data, 223 const std::string& mime_type) { 224 // Make sure T doesn't have virtual functions, custom constructors, etc. 225 static_assert(std::is_trivial<T>::value, "Only simple data is supported"); 226 Reply(status_code, data.data(), data.size() * sizeof(T), mime_type); 227 } 228 229 // Specialized overload to send the binary data. 230 // Only trivial data types (scalars, POD structures, etc) can be used. 231 template<typename T> Reply(int status_code,const T & data,const std::string & mime_type)232 void Reply(int status_code, const T& data, const std::string& mime_type) { 233 // Make sure T doesn't have virtual functions, custom constructors, etc. 234 static_assert(std::is_trivial<T>::value, "Only simple data is supported"); 235 Reply(status_code, &data, sizeof(T), mime_type); 236 } 237 238 // For handlers that want to simulate versions of HTTP protocol other 239 // than HTTP/1.1, call this method with the custom version string, 240 // for example "HTTP/1.0". SetProtocolVersion(const std::string & protocol_version)241 void SetProtocolVersion(const std::string& protocol_version) { 242 protocol_version_ = protocol_version; 243 } 244 245 protected: 246 // These methods are helpers to implement corresponding functionality 247 // of fake::Connection. 248 friend class Connection; 249 // Helper for fake::Connection::GetResponseStatusCode(). GetStatusCode()250 int GetStatusCode() const { return status_code_; } 251 // Helper for fake::Connection::GetResponseStatusText(). 252 std::string GetStatusText() const; 253 // Helper for fake::Connection::GetProtocolVersion(). GetProtocolVersion()254 std::string GetProtocolVersion() const { return protocol_version_; } 255 256 private: 257 int status_code_ = 0; 258 std::string protocol_version_ = "HTTP/1.1"; 259 260 DISALLOW_COPY_AND_ASSIGN(ServerResponse); 261 }; 262 263 } // namespace fake 264 } // namespace http 265 } // namespace brillo 266 267 #endif // LIBBRILLO_BRILLO_HTTP_HTTP_TRANSPORT_FAKE_H_ 268