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:
parent
cfd6e6cc36
commit
ab1c84cf53
15 changed files with 92 additions and 93 deletions
109
LibCore/CHttpJob.cpp
Normal file
109
LibCore/CHttpJob.cpp
Normal 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
33
LibCore/CHttpJob.h
Normal 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
44
LibCore/CHttpRequest.cpp
Normal 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
34
LibCore/CHttpRequest.h
Normal 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
12
LibCore/CHttpResponse.cpp
Normal 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
23
LibCore/CHttpResponse.h
Normal 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
27
LibCore/CNetworkJob.cpp
Normal 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
37
LibCore/CNetworkJob.h
Normal 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 };
|
||||
};
|
10
LibCore/CNetworkResponse.cpp
Normal file
10
LibCore/CNetworkResponse.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <LibCore/CNetworkResponse.h>
|
||||
|
||||
CNetworkResponse::CNetworkResponse(ByteBuffer&& payload)
|
||||
: m_payload(payload)
|
||||
{
|
||||
}
|
||||
|
||||
CNetworkResponse::~CNetworkResponse()
|
||||
{
|
||||
}
|
18
LibCore/CNetworkResponse.h
Normal file
18
LibCore/CNetworkResponse.h
Normal 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;
|
||||
};
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue