mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 03:57:44 +00:00
LibCore: Move HTTP classes from LibGUI to LibCore.
This commit is contained in:
parent
cfd6e6cc36
commit
ab1c84cf53
15 changed files with 92 additions and 93 deletions
|
@ -1,109 +0,0 @@
|
|||
#include <LibGUI/GHttpJob.h>
|
||||
#include <LibGUI/GHttpResponse.h>
|
||||
#include <LibCore/CTCPSocket.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
GHttpJob::GHttpJob(const GHttpRequest& request)
|
||||
: m_request(request)
|
||||
{
|
||||
}
|
||||
|
||||
GHttpJob::~GHttpJob()
|
||||
{
|
||||
}
|
||||
|
||||
void GHttpJob::on_socket_connected()
|
||||
{
|
||||
auto raw_request = m_request.to_raw_request();
|
||||
#if 0
|
||||
printf("raw_request:\n%s\n", String::from_byte_buffer(raw_request).characters());
|
||||
#endif
|
||||
|
||||
bool success = m_socket->send(raw_request);
|
||||
if (!success)
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::TransmissionFailed); });
|
||||
|
||||
Vector<byte> buffer;
|
||||
while (m_socket->is_connected()) {
|
||||
if (m_state == State::InStatus) {
|
||||
while (!m_socket->can_read_line())
|
||||
usleep(1);
|
||||
ASSERT(m_socket->can_read_line());
|
||||
auto line = m_socket->read_line(PAGE_SIZE);
|
||||
if (line.is_null()) {
|
||||
printf("Expected HTTP status\n");
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::TransmissionFailed); });
|
||||
}
|
||||
auto parts = String::from_byte_buffer(line, Chomp).split(' ');
|
||||
if (parts.size() < 3) {
|
||||
printf("Expected 3-part HTTP status, got '%s'\n", line.pointer());
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::ProtocolFailed); });
|
||||
}
|
||||
bool ok;
|
||||
m_code = parts[1].to_uint(ok);
|
||||
if (!ok) {
|
||||
printf("Expected numeric HTTP status\n");
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::ProtocolFailed); });
|
||||
}
|
||||
m_state = State::InHeaders;
|
||||
continue;
|
||||
}
|
||||
if (m_state == State::InHeaders) {
|
||||
while (!m_socket->can_read_line())
|
||||
usleep(1);
|
||||
auto line = m_socket->read_line(PAGE_SIZE);
|
||||
if (line.is_null()) {
|
||||
printf("Expected HTTP header\n");
|
||||
return did_fail(GNetworkJob::Error::ProtocolFailed);
|
||||
}
|
||||
auto chomped_line = String::from_byte_buffer(line, Chomp);
|
||||
if (chomped_line.is_empty()) {
|
||||
m_state = State::InBody;
|
||||
continue;
|
||||
}
|
||||
auto parts = chomped_line.split(':');
|
||||
if (parts.is_empty()) {
|
||||
printf("Expected HTTP header with key/value\n");
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::ProtocolFailed); });
|
||||
}
|
||||
auto name = parts[0];
|
||||
if (chomped_line.length() < name.length() + 2) {
|
||||
printf("Malformed HTTP header: '%s' (%d)\n", chomped_line.characters(), chomped_line.length());
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::ProtocolFailed); });
|
||||
}
|
||||
auto value = chomped_line.substring(name.length() + 2, chomped_line.length() - name.length() - 2);
|
||||
m_headers.set(name, value);
|
||||
printf("[%s] = '%s'\n", name.characters(), value.characters());
|
||||
continue;
|
||||
}
|
||||
ASSERT(m_state == State::InBody);
|
||||
auto payload = m_socket->receive(PAGE_SIZE);
|
||||
if (!payload) {
|
||||
if (m_socket->eof()) {
|
||||
m_state = State::Finished;
|
||||
break;
|
||||
}
|
||||
return deferred_invoke([this](auto&){ did_fail(GNetworkJob::Error::ProtocolFailed); });
|
||||
}
|
||||
buffer.append(payload.pointer(), payload.size());
|
||||
}
|
||||
|
||||
auto response = GHttpResponse::create(m_code, move(m_headers), ByteBuffer::copy(buffer.data(), buffer.size()));
|
||||
deferred_invoke([this, response] (auto&) {
|
||||
did_finish(move(response));
|
||||
});
|
||||
}
|
||||
|
||||
void GHttpJob::start()
|
||||
{
|
||||
ASSERT(!m_socket);
|
||||
m_socket = new CTCPSocket(this);
|
||||
m_socket->on_connected = [this] {
|
||||
printf("Socket on_connected callback\n");
|
||||
on_socket_connected();
|
||||
};
|
||||
bool success = m_socket->connect(m_request.hostname(), m_request.port());
|
||||
if (!success)
|
||||
return did_fail(GNetworkJob::Error::ConnectionFailed);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GNetworkJob.h>
|
||||
#include <LibGUI/GHttpRequest.h>
|
||||
#include <AK/HashMap.h>
|
||||
|
||||
class CTCPSocket;
|
||||
|
||||
class GHttpJob final : public GNetworkJob {
|
||||
public:
|
||||
explicit GHttpJob(const GHttpRequest&);
|
||||
virtual ~GHttpJob() override;
|
||||
|
||||
virtual void start() override;
|
||||
|
||||
virtual const char* class_name() const override { return "GHttpJob"; }
|
||||
|
||||
private:
|
||||
void on_socket_connected();
|
||||
|
||||
enum class State {
|
||||
InStatus,
|
||||
InHeaders,
|
||||
InBody,
|
||||
Finished,
|
||||
};
|
||||
|
||||
GHttpRequest m_request;
|
||||
CTCPSocket* m_socket { nullptr };
|
||||
State m_state { State::InStatus };
|
||||
int m_code { -1 };
|
||||
HashMap<String, String> m_headers;
|
||||
};
|
|
@ -1,45 +0,0 @@
|
|||
#include <LibGUI/GHttpRequest.h>
|
||||
#include <LibGUI/GHttpJob.h>
|
||||
#include <LibGUI/GEventLoop.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
|
||||
GHttpRequest::GHttpRequest()
|
||||
{
|
||||
}
|
||||
|
||||
GHttpRequest::~GHttpRequest()
|
||||
{
|
||||
}
|
||||
|
||||
GNetworkJob* GHttpRequest::schedule()
|
||||
{
|
||||
auto* job = new GHttpJob(*this);
|
||||
job->start();
|
||||
return job;
|
||||
}
|
||||
|
||||
String GHttpRequest::method_name() const
|
||||
{
|
||||
switch (m_method) {
|
||||
case Method::GET:
|
||||
return "GET";
|
||||
case Method::HEAD:
|
||||
return "HEAD";
|
||||
case Method::POST:
|
||||
return "POST";
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuffer GHttpRequest::to_raw_request() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append(method_name());
|
||||
builder.append(' ');
|
||||
builder.append(m_path);
|
||||
builder.append(" HTTP/1.0\nHost: ");
|
||||
builder.append(m_hostname);
|
||||
builder.append("\n\n");
|
||||
return builder.to_byte_buffer();
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/AKString.h>
|
||||
|
||||
class GNetworkJob;
|
||||
|
||||
class GHttpRequest {
|
||||
public:
|
||||
enum Method { Invalid, HEAD, GET, POST };
|
||||
|
||||
GHttpRequest();
|
||||
~GHttpRequest();
|
||||
|
||||
String hostname() const { return m_hostname; }
|
||||
int port() const { return m_port; }
|
||||
String path() const { return m_path; }
|
||||
Method method() const { return m_method; }
|
||||
|
||||
void set_hostname(const String& hostname) { m_hostname = hostname; }
|
||||
void set_port(int port) { m_port = port; }
|
||||
void set_path(const String& path) { m_path = path; }
|
||||
void set_method(Method method) { m_method = method; }
|
||||
|
||||
String method_name() const;
|
||||
ByteBuffer to_raw_request() const;
|
||||
|
||||
GNetworkJob* schedule();
|
||||
|
||||
private:
|
||||
String m_hostname;
|
||||
String m_path;
|
||||
int m_port { 80 };
|
||||
Method m_method { GET };
|
||||
};
|
|
@ -1,12 +0,0 @@
|
|||
#include <LibGUI/GHttpResponse.h>
|
||||
|
||||
GHttpResponse::GHttpResponse(int code, HashMap<String, String>&& headers, ByteBuffer&& payload)
|
||||
: GNetworkResponse(move(payload))
|
||||
, m_code(code)
|
||||
, m_headers(move(headers))
|
||||
{
|
||||
}
|
||||
|
||||
GHttpResponse::~GHttpResponse()
|
||||
{
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GNetworkResponse.h>
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/HashMap.h>
|
||||
|
||||
class GHttpResponse : public GNetworkResponse {
|
||||
public:
|
||||
virtual ~GHttpResponse() override;
|
||||
static Retained<GHttpResponse> create(int code, HashMap<String, String>&& headers, ByteBuffer&& payload)
|
||||
{
|
||||
return adopt(*new GHttpResponse(code, move(headers), move(payload)));
|
||||
}
|
||||
|
||||
int code() const { return m_code; }
|
||||
const HashMap<String, String>& headers() const { return m_headers; }
|
||||
|
||||
private:
|
||||
GHttpResponse(int code, HashMap<String, String>&&, ByteBuffer&&);
|
||||
|
||||
int m_code { 0 };
|
||||
HashMap<String, String> m_headers;
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
#include <LibGUI/GNetworkJob.h>
|
||||
#include <LibGUI/GNetworkResponse.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GNetworkJob::GNetworkJob()
|
||||
{
|
||||
}
|
||||
|
||||
GNetworkJob::~GNetworkJob()
|
||||
{
|
||||
}
|
||||
|
||||
void GNetworkJob::did_finish(Retained<GNetworkResponse>&& response)
|
||||
{
|
||||
m_response = move(response);
|
||||
printf("%s{%p} job did_finish!\n", class_name(), this);
|
||||
ASSERT(on_finish);
|
||||
on_finish(true);
|
||||
}
|
||||
|
||||
void GNetworkJob::did_fail(Error error)
|
||||
{
|
||||
m_error = error;
|
||||
dbgprintf("%s{%p} job did_fail! error=%u\n", class_name(), this, (unsigned)error);
|
||||
ASSERT(on_finish);
|
||||
on_finish(false);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibCore/CObject.h>
|
||||
#include <AK/Function.h>
|
||||
|
||||
class GNetworkResponse;
|
||||
|
||||
class GNetworkJob : public CObject {
|
||||
public:
|
||||
enum class Error {
|
||||
None,
|
||||
ConnectionFailed,
|
||||
TransmissionFailed,
|
||||
ProtocolFailed,
|
||||
};
|
||||
virtual ~GNetworkJob() override;
|
||||
|
||||
Function<void(bool success)> on_finish;
|
||||
|
||||
bool has_error() const { return m_error != Error::None; }
|
||||
Error error() const { return m_error; }
|
||||
GNetworkResponse* response() { return m_response.ptr(); }
|
||||
const GNetworkResponse* response() const { return m_response.ptr(); }
|
||||
|
||||
virtual void start() = 0;
|
||||
|
||||
virtual const char* class_name() const override { return "GNetworkJob"; }
|
||||
|
||||
protected:
|
||||
GNetworkJob();
|
||||
void did_finish(Retained<GNetworkResponse>&&);
|
||||
void did_fail(Error);
|
||||
|
||||
private:
|
||||
RetainPtr<GNetworkResponse> m_response;
|
||||
Error m_error { Error::None };
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
#include <LibGUI/GNetworkResponse.h>
|
||||
|
||||
GNetworkResponse::GNetworkResponse(ByteBuffer&& payload)
|
||||
: m_payload(payload)
|
||||
{
|
||||
}
|
||||
|
||||
GNetworkResponse::~GNetworkResponse()
|
||||
{
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Retainable.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
class GNetworkResponse : public Retainable<GNetworkResponse> {
|
||||
public:
|
||||
virtual ~GNetworkResponse();
|
||||
|
||||
bool is_error() const { return m_error; }
|
||||
const ByteBuffer& payload() const { return m_payload; }
|
||||
|
||||
protected:
|
||||
explicit GNetworkResponse(ByteBuffer&&);
|
||||
|
||||
bool m_error { false };
|
||||
ByteBuffer m_payload;
|
||||
};
|
|
@ -50,11 +50,6 @@ LIBGUI_OBJS = \
|
|||
GFileSystemModel.o \
|
||||
GSplitter.o \
|
||||
GTimer.o \
|
||||
GNetworkJob.o \
|
||||
GNetworkResponse.o \
|
||||
GHttpRequest.o \
|
||||
GHttpResponse.o \
|
||||
GHttpJob.o \
|
||||
GSpinBox.o \
|
||||
GGroupBox.o \
|
||||
GWindow.o
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue