Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 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 BUFFET_HTTP_TRANSPORT_FAKE_H_ |
| 6 | #define BUFFET_HTTP_TRANSPORT_FAKE_H_ |
| 7 | |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 8 | #include <map> |
| 9 | #include <string> |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 10 | #include <type_traits> |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 11 | #include <vector> |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 12 | |
| 13 | #include <base/callback.h> |
| 14 | #include <base/values.h> |
| 15 | |
| 16 | #include "buffet/http_transport.h" |
Alex Vakulenko | 8e34d39 | 2014-04-29 11:02:56 -0700 | [diff] [blame] | 17 | #include "buffet/http_utils.h" |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 18 | |
Alex Vakulenko | af23b32 | 2014-05-08 16:25:45 -0700 | [diff] [blame] | 19 | namespace buffet { |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 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 | virtual ~Transport(); |
| 35 | |
| 36 | // Server handler callback signature. |
| 37 | typedef base::Callback<void(const ServerRequest&, ServerResponse*)> |
| 38 | HandlerCallback; |
| 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, const std::string& method, |
| 50 | const HandlerCallback& handler); |
Alex Vakulenko | 8e34d39 | 2014-04-29 11:02:56 -0700 | [diff] [blame] | 51 | // Simple version of AddHandler. AddSimpleReplyHandler just returns the |
| 52 | // specified text response of given MIME type. |
| 53 | void AddSimpleReplyHandler(const std::string& url, |
| 54 | const std::string& method, |
| 55 | int status_code, |
| 56 | const std::string& reply_text, |
| 57 | const std::string& mime_type); |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 58 | // Retrieve a handler for specific |url| and request |method|. |
| 59 | HandlerCallback GetHandler(const std::string& url, |
| 60 | const std::string& method) const; |
| 61 | |
Alex Vakulenko | 8e34d39 | 2014-04-29 11:02:56 -0700 | [diff] [blame] | 62 | // For tests that want to assert on the number of HTTP requests sent, |
| 63 | // these methods can be used to do just that. |
| 64 | int GetRequestCount() const { return request_count_; } |
| 65 | void ResetRequestCount() { request_count_ = 0; } |
| 66 | |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 67 | // Overload from http::Transport |
Alex Vakulenko | 5a9e718 | 2014-08-11 15:59:58 -0700 | [diff] [blame] | 68 | std::unique_ptr<http::Connection> CreateConnection( |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 69 | std::shared_ptr<http::Transport> transport, |
| 70 | const std::string& url, |
| 71 | const std::string& method, |
| 72 | const HeaderList& headers, |
| 73 | const std::string& user_agent, |
| 74 | const std::string& referer, |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 75 | ErrorPtr* error) override; |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 76 | |
| 77 | private: |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 78 | // A list of user-supplied request handlers. |
| 79 | std::map<std::string, HandlerCallback> handlers_; |
Alex Vakulenko | 8e34d39 | 2014-04-29 11:02:56 -0700 | [diff] [blame] | 80 | // Counter incremented each time a request is made. |
| 81 | int request_count_ = 0; |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 82 | |
| 83 | DISALLOW_COPY_AND_ASSIGN(Transport); |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 84 | }; |
| 85 | |
| 86 | /////////////////////////////////////////////////////////////////////////////// |
| 87 | // A base class for ServerRequest and ServerResponse. It provides common |
| 88 | // functionality to work with request/response HTTP headers and data. |
| 89 | /////////////////////////////////////////////////////////////////////////////// |
| 90 | class ServerRequestResponseBase { |
| 91 | public: |
| 92 | ServerRequestResponseBase() = default; |
| 93 | |
| 94 | // Add/retrieve request/response body data. |
| 95 | void AddData(const void* data, size_t data_size); |
| 96 | const std::vector<unsigned char>& GetData() const { return data_; } |
| 97 | std::string GetDataAsString() const; |
Alex Vakulenko | 8e34d39 | 2014-04-29 11:02:56 -0700 | [diff] [blame] | 98 | std::unique_ptr<base::DictionaryValue> GetDataAsJson() const; |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 99 | |
| 100 | // Add/retrieve request/response HTTP headers. |
| 101 | void AddHeaders(const HeaderList& headers); |
| 102 | std::string GetHeader(const std::string& header_name) const; |
Alex Vakulenko | 28b31f5 | 2014-04-28 12:40:05 -0700 | [diff] [blame] | 103 | const std::map<std::string, std::string>& GetHeaders() const { |
| 104 | return headers_; |
| 105 | } |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 106 | |
| 107 | protected: |
| 108 | // Data buffer. |
| 109 | std::vector<unsigned char> data_; |
| 110 | // Header map. |
| 111 | std::map<std::string, std::string> headers_; |
| 112 | |
| 113 | private: |
| 114 | DISALLOW_COPY_AND_ASSIGN(ServerRequestResponseBase); |
| 115 | }; |
| 116 | |
| 117 | /////////////////////////////////////////////////////////////////////////////// |
| 118 | // A container class that encapsulates all the HTTP server request information. |
| 119 | /////////////////////////////////////////////////////////////////////////////// |
| 120 | class ServerRequest : public ServerRequestResponseBase { |
| 121 | public: |
| 122 | ServerRequest(const std::string& url, const std::string& method); |
| 123 | |
| 124 | // Get the actual request URL. Does not include the query string or fragment. |
| 125 | const std::string& GetURL() const { return url_; } |
| 126 | // Get the request method. |
| 127 | const std::string& GetMethod() const { return method_; } |
| 128 | // Get the POST/GET request parameters. These are parsed query string |
| 129 | // parameters from the URL. In addition, for POST requests with |
| 130 | // application/x-www-form-urlencoded content type, the request body is also |
| 131 | // parsed and individual fields can be accessed through this method. |
| 132 | std::string GetFormField(const std::string& field_name) const; |
| 133 | |
| 134 | private: |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 135 | // Request URL (without query string or URL fragment). |
| 136 | std::string url_; |
| 137 | // Request method |
| 138 | std::string method_; |
| 139 | // List of available request data form fields. |
| 140 | mutable std::map<std::string, std::string> form_fields_; |
| 141 | // Flag used on first request to GetFormField to parse the body of HTTP POST |
| 142 | // request with application/x-www-form-urlencoded content. |
| 143 | mutable bool form_fields_parsed_ = false; |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 144 | |
| 145 | DISALLOW_COPY_AND_ASSIGN(ServerRequest); |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 146 | }; |
| 147 | |
| 148 | /////////////////////////////////////////////////////////////////////////////// |
| 149 | // A container class that encapsulates all the HTTP server response information. |
| 150 | // The request handler will use this class to provide a response to the caller. |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 151 | // Call the Reply() or the appropriate ReplyNNN() specialization to provide |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 152 | // the response data. Additional calls to AddHeaders() can be made to provide |
| 153 | // custom response headers. The Reply-methods will already provide the |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 154 | // following response headers: |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 155 | // Content-Length |
| 156 | // Content-Type |
| 157 | /////////////////////////////////////////////////////////////////////////////// |
| 158 | class ServerResponse : public ServerRequestResponseBase { |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 159 | public: |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 160 | ServerResponse() = default; |
| 161 | |
| 162 | // Generic reply method. |
| 163 | void Reply(int status_code, const void* data, size_t data_size, |
| 164 | const char* mime_type); |
| 165 | // Reply with text body. |
| 166 | void ReplyText(int status_code, const std::string& text, |
| 167 | const char* mime_type); |
| 168 | // Reply with JSON object. The content type will be "application/json". |
| 169 | void ReplyJson(int status_code, const base::Value* json); |
Alex Vakulenko | 8e34d39 | 2014-04-29 11:02:56 -0700 | [diff] [blame] | 170 | // Special form for JSON response for simple objects that have a flat |
| 171 | // list of key-value pairs of string type. |
| 172 | void ReplyJson(int status_code, const FormFieldList& fields); |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 173 | |
| 174 | // Specialized overload to send the binary data as an array of simple |
| 175 | // data elements. Only trivial data types (scalars, POD structures, etc) |
| 176 | // can be used. |
| 177 | template<typename T> |
| 178 | void Reply(int status_code, const std::vector<T>& data, |
| 179 | const char* mime_type) { |
| 180 | // Make sure T doesn't have virtual functions, custom constructors, etc. |
| 181 | static_assert(std::is_trivial<T>::value, "Only simple data is supported"); |
| 182 | Reply(status_code, data.data(), data.size() * sizeof(T), mime_type); |
| 183 | } |
| 184 | |
| 185 | // Specialized overload to send the binary data. |
| 186 | // Only trivial data types (scalars, POD structures, etc) can be used. |
| 187 | template<typename T> |
| 188 | void Reply(int status_code, const T& data, const char* mime_type) { |
| 189 | // Make sure T doesn't have virtual functions, custom constructors, etc. |
| 190 | static_assert(std::is_trivial<T>::value, "Only simple data is supported"); |
| 191 | Reply(status_code, &data, sizeof(T), mime_type); |
| 192 | } |
| 193 | |
| 194 | // For handlers that want to simulate versions of HTTP protocol other |
| 195 | // than HTTP/1.1, call this method with the custom version string, |
| 196 | // for example "HTTP/1.0". |
| 197 | void SetProtocolVersion(const std::string& protocol_version) { |
| 198 | protocol_version_ = protocol_version; |
| 199 | } |
| 200 | |
| 201 | protected: |
| 202 | // These methods are helpers to implement corresponding functionality |
| 203 | // of fake::Connection. |
| 204 | friend class Connection; |
| 205 | // Helper for fake::Connection::GetResponseStatusCode(). |
| 206 | int GetStatusCode() const { return status_code_; } |
| 207 | // Helper for fake::Connection::GetResponseStatusText(). |
| 208 | std::string GetStatusText() const; |
| 209 | // Helper for fake::Connection::GetProtocolVersion(). |
| 210 | std::string GetProtocolVersion() const { return protocol_version_; } |
| 211 | |
| 212 | private: |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 213 | int status_code_ = 0; |
| 214 | std::string protocol_version_ = "HTTP/1.1"; |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 215 | |
| 216 | DISALLOW_COPY_AND_ASSIGN(ServerResponse); |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 217 | }; |
| 218 | |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 219 | } // namespace fake |
| 220 | } // namespace http |
Alex Vakulenko | af23b32 | 2014-05-08 16:25:45 -0700 | [diff] [blame] | 221 | } // namespace buffet |
Alex Vakulenko | 9cd5e27 | 2014-04-25 17:26:11 -0700 | [diff] [blame] | 222 | |
Alex Vakulenko | b3aac25 | 2014-05-07 17:35:24 -0700 | [diff] [blame] | 223 | #endif // BUFFET_HTTP_TRANSPORT_FAKE_H_ |