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 #include <brillo/http/http_transport_fake.h>
6
7 #include <utility>
8
9 #include <base/json/json_reader.h>
10 #include <base/json/json_writer.h>
11 #include <base/logging.h>
12 #include <brillo/bind_lambda.h>
13 #include <brillo/http/http_connection_fake.h>
14 #include <brillo/http/http_request.h>
15 #include <brillo/mime_utils.h>
16 #include <brillo/streams/memory_stream.h>
17 #include <brillo/strings/string_utils.h>
18 #include <brillo/url_utils.h>
19
20 namespace brillo {
21
22 using http::fake::Transport;
23 using http::fake::ServerRequestResponseBase;
24 using http::fake::ServerRequest;
25 using http::fake::ServerResponse;
26
Transport()27 Transport::Transport() {
28 VLOG(1) << "fake::Transport created";
29 }
30
~Transport()31 Transport::~Transport() {
32 VLOG(1) << "fake::Transport destroyed";
33 }
34
CreateConnection(const std::string & url,const std::string & method,const HeaderList & headers,const std::string & user_agent,const std::string & referer,brillo::ErrorPtr * error)35 std::shared_ptr<http::Connection> Transport::CreateConnection(
36 const std::string& url,
37 const std::string& method,
38 const HeaderList& headers,
39 const std::string& user_agent,
40 const std::string& referer,
41 brillo::ErrorPtr* error) {
42 std::shared_ptr<http::Connection> connection;
43 if (create_connection_error_) {
44 if (error)
45 *error = std::move(create_connection_error_);
46 return connection;
47 }
48 HeaderList headers_copy = headers;
49 if (!user_agent.empty()) {
50 headers_copy.push_back(
51 std::make_pair(http::request_header::kUserAgent, user_agent));
52 }
53 if (!referer.empty()) {
54 headers_copy.push_back(
55 std::make_pair(http::request_header::kReferer, referer));
56 }
57 connection =
58 std::make_shared<http::fake::Connection>(url, method, shared_from_this());
59 CHECK(connection) << "Unable to create Connection object";
60 if (!connection->SendHeaders(headers_copy, error))
61 connection.reset();
62 request_count_++;
63 return connection;
64 }
65
RunCallbackAsync(const base::Location &,const base::Closure & callback)66 void Transport::RunCallbackAsync(
67 const base::Location& /* from_here */,
68 const base::Closure& callback) {
69 if (!async_) {
70 callback.Run();
71 return;
72 }
73 async_callback_queue_.push(callback);
74 }
75
HandleOneAsyncRequest()76 bool Transport::HandleOneAsyncRequest() {
77 if (async_callback_queue_.empty())
78 return false;
79
80 base::Closure callback = async_callback_queue_.front();
81 async_callback_queue_.pop();
82 callback.Run();
83 return true;
84 }
85
HandleAllAsyncRequests()86 void Transport::HandleAllAsyncRequests() {
87 while (!async_callback_queue_.empty())
88 HandleOneAsyncRequest();
89 }
90
StartAsyncTransfer(http::Connection *,const SuccessCallback &,const ErrorCallback &)91 http::RequestID Transport::StartAsyncTransfer(
92 http::Connection* /* connection */,
93 const SuccessCallback& /* success_callback */,
94 const ErrorCallback& /* error_callback */) {
95 // Fake transport doesn't use this method.
96 LOG(FATAL) << "This method should not be called on fake transport";
97 return 0;
98 }
99
CancelRequest(RequestID)100 bool Transport::CancelRequest(RequestID /* request_id */) {
101 return false;
102 }
103
SetDefaultTimeout(base::TimeDelta)104 void Transport::SetDefaultTimeout(base::TimeDelta /* timeout */) {
105 }
106
GetHandlerMapKey(const std::string & url,const std::string & method)107 static inline std::string GetHandlerMapKey(const std::string& url,
108 const std::string& method) {
109 return method + ":" + url;
110 }
111
AddHandler(const std::string & url,const std::string & method,const HandlerCallback & handler)112 void Transport::AddHandler(const std::string& url,
113 const std::string& method,
114 const HandlerCallback& handler) {
115 // Make sure we can override/replace existing handlers.
116 handlers_[GetHandlerMapKey(url, method)] = handler;
117 }
118
AddSimpleReplyHandler(const std::string & url,const std::string & method,int status_code,const std::string & reply_text,const std::string & mime_type)119 void Transport::AddSimpleReplyHandler(const std::string& url,
120 const std::string& method,
121 int status_code,
122 const std::string& reply_text,
123 const std::string& mime_type) {
124 auto handler = [](int status_code,
125 const std::string& reply_text,
126 const std::string& mime_type,
127 const ServerRequest& /* request */,
128 ServerResponse* response) {
129 response->ReplyText(status_code, reply_text, mime_type);
130 };
131 AddHandler(
132 url, method, base::Bind(handler, status_code, reply_text, mime_type));
133 }
134
GetHandler(const std::string & url,const std::string & method) const135 Transport::HandlerCallback Transport::GetHandler(
136 const std::string& url,
137 const std::string& method) const {
138 // First try the exact combination of URL/Method
139 auto p = handlers_.find(GetHandlerMapKey(url, method));
140 if (p != handlers_.end())
141 return p->second;
142 // If not found, try URL/*
143 p = handlers_.find(GetHandlerMapKey(url, "*"));
144 if (p != handlers_.end())
145 return p->second;
146 // If still not found, try */method
147 p = handlers_.find(GetHandlerMapKey("*", method));
148 if (p != handlers_.end())
149 return p->second;
150 // Finally, try */*
151 p = handlers_.find(GetHandlerMapKey("*", "*"));
152 return (p != handlers_.end()) ? p->second : HandlerCallback();
153 }
154
SetData(StreamPtr stream)155 void ServerRequestResponseBase::SetData(StreamPtr stream) {
156 data_.clear();
157 if (stream) {
158 uint8_t buffer[1024];
159 size_t size = 0;
160 if (stream->CanGetSize())
161 data_.reserve(stream->GetRemainingSize());
162
163 do {
164 CHECK(stream->ReadBlocking(buffer, sizeof(buffer), &size, nullptr));
165 data_.insert(data_.end(), buffer, buffer + size);
166 } while (size > 0);
167 }
168 }
169
GetDataAsString() const170 std::string ServerRequestResponseBase::GetDataAsString() const {
171 if (data_.empty())
172 return std::string();
173 auto chars = reinterpret_cast<const char*>(data_.data());
174 return std::string(chars, data_.size());
175 }
176
177 std::unique_ptr<base::DictionaryValue>
GetDataAsJson() const178 ServerRequestResponseBase::GetDataAsJson() const {
179 std::unique_ptr<base::DictionaryValue> result;
180 if (brillo::mime::RemoveParameters(
181 GetHeader(request_header::kContentType)) ==
182 brillo::mime::application::kJson) {
183 auto value = base::JSONReader::Read(GetDataAsString());
184 result = base::DictionaryValue::From(std::move(value));
185 }
186 return result;
187 }
188
GetDataAsNormalizedJsonString() const189 std::string ServerRequestResponseBase::GetDataAsNormalizedJsonString() const {
190 std::string value;
191 // Make sure we serialize the JSON back without any pretty print so
192 // the string comparison works correctly.
193 auto json = GetDataAsJson();
194 if (json)
195 base::JSONWriter::Write(*json, &value);
196 return value;
197 }
198
AddHeaders(const HeaderList & headers)199 void ServerRequestResponseBase::AddHeaders(const HeaderList& headers) {
200 for (const auto& pair : headers) {
201 if (pair.second.empty())
202 headers_.erase(pair.first);
203 else
204 headers_.insert(pair);
205 }
206 }
207
GetHeader(const std::string & header_name) const208 std::string ServerRequestResponseBase::GetHeader(
209 const std::string& header_name) const {
210 auto p = headers_.find(header_name);
211 return p != headers_.end() ? p->second : std::string();
212 }
213
ServerRequest(const std::string & url,const std::string & method)214 ServerRequest::ServerRequest(const std::string& url, const std::string& method)
215 : method_(method) {
216 auto params = brillo::url::GetQueryStringParameters(url);
217 url_ = brillo::url::RemoveQueryString(url, true);
218 form_fields_.insert(params.begin(), params.end());
219 }
220
GetFormField(const std::string & field_name) const221 std::string ServerRequest::GetFormField(const std::string& field_name) const {
222 if (!form_fields_parsed_) {
223 std::string mime_type = brillo::mime::RemoveParameters(
224 GetHeader(request_header::kContentType));
225 if (mime_type == brillo::mime::application::kWwwFormUrlEncoded &&
226 !GetData().empty()) {
227 auto fields = brillo::data_encoding::WebParamsDecode(GetDataAsString());
228 form_fields_.insert(fields.begin(), fields.end());
229 }
230 form_fields_parsed_ = true;
231 }
232 auto p = form_fields_.find(field_name);
233 return p != form_fields_.end() ? p->second : std::string();
234 }
235
Reply(int status_code,const void * data,size_t data_size,const std::string & mime_type)236 void ServerResponse::Reply(int status_code,
237 const void* data,
238 size_t data_size,
239 const std::string& mime_type) {
240 data_.clear();
241 status_code_ = status_code;
242 SetData(MemoryStream::OpenCopyOf(data, data_size, nullptr));
243 AddHeaders({{response_header::kContentLength,
244 brillo::string_utils::ToString(data_size)},
245 {response_header::kContentType, mime_type}});
246 }
247
ReplyText(int status_code,const std::string & text,const std::string & mime_type)248 void ServerResponse::ReplyText(int status_code,
249 const std::string& text,
250 const std::string& mime_type) {
251 Reply(status_code, text.data(), text.size(), mime_type);
252 }
253
ReplyJson(int status_code,const base::Value * json)254 void ServerResponse::ReplyJson(int status_code, const base::Value* json) {
255 std::string text;
256 base::JSONWriter::WriteWithOptions(
257 *json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text);
258 std::string mime_type =
259 brillo::mime::AppendParameter(brillo::mime::application::kJson,
260 brillo::mime::parameters::kCharset,
261 "utf-8");
262 ReplyText(status_code, text, mime_type);
263 }
264
ReplyJson(int status_code,const http::FormFieldList & fields)265 void ServerResponse::ReplyJson(int status_code,
266 const http::FormFieldList& fields) {
267 base::DictionaryValue json;
268 for (const auto& pair : fields) {
269 json.SetString(pair.first, pair.second);
270 }
271 ReplyJson(status_code, &json);
272 }
273
GetStatusText() const274 std::string ServerResponse::GetStatusText() const {
275 static std::vector<std::pair<int, const char*>> status_text_map = {
276 {100, "Continue"},
277 {101, "Switching Protocols"},
278 {102, "Processing"},
279 {200, "OK"},
280 {201, "Created"},
281 {202, "Accepted"},
282 {203, "Non-Authoritative Information"},
283 {204, "No Content"},
284 {205, "Reset Content"},
285 {206, "Partial Content"},
286 {207, "Multi-Status"},
287 {208, "Already Reported"},
288 {226, "IM Used"},
289 {300, "Multiple Choices"},
290 {301, "Moved Permanently"},
291 {302, "Found"},
292 {303, "See Other"},
293 {304, "Not Modified"},
294 {305, "Use Proxy"},
295 {306, "Switch Proxy"},
296 {307, "Temporary Redirect"},
297 {308, "Permanent Redirect"},
298 {400, "Bad Request"},
299 {401, "Unauthorized"},
300 {402, "Payment Required"},
301 {403, "Forbidden"},
302 {404, "Not Found"},
303 {405, "Method Not Allowed"},
304 {406, "Not Acceptable"},
305 {407, "Proxy Authentication Required"},
306 {408, "Request Timeout"},
307 {409, "Conflict"},
308 {410, "Gone"},
309 {411, "Length Required"},
310 {412, "Precondition Failed"},
311 {413, "Request Entity Too Large"},
312 {414, "Request - URI Too Long"},
313 {415, "Unsupported Media Type"},
314 {429, "Too Many Requests"},
315 {431, "Request Header Fields Too Large"},
316 {500, "Internal Server Error"},
317 {501, "Not Implemented"},
318 {502, "Bad Gateway"},
319 {503, "Service Unavailable"},
320 {504, "Gateway Timeout"},
321 {505, "HTTP Version Not Supported"},
322 };
323
324 for (const auto& pair : status_text_map) {
325 if (pair.first == status_code_)
326 return pair.second;
327 }
328 return std::string();
329 }
330
331 } // namespace brillo
332