Everywhere: Remove LibGemini

This hasn't been maintained (or worked at all) for a long time,
and it's not a widely supported protocol, so let's drop it.
This commit is contained in:
Andreas Kling 2024-06-04 08:00:13 +02:00
parent e4cd91761d
commit f2fd8fc928
Notes: sideshowbarker 2024-07-16 19:17:47 +09:00
38 changed files with 9 additions and 1041 deletions

View file

@ -20,7 +20,7 @@ This process hosts the main HTML/CSS engine (**LibWeb**.) It also runs JavaScrip
### Process: RequestServer ### Process: RequestServer
This process can use networking protocols (like HTTP, HTTPS, and Gemini) to request files from the outside world. Each **WebContent** process gets its own **RequestServer** to do uploading or downloading on its behalf. This process can use networking protocols (like HTTP or HTTPS) to request files from the outside world. Each **WebContent** process gets its own **RequestServer** to do uploading or downloading on its behalf.
For DNS lookups, **RequestServer** asks for help from the system's global **LookupServer** service, which handles all outgoing DNS requests. For DNS lookups, **RequestServer** asks for help from the system's global **LookupServer** service, which handles all outgoing DNS requests.

View file

@ -16,7 +16,6 @@
#include <LibIPC/SingleServer.h> #include <LibIPC/SingleServer.h>
#include <LibTLS/Certificate.h> #include <LibTLS/Certificate.h>
#include <RequestServer/ConnectionFromClient.h> #include <RequestServer/ConnectionFromClient.h>
#include <RequestServer/GeminiProtocol.h>
#include <RequestServer/HttpProtocol.h> #include <RequestServer/HttpProtocol.h>
#include <RequestServer/HttpsProtocol.h> #include <RequestServer/HttpsProtocol.h>
@ -37,7 +36,6 @@ ErrorOr<int> service_main(int ipc_socket)
Core::EventLoop event_loop; Core::EventLoop event_loop;
RequestServer::GeminiProtocol::install();
RequestServer::HttpProtocol::install(); RequestServer::HttpProtocol::install();
RequestServer::HttpsProtocol::install(); RequestServer::HttpsProtocol::install();

View file

@ -212,7 +212,7 @@ add_executable(headless-browser
target_include_directories(headless-browser PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(headless-browser PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(headless-browser PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) target_include_directories(headless-browser PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/)
target_link_libraries(headless-browser PRIVATE AK LibCore LibWeb LibWebView LibWebSocket LibCrypto LibFileSystem LibGemini LibHTTP LibImageDecoderClient LibJS LibGfx LibMain LibSQL LibTLS LibIPC LibDiff LibProtocol LibURL) target_link_libraries(headless-browser PRIVATE AK LibCore LibWeb LibWebView LibWebSocket LibCrypto LibFileSystem LibHTTP LibImageDecoderClient LibJS LibGfx LibMain LibSQL LibTLS LibIPC LibDiff LibProtocol LibURL)
if (ANDROID) if (ANDROID)
include(cmake/AndroidExtras.cmake) include(cmake/AndroidExtras.cmake)

View file

@ -58,7 +58,6 @@
<array> <array>
<string>http</string> <string>http</string>
<string>https</string> <string>https</string>
<string>gemini</string>
</array> </array>
</dict> </dict>
<dict> <dict>

View file

@ -8,8 +8,6 @@ set(REQUESTSERVER_SOURCES
${REQUESTSERVER_SOURCE_DIR}/ConnectionFromClient.cpp ${REQUESTSERVER_SOURCE_DIR}/ConnectionFromClient.cpp
${REQUESTSERVER_SOURCE_DIR}/ConnectionCache.cpp ${REQUESTSERVER_SOURCE_DIR}/ConnectionCache.cpp
${REQUESTSERVER_SOURCE_DIR}/Request.cpp ${REQUESTSERVER_SOURCE_DIR}/Request.cpp
${REQUESTSERVER_SOURCE_DIR}/GeminiRequest.cpp
${REQUESTSERVER_SOURCE_DIR}/GeminiProtocol.cpp
${REQUESTSERVER_SOURCE_DIR}/HttpRequest.cpp ${REQUESTSERVER_SOURCE_DIR}/HttpRequest.cpp
${REQUESTSERVER_SOURCE_DIR}/HttpProtocol.cpp ${REQUESTSERVER_SOURCE_DIR}/HttpProtocol.cpp
${REQUESTSERVER_SOURCE_DIR}/HttpsRequest.cpp ${REQUESTSERVER_SOURCE_DIR}/HttpsRequest.cpp
@ -33,7 +31,7 @@ target_link_libraries(RequestServer PRIVATE requestserver)
target_include_directories(requestserver PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/Services/) target_include_directories(requestserver PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/Services/)
target_include_directories(requestserver PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..) target_include_directories(requestserver PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(requestserver PUBLIC LibCore LibMain LibCrypto LibFileSystem LibGemini LibHTTP LibIPC LibMain LibTLS LibWebView LibWebSocket LibURL LibThreading) target_link_libraries(requestserver PUBLIC LibCore LibMain LibCrypto LibFileSystem LibHTTP LibIPC LibMain LibTLS LibWebView LibWebSocket LibURL LibThreading)
if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
# Solaris has socket and networking related functions in two extra libraries # Solaris has socket and networking related functions in two extra libraries
target_link_libraries(requestserver PUBLIC nsl socket) target_link_libraries(requestserver PUBLIC nsl socket)

View file

@ -16,7 +16,6 @@
#include <LibMain/Main.h> #include <LibMain/Main.h>
#include <LibTLS/Certificate.h> #include <LibTLS/Certificate.h>
#include <RequestServer/ConnectionFromClient.h> #include <RequestServer/ConnectionFromClient.h>
#include <RequestServer/GeminiProtocol.h>
#include <RequestServer/HttpProtocol.h> #include <RequestServer/HttpProtocol.h>
#include <RequestServer/HttpsProtocol.h> #include <RequestServer/HttpsProtocol.h>
@ -59,7 +58,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Core::Platform::register_with_mach_server(mach_server_name); Core::Platform::register_with_mach_server(mach_server_name);
#endif #endif
RequestServer::GeminiProtocol::install();
RequestServer::HttpProtocol::install(); RequestServer::HttpProtocol::install();
RequestServer::HttpsProtocol::install(); RequestServer::HttpsProtocol::install();

View file

@ -429,7 +429,6 @@ if (BUILD_LAGOM)
Compress Compress
Crypto Crypto
Diff Diff
Gemini
Gfx Gfx
HTTP HTTP
ImageDecoderClient ImageDecoderClient

View file

@ -1,18 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringView.h>
#include <LibGemini/Document.h>
#include <stddef.h>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
AK::set_debug_enabled(false);
auto gemini = StringView(static_cast<unsigned char const*>(data), size);
(void)Gemini::Document::parse(gemini, {});
return 0;
}

View file

@ -8,7 +8,6 @@ set(FUZZER_TARGETS
DeflateCompression DeflateCompression
DeflateDecompression DeflateDecompression
FlacLoader FlacLoader
Gemini
GIFLoader GIFLoader
GzipDecompression GzipDecompression
GzipRoundtrip GzipRoundtrip
@ -76,7 +75,6 @@ set(FUZZER_DEPENDENCIES_DeflateCompression LibCompress)
set(FUZZER_DEPENDENCIES_DeflateDecompression LibCompress) set(FUZZER_DEPENDENCIES_DeflateDecompression LibCompress)
set(FUZZER_DEPENDENCIES_ELF LibELF) set(FUZZER_DEPENDENCIES_ELF LibELF)
set(FUZZER_DEPENDENCIES_FlacLoader LibAudio) set(FUZZER_DEPENDENCIES_FlacLoader LibAudio)
set(FUZZER_DEPENDENCIES_Gemini LibGemini)
set(FUZZER_DEPENDENCIES_GIFLoader LibGfx) set(FUZZER_DEPENDENCIES_GIFLoader LibGfx)
set(FUZZER_DEPENDENCIES_GzipDecompression LibCompress) set(FUZZER_DEPENDENCIES_GzipDecompression LibCompress)
set(FUZZER_DEPENDENCIES_GzipRoundtrip LibCompress) set(FUZZER_DEPENDENCIES_GzipRoundtrip LibCompress)

View file

@ -181,7 +181,6 @@ executable("headless-browser") {
"//Userland/Libraries/LibCrypto", "//Userland/Libraries/LibCrypto",
"//Userland/Libraries/LibDiff", "//Userland/Libraries/LibDiff",
"//Userland/Libraries/LibFileSystem", "//Userland/Libraries/LibFileSystem",
"//Userland/Libraries/LibGemini",
"//Userland/Libraries/LibGfx", "//Userland/Libraries/LibGfx",
"//Userland/Libraries/LibHTTP", "//Userland/Libraries/LibHTTP",
"//Userland/Libraries/LibIPC", "//Userland/Libraries/LibIPC",
@ -384,7 +383,6 @@ if (current_os != "mac") {
"//Userland/Libraries/LibCrypto", "//Userland/Libraries/LibCrypto",
"//Userland/Libraries/LibDiff", "//Userland/Libraries/LibDiff",
"//Userland/Libraries/LibFileSystem", "//Userland/Libraries/LibFileSystem",
"//Userland/Libraries/LibGemini",
"//Userland/Libraries/LibGfx", "//Userland/Libraries/LibGfx",
"//Userland/Libraries/LibHTTP", "//Userland/Libraries/LibHTTP",
"//Userland/Libraries/LibIDL", "//Userland/Libraries/LibIDL",
@ -418,7 +416,6 @@ if (current_os != "mac") {
"$root_out_dir/lib/liblagom-crypto.dylib", "$root_out_dir/lib/liblagom-crypto.dylib",
"$root_out_dir/lib/liblagom-diff.dylib", "$root_out_dir/lib/liblagom-diff.dylib",
"$root_out_dir/lib/liblagom-filesystem.dylib", "$root_out_dir/lib/liblagom-filesystem.dylib",
"$root_out_dir/lib/liblagom-gemini.dylib",
"$root_out_dir/lib/liblagom-gfx.dylib", "$root_out_dir/lib/liblagom-gfx.dylib",
"$root_out_dir/lib/liblagom-http.dylib", "$root_out_dir/lib/liblagom-http.dylib",
"$root_out_dir/lib/liblagom-idl.dylib", "$root_out_dir/lib/liblagom-idl.dylib",

View file

@ -9,7 +9,6 @@ executable("RequestServer") {
"//Userland/Libraries/LibCore", "//Userland/Libraries/LibCore",
"//Userland/Libraries/LibCrypto", "//Userland/Libraries/LibCrypto",
"//Userland/Libraries/LibFileSystem", "//Userland/Libraries/LibFileSystem",
"//Userland/Libraries/LibGemini",
"//Userland/Libraries/LibHTTP", "//Userland/Libraries/LibHTTP",
"//Userland/Libraries/LibIPC", "//Userland/Libraries/LibIPC",
"//Userland/Libraries/LibMain", "//Userland/Libraries/LibMain",
@ -22,8 +21,6 @@ executable("RequestServer") {
sources = [ sources = [
"//Userland/Services/RequestServer/ConnectionCache.cpp", "//Userland/Services/RequestServer/ConnectionCache.cpp",
"//Userland/Services/RequestServer/ConnectionFromClient.cpp", "//Userland/Services/RequestServer/ConnectionFromClient.cpp",
"//Userland/Services/RequestServer/GeminiProtocol.cpp",
"//Userland/Services/RequestServer/GeminiRequest.cpp",
"//Userland/Services/RequestServer/HttpProtocol.cpp", "//Userland/Services/RequestServer/HttpProtocol.cpp",
"//Userland/Services/RequestServer/HttpRequest.cpp", "//Userland/Services/RequestServer/HttpRequest.cpp",
"//Userland/Services/RequestServer/HttpsProtocol.cpp", "//Userland/Services/RequestServer/HttpsProtocol.cpp",

View file

@ -1,17 +0,0 @@
shared_library("LibGemini") {
output_name = "gemini"
include_dirs = [ "//Userland/Libraries" ]
sources = [
"Document.cpp",
"GeminiRequest.cpp",
"GeminiResponse.cpp",
"Job.cpp",
"Line.cpp",
]
deps = [
"//AK",
"//Userland/Libraries/LibCore",
"//Userland/Libraries/LibTLS",
"//Userland/Libraries/LibURL",
]
}

View file

@ -364,7 +364,6 @@ shared_library("LibWeb") {
"//Userland/Libraries/LibAudio", "//Userland/Libraries/LibAudio",
"//Userland/Libraries/LibCore", "//Userland/Libraries/LibCore",
"//Userland/Libraries/LibCrypto", "//Userland/Libraries/LibCrypto",
"//Userland/Libraries/LibGemini",
"//Userland/Libraries/LibGfx", "//Userland/Libraries/LibGfx",
"//Userland/Libraries/LibHTTP", "//Userland/Libraries/LibHTTP",
"//Userland/Libraries/LibIDL", "//Userland/Libraries/LibIDL",

View file

@ -4,13 +4,11 @@ add_subdirectory(LibAudio)
add_subdirectory(LibCompress) add_subdirectory(LibCompress)
add_subdirectory(LibConfig) add_subdirectory(LibConfig)
add_subdirectory(LibCore) add_subdirectory(LibCore)
add_subdirectory(LibCrypt)
add_subdirectory(LibCrypto) add_subdirectory(LibCrypto)
add_subdirectory(LibDesktop) add_subdirectory(LibDesktop)
add_subdirectory(LibDiff) add_subdirectory(LibDiff)
add_subdirectory(LibFileSystem) add_subdirectory(LibFileSystem)
add_subdirectory(LibFileSystemAccessClient) add_subdirectory(LibFileSystemAccessClient)
add_subdirectory(LibGemini)
add_subdirectory(LibGfx) add_subdirectory(LibGfx)
add_subdirectory(LibHTTP) add_subdirectory(LibHTTP)
add_subdirectory(LibIDL) add_subdirectory(LibIDL)

View file

@ -1,10 +0,0 @@
set(SOURCES
Document.cpp
GeminiRequest.cpp
GeminiResponse.cpp
Job.cpp
Line.cpp
)
serenity_lib(LibGemini gemini)
target_link_libraries(LibGemini PRIVATE LibCore LibTLS LibURL)

View file

@ -1,94 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteString.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibGemini/Document.h>
namespace Gemini {
ByteString Document::render_to_html() const
{
StringBuilder html_builder;
html_builder.append("<!DOCTYPE html>\n<html>\n"sv);
html_builder.append("<head>\n<title>"sv);
html_builder.append(m_url.serialize_path());
html_builder.append("</title>\n</head>\n"sv);
html_builder.append("<body>\n"sv);
for (auto& line : m_lines) {
html_builder.append(line->render_to_html());
}
html_builder.append("</body>"sv);
html_builder.append("</html>"sv);
return html_builder.to_byte_string();
}
NonnullRefPtr<Document> Document::parse(StringView lines, const URL::URL& url)
{
auto document = adopt_ref(*new Document(url));
document->read_lines(lines);
return document;
}
void Document::read_lines(StringView source)
{
auto close_list_if_needed = [&] {
if (m_inside_unordered_list) {
m_inside_unordered_list = false;
m_lines.append(make<Control>(Control::UnorderedListEnd));
}
};
for (auto& line : source.lines()) {
if (line.starts_with("```"sv)) {
close_list_if_needed();
m_inside_preformatted_block = !m_inside_preformatted_block;
if (m_inside_preformatted_block) {
m_lines.append(make<Control>(Control::PreformattedStart));
} else {
m_lines.append(make<Control>(Control::PreformattedEnd));
}
continue;
}
if (m_inside_preformatted_block) {
m_lines.append(make<Preformatted>(move(line)));
continue;
}
if (line.starts_with('*')) {
if (!m_inside_unordered_list)
m_lines.append(make<Control>(Control::UnorderedListStart));
m_lines.append(make<UnorderedList>(move(line)));
m_inside_unordered_list = true;
continue;
}
close_list_if_needed();
if (line.starts_with("=>"sv)) {
m_lines.append(make<Link>(move(line), *this));
continue;
}
if (line.starts_with('#')) {
size_t level = 0;
while (line.length() > level && line[level] == '#')
++level;
m_lines.append(make<Heading>(move(line), level));
continue;
}
m_lines.append(make<Text>(move(line)));
}
}
}

View file

@ -1,128 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/Forward.h>
#include <AK/NonnullOwnPtr.h>
#include <LibURL/URL.h>
namespace Gemini {
class Line {
public:
Line(ByteString string)
: m_text(move(string))
{
}
virtual ~Line() = default;
virtual ByteString render_to_html() const = 0;
protected:
ByteString m_text;
};
class Document : public RefCounted<Document> {
public:
ByteString render_to_html() const;
static NonnullRefPtr<Document> parse(StringView source, const URL::URL&);
const URL::URL& url() const { return m_url; }
private:
explicit Document(const URL::URL& url)
: m_url(url)
{
}
void read_lines(StringView);
Vector<NonnullOwnPtr<Line>> m_lines;
URL::URL m_url;
bool m_inside_preformatted_block { false };
bool m_inside_unordered_list { false };
};
class Text : public Line {
public:
Text(ByteString line)
: Line(move(line))
{
}
virtual ~Text() override = default;
virtual ByteString render_to_html() const override;
};
class Link : public Line {
public:
Link(ByteString line, Document const&);
virtual ~Link() override = default;
virtual ByteString render_to_html() const override;
private:
URL::URL m_url;
ByteString m_name;
};
class Preformatted : public Line {
public:
Preformatted(ByteString line)
: Line(move(line))
{
}
virtual ~Preformatted() override = default;
virtual ByteString render_to_html() const override;
};
class UnorderedList : public Line {
public:
UnorderedList(ByteString line)
: Line(move(line))
{
}
virtual ~UnorderedList() override = default;
virtual ByteString render_to_html() const override;
};
class Control : public Line {
public:
enum Kind {
UnorderedListStart,
UnorderedListEnd,
PreformattedStart,
PreformattedEnd,
};
Control(Kind kind)
: Line("")
, m_kind(kind)
{
}
virtual ~Control() override = default;
virtual ByteString render_to_html() const override;
private:
Kind m_kind;
};
class Heading : public Line {
public:
Heading(ByteString line, int level)
: Line(move(line))
, m_level(level)
{
}
virtual ~Heading() override = default;
virtual ByteString render_to_html() const override;
private:
int m_level { 1 };
};
}

View file

@ -1,16 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace Gemini {
class Document;
class GeminiRequest;
class GeminiResponse;
class Job;
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringBuilder.h>
#include <LibGemini/GeminiRequest.h>
#include <LibURL/URL.h>
namespace Gemini {
ErrorOr<ByteBuffer> GeminiRequest::to_raw_request() const
{
StringBuilder builder;
TRY(builder.try_append(m_url.to_byte_string()));
TRY(builder.try_append("\r\n"sv));
return builder.to_byte_buffer();
}
Optional<GeminiRequest> GeminiRequest::from_raw_request(ByteBuffer const& raw_request)
{
URL::URL url = StringView(raw_request);
if (!url.is_valid())
return {};
GeminiRequest request;
request.m_url = url;
return request;
}
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibCore/Forward.h>
#include <LibURL/URL.h>
namespace Gemini {
class GeminiRequest {
public:
GeminiRequest() = default;
~GeminiRequest() = default;
const URL::URL& url() const { return m_url; }
void set_url(const URL::URL& url) { m_url = url; }
ErrorOr<ByteBuffer> to_raw_request() const;
static Optional<GeminiRequest> from_raw_request(ByteBuffer const&);
private:
URL::URL m_url;
};
}

View file

@ -1,17 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGemini/GeminiResponse.h>
namespace Gemini {
GeminiResponse::GeminiResponse(int status, ByteString meta)
: m_status(status)
, m_meta(meta)
{
}
}

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <LibCore/NetworkResponse.h>
namespace Gemini {
class GeminiResponse : public Core::NetworkResponse {
public:
virtual ~GeminiResponse() override = default;
static NonnullRefPtr<GeminiResponse> create(int status, ByteString meta)
{
return adopt_ref(*new GeminiResponse(status, meta));
}
int status() const { return m_status; }
ByteString meta() const { return m_meta; }
private:
GeminiResponse(int status, ByteString);
int m_status { 0 };
ByteString m_meta;
};
}

View file

@ -1,283 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/Error.h>
#include <AK/String.h>
#include <AK/Utf8View.h>
#include <LibGemini/GeminiResponse.h>
#include <LibGemini/Job.h>
#include <unistd.h>
namespace Gemini {
Job::Job(GeminiRequest const& request, Stream& output_stream)
: Core::NetworkJob(output_stream)
, m_request(request)
{
}
void Job::start(Core::BufferedSocketBase& socket)
{
VERIFY(!m_socket);
m_socket = &socket;
on_socket_connected();
}
void Job::shutdown(ShutdownMode mode)
{
if (!m_socket)
return;
if (mode == ShutdownMode::CloseSocket) {
m_socket->close();
} else {
m_socket->on_ready_to_read = nullptr;
m_socket = nullptr;
}
}
void Job::register_on_ready_to_read(Function<void()> callback)
{
m_socket->on_ready_to_read = [this, callback = move(callback)] {
callback();
while (can_read()) {
callback();
}
};
}
bool Job::can_read_line() const
{
return MUST(m_socket->can_read_line());
}
ErrorOr<String> Job::read_line(size_t size)
{
ByteBuffer buffer = TRY(ByteBuffer::create_uninitialized(size));
auto bytes_read = TRY(m_socket->read_until(buffer, "\r\n"sv));
return String::from_utf8(StringView { bytes_read.data(), bytes_read.size() });
}
ErrorOr<ByteBuffer> Job::receive(size_t size)
{
ByteBuffer buffer = TRY(ByteBuffer::create_uninitialized(size));
auto nread = TRY(m_socket->read_some(buffer)).size();
return buffer.slice(0, nread);
}
bool Job::can_read() const
{
return MUST(m_socket->can_read_without_blocking());
}
bool Job::write(ReadonlyBytes bytes)
{
return !m_socket->write_until_depleted(bytes).is_error();
}
void Job::flush_received_buffers()
{
for (size_t i = 0; i < m_received_buffers.size(); ++i) {
auto& payload = m_received_buffers[i];
auto result = do_write(payload);
if (result.is_error()) {
if (!result.error().is_errno()) {
dbgln("Job: Failed to flush received buffers: {}", result.error());
continue;
}
if (result.error().code() == EINTR) {
i--;
continue;
}
break;
}
auto written = result.release_value();
m_buffered_size -= written;
if (written == payload.size()) {
// FIXME: Make this a take-first-friendly object?
m_received_buffers.take_first();
continue;
}
VERIFY(written < payload.size());
// FIXME: Propagate errors.
payload = MUST(payload.slice(written, payload.size() - written));
return;
}
}
void Job::on_socket_connected()
{
auto raw_request = m_request.to_raw_request().release_value_but_fixme_should_propagate_errors();
if constexpr (JOB_DEBUG) {
dbgln("Job: raw_request:");
dbgln("{}", ByteString::copy(raw_request));
}
bool success = write(raw_request);
if (!success)
deferred_invoke([this] { did_fail(Core::NetworkJob::Error::TransmissionFailed); });
register_on_ready_to_read([this] {
if (is_cancelled())
return;
if (m_state == State::Failed)
return;
// https://gemini.circumlunar.space/docs/specification.gmi
if (m_state == State::InStatus) {
if (!can_read_line())
return;
auto line_or_error = read_line(PAGE_SIZE);
if (line_or_error.is_error()) {
dbgln("Job: Error getting status line {}", line_or_error.error());
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::TransmissionFailed); });
}
auto line = line_or_error.release_value();
auto view = line.bytes_as_string_view();
auto first_code_point = line.code_points().begin().peek();
if (!first_code_point.has_value()) {
dbgln("Job: empty status line");
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
if (first_code_point.release_value() == 0xFEFF) {
dbgln("Job: Byte order mark as first character of status line");
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
auto maybe_space_index = view.find(' ');
if (!maybe_space_index.has_value()) {
dbgln("Job: Expected 2-part status line, got '{}'", line);
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
auto space_index = maybe_space_index.release_value();
auto first_part = view.substring_view(0, space_index);
auto second_part = view.substring_view(space_index + 1);
auto status = first_part.to_number<unsigned>();
if (!status.has_value()) {
dbgln("Job: Expected numeric status code");
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
auto meta_first_code_point = Utf8View(second_part).begin().peek();
if (meta_first_code_point.release_value() == 0xFEFF) {
dbgln("Job: Byte order mark as first character of meta");
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
if (second_part.length() > 1024) {
dbgln("Job: Meta too long");
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
m_status = status.release_value();
m_meta = second_part;
if (m_status >= 10 && m_status < 20) {
m_state = State::Finished;
} else if (m_status >= 20 && m_status < 30) {
m_state = State::InBody;
} else if (m_status >= 30 && m_status < 40) {
m_state = State::Finished;
} else if (m_status >= 40 && m_status < 50) {
m_state = State::Finished;
} else if (m_status >= 50 && m_status < 60) {
m_state = State::Finished;
} else if (m_status >= 60 && m_status < 70) {
m_state = State::InBody;
} else {
dbgln("Job: Expected status between 10 and 69; instead got {}", m_status);
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); });
}
if (!can_read()) {
dbgln("Can't read further :(");
return;
}
}
VERIFY(m_state == State::InBody || m_state == State::Finished);
while (MUST(m_socket->can_read_without_blocking())) {
auto read_size = 64 * KiB;
auto payload_or_error = receive(read_size);
if (payload_or_error.is_error()) {
dbgln("Job: Error in receive {}", payload_or_error.error());
m_state = State::Failed;
return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::TransmissionFailed); });
}
auto payload = payload_or_error.release_value();
if (payload.is_empty()) {
if (m_socket->is_eof()) {
finish_up();
break;
}
}
m_received_size += payload.size();
m_buffered_size += payload.size();
m_received_buffers.append(move(payload));
flush_received_buffers();
deferred_invoke([this] { did_progress({}, m_received_size); });
if (m_socket->is_eof())
break;
}
if (!m_socket->is_open() || m_socket->is_eof()) {
dbgln_if(JOB_DEBUG, "Connection appears to have closed, finishing up");
finish_up();
}
});
}
void Job::finish_up()
{
m_state = State::Finished;
flush_received_buffers();
if (m_buffered_size != 0) {
// We have to wait for the client to consume all the downloaded data
// before we can actually call `did_finish`. in a normal flow, this should
// never be hit since the client is reading as we are writing, unless there
// are too many concurrent downloads going on.
deferred_invoke([this] {
finish_up();
});
return;
}
auto response = GeminiResponse::create(m_status, m_meta);
deferred_invoke([this, response] {
did_finish(move(response));
});
}
ErrorOr<size_t> Job::response_length() const
{
if (m_state != State::Finished)
return AK::Error::from_string_literal("Gemini response has not finished");
return m_received_size;
}
}

View file

@ -1,63 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibCore/NetworkJob.h>
#include <LibCore/Socket.h>
#include <LibGemini/GeminiRequest.h>
#include <LibGemini/GeminiResponse.h>
namespace Gemini {
class Job : public Core::NetworkJob {
C_OBJECT(Job);
public:
explicit Job(GeminiRequest const&, Stream&);
virtual ~Job() override = default;
virtual void start(Core::BufferedSocketBase&) override;
virtual void shutdown(ShutdownMode) override;
GeminiResponse* response() { return static_cast<GeminiResponse*>(Core::NetworkJob::response()); }
GeminiResponse const* response() const { return static_cast<GeminiResponse const*>(Core::NetworkJob::response()); }
const URL::URL& url() const { return m_request.url(); }
Core::Socket const* socket() const { return m_socket; }
ErrorOr<size_t> response_length() const;
protected:
void finish_up();
void on_socket_connected();
void flush_received_buffers();
void register_on_ready_to_read(Function<void()>);
bool can_read_line() const;
ErrorOr<String> read_line(size_t);
bool can_read() const;
ErrorOr<ByteBuffer> receive(size_t);
bool write(ReadonlyBytes);
enum class State {
InStatus,
InBody,
Finished,
Failed,
};
GeminiRequest m_request;
State m_state { State::InStatus };
int m_status { -1 };
ByteString m_meta;
Vector<ByteBuffer, 2> m_received_buffers;
size_t m_received_size { 0 };
size_t m_buffered_size { 0 };
Core::BufferedSocketBase* m_socket { nullptr };
};
}

View file

@ -1,96 +0,0 @@
/*
* Copyright (c) 2020-2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringBuilder.h>
#include <LibGemini/Document.h>
namespace Gemini {
ByteString Text::render_to_html() const
{
StringBuilder builder;
builder.append(escape_html_entities(m_text));
builder.append("<br>\n"sv);
return builder.to_byte_string();
}
ByteString Heading::render_to_html() const
{
return ByteString::formatted("<h{}>{}</h{}>", m_level, escape_html_entities(m_text.substring_view(m_level, m_text.length() - m_level)), m_level);
}
ByteString UnorderedList::render_to_html() const
{
// 1.3.5.4.2 "Advanced clients can take the space of the bullet symbol into account"
// FIXME: The spec is unclear about what the space means, or where it goes
// somehow figure this out
StringBuilder builder;
builder.append("<li>"sv);
builder.append(escape_html_entities(m_text.substring_view(1, m_text.length() - 1)));
builder.append("</li>"sv);
return builder.to_byte_string();
}
ByteString Control::render_to_html() const
{
switch (m_kind) {
case Kind::PreformattedEnd:
return "</pre>";
case Kind::PreformattedStart:
return "<pre>";
case Kind::UnorderedListStart:
return "<ul>";
case Kind::UnorderedListEnd:
return "</ul>";
default:
dbgln("Unknown control kind _{}_", (int)m_kind);
VERIFY_NOT_REACHED();
return "";
}
}
Link::Link(ByteString text, Document const& document)
: Line(move(text))
{
size_t index = 2;
while (index < m_text.length() && (m_text[index] == ' ' || m_text[index] == '\t'))
++index;
auto url_string = m_text.substring_view(index, m_text.length() - index);
auto space_offset = url_string.find_any_of(" \t"sv);
ByteString url = url_string;
if (space_offset.has_value()) {
url = url_string.substring_view(0, space_offset.value());
auto offset = space_offset.value();
while (offset < url_string.length() && (url_string[offset] == ' ' || url_string[offset] == '\t'))
++offset;
m_name = url_string.substring_view(offset, url_string.length() - offset);
}
m_url = document.url().complete_url(url);
if (m_name.is_empty())
m_name = m_url.to_byte_string();
}
ByteString Link::render_to_html() const
{
StringBuilder builder;
builder.append("<a href=\""sv);
builder.append(escape_html_entities(m_url.to_byte_string()));
builder.append("\">"sv);
builder.append(escape_html_entities(m_name));
builder.append("</a><br>\n"sv);
return builder.to_byte_string();
}
ByteString Preformatted::render_to_html() const
{
StringBuilder builder;
builder.append(escape_html_entities(m_text));
builder.append('\n');
return builder.to_byte_string();
}
}

View file

@ -172,8 +172,6 @@ Optional<u16> default_port_for_scheme(StringView scheme)
return 443; return 443;
// NOTE: not in spec, but we support these too // NOTE: not in spec, but we support these too
if (scheme == "gemini")
return 1965;
if (scheme == "irc") if (scheme == "irc")
return 6667; return 6667;
if (scheme == "ircs") if (scheme == "ircs")

View file

@ -750,7 +750,7 @@ set(GENERATED_SOURCES
serenity_lib(LibWeb web) serenity_lib(LibWeb web)
target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibHTTP LibGemini LibGfx LibIPC LibLocale LibRegex LibSyntax LibTextCodec LibUnicode LibAudio LibVideo LibWasm LibXML LibIDL LibURL LibTLS) target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibHTTP LibGfx LibIPC LibLocale LibRegex LibSyntax LibTextCodec LibUnicode LibAudio LibVideo LibWasm LibXML LibIDL LibURL LibTLS)
if (HAS_ACCELERATED_GRAPHICS) if (HAS_ACCELERATED_GRAPHICS)
target_link_libraries(LibWeb PRIVATE ${ACCEL_GFX_LIBS}) target_link_libraries(LibWeb PRIVATE ${ACCEL_GFX_LIBS})

View file

@ -8,7 +8,6 @@
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/LexicalPath.h> #include <AK/LexicalPath.h>
#include <LibGemini/Document.h>
#include <LibGfx/ImageFormats/ImageDecoder.h> #include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibTextCodec/Decoder.h> #include <LibTextCodec/Decoder.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>

View file

@ -411,7 +411,7 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback
return; return;
} }
if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "gemini") { if (url.scheme() == "http" || url.scheme() == "https") {
auto protocol_request = start_network_request(request); auto protocol_request = start_network_request(request);
if (!protocol_request) { if (!protocol_request) {
if (error_callback) if (error_callback)
@ -471,7 +471,7 @@ void ResourceLoader::load_unbuffered(LoadRequest& request, OnHeadersReceived on_
return; return;
} }
if (!url.scheme().is_one_of("http"sv, "https"sv, "gemini"sv)) { if (!url.scheme().is_one_of("http"sv, "https"sv)) {
// FIXME: Non-network requests from fetch should not go through this path. // FIXME: Non-network requests from fetch should not go through this path.
on_complete(false, "Cannot establish connection non-network scheme"sv); on_complete(false, "Cannot establish connection non-network scheme"sv);
return; return;

View file

@ -154,7 +154,7 @@ Optional<URLParts> break_url_into_parts(StringView url_string)
if (url.scheme() == "file"sv) if (url.scheme() == "file"sv)
return break_file_url_into_parts(url, url_string); return break_file_url_into_parts(url, url_string);
if (url.scheme().is_one_of("http"sv, "https"sv, "gemini"sv)) if (url.scheme().is_one_of("http"sv, "https"sv))
return break_web_url_into_parts(url, url_string); return break_web_url_into_parts(url, url_string);
return {}; return {};

View file

@ -10,8 +10,6 @@ set(SOURCES
ConnectionFromClient.cpp ConnectionFromClient.cpp
ConnectionCache.cpp ConnectionCache.cpp
Request.cpp Request.cpp
GeminiRequest.cpp
GeminiProtocol.cpp
HttpRequest.cpp HttpRequest.cpp
HttpProtocol.cpp HttpProtocol.cpp
HttpsRequest.cpp HttpsRequest.cpp
@ -26,4 +24,4 @@ set(GENERATED_SOURCES
) )
serenity_bin(RequestServer) serenity_bin(RequestServer)
target_link_libraries(RequestServer PRIVATE LibCore LibCrypto LibIPC LibGemini LibHTTP LibMain LibTLS LibWebSocket LibURL LibThreading) target_link_libraries(RequestServer PRIVATE LibCore LibCrypto LibIPC LibHTTP LibMain LibTLS LibWebSocket LibURL LibThreading)

View file

@ -10,7 +10,6 @@ namespace RequestServer {
class ConnectionFromClient; class ConnectionFromClient;
class Request; class Request;
class GeminiProtocol;
class HttpRequest; class HttpRequest;
class HttpProtocol; class HttpProtocol;
class HttpsRequest; class HttpsRequest;

View file

@ -1,46 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ConnectionCache.h"
#include <LibGemini/GeminiRequest.h>
#include <LibGemini/Job.h>
#include <RequestServer/GeminiProtocol.h>
#include <RequestServer/GeminiRequest.h>
namespace RequestServer {
GeminiProtocol::GeminiProtocol()
: Protocol("gemini")
{
}
OwnPtr<Request> GeminiProtocol::start_request(i32 request_id, ConnectionFromClient& client, ByteString const&, const URL::URL& url, HashMap<ByteString, ByteString> const&, ReadonlyBytes, Core::ProxyData proxy_data)
{
Gemini::GeminiRequest request;
request.set_url(url);
auto pipe_result = get_pipe_for_request();
if (pipe_result.is_error())
return {};
auto output_stream = MUST(Core::File::adopt_fd(pipe_result.value().write_fd, Core::File::OpenMode::Write));
auto job = Gemini::Job::construct(request, *output_stream);
auto protocol_request = GeminiRequest::create_with_job({}, client, *job, move(output_stream), request_id);
protocol_request->set_request_fd(pipe_result.value().read_fd);
Core::EventLoop::current().deferred_invoke([=] {
ConnectionCache::ensure_connection(ConnectionCache::g_tls_connection_cache, url, job, proxy_data);
});
return protocol_request;
}
void GeminiProtocol::install()
{
Protocol::install(adopt_own(*new GeminiProtocol()));
}
}

View file

@ -1,25 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <RequestServer/Protocol.h>
namespace RequestServer {
class GeminiProtocol final : public Protocol {
public:
virtual ~GeminiProtocol() override = default;
static void install();
private:
GeminiProtocol();
virtual OwnPtr<Request> start_request(i32, ConnectionFromClient&, ByteString const& method, const URL::URL&, HashMap<ByteString, ByteString> const&, ReadonlyBytes body, Core::ProxyData proxy_data = {}) override;
};
}

View file

@ -1,65 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ConnectionCache.h"
#include <LibCore/EventLoop.h>
#include <LibGemini/GeminiResponse.h>
#include <LibGemini/Job.h>
#include <RequestServer/GeminiRequest.h>
namespace RequestServer {
GeminiRequest::GeminiRequest(ConnectionFromClient& client, NonnullRefPtr<Gemini::Job> job, NonnullOwnPtr<Core::File>&& output_stream, i32 request_id)
: Request(client, move(output_stream), request_id)
, m_job(move(job))
{
m_job->on_finish = [this](bool success) {
Core::deferred_invoke([url = m_job->url(), socket = m_job->socket()] {
ConnectionCache::request_did_finish(url, socket);
});
if (auto* response = m_job->response()) {
set_downloaded_size(MUST(m_job->response_length()));
if (!response->meta().is_empty()) {
HashMap<ByteString, ByteString, CaseInsensitiveStringTraits> headers;
headers.set("meta", response->meta());
// Note: We're setting content-type to meta only on status==SUCCESS
// we should perhaps have a better mechanism for this, since we
// are already shoehorning the concept of "headers" here
if (response->status() >= 20 && response->status() < 30) {
headers.set("content-type", response->meta());
}
set_response_headers(headers);
}
}
// signal 100% request progress so any listeners can react
// appropriately
did_progress(downloaded_size(), downloaded_size());
did_finish(success);
};
m_job->on_progress = [this](Optional<u64> total, u64 current) {
did_progress(move(total), current);
};
}
void GeminiRequest::set_certificate(ByteString, ByteString)
{
}
GeminiRequest::~GeminiRequest()
{
m_job->on_finish = nullptr;
m_job->on_progress = nullptr;
m_job->cancel();
}
NonnullOwnPtr<GeminiRequest> GeminiRequest::create_with_job(Badge<GeminiProtocol>, ConnectionFromClient& client, NonnullRefPtr<Gemini::Job> job, NonnullOwnPtr<Core::File>&& output_stream, i32 request_id)
{
return adopt_own(*new GeminiRequest(client, move(job), move(output_stream), request_id));
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Badge.h>
#include <LibCore/Forward.h>
#include <LibGemini/Forward.h>
#include <RequestServer/Request.h>
namespace RequestServer {
class GeminiRequest final : public Request {
public:
virtual ~GeminiRequest() override;
static NonnullOwnPtr<GeminiRequest> create_with_job(Badge<GeminiProtocol>, ConnectionFromClient&, NonnullRefPtr<Gemini::Job>, NonnullOwnPtr<Core::File>&&, i32 request_id);
Gemini::Job const& job() const { return *m_job; }
virtual URL::URL url() const override { return m_job->url(); }
private:
explicit GeminiRequest(ConnectionFromClient&, NonnullRefPtr<Gemini::Job>, NonnullOwnPtr<Core::File>&&, i32 request_id);
virtual void set_certificate(ByteString certificate, ByteString key) override;
NonnullRefPtr<Gemini::Job> m_job;
};
}

View file

@ -12,7 +12,6 @@
#include <LibMain/Main.h> #include <LibMain/Main.h>
#include <LibTLS/Certificate.h> #include <LibTLS/Certificate.h>
#include <RequestServer/ConnectionFromClient.h> #include <RequestServer/ConnectionFromClient.h>
#include <RequestServer/GeminiProtocol.h>
#include <RequestServer/HttpProtocol.h> #include <RequestServer/HttpProtocol.h>
#include <RequestServer/HttpsProtocol.h> #include <RequestServer/HttpsProtocol.h>
#include <signal.h> #include <signal.h>
@ -46,7 +45,6 @@ ErrorOr<int> serenity_main(Main::Arguments)
TRY(Core::System::unveil("/home/anon", "rwc")); TRY(Core::System::unveil("/home/anon", "rwc"));
TRY(Core::System::unveil(nullptr, nullptr)); TRY(Core::System::unveil(nullptr, nullptr));
RequestServer::GeminiProtocol::install();
RequestServer::HttpProtocol::install(); RequestServer::HttpProtocol::install();
RequestServer::HttpsProtocol::install(); RequestServer::HttpsProtocol::install();

View file

@ -73,7 +73,7 @@ target_link_libraries(abench PRIVATE LibAudio LibFileSystem)
target_link_libraries(aconv PRIVATE LibAudio LibFileSystem) target_link_libraries(aconv PRIVATE LibAudio LibFileSystem)
target_link_libraries(animation PRIVATE LibGfx) target_link_libraries(animation PRIVATE LibGfx)
target_link_libraries(gzip PRIVATE LibCompress) target_link_libraries(gzip PRIVATE LibCompress)
target_link_libraries(headless-browser PRIVATE LibCrypto LibFileSystem LibGemini LibGfx LibHTTP LibImageDecoderClient LibTLS LibWeb LibWebView LibWebSocket LibIPC LibJS LibDiff LibURL) target_link_libraries(headless-browser PRIVATE LibCrypto LibFileSystem LibGfx LibHTTP LibImageDecoderClient LibTLS LibWeb LibWebView LibWebSocket LibIPC LibJS LibDiff LibURL)
target_link_libraries(icc PRIVATE LibGfx LibVideo LibURL) target_link_libraries(icc PRIVATE LibGfx LibVideo LibURL)
target_link_libraries(image PRIVATE LibGfx) target_link_libraries(image PRIVATE LibGfx)
target_link_libraries(isobmff PRIVATE LibGfx) target_link_libraries(isobmff PRIVATE LibGfx)