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

Configure Feed

Select the types of activity you want to include in your feed.

feat(sync): asynchronous request

Fuwn 38b68b5b b0950591

+117 -3
+1
README.md
··· 47 47 | `meta` | Structure-ize a Gemini response's meta section | 48 48 | `macros` | Macros to aid with various Germ-related functionalities | 49 49 | `quick` | Quick functions to create valid Gemtext elements from input | 50 + | `sync` | An asynchronous version of `request` | 50 51 51 52 ### Examples 52 53
+3
crates/germ/Cargo.toml
··· 22 22 meta = [] 23 23 request = ["rustls", "url", "anyhow"] 24 24 quick = [] 25 + sync = ["tokio", "tokio-rustls"] 25 26 26 27 [dependencies] 27 28 anyhow = { version = "1.0.70", optional = true } # `Result` ··· 29 30 rustls = { version = "0.21.0", features = [ 30 31 "dangerous_configuration" 31 32 ], optional = true } # TLS 33 + tokio-rustls = { version = "0.24.0", optional = true } # Non-blocking TLS 34 + tokio = { version = "1.27.0", optional = true, default-features = false, features = ["net", "io-util", "rt-multi-thread", "macros"] } # Non-blocking I/O 32 35 url = { version = "2.3.1", optional = true } # URL Validation
+29
crates/germ/examples/async_request.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 + #[tokio::main] 20 + async fn main() { 21 + match germ::request::sync::request( 22 + &url::Url::parse("gemini://fuwn.me").unwrap(), 23 + ) 24 + .await 25 + { 26 + Ok(response) => println!("{:?}", response), 27 + Err(_) => {} 28 + } 29 + }
+4 -1
crates/germ/src/request.rs
··· 22 22 mod status; 23 23 mod verifier; 24 24 25 + #[cfg(feature = "sync")] 26 + pub mod sync; 27 + 25 28 use std::io::{Read, Write}; 26 29 27 30 pub use response::Response; 28 31 pub use status::Status; 29 - use verifier::GermVerifier; 32 + pub(crate) use verifier::GermVerifier; 30 33 31 34 /// Make a request to a Gemini server. The `url` **should** be prefixed with a 32 35 /// scheme (e.g. "gemini://").
+1 -1
crates/germ/src/request/response.rs
··· 32 32 } 33 33 34 34 impl Response { 35 - pub(super) fn new(data: &[u8], suite: Option<SupportedCipherSuite>) -> Self { 35 + pub(crate) fn new(data: &[u8], suite: Option<SupportedCipherSuite>) -> Self { 36 36 let string_form = String::from_utf8_lossy(data).to_string(); 37 37 let mut content = None; 38 38 let header;
+77
crates/germ/src/request/sync.rs
··· 1 + // This file is part of Germ <https://github.com/gemrest/germ>. 2 + // Copyright (C) 2022-2023 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 tokio::io::{AsyncReadExt, AsyncWriteExt}; 20 + 21 + use crate::request::Response; 22 + 23 + /// Make a request to a Gemini server 24 + /// 25 + /// The `url` **should** be prefixed with a scheme (e.g. "gemini://"). 26 + /// 27 + /// # Example 28 + /// 29 + /// ```rust 30 + /// match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap()) 31 + /// .await 32 + /// { 33 + /// Ok(response) => println!("{:?}", response), 34 + /// Err(_) => {} 35 + /// } 36 + /// ``` 37 + /// 38 + /// # Errors 39 + /// 40 + /// - May error if the URL is invalid 41 + /// - May error if the server is unreachable 42 + /// - May error if the TLS write fails 43 + /// - May error if the TLS read fails 44 + pub async fn request(url: &url::Url) -> anyhow::Result<Response> { 45 + let mut tls = tokio_rustls::TlsConnector::from(std::sync::Arc::new( 46 + rustls::ClientConfig::builder() 47 + .with_safe_defaults() 48 + .with_custom_certificate_verifier(std::sync::Arc::new( 49 + crate::request::GermVerifier::new(), 50 + )) 51 + .with_no_client_auth(), 52 + )) 53 + .connect( 54 + rustls::ServerName::try_from(url.domain().unwrap_or_default())?, 55 + tokio::net::TcpStream::connect(format!( 56 + "{}:{}", 57 + url.domain().unwrap_or(""), 58 + url.port().unwrap_or(1965) 59 + )) 60 + .await?, 61 + ) 62 + .await?; 63 + let cipher_suite = tls.get_mut().1.negotiated_cipher_suite(); 64 + 65 + tls.write_all(format!("{url}\r\n").as_bytes()).await?; 66 + 67 + Ok(Response::new( 68 + &{ 69 + let mut plain_text = Vec::new(); 70 + 71 + tls.read_to_end(&mut plain_text).await?; 72 + 73 + plain_text 74 + }, 75 + cipher_suite, 76 + )) 77 + }
+2 -1
crates/germ/src/request/verifier.rs
··· 20 20 21 21 use rustls::{client, client::ServerCertVerified, Certificate}; 22 22 23 - pub(super) struct GermVerifier; 23 + #[allow(clippy::module_name_repetitions)] 24 + pub struct GermVerifier; 24 25 25 26 impl GermVerifier { 26 27 pub const fn new() -> Self { Self {} }