mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
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:
parent
e4cd91761d
commit
f2fd8fc928
Notes:
sideshowbarker
2024-07-16 19:17:47 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/f2fd8fc928 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/37
38 changed files with 9 additions and 1041 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -429,7 +429,6 @@ if (BUILD_LAGOM)
|
||||||
Compress
|
Compress
|
||||||
Crypto
|
Crypto
|
||||||
Diff
|
Diff
|
||||||
Gemini
|
|
||||||
Gfx
|
Gfx
|
||||||
HTTP
|
HTTP
|
||||||
ImageDecoderClient
|
ImageDecoderClient
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
|
@ -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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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")
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue