···4747| `meta` | Structure-ize a Gemini response's meta section |
4848| `macros` | Macros to aid with various Germ-related functionalities |
4949| `quick` | Quick functions to create valid Gemtext elements from input |
5050+| `sync` | An asynchronous version of `request` |
50515152### Examples
5253
···11+// This file is part of Germ <https://github.com/gemrest/germ>.
22+// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me>
33+//
44+// This program is free software: you can redistribute it and/or modify
55+// it under the terms of the GNU General Public License as published by
66+// the Free Software Foundation, version 3.
77+//
88+// This program is distributed in the hope that it will be useful, but
99+// WITHOUT ANY WARRANTY; without even the implied warranty of
1010+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1111+// General Public License for more details.
1212+//
1313+// You should have received a copy of the GNU General Public License
1414+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515+//
1616+// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me>
1717+// SPDX-License-Identifier: GPL-3.0-only
1818+1919+#[tokio::main]
2020+async fn main() {
2121+ match germ::request::sync::request(
2222+ &url::Url::parse("gemini://fuwn.me").unwrap(),
2323+ )
2424+ .await
2525+ {
2626+ Ok(response) => println!("{:?}", response),
2727+ Err(_) => {}
2828+ }
2929+}
+4-1
crates/germ/src/request.rs
···2222mod status;
2323mod verifier;
24242525+#[cfg(feature = "sync")]
2626+pub mod sync;
2727+2528use std::io::{Read, Write};
26292730pub use response::Response;
2831pub use status::Status;
2929-use verifier::GermVerifier;
3232+pub(crate) use verifier::GermVerifier;
30333134/// Make a request to a Gemini server. The `url` **should** be prefixed with a
3235/// scheme (e.g. "gemini://").
···11+// This file is part of Germ <https://github.com/gemrest/germ>.
22+// Copyright (C) 2022-2023 Fuwn <contact@fuwn.me>
33+//
44+// This program is free software: you can redistribute it and/or modify
55+// it under the terms of the GNU General Public License as published by
66+// the Free Software Foundation, version 3.
77+//
88+// This program is distributed in the hope that it will be useful, but
99+// WITHOUT ANY WARRANTY; without even the implied warranty of
1010+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1111+// General Public License for more details.
1212+//
1313+// You should have received a copy of the GNU General Public License
1414+// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515+//
1616+// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me>
1717+// SPDX-License-Identifier: GPL-3.0-only
1818+1919+use tokio::io::{AsyncReadExt, AsyncWriteExt};
2020+2121+use crate::request::Response;
2222+2323+/// Make a request to a Gemini server
2424+///
2525+/// The `url` **should** be prefixed with a scheme (e.g. "gemini://").
2626+///
2727+/// # Example
2828+///
2929+/// ```rust
3030+/// match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap())
3131+/// .await
3232+/// {
3333+/// Ok(response) => println!("{:?}", response),
3434+/// Err(_) => {}
3535+/// }
3636+/// ```
3737+///
3838+/// # Errors
3939+///
4040+/// - May error if the URL is invalid
4141+/// - May error if the server is unreachable
4242+/// - May error if the TLS write fails
4343+/// - May error if the TLS read fails
4444+pub async fn request(url: &url::Url) -> anyhow::Result<Response> {
4545+ let mut tls = tokio_rustls::TlsConnector::from(std::sync::Arc::new(
4646+ rustls::ClientConfig::builder()
4747+ .with_safe_defaults()
4848+ .with_custom_certificate_verifier(std::sync::Arc::new(
4949+ crate::request::GermVerifier::new(),
5050+ ))
5151+ .with_no_client_auth(),
5252+ ))
5353+ .connect(
5454+ rustls::ServerName::try_from(url.domain().unwrap_or_default())?,
5555+ tokio::net::TcpStream::connect(format!(
5656+ "{}:{}",
5757+ url.domain().unwrap_or(""),
5858+ url.port().unwrap_or(1965)
5959+ ))
6060+ .await?,
6161+ )
6262+ .await?;
6363+ let cipher_suite = tls.get_mut().1.negotiated_cipher_suite();
6464+6565+ tls.write_all(format!("{url}\r\n").as_bytes()).await?;
6666+6767+ Ok(Response::new(
6868+ &{
6969+ let mut plain_text = Vec::new();
7070+7171+ tls.read_to_end(&mut plain_text).await?;
7272+7373+ plain_text
7474+ },
7575+ cipher_suite,
7676+ ))
7777+}