diff options
| author | Fuwn <[email protected]> | 2022-05-17 10:55:54 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2022-05-17 10:55:54 +0000 |
| commit | 8ffa9b9b44cd9366dc4a6c1e9e8925d8c3eb59cd (patch) | |
| tree | a0a2fe36dbb3df0f10ba16bc5aa82601b11c84c7 /src | |
| parent | docs(cargo): bump version (diff) | |
| download | germ-8ffa9b9b44cd9366dc4a6c1e9e8925d8c3eb59cd.tar.xz germ-8ffa9b9b44cd9366dc4a6c1e9e8925d8c3eb59cd.zip | |
feat(request): make gemini requests
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/request.rs | 68 | ||||
| -rw-r--r-- | src/request/response.rs | 59 | ||||
| -rw-r--r-- | src/request/status.rs | 103 | ||||
| -rw-r--r-- | src/request/verifier.rs | 39 |
5 files changed, 272 insertions, 0 deletions
@@ -34,3 +34,6 @@ pub mod ast; #[cfg(feature = "convert")] pub mod convert; + +#[cfg(feature = "request")] +pub mod request; diff --git a/src/request.rs b/src/request.rs new file mode 100644 index 0000000..f6297ef --- /dev/null +++ b/src/request.rs @@ -0,0 +1,68 @@ +// This file is part of Germ <https://github.com/gemrest/germ>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +//! Make Gemini requests and get sane, structured results + +mod response; +mod status; +mod verifier; + +use std::io::{Read, Write}; + +pub use response::Response; +pub use status::Status; +use verifier::GermVerifier; + +/// Make a request to a Gemini server. The `url` **should** be prefixed with a +/// scheme (e.g. "gemini://"). +/// +/// # Example +/// +/// ```rust +/// match germ::request::request(url::Url::parse("gemini://fuwn.me").unwrap()) { +/// Ok(response) => println!("{:?}", response), +/// Err(_) => {} +/// } +/// ``` +pub fn request(url: url::Url) -> anyhow::Result<Response> { + let config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(std::sync::Arc::new(GermVerifier::new())) + .with_no_client_auth(); + let mut connection = rustls::ClientConnection::new( + std::sync::Arc::new(config), + url.domain().unwrap_or("").try_into()?, + )?; + let mut stream = std::net::TcpStream::connect(format!( + "{}:{}", + url.domain().unwrap_or(""), + url.port().unwrap_or(1965) + ))?; + let mut tls = rustls::Stream::new(&mut connection, &mut stream); + + tls.write_all(format!("{}\r\n", url).as_bytes())?; + + let mut plain_text = Vec::new(); + + tls.read_to_end(&mut plain_text).unwrap(); + + Ok(Response::new( + plain_text, + tls.conn.negotiated_cipher_suite(), + )) +} diff --git a/src/request/response.rs b/src/request/response.rs new file mode 100644 index 0000000..0202967 --- /dev/null +++ b/src/request/response.rs @@ -0,0 +1,59 @@ +// This file is part of Germ <https://github.com/gemrest/germ>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use rustls::SupportedCipherSuite; + +use crate::request::Status; + +#[derive(Debug)] +pub struct Response { + pub status: Status, + pub meta: String, + pub content: Option<String>, + pub size: usize, + pub suite: Option<SupportedCipherSuite>, +} +impl Response { + pub(super) fn new( + data: Vec<u8>, + suite: Option<SupportedCipherSuite>, + ) -> Self { + let string_form = String::from_utf8_lossy(&data).to_string(); + let mut content = None; + let header; + + if !string_form.ends_with("\r\n") { + let mut string_split = string_form.split("\r\n"); + + header = string_split.next().unwrap_or("").to_string(); + content = Some(string_split.collect()); + } else { + header = string_form; + } + + let header_split = header.split_at(2); + + Self { + status: Status::from(header_split.0.parse::<i32>().unwrap_or(0)), + meta: header_split.1.trim_start().to_string(), + content, + size: data.len(), + suite, + } + } +} diff --git a/src/request/status.rs b/src/request/status.rs new file mode 100644 index 0000000..b2065ed --- /dev/null +++ b/src/request/status.rs @@ -0,0 +1,103 @@ +// This file is part of Germ <https://github.com/gemrest/germ>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +/// Simple Gemini status reporting +/// +/// # Examples +/// +/// ```rust +/// use germ::request::Status; +/// +/// assert_eq!(Status::from(10), Status::Input); +/// assert_eq!(i32::from(Status::Input), 10); +/// ``` +#[derive(Debug, PartialEq)] +pub enum Status { + Input, + SensitiveInput, + Success, + TemporaryRedirect, + PermanentRedirect, + TemporaryFailure, + ServerUnavailable, + CGIError, + ProxyError, + SlowDown, + PermanentFailure, + NotFound, + Gone, + ProxyRefused, + BadRequest, + ClientCertificateRequired, + CertificateNotAuthorised, + CertificateNotValid, + Unsupported, +} +impl Default for Status { + fn default() -> Self { Self::Success } +} +impl From<Status> for i32 { + fn from(n: Status) -> Self { + match n { + Status::Input => 10, + Status::SensitiveInput => 11, + Status::Success => 20, + Status::TemporaryRedirect => 30, + Status::PermanentRedirect => 31, + Status::TemporaryFailure => 40, + Status::ServerUnavailable => 41, + Status::CGIError => 42, + Status::ProxyError => 43, + Status::SlowDown => 44, + Status::PermanentFailure => 50, + Status::NotFound => 51, + Status::Gone => 52, + Status::ProxyRefused => 53, + Status::BadRequest => 59, + Status::ClientCertificateRequired => 60, + Status::CertificateNotAuthorised => 61, + Status::CertificateNotValid => 62, + _ => 0, + } + } +} +impl From<i32> for Status { + fn from(n: i32) -> Self { + match n { + 10 => Self::Input, + 11 => Self::SensitiveInput, + 20 => Self::Success, + 30 => Self::TemporaryRedirect, + 31 => Self::PermanentRedirect, + 40 => Self::TemporaryFailure, + 41 => Self::ServerUnavailable, + 42 => Self::CGIError, + 43 => Self::ProxyError, + 44 => Self::SlowDown, + 50 => Self::PermanentFailure, + 51 => Self::NotFound, + 52 => Self::Gone, + 53 => Self::ProxyRefused, + 59 => Self::BadRequest, + 60 => Self::ClientCertificateRequired, + 61 => Self::CertificateNotAuthorised, + 62 => Self::CertificateNotValid, + _ => Self::Unsupported, + } + } +} diff --git a/src/request/verifier.rs b/src/request/verifier.rs new file mode 100644 index 0000000..f2c6a35 --- /dev/null +++ b/src/request/verifier.rs @@ -0,0 +1,39 @@ +// This file is part of Germ <https://github.com/gemrest/germ>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +use std::time::SystemTime; + +use rustls::{client, client::ServerCertVerified, Certificate}; + +pub(super) struct GermVerifier; +impl GermVerifier { + pub fn new() -> Self { Self {} } +} +impl client::ServerCertVerifier for GermVerifier { + fn verify_server_cert( + &self, + _end_entity: &Certificate, + _intermediates: &[Certificate], + _server_name: &client::ServerName, + _scts: &mut dyn Iterator<Item = &[u8]>, + _ocsp_response: &[u8], + _now: SystemTime, + ) -> Result<ServerCertVerified, rustls::Error> { + Ok(ServerCertVerified::assertion()) + } +} |