Client.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
  3. * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
  5. * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  6. * Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
  7. *
  8. * SPDX-License-Identifier: BSD-2-Clause
  9. */
  10. #include <AK/ByteBuffer.h>
  11. #include <AK/Debug.h>
  12. #include <AK/Format.h>
  13. #include <AK/JsonObject.h>
  14. #include <AK/JsonParser.h>
  15. #include <AK/JsonValue.h>
  16. #include <AK/Span.h>
  17. #include <AK/StringBuilder.h>
  18. #include <AK/StringView.h>
  19. #include <LibCore/DateTime.h>
  20. #include <LibHTTP/HttpResponse.h>
  21. #include <LibWeb/WebDriver/Client.h>
  22. namespace Web::WebDriver {
  23. using RouteHandler = Response (*)(Client&, Parameters, JsonValue);
  24. struct Route {
  25. HTTP::HttpRequest::Method method {};
  26. StringView path;
  27. RouteHandler handler { nullptr };
  28. };
  29. struct MatchedRoute {
  30. RouteHandler handler;
  31. Vector<String> parameters;
  32. };
  33. #define ROUTE(method, path, handler) \
  34. Route \
  35. { \
  36. HTTP::HttpRequest::method, \
  37. path, \
  38. [](auto& client, auto parameters, auto payload) { \
  39. return client.handler(parameters, move(payload)); \
  40. } \
  41. }
  42. // https://w3c.github.io/webdriver/#dfn-endpoints
  43. static constexpr auto s_webdriver_endpoints = Array {
  44. ROUTE(POST, "/session"sv, new_session),
  45. ROUTE(DELETE, "/session/:session_id"sv, delete_session),
  46. ROUTE(GET, "/status"sv, get_status),
  47. ROUTE(GET, "/session/:session_id/timeouts"sv, get_timeouts),
  48. ROUTE(POST, "/session/:session_id/timeouts"sv, set_timeouts),
  49. ROUTE(POST, "/session/:session_id/url"sv, navigate_to),
  50. ROUTE(GET, "/session/:session_id/url"sv, get_current_url),
  51. ROUTE(POST, "/session/:session_id/back"sv, back),
  52. ROUTE(POST, "/session/:session_id/forward"sv, forward),
  53. ROUTE(POST, "/session/:session_id/refresh"sv, refresh),
  54. ROUTE(GET, "/session/:session_id/title"sv, get_title),
  55. ROUTE(GET, "/session/:session_id/window"sv, get_window_handle),
  56. ROUTE(DELETE, "/session/:session_id/window"sv, close_window),
  57. ROUTE(POST, "/session/:session_id/window"sv, switch_to_window),
  58. ROUTE(GET, "/session/:session_id/window/handles"sv, get_window_handles),
  59. ROUTE(POST, "/session/:session_id/window/new"sv, new_window),
  60. ROUTE(POST, "/session/:session_id/frame"sv, switch_to_frame),
  61. ROUTE(POST, "/session/:session_id/frame/parent"sv, switch_to_parent_frame),
  62. ROUTE(GET, "/session/:session_id/window/rect"sv, get_window_rect),
  63. ROUTE(POST, "/session/:session_id/window/rect"sv, set_window_rect),
  64. ROUTE(POST, "/session/:session_id/window/maximize"sv, maximize_window),
  65. ROUTE(POST, "/session/:session_id/window/minimize"sv, minimize_window),
  66. ROUTE(POST, "/session/:session_id/window/fullscreen"sv, fullscreen_window),
  67. ROUTE(POST, "/session/:session_id/window/consume-user-activation"sv, consume_user_activation),
  68. ROUTE(POST, "/session/:session_id/element"sv, find_element),
  69. ROUTE(POST, "/session/:session_id/elements"sv, find_elements),
  70. ROUTE(POST, "/session/:session_id/element/:element_id/element"sv, find_element_from_element),
  71. ROUTE(POST, "/session/:session_id/element/:element_id/elements"sv, find_elements_from_element),
  72. ROUTE(POST, "/session/:session_id/shadow/:shadow_id/element"sv, find_element_from_shadow_root),
  73. ROUTE(POST, "/session/:session_id/shadow/:shadow_id/elements"sv, find_elements_from_shadow_root),
  74. ROUTE(GET, "/session/:session_id/element/active"sv, get_active_element),
  75. ROUTE(GET, "/session/:session_id/element/:element_id/shadow"sv, get_element_shadow_root),
  76. ROUTE(GET, "/session/:session_id/element/:element_id/selected"sv, is_element_selected),
  77. ROUTE(GET, "/session/:session_id/element/:element_id/attribute/:name"sv, get_element_attribute),
  78. ROUTE(GET, "/session/:session_id/element/:element_id/property/:name"sv, get_element_property),
  79. ROUTE(GET, "/session/:session_id/element/:element_id/css/:name"sv, get_element_css_value),
  80. ROUTE(GET, "/session/:session_id/element/:element_id/text"sv, get_element_text),
  81. ROUTE(GET, "/session/:session_id/element/:element_id/name"sv, get_element_tag_name),
  82. ROUTE(GET, "/session/:session_id/element/:element_id/rect"sv, get_element_rect),
  83. ROUTE(GET, "/session/:session_id/element/:element_id/enabled"sv, is_element_enabled),
  84. ROUTE(GET, "/session/:session_id/element/:element_id/computedrole"sv, get_computed_role),
  85. ROUTE(GET, "/session/:session_id/element/:element_id/computedlabel"sv, get_computed_label),
  86. ROUTE(POST, "/session/:session_id/element/:element_id/click"sv, element_click),
  87. ROUTE(POST, "/session/:session_id/element/:element_id/clear"sv, element_clear),
  88. ROUTE(POST, "/session/:session_id/element/:element_id/value"sv, element_send_keys),
  89. ROUTE(GET, "/session/:session_id/source"sv, get_source),
  90. ROUTE(POST, "/session/:session_id/execute/sync"sv, execute_script),
  91. ROUTE(POST, "/session/:session_id/execute/async"sv, execute_async_script),
  92. ROUTE(GET, "/session/:session_id/cookie"sv, get_all_cookies),
  93. ROUTE(GET, "/session/:session_id/cookie/:name"sv, get_named_cookie),
  94. ROUTE(POST, "/session/:session_id/cookie"sv, add_cookie),
  95. ROUTE(DELETE, "/session/:session_id/cookie/:name"sv, delete_cookie),
  96. ROUTE(DELETE, "/session/:session_id/cookie"sv, delete_all_cookies),
  97. ROUTE(POST, "/session/:session_id/actions"sv, perform_actions),
  98. ROUTE(DELETE, "/session/:session_id/actions"sv, release_actions),
  99. ROUTE(POST, "/session/:session_id/alert/dismiss"sv, dismiss_alert),
  100. ROUTE(POST, "/session/:session_id/alert/accept"sv, accept_alert),
  101. ROUTE(GET, "/session/:session_id/alert/text"sv, get_alert_text),
  102. ROUTE(POST, "/session/:session_id/alert/text"sv, send_alert_text),
  103. ROUTE(GET, "/session/:session_id/screenshot"sv, take_screenshot),
  104. ROUTE(GET, "/session/:session_id/element/:element_id/screenshot"sv, take_element_screenshot),
  105. ROUTE(POST, "/session/:session_id/print"sv, print_page),
  106. };
  107. // https://w3c.github.io/webdriver/#dfn-match-a-request
  108. static ErrorOr<MatchedRoute, Error> match_route(HTTP::HttpRequest const& request)
  109. {
  110. dbgln_if(WEBDRIVER_ROUTE_DEBUG, "match_route({}, {})", HTTP::to_string_view(request.method()), request.resource());
  111. auto request_path = request.resource().view();
  112. Vector<String> parameters;
  113. auto next_segment = [](auto& path) -> Optional<StringView> {
  114. if (auto index = path.find('/'); index.has_value() && (*index + 1) < path.length()) {
  115. path = path.substring_view(*index + 1);
  116. if (index = path.find('/'); index.has_value())
  117. return path.substring_view(0, *index);
  118. return path;
  119. }
  120. path = {};
  121. return {};
  122. };
  123. for (auto const& route : s_webdriver_endpoints) {
  124. dbgln_if(WEBDRIVER_ROUTE_DEBUG, "- Checking {} {}", HTTP::to_string_view(route.method), route.path);
  125. if (route.method != request.method())
  126. continue;
  127. auto route_path = route.path;
  128. Optional<bool> match;
  129. auto on_failed_match = [&]() {
  130. request_path = request.resource();
  131. parameters.clear();
  132. match = false;
  133. };
  134. while (!match.has_value()) {
  135. auto request_segment = next_segment(request_path);
  136. auto route_segment = next_segment(route_path);
  137. if (!request_segment.has_value() && !route_segment.has_value())
  138. match = true;
  139. else if (request_segment.has_value() != route_segment.has_value())
  140. on_failed_match();
  141. else if (route_segment->starts_with(':'))
  142. TRY(parameters.try_append(TRY(String::from_utf8(*request_segment))));
  143. else if (request_segment != route_segment)
  144. on_failed_match();
  145. }
  146. if (*match) {
  147. dbgln_if(WEBDRIVER_ROUTE_DEBUG, "- Found match with parameters={}", parameters);
  148. return MatchedRoute { route.handler, move(parameters) };
  149. }
  150. }
  151. return Error::from_code(ErrorCode::UnknownCommand, "The command was not recognized.");
  152. }
  153. static JsonValue make_success_response(JsonValue value)
  154. {
  155. JsonObject result;
  156. result.set("value", move(value));
  157. return result;
  158. }
  159. Client::Client(NonnullOwnPtr<Core::BufferedTCPSocket> socket, Core::EventReceiver* parent)
  160. : Core::EventReceiver(parent)
  161. , m_socket(move(socket))
  162. {
  163. m_socket->on_ready_to_read = [this] {
  164. if (auto result = on_ready_to_read(); result.is_error())
  165. handle_error({}, result.release_error());
  166. };
  167. }
  168. Client::~Client()
  169. {
  170. m_socket->close();
  171. }
  172. void Client::die()
  173. {
  174. // We defer removing this connection to avoid closing its socket while we are inside the on_ready_to_read callback.
  175. deferred_invoke([this] { remove_from_parent(); });
  176. }
  177. ErrorOr<void, Client::WrappedError> Client::on_ready_to_read()
  178. {
  179. // FIXME: All this should be moved to LibHTTP and be made spec compliant.
  180. auto buffer = TRY(ByteBuffer::create_uninitialized(m_socket->buffer_size()));
  181. for (;;) {
  182. if (!TRY(m_socket->can_read_without_blocking()))
  183. break;
  184. auto data = TRY(m_socket->read_some(buffer));
  185. TRY(m_remaining_request.try_append(StringView { data }));
  186. if (m_socket->is_eof()) {
  187. die();
  188. break;
  189. }
  190. }
  191. if (m_remaining_request.is_empty())
  192. return {};
  193. auto parsed_request = HTTP::HttpRequest::from_raw_request(m_remaining_request.string_view().bytes());
  194. // If the request is not complete, we need to wait for more data to arrive.
  195. if (parsed_request.is_error() && parsed_request.error() == HTTP::HttpRequest::ParseError::RequestIncomplete)
  196. return {};
  197. m_remaining_request.clear();
  198. auto request = parsed_request.release_value();
  199. deferred_invoke([this, request = move(request)]() {
  200. auto body = read_body_as_json(request);
  201. if (body.is_error()) {
  202. handle_error(request, body.release_error());
  203. return;
  204. }
  205. if (auto result = handle_request(request, body.release_value()); result.is_error())
  206. handle_error(request, result.release_error());
  207. });
  208. return {};
  209. }
  210. ErrorOr<JsonValue, Client::WrappedError> Client::read_body_as_json(HTTP::HttpRequest const& request)
  211. {
  212. // FIXME: If we received a multipart body here, this would fail badly.
  213. // FIXME: Check the Content-Type is actually application/json.
  214. size_t content_length = 0;
  215. for (auto const& header : request.headers().headers()) {
  216. if (header.name.equals_ignoring_ascii_case("Content-Length"sv)) {
  217. content_length = header.value.to_number<size_t>(TrimWhitespace::Yes).value_or(0);
  218. break;
  219. }
  220. }
  221. if (content_length == 0)
  222. return JsonValue {};
  223. JsonParser json_parser(request.body());
  224. return TRY(json_parser.parse());
  225. }
  226. ErrorOr<void, Client::WrappedError> Client::handle_request(HTTP::HttpRequest const& request, JsonValue body)
  227. {
  228. if constexpr (WEBDRIVER_DEBUG) {
  229. dbgln("Got HTTP request: {} {}", request.method_name(), request.resource());
  230. dbgln("Body: {}", body);
  231. }
  232. auto [handler, parameters] = TRY(match_route(request));
  233. auto result = TRY((*handler)(*this, move(parameters), move(body)));
  234. return send_success_response(request, move(result));
  235. }
  236. void Client::handle_error(HTTP::HttpRequest const& request, WrappedError const& error)
  237. {
  238. error.visit(
  239. [](AK::Error const& error) {
  240. warnln("Internal error: {}", error);
  241. },
  242. [](HTTP::HttpRequest::ParseError const& error) {
  243. warnln("HTTP request parsing error: {}", HTTP::HttpRequest::parse_error_to_string(error));
  244. },
  245. [&](WebDriver::Error const& error) {
  246. if (send_error_response(request, error).is_error())
  247. warnln("Could not send error response");
  248. });
  249. die();
  250. }
  251. ErrorOr<void, Client::WrappedError> Client::send_success_response(HTTP::HttpRequest const& request, JsonValue result)
  252. {
  253. bool keep_alive = false;
  254. if (auto it = request.headers().headers().find_if([](auto& header) { return header.name.equals_ignoring_ascii_case("Connection"sv); }); !it.is_end())
  255. keep_alive = it->value.trim_whitespace().equals_ignoring_ascii_case("keep-alive"sv);
  256. result = make_success_response(move(result));
  257. auto content = result.serialized<StringBuilder>();
  258. StringBuilder builder;
  259. builder.append("HTTP/1.1 200 OK\r\n"sv);
  260. builder.append("Server: WebDriver (SerenityOS)\r\n"sv);
  261. builder.append("X-Frame-Options: SAMEORIGIN\r\n"sv);
  262. builder.append("X-Content-Type-Options: nosniff\r\n"sv);
  263. if (keep_alive)
  264. builder.append("Connection: keep-alive\r\n"sv);
  265. builder.append("Cache-Control: no-cache\r\n"sv);
  266. builder.append("Content-Type: application/json; charset=utf-8\r\n"sv);
  267. builder.appendff("Content-Length: {}\r\n", content.length());
  268. builder.append("\r\n"sv);
  269. builder.append(content);
  270. TRY(m_socket->write_until_depleted(builder.string_view()));
  271. if (!keep_alive)
  272. die();
  273. log_response(request, 200);
  274. return {};
  275. }
  276. ErrorOr<void, Client::WrappedError> Client::send_error_response(HTTP::HttpRequest const& request, Error const& error)
  277. {
  278. // FIXME: Implement to spec.
  279. dbgln_if(WEBDRIVER_DEBUG, "Sending error response: {} {}: {}", error.http_status, error.error, error.message);
  280. auto reason = HTTP::HttpResponse::reason_phrase_for_code(error.http_status);
  281. JsonObject error_response;
  282. error_response.set("error", error.error);
  283. error_response.set("message", error.message);
  284. error_response.set("stacktrace", "");
  285. if (error.data.has_value())
  286. error_response.set("data", *error.data);
  287. JsonObject result;
  288. result.set("value", move(error_response));
  289. auto content = result.serialized<StringBuilder>();
  290. StringBuilder builder;
  291. builder.appendff("HTTP/1.1 {} {}\r\n", error.http_status, reason);
  292. builder.append("Cache-Control: no-cache\r\n"sv);
  293. builder.append("Content-Type: application/json; charset=utf-8\r\n"sv);
  294. builder.appendff("Content-Length: {}\r\n", content.length());
  295. builder.append("\r\n"sv);
  296. builder.append(content);
  297. TRY(m_socket->write_until_depleted(builder.string_view()));
  298. log_response(request, error.http_status);
  299. return {};
  300. }
  301. void Client::log_response(HTTP::HttpRequest const& request, unsigned code)
  302. {
  303. outln("{} :: {:03d} :: {} {}", Core::DateTime::now().to_byte_string(), code, request.method_name(), request.resource());
  304. }
  305. }