Run rustfmt, clean up aquatic_http_protocol/Cargo.toml

This commit is contained in:
Joakim Frostegård 2021-08-15 22:26:11 +02:00
parent 0cc312a78d
commit d0e716f80b
65 changed files with 1754 additions and 2590 deletions

View file

@ -1,48 +1,43 @@
use std::str::FromStr;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use super::utils::*;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PeerId(
#[serde(
serialize_with = "serialize_20_bytes",
deserialize_with = "deserialize_20_bytes",
deserialize_with = "deserialize_20_bytes"
)]
pub [u8; 20]
pub [u8; 20],
);
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct InfoHash(
#[serde(
serialize_with = "serialize_20_bytes",
deserialize_with = "deserialize_20_bytes",
deserialize_with = "deserialize_20_bytes"
)]
pub [u8; 20]
pub [u8; 20],
);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnnounceEvent {
Started,
Stopped,
Completed,
Empty
Empty,
}
impl Default for AnnounceEvent {
fn default() -> Self {
Self::Empty
}
}
impl FromStr for AnnounceEvent {
type Err = String;
@ -52,18 +47,17 @@ impl FromStr for AnnounceEvent {
"stopped" => Ok(Self::Stopped),
"completed" => Ok(Self::Completed),
"empty" => Ok(Self::Empty),
value => Err(format!("Unknown value: {}", value))
value => Err(format!("Unknown value: {}", value)),
}
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for InfoHash {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut arr = [b'0'; 20];
for byte in arr.iter_mut(){
for byte in arr.iter_mut() {
*byte = u8::arbitrary(g);
}
@ -71,13 +65,12 @@ impl quickcheck::Arbitrary for InfoHash {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for PeerId {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let mut arr = [b'0'; 20];
for byte in arr.iter_mut(){
for byte in arr.iter_mut() {
*byte = u8::arbitrary(g);
}
@ -85,15 +78,14 @@ impl quickcheck::Arbitrary for PeerId {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for AnnounceEvent {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
match (bool::arbitrary(g), bool::arbitrary(g)){
match (bool::arbitrary(g), bool::arbitrary(g)) {
(false, false) => Self::Started,
(true, false) => Self::Started,
(false, true) => Self::Completed,
(true, true) => Self::Empty,
}
}
}
}

View file

@ -1,4 +1,4 @@
pub mod common;
pub mod request;
pub mod response;
mod utils;
mod utils;

View file

@ -1,12 +1,11 @@
use std::io::Write;
use anyhow::Context;
use smartstring::{SmartString, LazyCompact};
use smartstring::{LazyCompact, SmartString};
use super::common::*;
use super::utils::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AnnounceRequest {
pub info_hash: InfoHash,
@ -20,7 +19,6 @@ pub struct AnnounceRequest {
pub key: Option<SmartString<LazyCompact>>,
}
impl AnnounceRequest {
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<()> {
output.write_all(b"GET /announce?info_hash=")?;
@ -61,13 +59,11 @@ impl AnnounceRequest {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScrapeRequest {
pub info_hashes: Vec<InfoHash>,
}
impl ScrapeRequest {
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<()> {
output.write_all(b"GET /scrape?")?;
@ -91,49 +87,40 @@ impl ScrapeRequest {
}
}
#[derive(Debug)]
pub enum RequestParseError {
NeedMoreData,
Invalid(anyhow::Error),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Request {
Announce(AnnounceRequest),
Scrape(ScrapeRequest),
}
impl Request {
/// Parse Request from HTTP request bytes
pub fn from_bytes(bytes: &[u8]) -> Result<Self, RequestParseError> {
let mut headers = [httparse::EMPTY_HEADER; 16];
let mut http_request = httparse::Request::new(&mut headers);
let path = match http_request.parse(bytes){
let path = match http_request.parse(bytes) {
Ok(httparse::Status::Complete(_)) => {
if let Some(path) = http_request.path {
path
} else {
return Err(RequestParseError::Invalid(
anyhow::anyhow!("no http path")
))
return Err(RequestParseError::Invalid(anyhow::anyhow!("no http path")));
}
},
}
Ok(httparse::Status::Partial) => {
if let Some(path) = http_request.path {
path
} else {
return Err(RequestParseError::NeedMoreData)
return Err(RequestParseError::NeedMoreData);
}
}
Err(err) => {
return Err(RequestParseError::Invalid(
anyhow::Error::from(err)
))
},
Err(err) => return Err(RequestParseError::Invalid(anyhow::Error::from(err))),
};
Self::from_http_get_path(path).map_err(RequestParseError::Invalid)
@ -155,12 +142,10 @@ impl Request {
pub fn from_http_get_path(path: &str) -> anyhow::Result<Self> {
::log::debug!("request GET path: {}", path);
let mut split_parts= path.splitn(2, '?');
let mut split_parts = path.splitn(2, '?');
let location = split_parts.next()
.with_context(|| "no location")?;
let query_string = split_parts.next()
.with_context(|| "no query string")?;
let location = split_parts.next().with_context(|| "no location")?;
let query_string = split_parts.next().with_context(|| "no query string")?;
// -- Parse key-value pairs
@ -171,64 +156,67 @@ impl Request {
let mut event = AnnounceEvent::default();
let mut opt_numwant = None;
let mut opt_key = None;
let query_string_bytes = query_string.as_bytes();
let mut ampersand_iter = ::memchr::memchr_iter(b'&', query_string_bytes);
let mut position = 0usize;
for equal_sign_index in ::memchr::memchr_iter(b'=', query_string_bytes){
let segment_end = ampersand_iter.next()
.unwrap_or_else(|| query_string.len());
for equal_sign_index in ::memchr::memchr_iter(b'=', query_string_bytes) {
let segment_end = ampersand_iter.next().unwrap_or_else(|| query_string.len());
let key = query_string.get(position..equal_sign_index)
let key = query_string
.get(position..equal_sign_index)
.with_context(|| format!("no key at {}..{}", position, equal_sign_index))?;
let value = query_string.get(equal_sign_index + 1..segment_end)
.with_context(|| format!("no value at {}..{}", equal_sign_index + 1, segment_end))?;
let value = query_string
.get(equal_sign_index + 1..segment_end)
.with_context(|| {
format!("no value at {}..{}", equal_sign_index + 1, segment_end)
})?;
match key {
"info_hash" => {
let value = urldecode_20_bytes(value)?;
info_hashes.push(InfoHash(value));
},
}
"peer_id" => {
let value = urldecode_20_bytes(value)?;
opt_peer_id = Some(PeerId(value));
},
}
"port" => {
opt_port = Some(value.parse::<u16>().with_context(|| "parse port")?);
},
"left" => {
}
"left" => {
opt_bytes_left = Some(value.parse::<usize>().with_context(|| "parse left")?);
},
"event" => {
event = value.parse::<AnnounceEvent>().map_err(|err|
anyhow::anyhow!("invalid event: {}", err)
)?;
},
"compact" => {
}
"event" => {
event = value
.parse::<AnnounceEvent>()
.map_err(|err| anyhow::anyhow!("invalid event: {}", err))?;
}
"compact" => {
if value != "1" {
return Err(anyhow::anyhow!("compact set, but not to 1"));
}
},
"numwant" => {
}
"numwant" => {
opt_numwant = Some(value.parse::<usize>().with_context(|| "parse numwant")?);
},
}
"key" => {
if value.len() > 100 {
return Err(anyhow::anyhow!("'key' is too long"))
return Err(anyhow::anyhow!("'key' is too long"));
}
opt_key = Some(::urlencoding::decode(value)?.into());
},
}
k => {
::log::debug!("ignored unrecognized key: {}", k)
}
}
if segment_end == query_string.len(){
break
if segment_end == query_string.len() {
break;
} else {
position = segment_end + 1;
}
@ -250,9 +238,7 @@ impl Request {
Ok(Request::Announce(request))
} else {
let request = ScrapeRequest {
info_hashes,
};
let request = ScrapeRequest { info_hashes };
Ok(Request::Scrape(request))
}
@ -266,17 +252,23 @@ impl Request {
}
}
#[cfg(test)]
mod tests {
use quickcheck::{Arbitrary, Gen, TestResult, quickcheck};
use quickcheck::{quickcheck, Arbitrary, Gen, TestResult};
use super::*;
static ANNOUNCE_REQUEST_PATH: &str = "/announce?info_hash=%04%0bkV%3f%5cr%14%a6%b7%98%adC%c3%c9.%40%24%00%b9&peer_id=-ABC940-5ert69muw5t8&port=12345&uploaded=0&downloaded=0&left=1&numwant=0&key=4ab4b877&compact=1&supportcrypto=1&event=started";
static SCRAPE_REQUEST_PATH: &str = "/scrape?info_hash=%04%0bkV%3f%5cr%14%a6%b7%98%adC%c3%c9.%40%24%00%b9";
static REFERENCE_INFO_HASH: [u8; 20] = [0x04, 0x0b, b'k', b'V', 0x3f, 0x5c, b'r', 0x14, 0xa6, 0xb7, 0x98, 0xad, b'C', 0xc3, 0xc9, b'.', 0x40, 0x24, 0x00, 0xb9];
static REFERENCE_PEER_ID: [u8; 20] = [b'-', b'A', b'B', b'C', b'9', b'4', b'0', b'-', b'5', b'e', b'r', b't', b'6', b'9', b'm', b'u', b'w', b'5', b't', b'8'];
static SCRAPE_REQUEST_PATH: &str =
"/scrape?info_hash=%04%0bkV%3f%5cr%14%a6%b7%98%adC%c3%c9.%40%24%00%b9";
static REFERENCE_INFO_HASH: [u8; 20] = [
0x04, 0x0b, b'k', b'V', 0x3f, 0x5c, b'r', 0x14, 0xa6, 0xb7, 0x98, 0xad, b'C', 0xc3, 0xc9,
b'.', 0x40, 0x24, 0x00, 0xb9,
];
static REFERENCE_PEER_ID: [u8; 20] = [
b'-', b'A', b'B', b'C', b'9', b'4', b'0', b'-', b'5', b'e', b'r', b't', b'6', b'9', b'm',
b'u', b'w', b'5', b't', b'8',
];
fn get_reference_announce_request() -> Request {
Request::Announce(AnnounceRequest {
@ -287,12 +279,12 @@ mod tests {
event: AnnounceEvent::Started,
compact: true,
numwant: Some(0),
key: Some("4ab4b877".into())
key: Some("4ab4b877".into()),
})
}
#[test]
fn test_announce_request_from_bytes(){
fn test_announce_request_from_bytes() {
let mut bytes = Vec::new();
bytes.extend_from_slice(b"GET ");
@ -306,7 +298,7 @@ mod tests {
}
#[test]
fn test_scrape_request_from_bytes(){
fn test_scrape_request_from_bytes() {
let mut bytes = Vec::new();
bytes.extend_from_slice(b"GET ");
@ -348,7 +340,7 @@ mod tests {
impl Arbitrary for Request {
fn arbitrary(g: &mut Gen) -> Self {
if Arbitrary::arbitrary(g){
if Arbitrary::arbitrary(g) {
Self::Announce(Arbitrary::arbitrary(g))
} else {
Self::Scrape(Arbitrary::arbitrary(g))
@ -357,9 +349,12 @@ mod tests {
}
#[test]
fn quickcheck_serde_identity_request(){
fn quickcheck_serde_identity_request() {
fn prop(request: Request) -> TestResult {
if let Request::Announce(AnnounceRequest { key: Some(ref key), ..}) = request {
if let Request::Announce(AnnounceRequest {
key: Some(ref key), ..
}) = request
{
if key.len() > 30 {
return TestResult::discard();
}
@ -384,4 +379,4 @@ mod tests {
quickcheck(prop as fn(Request) -> TestResult);
}
}
}

View file

@ -1,42 +1,38 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use std::io::Write;
use std::net::{Ipv4Addr, Ipv6Addr};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use serde::{Serialize, Deserialize};
use super::common::*;
use super::utils::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResponsePeer<I: Eq>{
pub struct ResponsePeer<I: Eq> {
pub ip_address: I,
pub port: u16
pub port: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(transparent)]
pub struct ResponsePeerListV4(
#[serde(
serialize_with = "serialize_response_peers_ipv4",
deserialize_with = "deserialize_response_peers_ipv4",
deserialize_with = "deserialize_response_peers_ipv4"
)]
pub Vec<ResponsePeer<Ipv4Addr>>
pub Vec<ResponsePeer<Ipv4Addr>>,
);
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(transparent)]
pub struct ResponsePeerListV6(
#[serde(
serialize_with = "serialize_response_peers_ipv6",
deserialize_with = "deserialize_response_peers_ipv6",
deserialize_with = "deserialize_response_peers_ipv6"
)]
pub Vec<ResponsePeer<Ipv6Addr>>
pub Vec<ResponsePeer<Ipv6Addr>>,
);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScrapeStatistics {
pub complete: usize,
@ -44,7 +40,6 @@ pub struct ScrapeStatistics {
pub downloaded: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnnounceResponse {
#[serde(rename = "interval")]
@ -57,47 +52,44 @@ pub struct AnnounceResponse {
pub peers6: ResponsePeerListV6,
}
impl AnnounceResponse {
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize;
bytes_written += output.write(b"d8:completei")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.complete).as_bytes()
)?;
bytes_written += output.write(itoa::Buffer::new().format(self.complete).as_bytes())?;
bytes_written += output.write(b"e10:incompletei")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.incomplete).as_bytes()
)?;
bytes_written += output.write(itoa::Buffer::new().format(self.incomplete).as_bytes())?;
bytes_written += output.write(b"e8:intervali")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.announce_interval).as_bytes()
itoa::Buffer::new()
.format(self.announce_interval)
.as_bytes(),
)?;
bytes_written += output.write(b"e5:peers")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.peers.0.len() * 6).as_bytes()
itoa::Buffer::new()
.format(self.peers.0.len() * 6)
.as_bytes(),
)?;
bytes_written += output.write(b":")?;
for peer in self.peers.0.iter() {
bytes_written += output.write(
&u32::from(peer.ip_address).to_be_bytes()
)?;
bytes_written += output.write(&u32::from(peer.ip_address).to_be_bytes())?;
bytes_written += output.write(&peer.port.to_be_bytes())?;
}
bytes_written += output.write(b"6:peers6")?;
bytes_written += output.write(
itoa::Buffer::new().format(self.peers6.0.len() * 18).as_bytes()
itoa::Buffer::new()
.format(self.peers6.0.len() * 18)
.as_bytes(),
)?;
bytes_written += output.write(b":")?;
for peer in self.peers6.0.iter() {
bytes_written += output.write(
&u128::from(peer.ip_address).to_be_bytes()
)?;
bytes_written += output.write(&u128::from(peer.ip_address).to_be_bytes())?;
bytes_written += output.write(&peer.port.to_be_bytes())?;
}
bytes_written += output.write(b"e")?;
@ -106,31 +98,27 @@ impl AnnounceResponse {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScrapeResponse {
/// BTreeMap instead of HashMap since keys need to be serialized in order
pub files: BTreeMap<InfoHash, ScrapeStatistics>,
}
impl ScrapeResponse {
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize;
bytes_written += output.write(b"d5:filesd")?;
for (info_hash, statistics) in self.files.iter(){
for (info_hash, statistics) in self.files.iter() {
bytes_written += output.write(b"20:")?;
bytes_written += output.write(&info_hash.0)?;
bytes_written += output.write(b"d8:completei")?;
bytes_written += output.write(
itoa::Buffer::new().format(statistics.complete).as_bytes()
)?;
bytes_written +=
output.write(itoa::Buffer::new().format(statistics.complete).as_bytes())?;
bytes_written += output.write(b"e10:downloadedi0e10:incompletei")?;
bytes_written += output.write(
itoa::Buffer::new().format(statistics.incomplete).as_bytes()
)?;
bytes_written +=
output.write(itoa::Buffer::new().format(statistics.incomplete).as_bytes())?;
bytes_written += output.write(b"ee")?;
}
@ -140,14 +128,12 @@ impl ScrapeResponse {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailureResponse {
#[serde(rename = "failure reason")]
pub failure_reason: String,
}
impl FailureResponse {
fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize;
@ -155,9 +141,7 @@ impl FailureResponse {
let reason_bytes = self.failure_reason.as_bytes();
bytes_written += output.write(b"d14:failure reason")?;
bytes_written += output.write(
itoa::Buffer::new().format(reason_bytes.len()).as_bytes()
)?;
bytes_written += output.write(itoa::Buffer::new().format(reason_bytes.len()).as_bytes())?;
bytes_written += output.write(b":")?;
bytes_written += output.write(reason_bytes)?;
bytes_written += output.write(b"e")?;
@ -166,7 +150,6 @@ impl FailureResponse {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Response {
@ -175,7 +158,6 @@ pub enum Response {
Failure(FailureResponse),
}
impl Response {
pub fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
match self {
@ -189,29 +171,26 @@ impl Response {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ResponsePeer<Ipv4Addr> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self {
ip_address: Ipv4Addr::arbitrary(g),
port: u16::arbitrary(g)
port: u16::arbitrary(g),
}
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ResponsePeer<Ipv6Addr> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self {
ip_address: Ipv6Addr::arbitrary(g),
port: u16::arbitrary(g)
port: u16::arbitrary(g),
}
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ResponsePeerListV4 {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
@ -219,7 +198,6 @@ impl quickcheck::Arbitrary for ResponsePeerListV4 {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ResponsePeerListV6 {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
@ -227,7 +205,6 @@ impl quickcheck::Arbitrary for ResponsePeerListV6 {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ScrapeStatistics {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
@ -239,7 +216,6 @@ impl quickcheck::Arbitrary for ScrapeStatistics {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for AnnounceResponse {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
@ -253,7 +229,6 @@ impl quickcheck::Arbitrary for AnnounceResponse {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ScrapeResponse {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
@ -263,7 +238,6 @@ impl quickcheck::Arbitrary for ScrapeResponse {
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for FailureResponse {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
@ -273,7 +247,6 @@ impl quickcheck::Arbitrary for FailureResponse {
}
}
#[cfg(test)]
mod tests {
use quickcheck_macros::*;
@ -282,9 +255,7 @@ mod tests {
#[quickcheck]
fn test_announce_response_to_bytes(response: AnnounceResponse) -> bool {
let reference = bendy::serde::to_bytes(
&Response::Announce(response.clone())
).unwrap();
let reference = bendy::serde::to_bytes(&Response::Announce(response.clone())).unwrap();
let mut output = Vec::new();
@ -295,9 +266,7 @@ mod tests {
#[quickcheck]
fn test_scrape_response_to_bytes(response: ScrapeResponse) -> bool {
let reference = bendy::serde::to_bytes(
&Response::Scrape(response.clone())
).unwrap();
let reference = bendy::serde::to_bytes(&Response::Scrape(response.clone())).unwrap();
let mut hand_written = Vec::new();
@ -315,9 +284,7 @@ mod tests {
#[quickcheck]
fn test_failure_response_to_bytes(response: FailureResponse) -> bool {
let reference = bendy::serde::to_bytes(
&Response::Failure(response.clone())
).unwrap();
let reference = bendy::serde::to_bytes(&Response::Failure(response.clone())).unwrap();
let mut hand_written = Vec::new();
@ -332,4 +299,4 @@ mod tests {
success
}
}
}

View file

@ -1,23 +1,16 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use std::io::Write;
use std::net::{Ipv4Addr, Ipv6Addr};
use anyhow::Context;
use serde::{Serializer, Deserializer, de::Visitor};
use serde::{de::Visitor, Deserializer, Serializer};
use super::response::ResponsePeer;
pub fn urlencode_20_bytes(
input: [u8; 20],
output: &mut impl Write
) -> ::std::io::Result<()> {
pub fn urlencode_20_bytes(input: [u8; 20], output: &mut impl Write) -> ::std::io::Result<()> {
let mut tmp = [b'%'; 60];
for i in 0..input.len() {
hex::encode_to_slice(
&input[i..i + 1],
&mut tmp[i * 3 + 1..i * 3 + 3]
).unwrap();
hex::encode_to_slice(&input[i..i + 1], &mut tmp[i * 3 + 1..i * 3 + 3]).unwrap();
}
output.write_all(&tmp)?;
@ -25,15 +18,13 @@ pub fn urlencode_20_bytes(
Ok(())
}
pub fn urldecode_20_bytes(value: &str) -> anyhow::Result<[u8; 20]> {
let mut out_arr = [0u8; 20];
let mut chars = value.chars();
for i in 0..20 {
let c = chars.next()
.with_context(|| "less than 20 chars")?;
let c = chars.next().with_context(|| "less than 20 chars")?;
if c as u32 > 255 {
return Err(anyhow::anyhow!(
@ -43,38 +34,37 @@ pub fn urldecode_20_bytes(value: &str) -> anyhow::Result<[u8; 20]> {
}
if c == '%' {
let first = chars.next()
let first = chars
.next()
.with_context(|| "missing first urldecode char in pair")?;
let second = chars.next()
let second = chars
.next()
.with_context(|| "missing second urldecode char in pair")?;
let hex = [first as u8, second as u8];
hex::decode_to_slice(&hex, &mut out_arr[i..i+1]).map_err(|err|
anyhow::anyhow!("hex decode error: {:?}", err)
)?;
hex::decode_to_slice(&hex, &mut out_arr[i..i + 1])
.map_err(|err| anyhow::anyhow!("hex decode error: {:?}", err))?;
} else {
out_arr[i] = c as u8;
}
}
if chars.next().is_some(){
if chars.next().is_some() {
return Err(anyhow::anyhow!("more than 20 chars"));
}
Ok(out_arr)
}
#[inline]
pub fn serialize_20_bytes<S>(
bytes: &[u8; 20],
serializer: S
) -> Result<S::Ok, S::Error> where S: Serializer {
pub fn serialize_20_bytes<S>(bytes: &[u8; 20], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
}
struct TwentyByteVisitor;
impl<'de> Visitor<'de> for TwentyByteVisitor {
@ -86,7 +76,8 @@ impl<'de> Visitor<'de> for TwentyByteVisitor {
#[inline]
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where E: ::serde::de::Error,
where
E: ::serde::de::Error,
{
if value.len() != 20 {
return Err(::serde::de::Error::custom("not 20 bytes"));
@ -100,21 +91,21 @@ impl<'de> Visitor<'de> for TwentyByteVisitor {
}
}
#[inline]
pub fn deserialize_20_bytes<'de, D>(
deserializer: D
) -> Result<[u8; 20], D::Error>
where D: Deserializer<'de>
pub fn deserialize_20_bytes<'de, D>(deserializer: D) -> Result<[u8; 20], D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(TwentyByteVisitor)
}
pub fn serialize_response_peers_ipv4<S>(
response_peers: &[ResponsePeer<Ipv4Addr>],
serializer: S
) -> Result<S::Ok, S::Error> where S: Serializer {
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut bytes = Vec::with_capacity(response_peers.len() * 6);
for peer in response_peers {
@ -125,11 +116,13 @@ pub fn serialize_response_peers_ipv4<S>(
serializer.serialize_bytes(&bytes)
}
pub fn serialize_response_peers_ipv6<S>(
response_peers: &[ResponsePeer<Ipv6Addr>],
serializer: S
) -> Result<S::Ok, S::Error> where S: Serializer {
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut bytes = Vec::with_capacity(response_peers.len() * 6);
for peer in response_peers {
@ -140,10 +133,8 @@ pub fn serialize_response_peers_ipv6<S>(
serializer.serialize_bytes(&bytes)
}
struct ResponsePeersIpv4Visitor;
impl<'de> Visitor<'de> for ResponsePeersIpv4Visitor {
type Value = Vec<ResponsePeer<Ipv4Addr>>;
@ -153,45 +144,47 @@ impl<'de> Visitor<'de> for ResponsePeersIpv4Visitor {
#[inline]
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where E: ::serde::de::Error,
where
E: ::serde::de::Error,
{
let chunks = value.chunks_exact(6);
if !chunks.remainder().is_empty(){
if !chunks.remainder().is_empty() {
return Err(::serde::de::Error::custom("trailing bytes"));
}
let mut ip_bytes = [0u8; 4];
let mut port_bytes = [0u8; 2];
let peers = chunks.into_iter().map(|chunk | {
ip_bytes.copy_from_slice(&chunk[0..4]);
port_bytes.copy_from_slice(&chunk[4..6]);
let peers = chunks
.into_iter()
.map(|chunk| {
ip_bytes.copy_from_slice(&chunk[0..4]);
port_bytes.copy_from_slice(&chunk[4..6]);
ResponsePeer {
ip_address: Ipv4Addr::from(u32::from_be_bytes(ip_bytes)),
port: u16::from_be_bytes(port_bytes),
}
}).collect();
ResponsePeer {
ip_address: Ipv4Addr::from(u32::from_be_bytes(ip_bytes)),
port: u16::from_be_bytes(port_bytes),
}
})
.collect();
Ok(peers)
}
}
#[inline]
pub fn deserialize_response_peers_ipv4<'de, D>(
deserializer: D
deserializer: D,
) -> Result<Vec<ResponsePeer<Ipv4Addr>>, D::Error>
where D: Deserializer<'de>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(ResponsePeersIpv4Visitor)
}
struct ResponsePeersIpv6Visitor;
impl<'de> Visitor<'de> for ResponsePeersIpv6Visitor {
type Value = Vec<ResponsePeer<Ipv6Addr>>;
@ -201,42 +194,45 @@ impl<'de> Visitor<'de> for ResponsePeersIpv6Visitor {
#[inline]
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where E: ::serde::de::Error,
where
E: ::serde::de::Error,
{
let chunks = value.chunks_exact(18);
if !chunks.remainder().is_empty(){
if !chunks.remainder().is_empty() {
return Err(::serde::de::Error::custom("trailing bytes"));
}
let mut ip_bytes = [0u8; 16];
let mut port_bytes = [0u8; 2];
let peers = chunks.into_iter().map(|chunk| {
ip_bytes.copy_from_slice(&chunk[0..16]);
port_bytes.copy_from_slice(&chunk[16..18]);
let peers = chunks
.into_iter()
.map(|chunk| {
ip_bytes.copy_from_slice(&chunk[0..16]);
port_bytes.copy_from_slice(&chunk[16..18]);
ResponsePeer {
ip_address: Ipv6Addr::from(u128::from_be_bytes(ip_bytes)),
port: u16::from_be_bytes(port_bytes),
}
}).collect();
ResponsePeer {
ip_address: Ipv6Addr::from(u128::from_be_bytes(ip_bytes)),
port: u16::from_be_bytes(port_bytes),
}
})
.collect();
Ok(peers)
}
}
#[inline]
pub fn deserialize_response_peers_ipv6<'de, D>(
deserializer: D
deserializer: D,
) -> Result<Vec<ResponsePeer<Ipv6Addr>>, D::Error>
where D: Deserializer<'de>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(ResponsePeersIpv6Visitor)
}
#[cfg(test)]
mod tests {
use quickcheck_macros::*;
@ -246,10 +242,10 @@ mod tests {
use super::*;
#[test]
fn test_urlencode_20_bytes(){
fn test_urlencode_20_bytes() {
let mut input = [0u8; 20];
for (i, b) in input.iter_mut().enumerate(){
for (i, b) in input.iter_mut().enumerate() {
*b = i as u8 % 10;
}
@ -259,7 +255,7 @@ mod tests {
assert_eq!(output.len(), 60);
for (i, chunk) in output.chunks_exact(3).enumerate(){
for (i, chunk) in output.chunks_exact(3).enumerate() {
// Not perfect but should do the job
let reference = [b'%', b'0', input[i] + 48];
@ -284,9 +280,7 @@ mod tests {
g: u8,
h: u8,
) -> bool {
let input: [u8; 20] = [
a, b, c, d, e, f, g, h, b, c, d, a, e, f, g, h, a, b, d, c
];
let input: [u8; 20] = [a, b, c, d, e, f, g, h, b, c, d, a, e, f, g, h, a, b, d, c];
let mut output = Vec::new();
@ -302,9 +296,7 @@ mod tests {
}
#[quickcheck]
fn test_serde_response_peers_ipv4(
peers: Vec<ResponsePeer<Ipv4Addr>>,
) -> bool {
fn test_serde_response_peers_ipv4(peers: Vec<ResponsePeer<Ipv4Addr>>) -> bool {
let serialized = bendy::serde::to_bytes(&peers).unwrap();
let deserialized: Vec<ResponsePeer<Ipv4Addr>> =
::bendy::serde::from_bytes(&serialized).unwrap();
@ -313,9 +305,7 @@ mod tests {
}
#[quickcheck]
fn test_serde_response_peers_ipv6(
peers: Vec<ResponsePeer<Ipv6Addr>>,
) -> bool {
fn test_serde_response_peers_ipv6(peers: Vec<ResponsePeer<Ipv6Addr>>) -> bool {
let serialized = bendy::serde::to_bytes(&peers).unwrap();
let deserialized: Vec<ResponsePeer<Ipv6Addr>> =
::bendy::serde::from_bytes(&serialized).unwrap();
@ -324,13 +314,10 @@ mod tests {
}
#[quickcheck]
fn test_serde_info_hash(
info_hash: InfoHash,
) -> bool {
fn test_serde_info_hash(info_hash: InfoHash) -> bool {
let serialized = bendy::serde::to_bytes(&info_hash).unwrap();
let deserialized: InfoHash =
::bendy::serde::from_bytes(&serialized).unwrap();
let deserialized: InfoHash = ::bendy::serde::from_bytes(&serialized).unwrap();
info_hash == deserialized
}
}
}