tor-dirclient: Make Requestable return requests with String bodies.

It's simpler to always use a `String` to represent directory request bodies.
We no longer need the `StringBody` trait.
This commit is contained in:
Gabriela Moldovan 2023-08-16 15:09:52 +01:00
parent 2a7ba4ceb0
commit cd6c4674dc
No known key found for this signature in database
GPG Key ID: 3946E0ADE72BAC99
4 changed files with 25 additions and 53 deletions

View File

@ -1,5 +1,5 @@
DEPRECATED: `download()`
ADDED: `send_request()`
BREAKING: `Requestable` now has an associated `Body` type
ADDED: `StringBody` trait
ADDED: `tor-dirclient::request::HsDescUploadRequest`.
BREAKING: `Requestable::make_request` now returns `http::Request<String>`

View File

@ -52,12 +52,9 @@ impl StringBody for &str {
/// A request for an object that can be served over the Tor directory system.
pub trait Requestable {
/// The body type of the [`http::Request`].
type Body: StringBody;
/// Build an [`http::Request`] from this Requestable, if
/// it is well-formed.
fn make_request(&self) -> Result<http::Request<Self::Body>>;
fn make_request(&self) -> Result<http::Request<String>>;
/// Return true if partial downloads are potentially useful. This
/// is true for request types where we're going to be downloading
@ -219,9 +216,7 @@ impl Default for ConsensusRequest {
}
impl Requestable for ConsensusRequest {
type Body = ();
fn make_request(&self) -> Result<http::Request<()>> {
fn make_request(&self) -> Result<http::Request<String>> {
// Build the URL.
let mut uri = "/tor/status-vote/current/consensus".to_string();
match self.flavor {
@ -256,7 +251,7 @@ impl Requestable for ConsensusRequest {
req = req.header("X-Or-Diff-From-Consensus", &ids);
}
Ok(req.body(())?)
Ok(req.body(String::new())?)
}
fn partial_docs_ok(&self) -> bool {
@ -304,9 +299,7 @@ impl AuthCertRequest {
}
impl Requestable for AuthCertRequest {
type Body = ();
fn make_request(&self) -> Result<http::Request<()>> {
fn make_request(&self) -> Result<http::Request<String>> {
if self.ids.is_empty() {
return Err(RequestError::EmptyRequest);
}
@ -329,7 +322,7 @@ impl Requestable for AuthCertRequest {
let req = http::Request::builder().method("GET").uri(uri);
let req = add_common_headers(req);
Ok(req.body(())?)
Ok(req.body(String::new())?)
}
fn partial_docs_ok(&self) -> bool {
@ -376,9 +369,7 @@ impl MicrodescRequest {
}
impl Requestable for MicrodescRequest {
type Body = ();
fn make_request(&self) -> Result<http::Request<()>> {
fn make_request(&self) -> Result<http::Request<String>> {
let d_encode_b64 = |d: &[u8; 32]| Base64Unpadded::encode_string(&d[..]);
let ids = digest_list_stringify(&self.digests, d_encode_b64, "-")
.ok_or(RequestError::EmptyRequest)?;
@ -387,7 +378,7 @@ impl Requestable for MicrodescRequest {
let req = add_common_headers(req);
Ok(req.body(())?)
Ok(req.body(String::new())?)
}
fn partial_docs_ok(&self) -> bool {
@ -453,9 +444,7 @@ impl RouterDescRequest {
#[cfg(feature = "routerdesc")]
impl Requestable for RouterDescRequest {
type Body = ();
fn make_request(&self) -> Result<http::Request<()>> {
fn make_request(&self) -> Result<http::Request<String>> {
let mut uri = "/tor/server/".to_string();
match self.requested_descriptors {
@ -475,7 +464,7 @@ impl Requestable for RouterDescRequest {
let req = http::Request::builder().method("GET").uri(uri);
let req = add_common_headers(req);
Ok(req.body(())?)
Ok(req.body(String::new())?)
}
fn partial_docs_ok(&self) -> bool {
@ -521,14 +510,12 @@ impl RoutersOwnDescRequest {
#[cfg(feature = "routerdesc")]
impl Requestable for RoutersOwnDescRequest {
type Body = ();
fn make_request(&self) -> Result<http::Request<()>> {
fn make_request(&self) -> Result<http::Request<String>> {
let uri = "/tor/server/authority.z";
let req = http::Request::builder().method("GET").uri(uri);
let req = add_common_headers(req);
Ok(req.body(())?)
Ok(req.body(String::new())?)
}
fn partial_docs_ok(&self) -> bool {
@ -569,16 +556,14 @@ impl HsDescDownloadRequest {
#[cfg(feature = "hs-client")]
impl Requestable for HsDescDownloadRequest {
type Body = ();
fn make_request(&self) -> Result<http::Request<()>> {
fn make_request(&self) -> Result<http::Request<String>> {
let hsid = Base64Unpadded::encode_string(self.hsid.as_ref());
// We hardcode version 3 here; if we ever have a v4 onion service
// descriptor, it will need a different kind of Request.
let uri = format!("/tor/hs/3/{}", hsid);
let req = http::Request::builder().method("GET").uri(uri);
let req = add_common_headers(req);
Ok(req.body(())?)
Ok(req.body(String::new())?)
}
fn partial_docs_ok(&self) -> bool {
@ -607,9 +592,7 @@ impl HsDescUploadRequest {
#[cfg(feature = "hs-service")]
impl Requestable for HsDescUploadRequest {
type Body = String;
fn make_request(&self) -> Result<http::Request<Self::Body>> {
fn make_request(&self) -> Result<http::Request<String>> {
/// The upload URI.
const URI: &str = "/tor/hs/3/publish";

View File

@ -2,10 +2,8 @@
use std::fmt::Write;
use crate::request::StringBody;
/// Encode an HTTP request in a quick and dirty HTTP 1.0 format.
pub(crate) fn encode_request<B: StringBody>(req: &http::Request<B>) -> String {
pub(crate) fn encode_request(req: &http::Request<String>) -> String {
let mut s = format!("{} {} HTTP/1.0\r\n", req.method(), req.uri());
for (key, val) in req.headers().iter() {
@ -19,7 +17,7 @@ pub(crate) fn encode_request<B: StringBody>(req: &http::Request<B>) -> String {
.unwrap();
}
s.push_str("\r\n");
s.push_str(req.body().as_str());
s.push_str(req.body());
s
}
@ -38,16 +36,7 @@ mod test {
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
use super::*;
#[derive(Copy, Clone, Debug)]
struct TestBody;
impl StringBody for TestBody {
fn as_str(&self) -> &str {
"hello"
}
}
fn build_request<B: StringBody>(body: B, headers: &[(&str, &str)]) -> http::Request<B> {
fn build_request(body: String, headers: &[(&str, &str)]) -> http::Request<String> {
let mut builder = http::Request::builder().method("GET").uri("/index.html");
for (name, value) in headers {
@ -59,21 +48,21 @@ mod test {
#[test]
fn format() {
fn chk_format<B: StringBody + Clone>(body: B, expected_body: &str) {
fn chk_format(body: String) {
let req = build_request(body.clone(), &[]);
assert_eq!(
encode_request(&req),
format!("GET /index.html HTTP/1.0\r\n\r\n{expected_body}")
format!("GET /index.html HTTP/1.0\r\n\r\n{body}")
);
let req = build_request(body, &[("X-Marsupial", "Opossum")]);
let req = build_request(body.clone(), &[("X-Marsupial", "Opossum")]);
assert_eq!(
encode_request(&req),
format!("GET /index.html HTTP/1.0\r\nx-marsupial: Opossum\r\n\r\n{expected_body}")
format!("GET /index.html HTTP/1.0\r\nx-marsupial: Opossum\r\n\r\n{body}")
);
}
chk_format((), "");
chk_format(TestBody, "hello");
chk_format(Default::default());
chk_format("hello".into());
}
}

View File

@ -84,7 +84,7 @@ pub(crate) enum ClientRequest {
impl ClientRequest {
/// Turn a ClientRequest into a Requestable.
pub(crate) fn as_requestable(&self) -> &(dyn request::Requestable<Body = ()> + Send + Sync) {
pub(crate) fn as_requestable(&self) -> &(dyn request::Requestable + Send + Sync) {
use ClientRequest::*;
match self {
Consensus(a) => a,