mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-23 17:52:26 -05:00
Start working on a Downloader app and backing classes in LibGUI.
LibGUI is slowly becoming LibKitchensink but I'm okay with this for now.
This commit is contained in:
parent
c7365a00f8
commit
8f30657390
Notes:
sideshowbarker
2024-07-19 14:47:54 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/8f30657390d
21 changed files with 375 additions and 1 deletions
3
Applications/Downloader/.gitignore
vendored
Normal file
3
Applications/Downloader/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.o
|
||||
*.d
|
||||
Downloader
|
32
Applications/Downloader/Makefile
Normal file
32
Applications/Downloader/Makefile
Normal file
|
@ -0,0 +1,32 @@
|
|||
OBJS = \
|
||||
main.o
|
||||
|
||||
APP = Downloader
|
||||
|
||||
STANDARD_FLAGS = -std=c++17 -Wno-sized-deallocation
|
||||
WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings -Wimplicit-fallthrough
|
||||
FLAVOR_FLAGS = -fno-exceptions -fno-rtti
|
||||
OPTIMIZATION_FLAGS = -Os
|
||||
INCLUDE_FLAGS = -I../../Servers -I../.. -I. -I../../LibC
|
||||
|
||||
DEFINES = -DSERENITY -DSANITIZE_PTRS -DUSERLAND
|
||||
|
||||
CXXFLAGS = -MMD -MP $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(FLAVOR_FLAGS) $(STANDARD_FLAGS) $(INCLUDE_FLAGS) $(DEFINES)
|
||||
CXX = i686-pc-serenity-g++
|
||||
LD = i686-pc-serenity-g++
|
||||
AR = i686-pc-serenity-ar
|
||||
LDFLAGS = -L../../LibC -L../../LibGUI
|
||||
|
||||
all: $(APP)
|
||||
|
||||
$(APP): $(OBJS)
|
||||
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -lc
|
||||
|
||||
.cpp.o:
|
||||
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
||||
-include $(OBJS:%.o=%.d)
|
||||
|
||||
clean:
|
||||
@echo "CLEAN"; rm -f $(APPS) $(OBJS) *.d
|
||||
|
30
Applications/Downloader/main.cpp
Normal file
30
Applications/Downloader/main.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <LibGUI/GApplication.h>
|
||||
#include <LibGUI/GHttpRequest.h>
|
||||
#include <LibGUI/GHttpResponse.h>
|
||||
#include <LibGUI/GNetworkJob.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
GApplication app(argc, argv);
|
||||
|
||||
GHttpRequest request;
|
||||
request.set_hostname("www.google.com");
|
||||
request.set_path("/");
|
||||
|
||||
auto job = request.schedule();
|
||||
job->on_finish = [&job] (bool success) {
|
||||
if (!success) {
|
||||
dbgprintf("on_finish: request failed :(\n");
|
||||
return;
|
||||
}
|
||||
auto& response = static_cast<const GHttpResponse&>(*job->response());
|
||||
printf("on_receive: code=%d\n", response.code());
|
||||
printf("payload:\n");
|
||||
printf("%s", response.payload().pointer());
|
||||
printf("payload was %d bytes\n", response.payload().size());
|
||||
};
|
||||
|
||||
printf("Entering main loop...\n");
|
||||
return app.exec();
|
||||
}
|
|
@ -36,6 +36,8 @@ $make_cmd -C ../Applications/IRCClient clean && \
|
|||
$make_cmd -C ../Applications/IRCClient && \
|
||||
$make_cmd -C ../Applications/Taskbar clean && \
|
||||
$make_cmd -C ../Applications/Taskbar && \
|
||||
$make_cmd -C ../Applications/Downloader clean && \
|
||||
$make_cmd -C ../Applications/Downloader && \
|
||||
$make_cmd clean &&\
|
||||
$make_cmd && \
|
||||
sudo ./sync.sh
|
||||
|
|
|
@ -94,6 +94,8 @@ cp -v ../Servers/LookupServer/LookupServer mnt/bin/LookupServer
|
|||
cp -v ../Servers/WindowServer/WindowServer mnt/bin/WindowServer
|
||||
cp -v ../Applications/Taskbar/Taskbar mnt/bin/Taskbar
|
||||
ln -s Taskbar mnt/bin/tb
|
||||
cp -v ../Applications/Downloader/Downloader mnt/bin/Downloader
|
||||
ln -s Downloader mnt/bin/dl
|
||||
cp -v kernel.map mnt/
|
||||
sh sync-local.sh
|
||||
umount mnt || ( sleep 0.5 && sync && umount mnt )
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <AK/AKString.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <AK/Function.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
#include <LibGUI/GWindowType.h>
|
||||
|
||||
|
@ -28,6 +29,7 @@ public:
|
|||
KeyUp,
|
||||
Timer,
|
||||
DeferredDestroy,
|
||||
DeferredInvoke,
|
||||
WindowEntered,
|
||||
WindowLeft,
|
||||
WindowBecameInactive,
|
||||
|
@ -55,6 +57,19 @@ private:
|
|||
Type m_type { Invalid };
|
||||
};
|
||||
|
||||
class GDeferredInvocationEvent : public GEvent {
|
||||
friend class GEventLoop;
|
||||
public:
|
||||
GDeferredInvocationEvent(Function<void(GObject&)> invokee)
|
||||
: GEvent(GEvent::Type::DeferredInvoke)
|
||||
, m_invokee(move(invokee))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
Function<void(GObject&)> m_invokee;
|
||||
};
|
||||
|
||||
class GWMEvent : public GEvent {
|
||||
public:
|
||||
GWMEvent(Type type, int client_id, int window_id)
|
||||
|
|
|
@ -158,6 +158,9 @@ int GEventLoop::exec()
|
|||
default:
|
||||
dbgprintf("Event type %u with no receiver :(\n", event.type());
|
||||
}
|
||||
} else if (event.type() == GEvent::Type::DeferredInvoke) {
|
||||
printf("DeferredInvoke: receiver=%s{%p}\n", receiver->class_name(), receiver);
|
||||
static_cast<GDeferredInvocationEvent&>(event).m_invokee(*receiver);
|
||||
} else {
|
||||
receiver->event(event);
|
||||
}
|
||||
|
|
47
LibGUI/GHttpNetworkJob.cpp
Normal file
47
LibGUI/GHttpNetworkJob.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include <LibGUI/GHttpNetworkJob.h>
|
||||
#include <LibGUI/GHttpResponse.h>
|
||||
#include <LibGUI/GTCPSocket.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GHttpNetworkJob::GHttpNetworkJob(const GHttpRequest& request)
|
||||
: m_request(request)
|
||||
{
|
||||
}
|
||||
|
||||
GHttpNetworkJob::~GHttpNetworkJob()
|
||||
{
|
||||
}
|
||||
|
||||
void GHttpNetworkJob::start()
|
||||
{
|
||||
ASSERT(!m_socket);
|
||||
m_socket = new GTCPSocket(this);
|
||||
int success = m_socket->connect(m_request.hostname(), m_request.port());
|
||||
if (!success)
|
||||
return did_fail(GNetworkJob::Error::ConnectionFailed);
|
||||
|
||||
auto raw_request = m_request.to_raw_request();
|
||||
|
||||
printf("raw_request:\n%s\n", raw_request.pointer());
|
||||
|
||||
success = m_socket->send(raw_request);
|
||||
if (!success)
|
||||
return did_fail(GNetworkJob::Error::TransmissionFailed);
|
||||
|
||||
Vector<byte> buffer;
|
||||
while (m_socket->is_connected()) {
|
||||
auto payload = m_socket->receive(100000);
|
||||
if (!payload) {
|
||||
if (m_socket->eof())
|
||||
break;
|
||||
return did_fail(GNetworkJob::Error::TransmissionFailed);
|
||||
}
|
||||
buffer.append(payload.pointer(), payload.size());
|
||||
}
|
||||
|
||||
auto response = GHttpResponse::create(1, ByteBuffer::copy(buffer.data(), buffer.size()));
|
||||
deferred_invoke([this, response] (GObject&) {
|
||||
printf("in the deferred invoke lambda\n");
|
||||
did_finish(move(response));
|
||||
});
|
||||
}
|
20
LibGUI/GHttpNetworkJob.h
Normal file
20
LibGUI/GHttpNetworkJob.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GNetworkJob.h>
|
||||
#include <LibGUI/GHttpRequest.h>
|
||||
|
||||
class GTCPSocket;
|
||||
|
||||
class GHttpNetworkJob final : public GNetworkJob {
|
||||
public:
|
||||
explicit GHttpNetworkJob(const GHttpRequest&);
|
||||
virtual ~GHttpNetworkJob() override;
|
||||
|
||||
virtual void start() override;
|
||||
|
||||
virtual const char* class_name() const override { return "GHttpNetworkJob"; }
|
||||
|
||||
private:
|
||||
GHttpRequest m_request;
|
||||
GTCPSocket* m_socket { nullptr };
|
||||
};
|
45
LibGUI/GHttpRequest.cpp
Normal file
45
LibGUI/GHttpRequest.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include <LibGUI/GHttpRequest.h>
|
||||
#include <LibGUI/GHttpNetworkJob.h>
|
||||
#include <LibGUI/GEventLoop.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
|
||||
GHttpRequest::GHttpRequest()
|
||||
{
|
||||
}
|
||||
|
||||
GHttpRequest::~GHttpRequest()
|
||||
{
|
||||
}
|
||||
|
||||
GNetworkJob* GHttpRequest::schedule()
|
||||
{
|
||||
auto* job = new GHttpNetworkJob(*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();
|
||||
}
|
34
LibGUI/GHttpRequest.h
Normal file
34
LibGUI/GHttpRequest.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#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 };
|
||||
};
|
11
LibGUI/GHttpResponse.cpp
Normal file
11
LibGUI/GHttpResponse.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <LibGUI/GHttpResponse.h>
|
||||
|
||||
GHttpResponse::GHttpResponse(int code, ByteBuffer&& payload)
|
||||
: GNetworkResponse(move(payload))
|
||||
, m_code(code)
|
||||
{
|
||||
}
|
||||
|
||||
GHttpResponse::~GHttpResponse()
|
||||
{
|
||||
}
|
23
LibGUI/GHttpResponse.h
Normal file
23
LibGUI/GHttpResponse.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#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, ByteBuffer&& payload)
|
||||
{
|
||||
return adopt(*new GHttpResponse(code, move(payload)));
|
||||
}
|
||||
|
||||
int code() const { return m_code; }
|
||||
const HashMap<String, String>& headers() const { return m_headers; }
|
||||
|
||||
private:
|
||||
GHttpResponse(int code, ByteBuffer&&);
|
||||
|
||||
int m_code { 0 };
|
||||
HashMap<String, String> m_headers;
|
||||
};
|
|
@ -42,6 +42,10 @@ ByteBuffer GIODevice::read(int max_size)
|
|||
set_error(errno);
|
||||
return { };
|
||||
}
|
||||
if (nread == 0) {
|
||||
set_eof(true);
|
||||
return { };
|
||||
}
|
||||
buffer.trim(nread);
|
||||
return buffer;
|
||||
}
|
||||
|
|
27
LibGUI/GNetworkJob.cpp
Normal file
27
LibGUI/GNetworkJob.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#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{%} job did_fail! error=%u\n", class_name(), this, (unsigned)error);
|
||||
ASSERT(on_finish);
|
||||
on_finish(false);
|
||||
}
|
36
LibGUI/GNetworkJob.h
Normal file
36
LibGUI/GNetworkJob.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GObject.h>
|
||||
#include <AK/Function.h>
|
||||
|
||||
class GNetworkResponse;
|
||||
|
||||
class GNetworkJob : public GObject {
|
||||
public:
|
||||
enum class Error {
|
||||
None,
|
||||
ConnectionFailed,
|
||||
TransmissionFailed,
|
||||
};
|
||||
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 };
|
||||
};
|
10
LibGUI/GNetworkResponse.cpp
Normal file
10
LibGUI/GNetworkResponse.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <LibGUI/GNetworkResponse.h>
|
||||
|
||||
GNetworkResponse::GNetworkResponse(ByteBuffer&& payload)
|
||||
: m_payload(payload)
|
||||
{
|
||||
}
|
||||
|
||||
GNetworkResponse::~GNetworkResponse()
|
||||
{
|
||||
}
|
18
LibGUI/GNetworkResponse.h
Normal file
18
LibGUI/GNetworkResponse.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#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;
|
||||
};
|
|
@ -99,5 +99,9 @@ void GObject::dump_tree(int indent)
|
|||
for (auto* child : children()) {
|
||||
child->dump_tree(indent + 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GObject::deferred_invoke(Function<void(GObject&)> invokee)
|
||||
{
|
||||
GEventLoop::current().post_event(*this, make<GDeferredInvocationEvent>(move(invokee)));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <AK/Weakable.h>
|
||||
|
||||
|
@ -32,6 +33,8 @@ public:
|
|||
|
||||
void dump_tree(int indent = 0);
|
||||
|
||||
void deferred_invoke(Function<void(GObject&)>);
|
||||
|
||||
virtual bool is_widget() const { return false; }
|
||||
virtual bool is_window() const { return false; }
|
||||
|
||||
|
|
|
@ -58,6 +58,11 @@ LIBGUI_OBJS = \
|
|||
GFileSystemModel.o \
|
||||
GSplitter.o \
|
||||
GTimer.o \
|
||||
GNetworkJob.o \
|
||||
GNetworkResponse.o \
|
||||
GHttpRequest.o \
|
||||
GHttpResponse.o \
|
||||
GHttpNetworkJob.o \
|
||||
GWindow.o
|
||||
|
||||
OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS)
|
||||
|
|
Loading…
Add table
Reference in a new issue