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 tracked_objects::Location &,const base::Closure & callback)66 void Transport::RunCallbackAsync(
67     const tracked_objects::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 = [status_code, reply_text, mime_type](
125       const ServerRequest& /* request */, ServerResponse* response) {
126     response->ReplyText(status_code, reply_text, mime_type);
127   };
128   AddHandler(url, method, base::Bind(handler));
129 }
130 
GetHandler(const std::string & url,const std::string & method) const131 Transport::HandlerCallback Transport::GetHandler(
132     const std::string& url,
133     const std::string& method) const {
134   // First try the exact combination of URL/Method
135   auto p = handlers_.find(GetHandlerMapKey(url, method));
136   if (p != handlers_.end())
137     return p->second;
138   // If not found, try URL/*
139   p = handlers_.find(GetHandlerMapKey(url, "*"));
140   if (p != handlers_.end())
141     return p->second;
142   // If still not found, try */method
143   p = handlers_.find(GetHandlerMapKey("*", method));
144   if (p != handlers_.end())
145     return p->second;
146   // Finally, try */*
147   p = handlers_.find(GetHandlerMapKey("*", "*"));
148   return (p != handlers_.end()) ? p->second : HandlerCallback();
149 }
150 
SetData(StreamPtr stream)151 void ServerRequestResponseBase::SetData(StreamPtr stream) {
152   data_.clear();
153   if (stream) {
154     uint8_t buffer[1024];
155     size_t size = 0;
156     if (stream->CanGetSize())
157       data_.reserve(stream->GetRemainingSize());
158 
159     do {
160       CHECK(stream->ReadBlocking(buffer, sizeof(buffer), &size, nullptr));
161       data_.insert(data_.end(), buffer, buffer + size);
162     } while (size > 0);
163   }
164 }
165 
GetDataAsString() const166 std::string ServerRequestResponseBase::GetDataAsString() const {
167   if (data_.empty())
168     return std::string();
169   auto chars = reinterpret_cast<const char*>(data_.data());
170   return std::string(chars, data_.size());
171 }
172 
173 std::unique_ptr<base::DictionaryValue>
GetDataAsJson() const174 ServerRequestResponseBase::GetDataAsJson() const {
175   if (brillo::mime::RemoveParameters(
176           GetHeader(request_header::kContentType)) ==
177       brillo::mime::application::kJson) {
178     auto value = base::JSONReader::Read(GetDataAsString());
179     if (value) {
180       base::DictionaryValue* dict = nullptr;
181       if (value->GetAsDictionary(&dict)) {
182         // |value| is now owned by |dict|.
183         base::IgnoreResult(value.release());
184         return std::unique_ptr<base::DictionaryValue>(dict);
185       }
186     }
187   }
188   return std::unique_ptr<base::DictionaryValue>();
189 }
190 
GetDataAsNormalizedJsonString() const191 std::string ServerRequestResponseBase::GetDataAsNormalizedJsonString() const {
192   std::string value;
193   // Make sure we serialize the JSON back without any pretty print so
194   // the string comparison works correctly.
195   auto json = GetDataAsJson();
196   if (json)
197     base::JSONWriter::Write(*json, &value);
198   return value;
199 }
200 
AddHeaders(const HeaderList & headers)201 void ServerRequestResponseBase::AddHeaders(const HeaderList& headers) {
202   for (const auto& pair : headers) {
203     if (pair.second.empty())
204       headers_.erase(pair.first);
205     else
206       headers_.insert(pair);
207   }
208 }
209 
GetHeader(const std::string & header_name) const210 std::string ServerRequestResponseBase::GetHeader(
211     const std::string& header_name) const {
212   auto p = headers_.find(header_name);
213   return p != headers_.end() ? p->second : std::string();
214 }
215 
ServerRequest(const std::string & url,const std::string & method)216 ServerRequest::ServerRequest(const std::string& url, const std::string& method)
217     : method_(method) {
218   auto params = brillo::url::GetQueryStringParameters(url);
219   url_ = brillo::url::RemoveQueryString(url, true);
220   form_fields_.insert(params.begin(), params.end());
221 }
222 
GetFormField(const std::string & field_name) const223 std::string ServerRequest::GetFormField(const std::string& field_name) const {
224   if (!form_fields_parsed_) {
225     std::string mime_type = brillo::mime::RemoveParameters(
226         GetHeader(request_header::kContentType));
227     if (mime_type == brillo::mime::application::kWwwFormUrlEncoded &&
228         !GetData().empty()) {
229       auto fields = brillo::data_encoding::WebParamsDecode(GetDataAsString());
230       form_fields_.insert(fields.begin(), fields.end());
231     }
232     form_fields_parsed_ = true;
233   }
234   auto p = form_fields_.find(field_name);
235   return p != form_fields_.end() ? p->second : std::string();
236 }
237 
Reply(int status_code,const void * data,size_t data_size,const std::string & mime_type)238 void ServerResponse::Reply(int status_code,
239                            const void* data,
240                            size_t data_size,
241                            const std::string& mime_type) {
242   data_.clear();
243   status_code_ = status_code;
244   SetData(MemoryStream::OpenCopyOf(data, data_size, nullptr));
245   AddHeaders({{response_header::kContentLength,
246                brillo::string_utils::ToString(data_size)},
247               {response_header::kContentType, mime_type}});
248 }
249 
ReplyText(int status_code,const std::string & text,const std::string & mime_type)250 void ServerResponse::ReplyText(int status_code,
251                                const std::string& text,
252                                const std::string& mime_type) {
253   Reply(status_code, text.data(), text.size(), mime_type);
254 }
255 
ReplyJson(int status_code,const base::Value * json)256 void ServerResponse::ReplyJson(int status_code, const base::Value* json) {
257   std::string text;
258   base::JSONWriter::WriteWithOptions(
259       *json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text);
260   std::string mime_type =
261       brillo::mime::AppendParameter(brillo::mime::application::kJson,
262                                       brillo::mime::parameters::kCharset,
263                                       "utf-8");
264   ReplyText(status_code, text, mime_type);
265 }
266 
ReplyJson(int status_code,const http::FormFieldList & fields)267 void ServerResponse::ReplyJson(int status_code,
268                                const http::FormFieldList& fields) {
269   base::DictionaryValue json;
270   for (const auto& pair : fields) {
271     json.SetString(pair.first, pair.second);
272   }
273   ReplyJson(status_code, &json);
274 }
275 
GetStatusText() const276 std::string ServerResponse::GetStatusText() const {
277   static std::vector<std::pair<int, const char*>> status_text_map = {
278       {100, "Continue"},
279       {101, "Switching Protocols"},
280       {102, "Processing"},
281       {200, "OK"},
282       {201, "Created"},
283       {202, "Accepted"},
284       {203, "Non-Authoritative Information"},
285       {204, "No Content"},
286       {205, "Reset Content"},
287       {206, "Partial Content"},
288       {207, "Multi-Status"},
289       {208, "Already Reported"},
290       {226, "IM Used"},
291       {300, "Multiple Choices"},
292       {301, "Moved Permanently"},
293       {302, "Found"},
294       {303, "See Other"},
295       {304, "Not Modified"},
296       {305, "Use Proxy"},
297       {306, "Switch Proxy"},
298       {307, "Temporary Redirect"},
299       {308, "Permanent Redirect"},
300       {400, "Bad Request"},
301       {401, "Unauthorized"},
302       {402, "Payment Required"},
303       {403, "Forbidden"},
304       {404, "Not Found"},
305       {405, "Method Not Allowed"},
306       {406, "Not Acceptable"},
307       {407, "Proxy Authentication Required"},
308       {408, "Request Timeout"},
309       {409, "Conflict"},
310       {410, "Gone"},
311       {411, "Length Required"},
312       {412, "Precondition Failed"},
313       {413, "Request Entity Too Large"},
314       {414, "Request - URI Too Long"},
315       {415, "Unsupported Media Type"},
316       {429, "Too Many Requests"},
317       {431, "Request Header Fields Too Large"},
318       {500, "Internal Server Error"},
319       {501, "Not Implemented"},
320       {502, "Bad Gateway"},
321       {503, "Service Unavailable"},
322       {504, "Gateway Timeout"},
323       {505, "HTTP Version Not Supported"},
324   };
325 
326   for (const auto& pair : status_text_map) {
327     if (pair.first == status_code_)
328       return pair.second;
329   }
330   return std::string();
331 }
332 
333 }  // namespace brillo
334