| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-03-03 11:35:10 -07:00
										 |  |  |  * Copyright (c) 2022, the SerenityOS developers. | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  | #include <AK/Base64.h>
 | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-21 01:55:25 +04:30
										 |  |  | #include <LibHTTP/HttpRequest.h>
 | 
					
						
							| 
									
										
										
										
											2022-02-02 19:21:55 +03:30
										 |  |  | #include <LibHTTP/Job.h>
 | 
					
						
							| 
									
										
										
										
											2024-03-18 16:22:27 +13:00
										 |  |  | #include <LibURL/Parser.h>
 | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-21 01:55:25 +04:30
										 |  |  | namespace HTTP { | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  | StringView to_string_view(HttpRequest::Method method) | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     switch (method) { | 
					
						
							|  |  |  |     case HttpRequest::Method::GET: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "GET"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::HEAD: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "HEAD"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::POST: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "POST"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::DELETE: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "DELETE"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::PATCH: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "PATCH"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::OPTIONS: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "OPTIONS"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::TRACE: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "TRACE"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::CONNECT: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "CONNECT"sv; | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  |     case HttpRequest::Method::PUT: | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |         return "PUT"sv; | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  |     default: | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  | StringView HttpRequest::method_name() const | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-02-08 22:07:52 -05:00
										 |  |  |     return to_string_view(m_method); | 
					
						
							| 
									
										
										
										
											2022-10-11 11:27:52 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  | ErrorOr<ByteBuffer> HttpRequest::to_raw_request() const | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  |     TRY(builder.try_append(method_name())); | 
					
						
							|  |  |  |     TRY(builder.try_append(' ')); | 
					
						
							| 
									
										
										
										
											2021-05-29 21:54:35 +02:00
										 |  |  |     // NOTE: The percent_encode is so that e.g. spaces are properly encoded.
 | 
					
						
							| 
									
										
										
										
											2023-04-14 20:12:03 +01:00
										 |  |  |     auto path = m_url.serialize_path(); | 
					
						
							| 
									
										
										
										
											2021-05-29 21:54:35 +02:00
										 |  |  |     VERIFY(!path.is_empty()); | 
					
						
							| 
									
										
										
										
											2023-04-14 20:12:03 +01:00
										 |  |  |     TRY(builder.try_append(URL::percent_encode(path, URL::PercentEncodeSet::EncodeURI))); | 
					
						
							| 
									
										
										
										
											2023-08-12 19:28:19 +12:00
										 |  |  |     if (m_url.query().has_value()) { | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  |         TRY(builder.try_append('?')); | 
					
						
							| 
									
										
										
										
											2023-08-12 19:28:19 +12:00
										 |  |  |         TRY(builder.try_append(*m_url.query())); | 
					
						
							| 
									
										
										
										
											2020-05-04 22:24:57 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  |     TRY(builder.try_append(" HTTP/1.1\r\nHost: "sv)); | 
					
						
							| 
									
										
										
										
											2023-07-27 21:40:41 +12:00
										 |  |  |     TRY(builder.try_append(TRY(m_url.serialized_host()))); | 
					
						
							| 
									
										
										
										
											2022-03-29 19:59:06 +01:00
										 |  |  |     if (m_url.port().has_value()) | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  |         TRY(builder.try_appendff(":{}", *m_url.port())); | 
					
						
							|  |  |  |     TRY(builder.try_append("\r\n"sv)); | 
					
						
							| 
									
										
										
										
											2023-10-29 23:38:54 +02:00
										 |  |  |     // Start headers.
 | 
					
						
							|  |  |  |     bool has_content_length = false; | 
					
						
							| 
									
										
										
										
											2020-05-21 12:27:42 +02:00
										 |  |  |     for (auto& header : m_headers) { | 
					
						
							| 
									
										
										
										
											2023-10-29 23:38:54 +02:00
										 |  |  |         if (header.name.equals_ignoring_ascii_case("Content-Length"sv)) | 
					
						
							|  |  |  |             has_content_length = true; | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  |         TRY(builder.try_append(header.name)); | 
					
						
							|  |  |  |         TRY(builder.try_append(": "sv)); | 
					
						
							|  |  |  |         TRY(builder.try_append(header.value)); | 
					
						
							|  |  |  |         TRY(builder.try_append("\r\n"sv)); | 
					
						
							| 
									
										
										
										
											2020-05-21 12:27:42 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-05 04:02:41 +00:00
										 |  |  |     if (!m_body.is_empty() || method() == Method::POST) { | 
					
						
							| 
									
										
										
										
											2023-10-29 23:38:54 +02:00
										 |  |  |         // Add Content-Length header if it's not already present.
 | 
					
						
							|  |  |  |         if (!has_content_length) { | 
					
						
							|  |  |  |             TRY(builder.try_appendff("Content-Length: {}\r\n", m_body.size())); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Finish headers.
 | 
					
						
							|  |  |  |         TRY(builder.try_append("\r\n"sv)); | 
					
						
							| 
									
										
										
										
											2023-03-09 14:51:20 +00:00
										 |  |  |         TRY(builder.try_append((char const*)m_body.data(), m_body.size())); | 
					
						
							| 
									
										
										
										
											2023-10-29 23:38:54 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         // Finish headers.
 | 
					
						
							|  |  |  |         TRY(builder.try_append("\r\n"sv)); | 
					
						
							| 
									
										
										
										
											2020-09-28 11:55:26 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-09 15:00:14 +00:00
										 |  |  |     return builder.to_byte_buffer(); | 
					
						
							| 
									
										
										
										
											2019-04-07 14:36:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 02:52:06 +03:00
										 |  |  | ErrorOr<HttpRequest, HttpRequest::ParseError> HttpRequest::from_raw_request(ReadonlyBytes raw_request) | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     enum class State { | 
					
						
							|  |  |  |         InMethod, | 
					
						
							|  |  |  |         InResource, | 
					
						
							|  |  |  |         InProtocol, | 
					
						
							|  |  |  |         InHeaderName, | 
					
						
							|  |  |  |         InHeaderValue, | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  |         InBody, | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     State state { State::InMethod }; | 
					
						
							| 
									
										
										
										
											2020-02-20 12:54:15 +01:00
										 |  |  |     size_t index = 0; | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto peek = [&](int offset = 0) -> u8 { | 
					
						
							|  |  |  |         if (index + offset >= raw_request.size()) | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         return raw_request[index + offset]; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto consume = [&]() -> u8 { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY(index < raw_request.size()); | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |         return raw_request[index++]; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector<u8, 256> buffer; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 03:06:13 +03:00
										 |  |  |     Optional<unsigned> content_length; | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |     ByteString method; | 
					
						
							|  |  |  |     ByteString resource; | 
					
						
							|  |  |  |     ByteString protocol; | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |     Vector<Header> headers; | 
					
						
							|  |  |  |     Header current_header; | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  |     ByteBuffer body; | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto commit_and_advance_to = [&](auto& output, State new_state) { | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |         output = ByteString::copy(buffer); | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |         buffer.clear(); | 
					
						
							|  |  |  |         state = new_state; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (index < raw_request.size()) { | 
					
						
							|  |  |  |         // FIXME: Figure out what the appropriate limitations should be.
 | 
					
						
							|  |  |  |         if (buffer.size() > 65536) | 
					
						
							| 
									
										
										
										
											2023-03-23 02:52:06 +03:00
										 |  |  |             return ParseError::RequestTooLarge; | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |         switch (state) { | 
					
						
							|  |  |  |         case State::InMethod: | 
					
						
							|  |  |  |             if (peek() == ' ') { | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 commit_and_advance_to(method, State::InResource); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             buffer.append(consume()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case State::InResource: | 
					
						
							|  |  |  |             if (peek() == ' ') { | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 commit_and_advance_to(resource, State::InProtocol); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             buffer.append(consume()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case State::InProtocol: | 
					
						
							|  |  |  |             if (peek(0) == '\r' && peek(1) == '\n') { | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 commit_and_advance_to(protocol, State::InHeaderName); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             buffer.append(consume()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case State::InHeaderName: | 
					
						
							|  |  |  |             if (peek(0) == ':' && peek(1) == ' ') { | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 commit_and_advance_to(current_header.name, State::InHeaderValue); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             buffer.append(consume()); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case State::InHeaderValue: | 
					
						
							|  |  |  |             if (peek(0) == '\r' && peek(1) == '\n') { | 
					
						
							|  |  |  |                 consume(); | 
					
						
							|  |  |  |                 consume(); | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 // Detect end of headers
 | 
					
						
							|  |  |  |                 auto next_state = State::InHeaderName; | 
					
						
							|  |  |  |                 if (peek(0) == '\r' && peek(1) == '\n') { | 
					
						
							|  |  |  |                     consume(); | 
					
						
							|  |  |  |                     consume(); | 
					
						
							|  |  |  |                     next_state = State::InBody; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 commit_and_advance_to(current_header.value, next_state); | 
					
						
							| 
									
										
										
										
											2023-03-23 03:06:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (current_header.name.equals_ignoring_ascii_case("Content-Length"sv)) | 
					
						
							| 
									
										
										
										
											2023-12-23 15:59:14 +13:00
										 |  |  |                     content_length = current_header.value.to_number<unsigned>(); | 
					
						
							| 
									
										
										
										
											2023-03-23 03:06:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |                 headers.append(move(current_header)); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             buffer.append(consume()); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  |         case State::InBody: | 
					
						
							|  |  |  |             buffer.append(consume()); | 
					
						
							| 
									
										
										
										
											2022-10-18 22:07:41 +01:00
										 |  |  |             if (index == raw_request.size()) { | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  |                 // End of data, so store the body
 | 
					
						
							|  |  |  |                 auto maybe_body = ByteBuffer::copy(buffer); | 
					
						
							| 
									
										
										
										
											2023-03-23 02:52:06 +03:00
										 |  |  |                 if (maybe_body.is_error()) { | 
					
						
							|  |  |  |                     VERIFY(maybe_body.error().code() == ENOMEM); | 
					
						
							|  |  |  |                     return ParseError::OutOfMemory; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  |                 body = maybe_body.release_value(); | 
					
						
							|  |  |  |                 buffer.clear(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 03:06:13 +03:00
										 |  |  |     if (state != State::InBody) | 
					
						
							|  |  |  |         return ParseError::RequestIncomplete; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (content_length.has_value() && content_length.value() != body.size()) | 
					
						
							|  |  |  |         return ParseError::RequestIncomplete; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |     HttpRequest request; | 
					
						
							|  |  |  |     if (method == "GET") | 
					
						
							|  |  |  |         request.m_method = Method::GET; | 
					
						
							|  |  |  |     else if (method == "HEAD") | 
					
						
							|  |  |  |         request.m_method = Method::HEAD; | 
					
						
							|  |  |  |     else if (method == "POST") | 
					
						
							|  |  |  |         request.m_method = Method::POST; | 
					
						
							| 
									
										
										
										
											2022-06-27 21:39:03 +01:00
										 |  |  |     else if (method == "DELETE") | 
					
						
							|  |  |  |         request.set_method(HTTP::HttpRequest::Method::DELETE); | 
					
						
							|  |  |  |     else if (method == "PATCH") | 
					
						
							|  |  |  |         request.set_method(HTTP::HttpRequest::Method::PATCH); | 
					
						
							|  |  |  |     else if (method == "OPTIONS") | 
					
						
							|  |  |  |         request.set_method(HTTP::HttpRequest::Method::OPTIONS); | 
					
						
							|  |  |  |     else if (method == "TRACE") | 
					
						
							|  |  |  |         request.set_method(HTTP::HttpRequest::Method::TRACE); | 
					
						
							|  |  |  |     else if (method == "CONNECT") | 
					
						
							|  |  |  |         request.set_method(HTTP::HttpRequest::Method::CONNECT); | 
					
						
							|  |  |  |     else if (method == "PUT") | 
					
						
							|  |  |  |         request.set_method(HTTP::HttpRequest::Method::PUT); | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2023-03-23 02:52:06 +03:00
										 |  |  |         return ParseError::UnsupportedMethod; | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     request.m_headers = move(headers); | 
					
						
							| 
									
										
										
										
											2022-10-22 15:38:21 +02:00
										 |  |  |     auto url_parts = resource.split_limit('?', 2, SplitBehavior::KeepEmpty); | 
					
						
							| 
									
										
										
										
											2022-07-29 04:43:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |     auto url_part_to_string = [](ByteString const& url_part) -> ErrorOr<String, ParseError> { | 
					
						
							|  |  |  |         auto query_string_or_error = String::from_byte_string(url_part); | 
					
						
							| 
									
										
										
										
											2023-10-05 18:06:10 +01:00
										 |  |  |         if (!query_string_or_error.is_error()) | 
					
						
							|  |  |  |             return query_string_or_error.release_value(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (query_string_or_error.error().code() == ENOMEM) | 
					
						
							|  |  |  |             return ParseError::OutOfMemory; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return ParseError::InvalidURL; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-29 04:43:40 +03:00
										 |  |  |     request.m_url.set_cannot_be_a_base_url(true); | 
					
						
							|  |  |  |     if (url_parts.size() == 2) { | 
					
						
							|  |  |  |         request.m_resource = url_parts[0]; | 
					
						
							|  |  |  |         request.m_url.set_paths({ url_parts[0] }); | 
					
						
							| 
									
										
										
										
											2023-10-05 18:06:10 +01:00
										 |  |  |         request.m_url.set_query(TRY(url_part_to_string(url_parts[1]))); | 
					
						
							| 
									
										
										
										
											2022-07-29 04:43:40 +03:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         request.m_resource = resource; | 
					
						
							|  |  |  |         request.m_url.set_paths({ resource }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-11 15:01:47 +01:00
										 |  |  |     request.set_body(move(body)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 11:27:36 +01:00
										 |  |  |     return request; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  | void HttpRequest::set_headers(HashMap<ByteString, ByteString> const& headers) | 
					
						
							| 
									
										
										
										
											2020-05-21 12:27:42 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     for (auto& it : headers) | 
					
						
							|  |  |  |         m_headers.append({ it.key, it.value }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-18 16:22:27 +13:00
										 |  |  | Optional<HttpRequest::Header> HttpRequest::get_http_basic_authentication_header(URL::URL const& url) | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!url.includes_credentials()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							| 
									
										
										
										
											2023-08-12 16:52:38 +12:00
										 |  |  |     builder.append(url.username().release_value_but_fixme_should_propagate_errors()); | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  |     builder.append(':'); | 
					
						
							| 
									
										
										
										
											2023-08-12 16:52:38 +12:00
										 |  |  |     builder.append(url.password().release_value_but_fixme_should_propagate_errors()); | 
					
						
							| 
									
										
										
										
											2022-12-19 00:23:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // FIXME: change to TRY() and make method fallible
 | 
					
						
							|  |  |  |     auto token = MUST(encode_base64(MUST(builder.to_string()).bytes())); | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  |     builder.clear(); | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |     builder.append("Basic "sv); | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  |     builder.append(token); | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |     return Header { "Authorization", builder.to_byte_string() }; | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  | Optional<HttpRequest::BasicAuthenticationCredentials> HttpRequest::parse_http_basic_authentication_header(ByteString const& value) | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |     if (!value.starts_with("Basic "sv, AK::CaseSensitivity::CaseInsensitive)) | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  |         return {}; | 
					
						
							|  |  |  |     auto token = value.substring_view(6); | 
					
						
							|  |  |  |     if (token.is_empty()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-10-23 15:43:59 +02:00
										 |  |  |     auto decoded_token_bb = decode_base64(token); | 
					
						
							| 
									
										
										
										
											2022-01-20 17:18:17 +00:00
										 |  |  |     if (decoded_token_bb.is_error()) | 
					
						
							| 
									
										
										
										
											2021-10-23 15:43:59 +02:00
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2023-12-16 17:49:34 +03:30
										 |  |  |     auto decoded_token = ByteString::copy(decoded_token_bb.value()); | 
					
						
							| 
									
										
										
										
											2021-06-06 17:01:35 +02:00
										 |  |  |     auto colon_index = decoded_token.find(':'); | 
					
						
							|  |  |  |     if (!colon_index.has_value()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     auto username = decoded_token.substring_view(0, colon_index.value()); | 
					
						
							|  |  |  |     auto password = decoded_token.substring_view(colon_index.value() + 1); | 
					
						
							|  |  |  |     return BasicAuthenticationCredentials { username, password }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  | } |