mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:22: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(LibDebug) | ||||
| add_subdirectory(LibDesktop) | ||||
| add_subdirectory(LibGemini) | ||||
| add_subdirectory(LibGfx) | ||||
| add_subdirectory(LibGUI) | ||||
| 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