1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 23:37:36 +00:00

LibCore: Move HTTP classes from LibGUI to LibCore.

This commit is contained in:
Andreas Kling 2019-04-10 22:28:10 +02:00
parent cfd6e6cc36
commit ab1c84cf53
15 changed files with 92 additions and 93 deletions

109
LibCore/CHttpJob.cpp Normal file
View file

@ -0,0 +1,109 @@
#include <LibCore/CHttpJob.h>
#include <LibCore/CHttpResponse.h>
#include <LibCore/CTCPSocket.h>
#include <stdio.h>
#include <unistd.h>
CHttpJob::CHttpJob(const CHttpRequest& request)
: m_request(request)
{
}
CHttpJob::~CHttpJob()
{
}
void CHttpJob::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(CNetworkJob::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(CNetworkJob::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(CNetworkJob::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(CNetworkJob::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(CNetworkJob::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(CNetworkJob::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(CNetworkJob::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(CNetworkJob::Error::ProtocolFailed); });
}
buffer.append(payload.pointer(), payload.size());
}
auto response = CHttpResponse::create(m_code, move(m_headers), ByteBuffer::copy(buffer.data(), buffer.size()));
deferred_invoke([this, response] (auto&) {
did_finish(move(response));
});
}
void CHttpJob::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(CNetworkJob::Error::ConnectionFailed);
}

33
LibCore/CHttpJob.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include <LibCore/CNetworkJob.h>
#include <LibCore/CHttpRequest.h>
#include <AK/HashMap.h>
class CTCPSocket;
class CHttpJob final : public CNetworkJob {
public:
explicit CHttpJob(const CHttpRequest&);
virtual ~CHttpJob() override;
virtual void start() override;
virtual const char* class_name() const override { return "CHttpJob"; }
private:
void on_socket_connected();
enum class State {
InStatus,
InHeaders,
InBody,
Finished,
};
CHttpRequest m_request;
CTCPSocket* m_socket { nullptr };
State m_state { State::InStatus };
int m_code { -1 };
HashMap<String, String> m_headers;
};

44
LibCore/CHttpRequest.cpp Normal file
View file

@ -0,0 +1,44 @@
#include <LibCore/CHttpRequest.h>
#include <LibCore/CHttpJob.h>
#include <AK/StringBuilder.h>
CHttpRequest::CHttpRequest()
{
}
CHttpRequest::~CHttpRequest()
{
}
CNetworkJob* CHttpRequest::schedule()
{
auto* job = new CHttpJob(*this);
job->start();
return job;
}
String CHttpRequest::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 CHttpRequest::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();
}

34
LibCore/CHttpRequest.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include <AK/AKString.h>
class CNetworkJob;
class CHttpRequest {
public:
enum Method { Invalid, HEAD, GET, POST };
CHttpRequest();
~CHttpRequest();
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;
CNetworkJob* schedule();
private:
String m_hostname;
String m_path;
int m_port { 80 };
Method m_method { GET };
};

12
LibCore/CHttpResponse.cpp Normal file
View file

@ -0,0 +1,12 @@
#include <LibCore/CHttpResponse.h>
CHttpResponse::CHttpResponse(int code, HashMap<String, String>&& headers, ByteBuffer&& payload)
: CNetworkResponse(move(payload))
, m_code(code)
, m_headers(move(headers))
{
}
CHttpResponse::~CHttpResponse()
{
}

23
LibCore/CHttpResponse.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <LibCore/CNetworkResponse.h>
#include <AK/AKString.h>
#include <AK/HashMap.h>
class CHttpResponse : public CNetworkResponse {
public:
virtual ~CHttpResponse() override;
static Retained<CHttpResponse> create(int code, HashMap<String, String>&& headers, ByteBuffer&& payload)
{
return adopt(*new CHttpResponse(code, move(headers), move(payload)));
}
int code() const { return m_code; }
const HashMap<String, String>& headers() const { return m_headers; }
private:
CHttpResponse(int code, HashMap<String, String>&&, ByteBuffer&&);
int m_code { 0 };
HashMap<String, String> m_headers;
};

27
LibCore/CNetworkJob.cpp Normal file
View file

@ -0,0 +1,27 @@
#include <LibCore/CNetworkJob.h>
#include <LibCore/CNetworkResponse.h>
#include <stdio.h>
CNetworkJob::CNetworkJob()
{
}
CNetworkJob::~CNetworkJob()
{
}
void CNetworkJob::did_finish(Retained<CNetworkResponse>&& response)
{
m_response = move(response);
printf("%s{%p} job did_finish!\n", class_name(), this);
ASSERT(on_finish);
on_finish(true);
}
void CNetworkJob::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);
}

37
LibCore/CNetworkJob.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include <LibCore/CObject.h>
#include <AK/Function.h>
class CNetworkResponse;
class CNetworkJob : public CObject {
public:
enum class Error {
None,
ConnectionFailed,
TransmissionFailed,
ProtocolFailed,
};
virtual ~CNetworkJob() override;
Function<void(bool success)> on_finish;
bool has_error() const { return m_error != Error::None; }
Error error() const { return m_error; }
CNetworkResponse* response() { return m_response.ptr(); }
const CNetworkResponse* response() const { return m_response.ptr(); }
virtual void start() = 0;
virtual const char* class_name() const override { return "CNetworkJob"; }
protected:
CNetworkJob();
void did_finish(Retained<CNetworkResponse>&&);
void did_fail(Error);
private:
RetainPtr<CNetworkResponse> m_response;
Error m_error { Error::None };
};

View file

@ -0,0 +1,10 @@
#include <LibCore/CNetworkResponse.h>
CNetworkResponse::CNetworkResponse(ByteBuffer&& payload)
: m_payload(payload)
{
}
CNetworkResponse::~CNetworkResponse()
{
}

View file

@ -0,0 +1,18 @@
#pragma once
#include <AK/Retainable.h>
#include <AK/ByteBuffer.h>
class CNetworkResponse : public Retainable<CNetworkResponse> {
public:
virtual ~CNetworkResponse();
bool is_error() const { return m_error; }
const ByteBuffer& payload() const { return m_payload; }
protected:
explicit CNetworkResponse(ByteBuffer&&);
bool m_error { false };
ByteBuffer m_payload;
};

View file

@ -5,6 +5,11 @@ OBJS = \
CTCPSocket.o \
CElapsedTimer.o \
CNotifier.o \
CHttpRequest.o \
CHttpResponse.o \
CHttpJob.o \
CNetworkJob.o \
CNetworkResponse.o \
CObject.o \
CEventLoop.o \
CEvent.o