🦠 The Definitive Gemini Protocol Toolkit
gemini gemini-protocol gemtext parser zero-dependency toolkit ast converter html markdown cli networking

refactor: module for blocking, default to non-blocking

fuwn.net 39706164 8afa8b98

verified
+95 -65
+2 -2
Cargo.toml
··· 16 16 17 17 [features] 18 18 ast = [] 19 + blocking = ["rustls", "url", "anyhow"] 19 20 convert = ["ast"] 20 21 default = ["ast", "convert", "meta", "request"] 21 22 macros = ["ast", "convert"] 22 23 meta = [] 23 - request = ["rustls", "url", "anyhow"] 24 + request = ["rustls", "url", "anyhow", "tokio", "tokio-rustls"] 24 25 quick = [] 25 - sync = ["tokio", "tokio-rustls"] 26 26 27 27 [dependencies] 28 28 anyhow = { version = "1.0.70", optional = true } # `Result`
+10 -9
README.md
··· 39 39 40 40 ### Features 41 41 42 - | Feature | Description | 43 - | --------- | ---------------------------------------------------------------- | 44 - | `ast` | Construct AST trees from raw Gemtext. | 45 - | `convert` | Convert from Gemtext to markup formats such as HTML or Markdown. | 46 - | `request` | Make Gemini requests and get sane, structured results. | 47 - | `meta` | Structure-ize a Gemini response's meta section | 48 - | `macros` | Macros to aid with various Germ-related functionalities | 49 - | `quick` | Quick functions to create valid Gemtext elements from input | 50 - | `sync` | An asynchronous version of `request` | 42 + | Feature | Description | 43 + | ---------- | ---------------------------------------------------------------- | 44 + | `default` | `ast`, `convert`, `meta`, `request` | 45 + | `ast` | Construct AST trees from raw Gemtext. | 46 + | `blocking` | Blocking equivalent of `request` | 47 + | `convert` | Convert from Gemtext to markup formats such as HTML or Markdown. | 48 + | `request` | Make Gemini requests and get sane, structured results. | 49 + | `meta` | Structure-ize a Gemini response's meta section | 50 + | `macros` | Macros to aid with various Germ-related functionalities | 51 + | `quick` | Quick functions to create valid Gemtext elements from input | 51 52 52 53 ### Examples 53 54
+3 -6
examples/async_request.rs examples/request_blocking.rs
··· 16 16 // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 - #[tokio::main] 20 - async fn main() { 21 - match germ::request::sync::request( 19 + fn main() { 20 + match germ::request::blocking::request( 22 21 &url::Url::parse("gemini://fuwn.me").unwrap(), 23 - ) 24 - .await 25 - { 22 + ) { 26 23 Ok(response) => println!("{:?}", response), 27 24 Err(_) => {} 28 25 }
+5 -2
examples/request.rs
··· 16 16 // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 - fn main() { 20 - match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap()) { 19 + #[tokio::main] 20 + async fn main() { 21 + match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap()) 22 + .await 23 + { 21 24 Ok(response) => println!("{:?}", response), 22 25 Err(_) => {} 23 26 }
+3 -1
examples/request_to_gemtext_from_ast.rs examples/request_blocking_to_gemtext_from_ast.rs
··· 17 17 // SPDX-License-Identifier: GPL-3.0-only 18 18 19 19 fn main() { 20 - match germ::request::request(&url::Url::parse("gemini://fuwn.me/").unwrap()) { 20 + match germ::request::blocking::request( 21 + &url::Url::parse("gemini://fuwn.me/").unwrap(), 22 + ) { 21 23 Ok(response) => println!( 22 24 "{}", 23 25 germ::ast::Ast::from_string(
+8 -44
src/request.rs
··· 1 1 // This file is part of Germ <https://github.com/gemrest/germ>. 2 - // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 2 + // Copyright (C) 2022-2023 Fuwn <contact@fuwn.me> 3 3 // 4 4 // This program is free software: you can redistribute it and/or modify 5 5 // it under the terms of the GNU General Public License as published by ··· 22 22 mod status; 23 23 mod verifier; 24 24 25 - #[cfg(feature = "sync")] pub mod sync; 25 + #[cfg(feature = "blocking")] 26 + pub mod blocking; 26 27 27 - use std::io::{Read, Write}; 28 + #[cfg(feature = "request")] 29 + pub mod non_blocking; 30 + 31 + #[cfg(feature = "request")] 32 + pub use non_blocking::request; 28 33 29 34 pub(crate) use verifier::GermVerifier; 30 35 pub use {response::Response, status::Status}; 31 - 32 - /// Make a request to a Gemini server. The `url` **should** be prefixed with a 33 - /// scheme (e.g. "gemini://"). 34 - /// 35 - /// # Example 36 - /// 37 - /// ```rust 38 - /// match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap()) { 39 - /// Ok(response) => println!("{:?}", response), 40 - /// Err(_) => {} 41 - /// } 42 - /// ``` 43 - /// 44 - /// # Errors 45 - /// - May error if the URL is invalid 46 - /// - May error if the TLS write fails 47 - /// - May error if the TLS read fails 48 - pub fn request(url: &url::Url) -> anyhow::Result<Response> { 49 - let config = rustls::ClientConfig::builder() 50 - .with_safe_defaults() 51 - .with_custom_certificate_verifier(std::sync::Arc::new(GermVerifier::new())) 52 - .with_no_client_auth(); 53 - let mut connection = rustls::ClientConnection::new( 54 - std::sync::Arc::new(config), 55 - url.domain().unwrap_or("").try_into()?, 56 - )?; 57 - let mut stream = std::net::TcpStream::connect(format!( 58 - "{}:{}", 59 - url.domain().unwrap_or(""), 60 - url.port().unwrap_or(1965) 61 - ))?; 62 - let mut tls = rustls::Stream::new(&mut connection, &mut stream); 63 - 64 - tls.write_all(format!("{url}\r\n").as_bytes())?; 65 - 66 - let mut plain_text = Vec::new(); 67 - 68 - tls.read_to_end(&mut plain_text)?; 69 - 70 - Ok(Response::new(&plain_text, tls.conn.negotiated_cipher_suite())) 71 - }
+63
src/request/blocking.rs
··· 1 + // This file is part of Germ <https://github.com/gemrest/germ>. 2 + // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 3 + // 4 + // This program is free software: you can redistribute it and/or modify 5 + // it under the terms of the GNU General Public License as published by 6 + // the Free Software Foundation, version 3. 7 + // 8 + // This program is distributed in the hope that it will be useful, but 9 + // WITHOUT ANY WARRANTY; without even the implied warranty of 10 + // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 + // General Public License for more details. 12 + // 13 + // You should have received a copy of the GNU General Public License 14 + // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 + // 16 + // Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17 + // SPDX-License-Identifier: GPL-3.0-only 18 + 19 + use { 20 + crate::request::{GermVerifier, Response}, 21 + std::io::{Read, Write}, 22 + }; 23 + 24 + /// Make a request to a Gemini server. The `url` **should** be prefixed with a 25 + /// scheme (e.g. "gemini://"). 26 + /// 27 + /// # Example 28 + /// 29 + /// ```rust 30 + /// match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap()) { 31 + /// Ok(response) => println!("{:?}", response), 32 + /// Err(_) => {} 33 + /// } 34 + /// ``` 35 + /// 36 + /// # Errors 37 + /// - May error if the URL is invalid 38 + /// - May error if the TLS write fails 39 + /// - May error if the TLS read fails 40 + pub fn request(url: &url::Url) -> anyhow::Result<Response> { 41 + let config = rustls::ClientConfig::builder() 42 + .with_safe_defaults() 43 + .with_custom_certificate_verifier(std::sync::Arc::new(GermVerifier::new())) 44 + .with_no_client_auth(); 45 + let mut connection = rustls::ClientConnection::new( 46 + std::sync::Arc::new(config), 47 + url.domain().unwrap_or("").try_into()?, 48 + )?; 49 + let mut stream = std::net::TcpStream::connect(format!( 50 + "{}:{}", 51 + url.domain().unwrap_or(""), 52 + url.port().unwrap_or(1965) 53 + ))?; 54 + let mut tls = rustls::Stream::new(&mut connection, &mut stream); 55 + 56 + tls.write_all(format!("{url}\r\n").as_bytes())?; 57 + 58 + let mut plain_text = Vec::new(); 59 + 60 + tls.read_to_end(&mut plain_text)?; 61 + 62 + Ok(Response::new(&plain_text, tls.conn.negotiated_cipher_suite())) 63 + }
+1 -1
src/request/sync.rs src/request/non_blocking.rs
··· 1 1 // This file is part of Germ <https://github.com/gemrest/germ>. 2 - // Copyright (C) 2022-2023 Fuwn <contact@fuwn.me> 2 + // Copyright (C) 2022-2024 Fuwn <contact@fuwn.me> 3 3 // 4 4 // This program is free software: you can redistribute it and/or modify 5 5 // it under the terms of the GNU General Public License as published by