• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/base/build_config.h"
18 
19 #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
20 
21 #include "src/trace_processor/rpc/httpd.h"
22 
23 #include <map>
24 #include <string>
25 
26 #include "perfetto/ext/base/paged_memory.h"
27 #include "perfetto/ext/base/string_utils.h"
28 #include "perfetto/ext/base/string_view.h"
29 #include "perfetto/ext/base/unix_socket.h"
30 #include "perfetto/ext/base/unix_task_runner.h"
31 #include "perfetto/protozero/scattered_heap_buffer.h"
32 #include "perfetto/trace_processor/trace_processor.h"
33 #include "src/trace_processor/rpc/rpc.h"
34 
35 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
36 
37 namespace perfetto {
38 namespace trace_processor {
39 
40 namespace {
41 
42 constexpr char kBindAddr[] = "127.0.0.1:9001";
43 
44 // 32 MiB payload + 128K for HTTP headers.
45 constexpr size_t kMaxRequestSize = (32 * 1024 + 128) * 1024;
46 
47 // Owns the socket and data for one HTTP client connection.
48 struct Client {
Clientperfetto::trace_processor::__anonddf6ecb40111::Client49   Client(std::unique_ptr<base::UnixSocket> s)
50       : sock(std::move(s)),
51         rxbuf(base::PagedMemory::Allocate(kMaxRequestSize)) {}
rxbuf_availperfetto::trace_processor::__anonddf6ecb40111::Client52   size_t rxbuf_avail() { return rxbuf.size() - rxbuf_used; }
53 
54   std::unique_ptr<base::UnixSocket> sock;
55   base::PagedMemory rxbuf;
56   size_t rxbuf_used = 0;
57 };
58 
59 struct HttpRequest {
60   base::StringView method;
61   base::StringView uri;
62   base::StringView origin;
63   base::StringView body;
64   int id = 0;
65 };
66 
67 class HttpServer : public base::UnixSocket::EventListener {
68  public:
69   explicit HttpServer(std::unique_ptr<TraceProcessor>);
70   ~HttpServer() override;
71   void Run();
72 
73  private:
74   size_t ParseOneHttpRequest(Client* client);
75   void HandleRequest(Client*, const HttpRequest&);
76 
77   void OnNewIncomingConnection(base::UnixSocket*,
78                                std::unique_ptr<base::UnixSocket>) override;
79   void OnConnect(base::UnixSocket* self, bool connected) override;
80   void OnDisconnect(base::UnixSocket* self) override;
81   void OnDataAvailable(base::UnixSocket* self) override;
82 
83   Rpc trace_processor_rpc_;
84   base::UnixTaskRunner task_runner_;
85   std::unique_ptr<base::UnixSocket> sock_;
86   std::vector<Client> clients_;
87 };
88 
Append(std::vector<char> & buf,const char * str)89 void Append(std::vector<char>& buf, const char* str) {
90   buf.insert(buf.end(), str, str + strlen(str));
91 }
92 
Append(std::vector<char> & buf,const std::string & str)93 void Append(std::vector<char>& buf, const std::string& str) {
94   buf.insert(buf.end(), str.begin(), str.end());
95 }
96 
HttpReply(base::UnixSocket * sock,const char * http_code,std::initializer_list<const char * > headers={},const uint8_t * body=nullptr,size_t body_len=0)97 void HttpReply(base::UnixSocket* sock,
98                const char* http_code,
99                std::initializer_list<const char*> headers = {},
100                const uint8_t* body = nullptr,
101                size_t body_len = 0) {
102   std::vector<char> response;
103   response.reserve(4096);
104   Append(response, "HTTP/1.1 ");
105   Append(response, http_code);
106   Append(response, "\r\n");
107   for (const char* hdr : headers) {
108     Append(response, hdr);
109     Append(response, "\r\n");
110   }
111   Append(response, "Content-Length: ");
112   Append(response, std::to_string(body_len));
113   Append(response, "\r\n\r\n");  // End-of-headers marker.
114   sock->Send(response.data(), response.size());
115   if (body_len)
116     sock->Send(body, body_len);
117 }
118 
ShutdownBadRequest(base::UnixSocket * sock,const char * reason)119 void ShutdownBadRequest(base::UnixSocket* sock, const char* reason) {
120   HttpReply(sock, "500 Bad Request", {},
121             reinterpret_cast<const uint8_t*>(reason), strlen(reason));
122   sock->Shutdown(/*notify=*/true);
123 }
124 
HttpServer(std::unique_ptr<TraceProcessor> preloaded_instance)125 HttpServer::HttpServer(std::unique_ptr<TraceProcessor> preloaded_instance)
126     : trace_processor_rpc_(std::move(preloaded_instance)) {}
127 HttpServer::~HttpServer() = default;
128 
Run()129 void HttpServer::Run() {
130   PERFETTO_ILOG("[HTTP] Starting RPC server on %s", kBindAddr);
131   sock_ = base::UnixSocket::Listen(kBindAddr, this, &task_runner_,
132                                    base::SockFamily::kInet,
133                                    base::SockType::kStream);
134   task_runner_.Run();
135 }
136 
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> sock)137 void HttpServer::OnNewIncomingConnection(
138     base::UnixSocket*,
139     std::unique_ptr<base::UnixSocket> sock) {
140   PERFETTO_LOG("[HTTP] New connection");
141   clients_.emplace_back(std::move(sock));
142 }
143 
OnConnect(base::UnixSocket *,bool)144 void HttpServer::OnConnect(base::UnixSocket*, bool) {}
145 
OnDisconnect(base::UnixSocket * sock)146 void HttpServer::OnDisconnect(base::UnixSocket* sock) {
147   PERFETTO_LOG("[HTTP] Client disconnected");
148   for (auto it = clients_.begin(); it != clients_.end(); ++it) {
149     if (it->sock.get() == sock) {
150       clients_.erase(it);
151       return;
152     }
153   }
154   PERFETTO_DFATAL("[HTTP] untracked client in OnDisconnect()");
155 }
156 
OnDataAvailable(base::UnixSocket * sock)157 void HttpServer::OnDataAvailable(base::UnixSocket* sock) {
158   Client* client = nullptr;
159   for (auto it = clients_.begin(); it != clients_.end() && !client; ++it)
160     client = (it->sock.get() == sock) ? &*it : nullptr;
161   PERFETTO_CHECK(client);
162 
163   char* rxbuf = reinterpret_cast<char*>(client->rxbuf.Get());
164   for (;;) {
165     size_t avail = client->rxbuf_avail();
166     PERFETTO_CHECK(avail <= kMaxRequestSize);
167     if (avail == 0)
168       return ShutdownBadRequest(sock, "Request body too big");
169     size_t rsize = sock->Receive(&rxbuf[client->rxbuf_used], avail);
170     client->rxbuf_used += rsize;
171     if (rsize == 0 || client->rxbuf_avail() == 0)
172       break;
173   }
174 
175   // At this point |rxbuf| can contain a partial HTTP request, a full one or
176   // more (in case of HTTP Keepalive pipelining).
177   for (;;) {
178     size_t bytes_consumed = ParseOneHttpRequest(client);
179     if (bytes_consumed == 0)
180       break;
181     memmove(rxbuf, &rxbuf[bytes_consumed], client->rxbuf_used - bytes_consumed);
182     client->rxbuf_used -= bytes_consumed;
183   }
184 }
185 
186 // Parses the HTTP request and invokes HandleRequest(). It returns the size of
187 // the HTTP header + body that has been processed or 0 if there isn't enough
188 // data for a full HTTP request in the buffer.
ParseOneHttpRequest(Client * client)189 size_t HttpServer::ParseOneHttpRequest(Client* client) {
190   auto* rxbuf = reinterpret_cast<char*>(client->rxbuf.Get());
191   base::StringView buf_view(rxbuf, client->rxbuf_used);
192   size_t pos = 0;
193   size_t body_offset = 0;
194   size_t body_size = 0;
195   bool has_parsed_first_line = false;
196   HttpRequest http_req;
197 
198   // This loop parses the HTTP request headers and sets the |body_offset|.
199   for (;;) {
200     size_t next = buf_view.find("\r\n", pos);
201     size_t col;
202     if (next == std::string::npos)
203       break;
204 
205     if (!has_parsed_first_line) {
206       // Parse the "GET /xxx HTTP/1.1" line.
207       has_parsed_first_line = true;
208       size_t space = buf_view.find(' ');
209       if (space == std::string::npos || space + 2 >= client->rxbuf_used) {
210         ShutdownBadRequest(client->sock.get(), "Malformed HTTP request");
211         return 0;
212       }
213       http_req.method = buf_view.substr(0, space);
214       size_t uri_size = buf_view.find(' ', space + 1) - space - 1;
215       http_req.uri = buf_view.substr(space + 1, uri_size);
216     } else if (next == pos) {
217       // The CR-LF marker that separates headers from body.
218       body_offset = next + 2;
219       break;
220     } else if ((col = buf_view.find(':', pos)) < next) {
221       // Parse HTTP headers. They look like: "Content-Length: 1234".
222       auto hdr_name = buf_view.substr(pos, col - pos);
223       auto hdr_value = buf_view.substr(col + 2, next - col - 2);
224       if (hdr_name.CaseInsensitiveEq("content-length")) {
225         body_size = static_cast<size_t>(atoi(hdr_value.ToStdString().c_str()));
226       } else if (hdr_name.CaseInsensitiveEq("origin")) {
227         http_req.origin = hdr_value;
228       } else if (hdr_name.CaseInsensitiveEq("x-seq-id")) {
229         http_req.id = atoi(hdr_value.ToStdString().c_str());
230       }
231     }
232     pos = next + 2;
233   }
234 
235   // If we have a full header but not yet the full body, return and try again
236   // next time we receive some more data.
237   size_t http_req_size = body_offset + body_size;
238   if (!body_offset || client->rxbuf_used < http_req_size)
239     return 0;
240 
241   http_req.body = base::StringView(&rxbuf[body_offset], body_size);
242   HandleRequest(client, http_req);
243   return http_req_size;
244 }
245 
HandleRequest(Client * client,const HttpRequest & req)246 void HttpServer::HandleRequest(Client* client, const HttpRequest& req) {
247   static int last_req_id = 0;
248   if (req.id) {
249     if (last_req_id && req.id != last_req_id + 1 && req.id != 1)
250       PERFETTO_ELOG("HTTP Request out of order");
251     last_req_id = req.id;
252   }
253 
254   PERFETTO_LOG("[HTTP] %04d %s %s (body: %zu bytes)", req.id,
255                req.method.ToStdString().c_str(), req.uri.ToStdString().c_str(),
256                req.body.size());
257   std::string allow_origin_hdr =
258       "Access-Control-Allow-Origin: " + req.origin.ToStdString();
259   std::initializer_list<const char*> headers = {
260       "Connection: Keep-Alive",                //
261       "Cache-Control: no-cache",               //
262       "Keep-Alive: timeout=5, max=1000",       //
263       "Content-Type: application/x-protobuf",  //
264       allow_origin_hdr.c_str()};
265 
266   if (req.method == "OPTIONS") {
267     // CORS headers.
268     return HttpReply(client->sock.get(), "204 No Content",
269                      {
270                          "Access-Control-Allow-Methods: POST, GET, OPTIONS",
271                          "Access-Control-Allow-Headers: *",
272                          "Access-Control-Max-Age: 86400",
273                          allow_origin_hdr.c_str(),
274                      });
275   }
276 
277   if (req.uri == "/parse") {
278     trace_processor_rpc_.Parse(
279         reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size());
280     return HttpReply(client->sock.get(), "200 OK", headers);
281   }
282 
283   if (req.uri == "/notify_eof") {
284     trace_processor_rpc_.NotifyEndOfFile();
285     return HttpReply(client->sock.get(), "200 OK", headers);
286   }
287 
288   if (req.uri == "/restore_initial_tables") {
289     trace_processor_rpc_.RestoreInitialTables();
290     return HttpReply(client->sock.get(), "200 OK", headers);
291   }
292 
293   if (req.uri == "/raw_query") {
294     PERFETTO_CHECK(req.body.size() > 0u);
295     std::vector<uint8_t> response = trace_processor_rpc_.RawQuery(
296         reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size());
297     return HttpReply(client->sock.get(), "200 OK", headers, response.data(),
298                      response.size());
299   }
300 
301   if (req.uri == "/status") {
302     protozero::HeapBuffered<protos::pbzero::StatusResult> res;
303     res->set_loaded_trace_name(
304         trace_processor_rpc_.GetCurrentTraceName().c_str());
305     std::vector<uint8_t> buf = res.SerializeAsArray();
306     return HttpReply(client->sock.get(), "200 OK", headers, buf.data(),
307                      buf.size());
308   }
309 
310   if (req.uri == "/compute_metric") {
311     std::vector<uint8_t> res = trace_processor_rpc_.ComputeMetric(
312         reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size());
313     return HttpReply(client->sock.get(), "200 OK", headers, res.data(),
314                      res.size());
315   }
316 
317   if (req.uri == "/enable_metatrace") {
318     trace_processor_rpc_.EnableMetatrace();
319     return HttpReply(client->sock.get(), "200 OK", headers);
320   }
321 
322   if (req.uri == "/disable_and_read_metatrace") {
323     std::vector<uint8_t> res = trace_processor_rpc_.DisableAndReadMetatrace();
324     return HttpReply(client->sock.get(), "200 OK", headers, res.data(),
325                      res.size());
326   }
327 
328   return HttpReply(client->sock.get(), "404 Not Found", headers);
329 }
330 
331 }  // namespace
332 
RunHttpRPCServer(std::unique_ptr<TraceProcessor> preloaded_instance)333 void RunHttpRPCServer(std::unique_ptr<TraceProcessor> preloaded_instance) {
334   HttpServer srv(std::move(preloaded_instance));
335   srv.Run();
336 }
337 
338 }  // namespace trace_processor
339 }  // namespace perfetto
340 
341 #endif  // PERFETTO_TP_HTTPD
342