mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 03:32:45 +00:00 
			
		
		
		
	LibGemini: Implement basic Gemini protocol support
This commit is contained in:
		
							parent
							
								
									656d1e1318
								
							
						
					
					
						commit
						308a755c38
					
				
					 11 changed files with 704 additions and 0 deletions
				
			
		|  | @ -4,6 +4,7 @@ add_subdirectory(LibCore) | ||||||
| add_subdirectory(LibCrypto) | add_subdirectory(LibCrypto) | ||||||
| add_subdirectory(LibDebug) | add_subdirectory(LibDebug) | ||||||
| add_subdirectory(LibDesktop) | add_subdirectory(LibDesktop) | ||||||
|  | add_subdirectory(LibGemini) | ||||||
| add_subdirectory(LibGfx) | add_subdirectory(LibGfx) | ||||||
| add_subdirectory(LibGUI) | add_subdirectory(LibGUI) | ||||||
| add_subdirectory(LibHTTP) | add_subdirectory(LibHTTP) | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								Libraries/LibGemini/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Libraries/LibGemini/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | set(SOURCES | ||||||
|  |     GeminiJob.cpp | ||||||
|  |     GeminiRequest.cpp | ||||||
|  |     GeminiResponse.cpp | ||||||
|  |     Job.cpp | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | serenity_lib(LibGemini gemini) | ||||||
|  | target_link_libraries(LibGemini LibCore LibTLS) | ||||||
							
								
								
									
										34
									
								
								Libraries/LibGemini/Forward.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Libraries/LibGemini/Forward.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | class GeminiRequest; | ||||||
|  | class GeminiResponse; | ||||||
|  | class GeminiJob; | ||||||
|  | class Job; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										136
									
								
								Libraries/LibGemini/GeminiJob.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Libraries/LibGemini/GeminiJob.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <LibCore/EventLoop.h> | ||||||
|  | #include <LibGemini/GeminiJob.h> | ||||||
|  | #include <LibGemini/GeminiResponse.h> | ||||||
|  | #include <LibTLS/TLSv12.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | //#define GEMINIJOB_DEBUG
 | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | void GeminiJob::start() | ||||||
|  | { | ||||||
|  |     ASSERT(!m_socket); | ||||||
|  |     m_socket = TLS::TLSv12::construct(this); | ||||||
|  |     m_socket->on_tls_connected = [this] { | ||||||
|  | #ifdef GEMINIJOB_DEBUG | ||||||
|  |         dbg() << "GeminiJob: on_connected callback"; | ||||||
|  | #endif | ||||||
|  |         on_socket_connected(); | ||||||
|  |     }; | ||||||
|  |     m_socket->on_tls_error = [this](TLS::AlertDescription error) { | ||||||
|  |         if (error == TLS::AlertDescription::HandshakeFailure) { | ||||||
|  |             deferred_invoke([this](auto&) { | ||||||
|  |                 return did_fail(Core::NetworkJob::Error::ProtocolFailed); | ||||||
|  |             }); | ||||||
|  |         } else if (error == TLS::AlertDescription::DecryptError) { | ||||||
|  |             deferred_invoke([this](auto&) { | ||||||
|  |                 return did_fail(Core::NetworkJob::Error::ConnectionFailed); | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             deferred_invoke([this](auto&) { | ||||||
|  |                 return did_fail(Core::NetworkJob::Error::TransmissionFailed); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     m_socket->on_tls_finished = [this] { | ||||||
|  |         finish_up(); | ||||||
|  |     }; | ||||||
|  |     bool success = ((TLS::TLSv12&)*m_socket).connect(m_request.url().host(), m_request.url().port()); | ||||||
|  |     if (!success) { | ||||||
|  |         deferred_invoke([this](auto&) { | ||||||
|  |             return did_fail(Core::NetworkJob::Error::ConnectionFailed); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GeminiJob::shutdown() | ||||||
|  | { | ||||||
|  |     if (!m_socket) | ||||||
|  |         return; | ||||||
|  |     m_socket->on_tls_ready_to_read = nullptr; | ||||||
|  |     m_socket->on_tls_connected = nullptr; | ||||||
|  |     remove_child(*m_socket); | ||||||
|  |     m_socket = nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GeminiJob::read_while_data_available(Function<IterationDecision()> read) | ||||||
|  | { | ||||||
|  |     while (m_socket->can_read()) { | ||||||
|  |         if (read() == IterationDecision::Break) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GeminiJob::register_on_ready_to_read(Function<void()> callback) | ||||||
|  | { | ||||||
|  |     m_socket->on_tls_ready_to_read = [callback = move(callback)](auto&) { | ||||||
|  |         callback(); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GeminiJob::register_on_ready_to_write(Function<void()> callback) | ||||||
|  | { | ||||||
|  |     m_socket->on_tls_ready_to_write = [callback = move(callback)](auto&) { | ||||||
|  |         callback(); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GeminiJob::can_read_line() const | ||||||
|  | { | ||||||
|  |     return m_socket->can_read_line(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ByteBuffer GeminiJob::read_line(size_t size) | ||||||
|  | { | ||||||
|  |     return m_socket->read_line(size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ByteBuffer GeminiJob::receive(size_t size) | ||||||
|  | { | ||||||
|  |     return m_socket->read(size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GeminiJob::can_read() const | ||||||
|  | { | ||||||
|  |     return m_socket->can_read(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GeminiJob::eof() const | ||||||
|  | { | ||||||
|  |     return m_socket->eof(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GeminiJob::write(const ByteBuffer& data) | ||||||
|  | { | ||||||
|  |     return m_socket->write(data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								Libraries/LibGemini/GeminiJob.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								Libraries/LibGemini/GeminiJob.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibCore/NetworkJob.h> | ||||||
|  | #include <LibGemini/GeminiRequest.h> | ||||||
|  | #include <LibGemini/GeminiResponse.h> | ||||||
|  | #include <LibGemini/Job.h> | ||||||
|  | #include <LibTLS/TLSv12.h> | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | class GeminiJob final : public Job { | ||||||
|  |     C_OBJECT(GeminiJob) | ||||||
|  | public: | ||||||
|  |     explicit GeminiJob(const GeminiRequest& request) | ||||||
|  |         : Job(request) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual ~GeminiJob() override | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual void start() override; | ||||||
|  |     virtual void shutdown() override; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     virtual void register_on_ready_to_read(Function<void()>) override; | ||||||
|  |     virtual void register_on_ready_to_write(Function<void()>) override; | ||||||
|  |     virtual bool can_read_line() const override; | ||||||
|  |     virtual ByteBuffer read_line(size_t) override; | ||||||
|  |     virtual bool can_read() const override; | ||||||
|  |     virtual ByteBuffer receive(size_t) override; | ||||||
|  |     virtual bool eof() const override; | ||||||
|  |     virtual bool write(const ByteBuffer&) override; | ||||||
|  |     virtual bool is_established() const override { return m_socket->is_established(); } | ||||||
|  |     virtual bool should_fail_on_empty_payload() const override { return false; } | ||||||
|  |     virtual void read_while_data_available(Function<IterationDecision()>) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     RefPtr<TLS::TLSv12> m_socket; | ||||||
|  |     bool m_queued_finish { false }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								Libraries/LibGemini/GeminiRequest.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								Libraries/LibGemini/GeminiRequest.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <AK/StringBuilder.h> | ||||||
|  | #include <AK/URL.h> | ||||||
|  | #include <LibGemini/GeminiJob.h> | ||||||
|  | #include <LibGemini/GeminiRequest.h> | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | GeminiRequest::GeminiRequest() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GeminiRequest::~GeminiRequest() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RefPtr<Core::NetworkJob> GeminiRequest::schedule() | ||||||
|  | { | ||||||
|  |     auto job = GeminiJob::construct(*this); | ||||||
|  |     job->start(); | ||||||
|  |     return job; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ByteBuffer GeminiRequest::to_raw_request() const | ||||||
|  | { | ||||||
|  |     StringBuilder builder; | ||||||
|  |     builder.append(m_url.to_string()); | ||||||
|  |     builder.append("\r\n"); | ||||||
|  |     return builder.to_byte_buffer(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Optional<GeminiRequest> GeminiRequest::from_raw_request(const ByteBuffer& raw_request) | ||||||
|  | { | ||||||
|  |     URL url = StringView(raw_request); | ||||||
|  |     if (!url.is_valid()) | ||||||
|  |         return {}; | ||||||
|  |     GeminiRequest request; | ||||||
|  |     request.m_url = url; | ||||||
|  |     return request; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								Libraries/LibGemini/GeminiRequest.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Libraries/LibGemini/GeminiRequest.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/Optional.h> | ||||||
|  | #include <AK/String.h> | ||||||
|  | #include <AK/URL.h> | ||||||
|  | #include <LibCore/Forward.h> | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | class GeminiRequest { | ||||||
|  | public: | ||||||
|  |     GeminiRequest(); | ||||||
|  |     ~GeminiRequest(); | ||||||
|  | 
 | ||||||
|  |     const URL& url() const { return m_url; } | ||||||
|  |     void set_url(const URL& url) { m_url = url; } | ||||||
|  | 
 | ||||||
|  |     ByteBuffer to_raw_request() const; | ||||||
|  | 
 | ||||||
|  |     RefPtr<Core::NetworkJob> schedule(); | ||||||
|  | 
 | ||||||
|  |     static Optional<GeminiRequest> from_raw_request(const ByteBuffer&); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     URL m_url; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								Libraries/LibGemini/GeminiResponse.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Libraries/LibGemini/GeminiResponse.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <LibGemini/GeminiResponse.h> | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | GeminiResponse::GeminiResponse(int status, String meta, ByteBuffer&& payload) | ||||||
|  |     : Core::NetworkResponse(move(payload)) | ||||||
|  |     , m_status(status) | ||||||
|  |     , m_meta(meta) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GeminiResponse::~GeminiResponse() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								Libraries/LibGemini/GeminiResponse.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								Libraries/LibGemini/GeminiResponse.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/String.h> | ||||||
|  | #include <LibCore/NetworkResponse.h> | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | class GeminiResponse : public Core::NetworkResponse { | ||||||
|  | public: | ||||||
|  |     virtual ~GeminiResponse() override; | ||||||
|  |     static NonnullRefPtr<GeminiResponse> create(int status, String meta, ByteBuffer&& payload) | ||||||
|  |     { | ||||||
|  |         return adopt(*new GeminiResponse(status, meta, move(payload))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int status() const { return m_status; } | ||||||
|  |     String meta() const { return m_meta; } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     GeminiResponse(int status, String, ByteBuffer&&); | ||||||
|  | 
 | ||||||
|  |     int m_status { 0 }; | ||||||
|  |     String m_meta; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								Libraries/LibGemini/Job.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								Libraries/LibGemini/Job.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,160 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <LibGemini/GeminiResponse.h> | ||||||
|  | #include <LibGemini/Job.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | //#define JOB_DEBUG
 | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | Job::Job(const GeminiRequest& request) | ||||||
|  |     : m_request(request) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Job::~Job() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Job::on_socket_connected() | ||||||
|  | { | ||||||
|  |     register_on_ready_to_write([this] { | ||||||
|  |         if (m_sent_data) | ||||||
|  |             return; | ||||||
|  |         m_sent_data = true; | ||||||
|  |         auto raw_request = m_request.to_raw_request(); | ||||||
|  | #ifdef JOB_DEBUG | ||||||
|  |         dbg() << "Job: raw_request:"; | ||||||
|  |         dbg() << String::copy(raw_request).characters(); | ||||||
|  | #endif | ||||||
|  |         bool success = write(raw_request); | ||||||
|  |         if (!success) | ||||||
|  |             deferred_invoke([this](auto&) { did_fail(Core::NetworkJob::Error::TransmissionFailed); }); | ||||||
|  |     }); | ||||||
|  |     register_on_ready_to_read([this] { | ||||||
|  |         if (is_cancelled()) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         if (m_state == State::InStatus) { | ||||||
|  |             if (!can_read_line()) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             auto line = read_line(PAGE_SIZE); | ||||||
|  |             if (line.is_null()) { | ||||||
|  |                 fprintf(stderr, "Job: Expected status line\n"); | ||||||
|  |                 return deferred_invoke([this](auto&) { did_fail(Core::NetworkJob::Error::TransmissionFailed); }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             auto parts = String::copy(line, Chomp).split_limit(' ', 2); | ||||||
|  |             if (parts.size() != 2) { | ||||||
|  |                 fprintf(stderr, "Job: Expected 2-part status line, got '%s'\n", line.data()); | ||||||
|  |                 return deferred_invoke([this](auto&) { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bool ok; | ||||||
|  |             m_status = parts[0].to_uint(ok); | ||||||
|  |             if (!ok) { | ||||||
|  |                 fprintf(stderr, "Job: Expected numeric status code\n"); | ||||||
|  |                 return deferred_invoke([this](auto&) { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_meta = parts[1]; | ||||||
|  | 
 | ||||||
|  |             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 { | ||||||
|  |                 fprintf(stderr, "Job: Expected status between 10 and 69; instead got %d\n", m_status); | ||||||
|  |                 return deferred_invoke([this](auto&) { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ASSERT(m_state == State::InBody || m_state == State::Finished); | ||||||
|  | 
 | ||||||
|  |         read_while_data_available([&] { | ||||||
|  |             auto read_size = 64 * KB; | ||||||
|  | 
 | ||||||
|  |             auto payload = receive(read_size); | ||||||
|  |             if (!payload) { | ||||||
|  |                 if (eof()) { | ||||||
|  |                     finish_up(); | ||||||
|  |                     return IterationDecision::Break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (should_fail_on_empty_payload()) { | ||||||
|  |                     deferred_invoke([this](auto&) { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); | ||||||
|  |                     return IterationDecision::Break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_received_buffers.append(payload); | ||||||
|  |             m_received_size += payload.size(); | ||||||
|  | 
 | ||||||
|  |             did_progress({}, m_received_size); | ||||||
|  | 
 | ||||||
|  |             return IterationDecision::Continue; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (!is_established()) { | ||||||
|  | #ifdef JOB_DEBUG | ||||||
|  |             dbg() << "Connection appears to have closed, finishing up"; | ||||||
|  | #endif | ||||||
|  |             finish_up(); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Job::finish_up() | ||||||
|  | { | ||||||
|  |     m_state = State::Finished; | ||||||
|  |     auto flattened_buffer = ByteBuffer::create_uninitialized(m_received_size); | ||||||
|  |     u8* flat_ptr = flattened_buffer.data(); | ||||||
|  |     for (auto& received_buffer : m_received_buffers) { | ||||||
|  |         memcpy(flat_ptr, received_buffer.data(), received_buffer.size()); | ||||||
|  |         flat_ptr += received_buffer.size(); | ||||||
|  |     } | ||||||
|  |     m_received_buffers.clear(); | ||||||
|  | 
 | ||||||
|  |     auto response = GeminiResponse::create(m_status, m_meta, move(flattened_buffer)); | ||||||
|  |     deferred_invoke([this, response](auto&) { | ||||||
|  |         did_finish(move(response)); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								Libraries/LibGemini/Job.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								Libraries/LibGemini/Job.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, The SerenityOS developers. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/Optional.h> | ||||||
|  | #include <LibCore/NetworkJob.h> | ||||||
|  | #include <LibCore/TCPSocket.h> | ||||||
|  | #include <LibGemini/GeminiRequest.h> | ||||||
|  | #include <LibGemini/GeminiResponse.h> | ||||||
|  | 
 | ||||||
|  | namespace Gemini { | ||||||
|  | 
 | ||||||
|  | class Job : public Core::NetworkJob { | ||||||
|  | public: | ||||||
|  |     explicit Job(const GeminiRequest&); | ||||||
|  |     virtual ~Job() override; | ||||||
|  | 
 | ||||||
|  |     virtual void start() override = 0; | ||||||
|  |     virtual void shutdown() override = 0; | ||||||
|  | 
 | ||||||
|  |     GeminiResponse* response() { return static_cast<GeminiResponse*>(Core::NetworkJob::response()); } | ||||||
|  |     const GeminiResponse* response() const { return static_cast<const GeminiResponse*>(Core::NetworkJob::response()); } | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     void finish_up(); | ||||||
|  |     void on_socket_connected(); | ||||||
|  |     virtual void register_on_ready_to_read(Function<void()>) = 0; | ||||||
|  |     virtual void register_on_ready_to_write(Function<void()>) = 0; | ||||||
|  |     virtual bool can_read_line() const = 0; | ||||||
|  |     virtual ByteBuffer read_line(size_t) = 0; | ||||||
|  |     virtual bool can_read() const = 0; | ||||||
|  |     virtual ByteBuffer receive(size_t) = 0; | ||||||
|  |     virtual bool eof() const = 0; | ||||||
|  |     virtual bool write(const ByteBuffer&) = 0; | ||||||
|  |     virtual bool is_established() const = 0; | ||||||
|  |     virtual bool should_fail_on_empty_payload() const { return false; } | ||||||
|  |     virtual void read_while_data_available(Function<IterationDecision()> read) { read(); }; | ||||||
|  | 
 | ||||||
|  |     enum class State { | ||||||
|  |         InStatus, | ||||||
|  |         InBody, | ||||||
|  |         Finished, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     GeminiRequest m_request; | ||||||
|  |     State m_state { State::InStatus }; | ||||||
|  |     int m_status { -1 }; | ||||||
|  |     String m_meta; | ||||||
|  |     Vector<ByteBuffer> m_received_buffers; | ||||||
|  |     size_t m_received_size { 0 }; | ||||||
|  |     bool m_sent_data { false }; | ||||||
|  |     bool m_should_have_payload { false }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Conrad Pankoff
						Conrad Pankoff