mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:17:35 +00:00
ProtocolServer+LibWeb: Support more detailed HTTP requests
This patch adds the ability for ProtocolServer clients to specify which HTTP method to use, and also to include an optional HTTP request body.
This commit is contained in:
parent
cfafd4d52d
commit
2946a684ef
21 changed files with 78 additions and 26 deletions
|
@ -56,7 +56,7 @@ DownloadWidget::DownloadWidget(const URL& url)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_elapsed_timer.start();
|
m_elapsed_timer.start();
|
||||||
m_download = Web::ResourceLoader::the().protocol_client().start_download(url.to_string());
|
m_download = Web::ResourceLoader::the().protocol_client().start_download("GET", url.to_string());
|
||||||
ASSERT(m_download);
|
ASSERT(m_download);
|
||||||
m_download->on_progress = [this](Optional<u32> total_size, u32 downloaded_size) {
|
m_download->on_progress = [this](Optional<u32> total_size, u32 downloaded_size) {
|
||||||
did_progress(total_size.value(), downloaded_size);
|
did_progress(total_size.value(), downloaded_size);
|
||||||
|
|
|
@ -79,6 +79,10 @@ ByteBuffer HttpRequest::to_raw_request() const
|
||||||
builder.append("\r\n");
|
builder.append("\r\n");
|
||||||
}
|
}
|
||||||
builder.append("Connection: close\r\n\r\n");
|
builder.append("Connection: close\r\n\r\n");
|
||||||
|
if (!m_body.is_empty()) {
|
||||||
|
builder.append((const char*)m_body.data(), m_body.size());
|
||||||
|
builder.append("\r\n");
|
||||||
|
}
|
||||||
return builder.to_byte_buffer();
|
return builder.to_byte_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/ByteBuffer.h>
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
|
@ -60,6 +61,9 @@ public:
|
||||||
Method method() const { return m_method; }
|
Method method() const { return m_method; }
|
||||||
void set_method(Method method) { m_method = method; }
|
void set_method(Method method) { m_method = method; }
|
||||||
|
|
||||||
|
const ByteBuffer& body() const { return m_body; }
|
||||||
|
void set_body(const ByteBuffer& body) { m_body = body; }
|
||||||
|
|
||||||
String method_name() const;
|
String method_name() const;
|
||||||
ByteBuffer to_raw_request() const;
|
ByteBuffer to_raw_request() const;
|
||||||
|
|
||||||
|
@ -74,6 +78,7 @@ private:
|
||||||
String m_resource;
|
String m_resource;
|
||||||
Method m_method { GET };
|
Method m_method { GET };
|
||||||
Vector<Header> m_headers;
|
Vector<Header> m_headers;
|
||||||
|
ByteBuffer m_body;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,13 +47,13 @@ bool Client::is_supported_protocol(const String& protocol)
|
||||||
return send_sync<Messages::ProtocolServer::IsSupportedProtocol>(protocol)->supported();
|
return send_sync<Messages::ProtocolServer::IsSupportedProtocol>(protocol)->supported();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Download> Client::start_download(const String& url, const HashMap<String, String>& request_headers)
|
RefPtr<Download> Client::start_download(const String& method, const String& url, const HashMap<String, String>& request_headers, const ByteBuffer& request_body)
|
||||||
{
|
{
|
||||||
IPC::Dictionary header_dictionary;
|
IPC::Dictionary header_dictionary;
|
||||||
for (auto& it : request_headers)
|
for (auto& it : request_headers)
|
||||||
header_dictionary.add(it.key, it.value);
|
header_dictionary.add(it.key, it.value);
|
||||||
|
|
||||||
i32 download_id = send_sync<Messages::ProtocolServer::StartDownload>(url, header_dictionary)->download_id();
|
i32 download_id = send_sync<Messages::ProtocolServer::StartDownload>(method, url, header_dictionary, String::copy(request_body))->download_id();
|
||||||
if (download_id < 0)
|
if (download_id < 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
auto download = Download::create_from_id({}, *this, download_id);
|
auto download = Download::create_from_id({}, *this, download_id);
|
||||||
|
|
|
@ -44,7 +44,7 @@ public:
|
||||||
virtual void handshake() override;
|
virtual void handshake() override;
|
||||||
|
|
||||||
bool is_supported_protocol(const String&);
|
bool is_supported_protocol(const String&);
|
||||||
RefPtr<Download> start_download(const String& url, const HashMap<String, String>& request_headers = {});
|
RefPtr<Download> start_download(const String& method, const String& url, const HashMap<String, String>& request_headers = {}, const ByteBuffer& request_body = {});
|
||||||
|
|
||||||
bool stop_download(Badge<Download>, Download&);
|
bool stop_download(Badge<Download>, Download&);
|
||||||
bool set_certificate(Badge<Download>, Download&, String, String);
|
bool set_certificate(Badge<Download>, Download&, String, String);
|
||||||
|
|
|
@ -139,17 +139,15 @@ RefPtr<DOM::Document> FrameLoader::create_document_from_mime_type(const ByteBuff
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FrameLoader::load(const URL& url, Type type)
|
bool FrameLoader::load(const LoadRequest& request, Type type)
|
||||||
{
|
{
|
||||||
dbg() << "FrameLoader::load: " << url;
|
if (!request.is_valid()) {
|
||||||
|
load_error_page(request.url(), "Invalid request");
|
||||||
if (!url.is_valid()) {
|
|
||||||
load_error_page(url, "Invalid URL");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadRequest request;
|
auto& url = request.url();
|
||||||
request.set_url(url);
|
|
||||||
set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
|
set_resource(ResourceLoader::the().load_resource(Resource::Type::Generic, request));
|
||||||
|
|
||||||
if (type == Type::Navigation)
|
if (type == Type::Navigation)
|
||||||
|
@ -180,6 +178,21 @@ bool FrameLoader::load(const URL& url, Type type)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FrameLoader::load(const URL& url, Type type)
|
||||||
|
{
|
||||||
|
dbg() << "FrameLoader::load: " << url;
|
||||||
|
|
||||||
|
if (!url.is_valid()) {
|
||||||
|
load_error_page(url, "Invalid URL");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadRequest request;
|
||||||
|
request.set_url(url);
|
||||||
|
|
||||||
|
return load(request, type);
|
||||||
|
}
|
||||||
|
|
||||||
void FrameLoader::load_error_page(const URL& failed_url, const String& error)
|
void FrameLoader::load_error_page(const URL& failed_url, const String& error)
|
||||||
{
|
{
|
||||||
auto error_page_url = "file:///res/html/error.html";
|
auto error_page_url = "file:///res/html/error.html";
|
||||||
|
|
|
@ -45,6 +45,7 @@ public:
|
||||||
~FrameLoader();
|
~FrameLoader();
|
||||||
|
|
||||||
bool load(const URL&, Type);
|
bool load(const URL&, Type);
|
||||||
|
bool load(const LoadRequest&, Type);
|
||||||
|
|
||||||
Frame& frame() { return m_frame; }
|
Frame& frame() { return m_frame; }
|
||||||
const Frame& frame() const { return m_frame; }
|
const Frame& frame() const { return m_frame; }
|
||||||
|
|
|
@ -96,7 +96,7 @@ RefPtr<Resource> ResourceLoader::load_resource(Resource::Type type, const LoadRe
|
||||||
s_resource_cache.set(request, resource);
|
s_resource_cache.set(request, resource);
|
||||||
|
|
||||||
load(
|
load(
|
||||||
request.url(),
|
request,
|
||||||
[=](auto& data, auto& headers) {
|
[=](auto& data, auto& headers) {
|
||||||
const_cast<Resource&>(*resource).did_load({}, data, headers);
|
const_cast<Resource&>(*resource).did_load({}, data, headers);
|
||||||
},
|
},
|
||||||
|
@ -107,8 +107,9 @@ RefPtr<Resource> ResourceLoader::load_resource(Resource::Type type, const LoadRe
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
|
void ResourceLoader::load(const LoadRequest& request, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
|
||||||
{
|
{
|
||||||
|
auto& url = request.url();
|
||||||
if (is_port_blocked(url.port())) {
|
if (is_port_blocked(url.port())) {
|
||||||
dbg() << "ResourceLoader::load: Error: blocked port " << url.port() << " for URL: " << url;
|
dbg() << "ResourceLoader::load: Error: blocked port " << url.port() << " for URL: " << url;
|
||||||
return;
|
return;
|
||||||
|
@ -157,7 +158,12 @@ void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&, const
|
||||||
if (url.protocol() == "http" || url.protocol() == "https" || url.protocol() == "gemini") {
|
if (url.protocol() == "http" || url.protocol() == "https" || url.protocol() == "gemini") {
|
||||||
HashMap<String, String> headers;
|
HashMap<String, String> headers;
|
||||||
headers.set("User-Agent", m_user_agent);
|
headers.set("User-Agent", m_user_agent);
|
||||||
auto download = protocol_client().start_download(url.to_string(), headers);
|
|
||||||
|
for (auto& it : request.headers()) {
|
||||||
|
headers.set(it.key, it.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto download = protocol_client().start_download(request.method(), url.to_string(), headers, request.body());
|
||||||
if (!download) {
|
if (!download) {
|
||||||
if (error_callback)
|
if (error_callback)
|
||||||
error_callback("Failed to initiate load");
|
error_callback("Failed to initiate load");
|
||||||
|
@ -192,6 +198,13 @@ void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&, const
|
||||||
error_callback(String::format("Protocol not implemented: %s", url.protocol().characters()));
|
error_callback(String::format("Protocol not implemented: %s", url.protocol().characters()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
|
||||||
|
{
|
||||||
|
LoadRequest request;
|
||||||
|
request.set_url(url);
|
||||||
|
load(request, move(success_callback), move(error_callback));
|
||||||
|
}
|
||||||
|
|
||||||
bool ResourceLoader::is_port_blocked(int port)
|
bool ResourceLoader::is_port_blocked(int port)
|
||||||
{
|
{
|
||||||
int ports[] { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42,
|
int ports[] { 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42,
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
|
|
||||||
RefPtr<Resource> load_resource(Resource::Type, const LoadRequest&);
|
RefPtr<Resource> load_resource(Resource::Type, const LoadRequest&);
|
||||||
|
|
||||||
|
void load(const LoadRequest&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
|
||||||
void load(const URL&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
|
void load(const URL&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
|
||||||
void load_sync(const URL&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
|
void load_sync(const URL&, Function<void(const ByteBuffer&, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback = nullptr);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,11 @@ void Page::load(const URL& url)
|
||||||
main_frame().loader().load(url, FrameLoader::Type::Navigation);
|
main_frame().loader().load(url, FrameLoader::Type::Navigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Page::load(const LoadRequest& request)
|
||||||
|
{
|
||||||
|
main_frame().loader().load(request, FrameLoader::Type::Navigation);
|
||||||
|
}
|
||||||
|
|
||||||
Gfx::Palette Page::palette() const
|
Gfx::Palette Page::palette() const
|
||||||
{
|
{
|
||||||
return m_client.palette();
|
return m_client.palette();
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <AK/Noncopyable.h>
|
#include <AK/Noncopyable.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <AK/RefPtr.h>
|
#include <AK/RefPtr.h>
|
||||||
|
#include <AK/URL.h>
|
||||||
#include <Kernel/API/KeyCode.h>
|
#include <Kernel/API/KeyCode.h>
|
||||||
#include <LibGUI/Window.h>
|
#include <LibGUI/Window.h>
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
|
@ -59,6 +60,7 @@ public:
|
||||||
void set_focused_frame(Badge<EventHandler>, Frame&);
|
void set_focused_frame(Badge<EventHandler>, Frame&);
|
||||||
|
|
||||||
void load(const URL&);
|
void load(const URL&);
|
||||||
|
void load(const LoadRequest&);
|
||||||
|
|
||||||
bool handle_mouseup(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
|
bool handle_mouseup(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
|
||||||
bool handle_mousedown(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
|
bool handle_mousedown(const Gfx::IntPoint&, unsigned button, unsigned modifiers);
|
||||||
|
|
|
@ -66,7 +66,7 @@ OwnPtr<Messages::ProtocolServer::StartDownloadResponse> ClientConnection::handle
|
||||||
auto* protocol = Protocol::find_by_name(url.protocol());
|
auto* protocol = Protocol::find_by_name(url.protocol());
|
||||||
if (!protocol)
|
if (!protocol)
|
||||||
return make<Messages::ProtocolServer::StartDownloadResponse>(-1);
|
return make<Messages::ProtocolServer::StartDownloadResponse>(-1);
|
||||||
auto download = protocol->start_download(*this, url, message.request_headers().entries());
|
auto download = protocol->start_download(*this, message.method(), url, message.request_headers().entries(), message.request_body().to_byte_buffer());
|
||||||
if (!download)
|
if (!download)
|
||||||
return make<Messages::ProtocolServer::StartDownloadResponse>(-1);
|
return make<Messages::ProtocolServer::StartDownloadResponse>(-1);
|
||||||
auto id = download->id();
|
auto id = download->id();
|
||||||
|
|
|
@ -40,7 +40,7 @@ GeminiProtocol::~GeminiProtocol()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnPtr<Download> GeminiProtocol::start_download(ClientConnection& client, const URL& url, const HashMap<String, String>&)
|
OwnPtr<Download> GeminiProtocol::start_download(ClientConnection& client, const String&, const URL& url, const HashMap<String, String>&, const ByteBuffer&)
|
||||||
{
|
{
|
||||||
Gemini::GeminiRequest request;
|
Gemini::GeminiRequest request;
|
||||||
request.set_url(url);
|
request.set_url(url);
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
GeminiProtocol();
|
GeminiProtocol();
|
||||||
virtual ~GeminiProtocol() override;
|
virtual ~GeminiProtocol() override;
|
||||||
|
|
||||||
virtual OwnPtr<Download> start_download(ClientConnection&, const URL&, const HashMap<String, String>&) override;
|
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>&, const ByteBuffer& request_body) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,12 +40,16 @@ HttpProtocol::~HttpProtocol()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnPtr<Download> HttpProtocol::start_download(ClientConnection& client, const URL& url, const HashMap<String, String>& headers)
|
OwnPtr<Download> HttpProtocol::start_download(ClientConnection& client, const String& method, const URL& url, const HashMap<String, String>& headers, const ByteBuffer& request_body)
|
||||||
{
|
{
|
||||||
HTTP::HttpRequest request;
|
HTTP::HttpRequest request;
|
||||||
request.set_method(HTTP::HttpRequest::Method::GET);
|
if (method.equals_ignoring_case("post"))
|
||||||
|
request.set_method(HTTP::HttpRequest::Method::POST);
|
||||||
|
else
|
||||||
|
request.set_method(HTTP::HttpRequest::Method::GET);
|
||||||
request.set_url(url);
|
request.set_url(url);
|
||||||
request.set_headers(headers);
|
request.set_headers(headers);
|
||||||
|
request.set_body(request_body);
|
||||||
auto job = request.schedule();
|
auto job = request.schedule();
|
||||||
if (!job)
|
if (!job)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
HttpProtocol();
|
HttpProtocol();
|
||||||
virtual ~HttpProtocol() override;
|
virtual ~HttpProtocol() override;
|
||||||
|
|
||||||
virtual OwnPtr<Download> start_download(ClientConnection&, const URL&, const HashMap<String, String>& headers) override;
|
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, const ByteBuffer& request_body) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,12 +40,16 @@ HttpsProtocol::~HttpsProtocol()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnPtr<Download> HttpsProtocol::start_download(ClientConnection& client, const URL& url, const HashMap<String, String>& headers)
|
OwnPtr<Download> HttpsProtocol::start_download(ClientConnection& client, const String& method, const URL& url, const HashMap<String, String>& headers, const ByteBuffer& request_body)
|
||||||
{
|
{
|
||||||
HTTP::HttpRequest request;
|
HTTP::HttpRequest request;
|
||||||
request.set_method(HTTP::HttpRequest::Method::GET);
|
if (method.equals_ignoring_case("post"))
|
||||||
|
request.set_method(HTTP::HttpRequest::Method::POST);
|
||||||
|
else
|
||||||
|
request.set_method(HTTP::HttpRequest::Method::GET);
|
||||||
request.set_url(url);
|
request.set_url(url);
|
||||||
request.set_headers(headers);
|
request.set_headers(headers);
|
||||||
|
request.set_body(request_body);
|
||||||
auto job = HTTP::HttpsJob::construct(request);
|
auto job = HTTP::HttpsJob::construct(request);
|
||||||
auto download = HttpsDownload::create_with_job({}, client, (HTTP::HttpsJob&)*job);
|
auto download = HttpsDownload::create_with_job({}, client, (HTTP::HttpsJob&)*job);
|
||||||
job->start();
|
job->start();
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
HttpsProtocol();
|
HttpsProtocol();
|
||||||
virtual ~HttpsProtocol() override;
|
virtual ~HttpsProtocol() override;
|
||||||
|
|
||||||
virtual OwnPtr<Download> start_download(ClientConnection&, const URL&, const HashMap<String, String>& headers) override;
|
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, const ByteBuffer& request_body) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
virtual ~Protocol();
|
virtual ~Protocol();
|
||||||
|
|
||||||
const String& name() const { return m_name; }
|
const String& name() const { return m_name; }
|
||||||
virtual OwnPtr<Download> start_download(ClientConnection&, const URL&, const HashMap<String, String>& headers) = 0;
|
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, const ByteBuffer& request_body) = 0;
|
||||||
|
|
||||||
static Protocol* find_by_name(const String&);
|
static Protocol* find_by_name(const String&);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ endpoint ProtocolServer = 9
|
||||||
IsSupportedProtocol(String protocol) => (bool supported)
|
IsSupportedProtocol(String protocol) => (bool supported)
|
||||||
|
|
||||||
// Download API
|
// Download API
|
||||||
StartDownload(URL url, IPC::Dictionary request_headers) => (i32 download_id)
|
StartDownload(String method, URL url, IPC::Dictionary request_headers, String request_body) => (i32 download_id)
|
||||||
StopDownload(i32 download_id) => (bool success)
|
StopDownload(i32 download_id) => (bool success)
|
||||||
SetCertificate(i32 download_id, String certificate, String key) => (bool success)
|
SetCertificate(i32 download_id, String certificate, String key) => (bool success)
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,7 @@ int main(int argc, char** argv)
|
||||||
Core::EventLoop loop;
|
Core::EventLoop loop;
|
||||||
auto protocol_client = Protocol::Client::construct();
|
auto protocol_client = Protocol::Client::construct();
|
||||||
|
|
||||||
auto download = protocol_client->start_download(url.to_string());
|
auto download = protocol_client->start_download("GET", url.to_string());
|
||||||
if (!download) {
|
if (!download) {
|
||||||
fprintf(stderr, "Failed to start download for '%s'\n", url_str);
|
fprintf(stderr, "Failed to start download for '%s'\n", url_str);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue