tor-dirclient: Make the body type of a `Requestable` type configurable.
Previously, the `Requestable` trait assumed the body of the request would always be empty (`http::Request<()>`). This change replaces the hardcoded `()` body type with the `Requestable::Body` associated type (which will allow implementors to create requests with non-empty bodies). This will enable us to reuse the `Requestable` trait for building `POST` requests for uploading descriptors.
This commit is contained in:
parent
0fde1d09f5
commit
8ce948bef9
|
@ -1 +1,3 @@
|
||||||
BREAKING: `download()` is renamed to `send_request()`
|
BREAKING: `download()` is renamed to `send_request()`
|
||||||
|
BREAKING: `Requestable` now has an associated `Body` type
|
||||||
|
ADDED: `StringBody` trait
|
||||||
|
|
|
@ -24,11 +24,40 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use crate::err::RequestError;
|
use crate::err::RequestError;
|
||||||
|
|
||||||
|
/// The body of an [`http::Request`] made by a `Requestable` implementation.
|
||||||
|
pub trait StringBody {
|
||||||
|
/// The string representation of the request body.
|
||||||
|
///
|
||||||
|
/// Bodies consisting of binary data are not supported.
|
||||||
|
fn str(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringBody for () {
|
||||||
|
fn str(&self) -> &str {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringBody for String {
|
||||||
|
fn str(&self) -> &str {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringBody for &str {
|
||||||
|
fn str(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A request for an object that can be served over the Tor directory system.
|
/// A request for an object that can be served over the Tor directory system.
|
||||||
pub trait Requestable {
|
pub trait Requestable {
|
||||||
|
/// The body type of the [`http::Request`].
|
||||||
|
type Body: StringBody;
|
||||||
|
|
||||||
/// Build an [`http::Request`] from this Requestable, if
|
/// Build an [`http::Request`] from this Requestable, if
|
||||||
/// it is well-formed.
|
/// it is well-formed.
|
||||||
fn make_request(&self) -> Result<http::Request<()>>;
|
fn make_request(&self) -> Result<http::Request<Self::Body>>;
|
||||||
|
|
||||||
/// Return true if partial downloads are potentially useful. This
|
/// Return true if partial downloads are potentially useful. This
|
||||||
/// is true for request types where we're going to be downloading
|
/// is true for request types where we're going to be downloading
|
||||||
|
@ -190,6 +219,8 @@ impl Default for ConsensusRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Requestable for ConsensusRequest {
|
impl Requestable for ConsensusRequest {
|
||||||
|
type Body = ();
|
||||||
|
|
||||||
fn make_request(&self) -> Result<http::Request<()>> {
|
fn make_request(&self) -> Result<http::Request<()>> {
|
||||||
// Build the URL.
|
// Build the URL.
|
||||||
let mut uri = "/tor/status-vote/current/consensus".to_string();
|
let mut uri = "/tor/status-vote/current/consensus".to_string();
|
||||||
|
@ -273,6 +304,8 @@ impl AuthCertRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Requestable for AuthCertRequest {
|
impl Requestable for AuthCertRequest {
|
||||||
|
type Body = ();
|
||||||
|
|
||||||
fn make_request(&self) -> Result<http::Request<()>> {
|
fn make_request(&self) -> Result<http::Request<()>> {
|
||||||
if self.ids.is_empty() {
|
if self.ids.is_empty() {
|
||||||
return Err(RequestError::EmptyRequest);
|
return Err(RequestError::EmptyRequest);
|
||||||
|
@ -343,6 +376,8 @@ impl MicrodescRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Requestable for MicrodescRequest {
|
impl Requestable for MicrodescRequest {
|
||||||
|
type Body = ();
|
||||||
|
|
||||||
fn make_request(&self) -> Result<http::Request<()>> {
|
fn make_request(&self) -> Result<http::Request<()>> {
|
||||||
let d_encode_b64 = |d: &[u8; 32]| Base64Unpadded::encode_string(&d[..]);
|
let d_encode_b64 = |d: &[u8; 32]| Base64Unpadded::encode_string(&d[..]);
|
||||||
let ids = digest_list_stringify(&self.digests, d_encode_b64, "-")
|
let ids = digest_list_stringify(&self.digests, d_encode_b64, "-")
|
||||||
|
@ -418,6 +453,8 @@ impl RouterDescRequest {
|
||||||
|
|
||||||
#[cfg(feature = "routerdesc")]
|
#[cfg(feature = "routerdesc")]
|
||||||
impl Requestable for RouterDescRequest {
|
impl Requestable for RouterDescRequest {
|
||||||
|
type Body = ();
|
||||||
|
|
||||||
fn make_request(&self) -> Result<http::Request<()>> {
|
fn make_request(&self) -> Result<http::Request<()>> {
|
||||||
let mut uri = "/tor/server/".to_string();
|
let mut uri = "/tor/server/".to_string();
|
||||||
|
|
||||||
|
@ -484,6 +521,8 @@ impl RoutersOwnDescRequest {
|
||||||
|
|
||||||
#[cfg(feature = "routerdesc")]
|
#[cfg(feature = "routerdesc")]
|
||||||
impl Requestable for RoutersOwnDescRequest {
|
impl Requestable for RoutersOwnDescRequest {
|
||||||
|
type Body = ();
|
||||||
|
|
||||||
fn make_request(&self) -> Result<http::Request<()>> {
|
fn make_request(&self) -> Result<http::Request<()>> {
|
||||||
let uri = "/tor/server/authority.z";
|
let uri = "/tor/server/authority.z";
|
||||||
let req = http::Request::builder().method("GET").uri(uri);
|
let req = http::Request::builder().method("GET").uri(uri);
|
||||||
|
@ -530,6 +569,8 @@ impl HsDescDownloadRequest {
|
||||||
|
|
||||||
#[cfg(feature = "hs-client")]
|
#[cfg(feature = "hs-client")]
|
||||||
impl Requestable for HsDescDownloadRequest {
|
impl Requestable for HsDescDownloadRequest {
|
||||||
|
type Body = ();
|
||||||
|
|
||||||
fn make_request(&self) -> Result<http::Request<()>> {
|
fn make_request(&self) -> Result<http::Request<()>> {
|
||||||
let hsid = Base64Unpadded::encode_string(self.hsid.as_ref());
|
let hsid = Base64Unpadded::encode_string(self.hsid.as_ref());
|
||||||
// We hardcode version 3 here; if we ever have a v4 onion service
|
// We hardcode version 3 here; if we ever have a v4 onion service
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use crate::request::StringBody;
|
||||||
|
|
||||||
/// Encode an HTTP request in a quick and dirty HTTP 1.0 format.
|
/// Encode an HTTP request in a quick and dirty HTTP 1.0 format.
|
||||||
pub(crate) fn encode_request(req: &http::Request<()>) -> String {
|
pub(crate) fn encode_request<B: StringBody>(req: &http::Request<B>) -> String {
|
||||||
let mut s = format!("{} {} HTTP/1.0\r\n", req.method(), req.uri());
|
let mut s = format!("{} {} HTTP/1.0\r\n", req.method(), req.uri());
|
||||||
|
|
||||||
for (key, val) in req.headers().iter() {
|
for (key, val) in req.headers().iter() {
|
||||||
|
@ -16,8 +18,8 @@ pub(crate) fn encode_request(req: &http::Request<()>) -> String {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
// TODO HSS encode the body
|
|
||||||
s.push_str("\r\n");
|
s.push_str("\r\n");
|
||||||
|
s.push_str(req.body().str());
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ pub(crate) enum ClientRequest {
|
||||||
|
|
||||||
impl ClientRequest {
|
impl ClientRequest {
|
||||||
/// Turn a ClientRequest into a Requestable.
|
/// Turn a ClientRequest into a Requestable.
|
||||||
pub(crate) fn as_requestable(&self) -> &(dyn request::Requestable + Send + Sync) {
|
pub(crate) fn as_requestable(&self) -> &(dyn request::Requestable<Body = ()> + Send + Sync) {
|
||||||
use ClientRequest::*;
|
use ClientRequest::*;
|
||||||
match self {
|
match self {
|
||||||
Consensus(a) => a,
|
Consensus(a) => a,
|
||||||
|
|
Loading…
Reference in New Issue