http_conn_handler.cc
1 // Copyright 2018, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 #include "util/http/http_conn_handler.h"
5 
6 #include <boost/beast/core.hpp> // for flat_buffer.
7 #include <boost/beast/http.hpp>
8 
9 #include "absl/strings/str_join.h"
10 #include "absl/strings/str_split.h"
11 #include "base/logging.h"
12 #include "strings/stringpiece.h"
13 #include "util/asio/yield.h"
14 #include "util/http/status_page.h"
15 
16 using namespace std;
17 
18 namespace util {
19 namespace http {
20 using namespace boost;
21 namespace h2 = beast::http;
22 
23 using fibers_ext::yield;
24 
25 namespace {
26 
27 inline system::error_code to_asio(system::error_code ec) {
28  if (ec == h2::error::end_of_stream)
29  return asio::error::eof;
30  return ec;
31 }
32 
33 void FilezHandler(const QueryArgs& args, HttpHandler::SendFunction* send) {
34  StringPiece file_name;
35  for (const auto& k_v : args) {
36  if (k_v.first == "file") {
37  file_name = k_v.second;
38  }
39  }
40  if (file_name.empty()) {
41  http::StringResponse resp = MakeStringResponse(h2::status::unauthorized);
42  return send->Invoke(std::move(resp));
43  }
44  string fname = strings::AsString(file_name);
45  FileResponse fresp;
46  auto ec = LoadFileResponse(fname, &fresp);
47  if (ec) {
48  StringResponse res = MakeStringResponse(h2::status::not_found);
49  SetMime(kTextMime, &res);
50  if (ec == boost::system::errc::no_such_file_or_directory)
51  res.body() = "The resource '" + fname + "' was not found.";
52  else
53  res.body() = "Error '" + ec.message() + "'.";
54  return send->Invoke(std::move(res));
55  }
56 
57  return send->Invoke(std::move(fresp));
58 }
59 
60 } // namespace
61 
62 HttpHandler::HttpHandler(const ListenerBase* lb, IoContext* cntx)
63  : ConnectionHandler(cntx), registry_(lb) {
64  favicon_ = "https://rawcdn.githack.com/romange/gaia/master/util/http/favicon-32x32.png";
65  resource_prefix_ = "https://cdn.jsdelivr.net/gh/romange/gaia/util/http";
66 }
67 
68 system::error_code HttpHandler::HandleRequest() {
69  beast::flat_buffer buffer;
70  RequestType request;
71 
72  system::error_code ec;
73 
74  h2::read(*socket_, buffer, request, ec);
75  if (ec) {
76  return to_asio(ec);
77  }
78  VLOG(1) << "Full Url: " << request.target();
79 
80  SendFunction send(*socket_);
81  HandleRequestInternal(request, &send);
82 
83  VLOG(1) << "HandleRequestEnd: " << send.ec;
84 
85  return to_asio(send.ec);
86 }
87 
88 void HttpHandler::HandleRequestInternal(const RequestType& request, SendFunction* send) {
89  StringPiece target = as_absl(request.target());
90  if (target == "/favicon.ico") {
91  h2::response<h2::string_body> resp = MakeStringResponse(h2::status::moved_permanently);
92  resp.set(h2::field::location, favicon_);
93  resp.set(h2::field::server, "GAIA");
94  resp.keep_alive(request.keep_alive());
95 
96  return send->Invoke(std::move(resp));
97  }
98 
99  StringPiece path, query;
100  tie(path, query) = ParseQuery(target);
101  auto args = SplitQuery(query);
102 
103  if (path == "/") {
104  return send->Invoke(BuildStatusPage(args, resource_prefix_));
105  }
106 
107  if (path == "/flagz") {
108  h2::response<h2::string_body> resp(h2::status::ok, request.version());
109  if (Authorize(args)) {
110  resp = ParseFlagz(args);
111  } else {
112  resp.result(h2::status::unauthorized);
113  }
114  return send->Invoke(std::move(resp));
115  }
116 
117  if (path == "/filez") {
118  FilezHandler(args, send);
119  return;
120  }
121 
122  if (path == "/profilez") {
123  send->Invoke(ProfilezHandler(args));
124  return;
125  }
126 
127  if (registry_) {
128  auto it = registry_->cb_map_.find(path);
129  if (it == registry_->cb_map_.end() || (it->second.is_protected && !Authorize(args))) {
130  h2::response<h2::string_body> resp(h2::status::unauthorized, request.version());
131  return send->Invoke(std::move(resp));
132  }
133  it->second.cb(args, send);
134  }
135 }
136 
137 bool ListenerBase::RegisterCb(StringPiece path, bool protect, RequestCb cb) {
138  CbInfo info{protect, cb};
139  auto res = cb_map_.emplace(path, info);
140  return res.second;
141 }
142 
143 } // namespace http
144 } // namespace util