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 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 /////////////////////////////////////////////////////////////////////////////// 125 class ServerRequestResponseBase { 126 public: 127 ServerRequestResponseBase() = default; 128 129 // Add/retrieve request/response body data. 130 void SetData(StreamPtr stream); GetData()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; GetHeaders()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 /////////////////////////////////////////////////////////////////////////////// 158 class 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. GetURL()163 const std::string& GetURL() const { return url_; } 164 // Get the request method. GetMethod()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 /////////////////////////////////////////////////////////////////////////////// 196 class 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> Reply(int status_code,const std::vector<T> & data,const std::string & mime_type)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> Reply(int status_code,const T & data,const std::string & mime_type)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". SetProtocolVersion(const std::string & protocol_version)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(). GetStatusCode()248 int GetStatusCode() const { return status_code_; } 249 // Helper for fake::Connection::GetResponseStatusText(). 250 std::string GetStatusText() const; 251 // Helper for fake::Connection::GetProtocolVersion(). 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