Merge pull request #180 from greatest-ape/work-2023-01-29

protocol crates: rename some methods, minor improvements
This commit is contained in:
Joakim Frostegård 2024-01-29 21:00:46 +01:00 committed by GitHub
commit 1807c4a1e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 103 additions and 82 deletions

View file

@ -389,7 +389,7 @@ where
let mut position = RESPONSE_HEADER.len(); let mut position = RESPONSE_HEADER.len();
let body_len = response let body_len = response
.write(&mut &mut self.response_buffer[position..]) .write_bytes(&mut &mut self.response_buffer[position..])
.map_err(ConnectionError::ResponseBufferWrite)?; .map_err(ConnectionError::ResponseBufferWrite)?;
position += body_len; position += body_len;

View file

@ -25,7 +25,7 @@ pub fn parse_request(
match http_request.parse(buffer).with_context(|| "httparse")? { match http_request.parse(buffer).with_context(|| "httparse")? {
httparse::Status::Complete(_) => { httparse::Status::Complete(_) => {
let path = http_request.path.ok_or(anyhow::anyhow!("no http path"))?; let path = http_request.path.ok_or(anyhow::anyhow!("no http path"))?;
let request = Request::from_http_get_path(path)?; let request = Request::parse_http_get_path(path)?;
let opt_peer_ip = if config.network.runs_behind_reverse_proxy { let opt_peer_ip = if config.network.runs_behind_reverse_proxy {
let header_name = &config.network.reverse_proxy_ip_header_name; let header_name = &config.network.reverse_proxy_ip_header_name;

View file

@ -229,7 +229,7 @@ where
} }
if let Some(body_start_index) = opt_body_start_index { if let Some(body_start_index) = opt_body_start_index {
match Response::from_bytes(&interesting_bytes[body_start_index..]) { match Response::parse_bytes(&interesting_bytes[body_start_index..]) {
Ok(response) => { Ok(response) => {
match response { match response {
Response::Announce(_) => { Response::Announce(_) => {

View file

@ -33,7 +33,7 @@ pub fn bench(c: &mut Criterion) {
b.iter(|| { b.iter(|| {
buffer.set_position(0); buffer.set_position(0);
Response::write(black_box(&response), black_box(&mut buffer)).unwrap(); Response::write_bytes(black_box(&response), black_box(&mut buffer)).unwrap();
}) })
}); });
} }

View file

@ -7,7 +7,7 @@ static INPUT: &[u8] = b"GET /announce?info_hash=%04%0bkV%3f%5cr%14%a6%b7%98%adC%
pub fn bench(c: &mut Criterion) { pub fn bench(c: &mut Criterion) {
c.bench_function("request-from-bytes", |b| { c.bench_function("request-from-bytes", |b| {
b.iter(|| Request::from_bytes(black_box(INPUT))) b.iter(|| Request::parse_bytes(black_box(INPUT)))
}); });
} }

View file

@ -21,7 +21,7 @@ pub struct AnnounceRequest {
} }
impl AnnounceRequest { impl AnnounceRequest {
fn write<W: Write>(&self, output: &mut W, url_suffix: &[u8]) -> ::std::io::Result<()> { fn write_bytes<W: Write>(&self, output: &mut W, url_suffix: &[u8]) -> ::std::io::Result<()> {
output.write_all(b"GET /announce")?; output.write_all(b"GET /announce")?;
output.write_all(url_suffix)?; output.write_all(url_suffix)?;
output.write_all(b"?info_hash=")?; output.write_all(b"?info_hash=")?;
@ -67,7 +67,7 @@ impl AnnounceRequest {
Ok(()) Ok(())
} }
pub fn from_query_string(query_string: &str) -> anyhow::Result<Self> { pub fn parse_query_string(query_string: &str) -> anyhow::Result<Self> {
// -- Parse key-value pairs // -- Parse key-value pairs
let mut opt_info_hash = None; let mut opt_info_hash = None;
@ -173,7 +173,7 @@ pub struct ScrapeRequest {
} }
impl ScrapeRequest { impl ScrapeRequest {
fn write<W: Write>(&self, output: &mut W, url_suffix: &[u8]) -> ::std::io::Result<()> { fn write_bytes<W: Write>(&self, output: &mut W, url_suffix: &[u8]) -> ::std::io::Result<()> {
output.write_all(b"GET /scrape")?; output.write_all(b"GET /scrape")?;
output.write_all(url_suffix)?; output.write_all(url_suffix)?;
output.write_all(b"?")?; output.write_all(b"?")?;
@ -196,7 +196,7 @@ impl ScrapeRequest {
Ok(()) Ok(())
} }
pub fn from_query_string(query_string: &str) -> anyhow::Result<Self> { pub fn parse_query_string(query_string: &str) -> anyhow::Result<Self> {
// -- Parse key-value pairs // -- Parse key-value pairs
let mut info_hashes = Vec::new(); let mut info_hashes = Vec::new();
@ -252,14 +252,14 @@ pub enum Request {
impl Request { impl Request {
/// Parse Request from HTTP request bytes /// Parse Request from HTTP request bytes
pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Option<Self>> { pub fn parse_bytes(bytes: &[u8]) -> anyhow::Result<Option<Self>> {
let mut headers = [httparse::EMPTY_HEADER; 16]; let mut headers = [httparse::EMPTY_HEADER; 16];
let mut http_request = httparse::Request::new(&mut headers); let mut http_request = httparse::Request::new(&mut headers);
match http_request.parse(bytes) { match http_request.parse(bytes) {
Ok(httparse::Status::Complete(_)) => { Ok(httparse::Status::Complete(_)) => {
if let Some(path) = http_request.path { if let Some(path) = http_request.path {
Self::from_http_get_path(path).map(Some) Self::parse_http_get_path(path).map(Some)
} else { } else {
Err(anyhow::anyhow!("no http path")) Err(anyhow::anyhow!("no http path"))
} }
@ -282,7 +282,7 @@ impl Request {
/// UTF-8 string, meaning that non-ascii bytes are invalid characters. /// UTF-8 string, meaning that non-ascii bytes are invalid characters.
/// Therefore, these bytes must be converted to their equivalent multi-byte /// Therefore, these bytes must be converted to their equivalent multi-byte
/// UTF-8 encodings. /// UTF-8 encodings.
pub fn from_http_get_path(path: &str) -> anyhow::Result<Self> { pub fn parse_http_get_path(path: &str) -> anyhow::Result<Self> {
::log::debug!("request GET path: {}", path); ::log::debug!("request GET path: {}", path);
let mut split_parts = path.splitn(2, '?'); let mut split_parts = path.splitn(2, '?');
@ -291,11 +291,11 @@ impl Request {
let query_string = split_parts.next().with_context(|| "no query string")?; let query_string = split_parts.next().with_context(|| "no query string")?;
if location == "/announce" { if location == "/announce" {
Ok(Request::Announce(AnnounceRequest::from_query_string( Ok(Request::Announce(AnnounceRequest::parse_query_string(
query_string, query_string,
)?)) )?))
} else if location == "/scrape" { } else if location == "/scrape" {
Ok(Request::Scrape(ScrapeRequest::from_query_string( Ok(Request::Scrape(ScrapeRequest::parse_query_string(
query_string, query_string,
)?)) )?))
} else { } else {
@ -305,8 +305,8 @@ impl Request {
pub fn write<W: Write>(&self, output: &mut W, url_suffix: &[u8]) -> ::std::io::Result<()> { pub fn write<W: Write>(&self, output: &mut W, url_suffix: &[u8]) -> ::std::io::Result<()> {
match self { match self {
Self::Announce(r) => r.write(output, url_suffix), Self::Announce(r) => r.write_bytes(output, url_suffix),
Self::Scrape(r) => r.write(output, url_suffix), Self::Scrape(r) => r.write_bytes(output, url_suffix),
} }
} }
} }
@ -351,7 +351,7 @@ mod tests {
bytes.extend_from_slice(ANNOUNCE_REQUEST_PATH.as_bytes()); bytes.extend_from_slice(ANNOUNCE_REQUEST_PATH.as_bytes());
bytes.extend_from_slice(b" HTTP/1.1\r\n\r\n"); bytes.extend_from_slice(b" HTTP/1.1\r\n\r\n");
let parsed_request = Request::from_bytes(&bytes[..]).unwrap().unwrap(); let parsed_request = Request::parse_bytes(&bytes[..]).unwrap().unwrap();
let reference_request = get_reference_announce_request(); let reference_request = get_reference_announce_request();
assert_eq!(parsed_request, reference_request); assert_eq!(parsed_request, reference_request);
@ -365,7 +365,7 @@ mod tests {
bytes.extend_from_slice(SCRAPE_REQUEST_PATH.as_bytes()); bytes.extend_from_slice(SCRAPE_REQUEST_PATH.as_bytes());
bytes.extend_from_slice(b" HTTP/1.1\r\n\r\n"); bytes.extend_from_slice(b" HTTP/1.1\r\n\r\n");
let parsed_request = Request::from_bytes(&bytes[..]).unwrap().unwrap(); let parsed_request = Request::parse_bytes(&bytes[..]).unwrap().unwrap();
let reference_request = Request::Scrape(ScrapeRequest { let reference_request = Request::Scrape(ScrapeRequest {
info_hashes: vec![InfoHash(REFERENCE_INFO_HASH)], info_hashes: vec![InfoHash(REFERENCE_INFO_HASH)],
}); });
@ -432,7 +432,7 @@ mod tests {
request.write(&mut bytes, &[]).unwrap(); request.write(&mut bytes, &[]).unwrap();
let parsed_request = Request::from_bytes(&bytes[..]).unwrap().unwrap(); let parsed_request = Request::parse_bytes(&bytes[..]).unwrap().unwrap();
let success = request == parsed_request; let success = request == parsed_request;

View file

@ -61,7 +61,7 @@ pub struct AnnounceResponse {
} }
impl AnnounceResponse { impl AnnounceResponse {
pub fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> { pub fn write_bytes<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize; let mut bytes_written = 0usize;
bytes_written += output.write(b"d8:completei")?; bytes_written += output.write(b"d8:completei")?;
@ -124,7 +124,7 @@ pub struct ScrapeResponse {
} }
impl ScrapeResponse { impl ScrapeResponse {
pub fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> { pub fn write_bytes<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize; let mut bytes_written = 0usize;
bytes_written += output.write(b"d5:filesd")?; bytes_written += output.write(b"d5:filesd")?;
@ -160,7 +160,7 @@ impl FailureResponse {
} }
} }
pub fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> { pub fn write_bytes<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
let mut bytes_written = 0usize; let mut bytes_written = 0usize;
let reason_bytes = self.failure_reason.as_bytes(); let reason_bytes = self.failure_reason.as_bytes();
@ -184,14 +184,14 @@ pub enum Response {
} }
impl Response { impl Response {
pub fn write<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> { pub fn write_bytes<W: Write>(&self, output: &mut W) -> ::std::io::Result<usize> {
match self { match self {
Response::Announce(r) => r.write(output), Response::Announce(r) => r.write_bytes(output),
Response::Failure(r) => r.write(output), Response::Failure(r) => r.write_bytes(output),
Response::Scrape(r) => r.write(output), Response::Scrape(r) => r.write_bytes(output),
} }
} }
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ::serde_bencode::Error> { pub fn parse_bytes(bytes: &[u8]) -> Result<Self, ::serde_bencode::Error> {
::serde_bencode::from_bytes(bytes) ::serde_bencode::from_bytes(bytes)
} }
} }
@ -285,7 +285,7 @@ mod tests {
let mut hand_written = Vec::new(); let mut hand_written = Vec::new();
response.write(&mut hand_written).unwrap(); response.write_bytes(&mut hand_written).unwrap();
let success = hand_written == reference; let success = hand_written == reference;
@ -303,7 +303,7 @@ mod tests {
let mut hand_written = Vec::new(); let mut hand_written = Vec::new();
response.write(&mut hand_written).unwrap(); response.write_bytes(&mut hand_written).unwrap();
let success = hand_written == reference; let success = hand_written == reference;
@ -321,7 +321,7 @@ mod tests {
let mut hand_written = Vec::new(); let mut hand_written = Vec::new();
response.write(&mut hand_written).unwrap(); response.write_bytes(&mut hand_written).unwrap();
let success = hand_written == reference; let success = hand_written == reference;

View file

@ -290,7 +290,7 @@ mod tests {
let mut buf = Vec::new(); let mut buf = Vec::new();
response.write(&mut buf).unwrap(); response.write_bytes(&mut buf).unwrap();
println!("Buffer len: {}", buf.len()); println!("Buffer len: {}", buf.len());

View file

@ -198,7 +198,7 @@ impl SocketWorker {
} }
let src = CanonicalSocketAddr::new(src); let src = CanonicalSocketAddr::new(src);
let request_parsable = match Request::from_bytes( let request_parsable = match Request::parse_bytes(
&self.buffer[..bytes_read], &self.buffer[..bytes_read],
self.config.protocol.max_scrape_torrents, self.config.protocol.max_scrape_torrents,
) { ) {
@ -431,7 +431,7 @@ impl SocketWorker {
) { ) {
let mut buffer = Cursor::new(&mut buffer[..]); let mut buffer = Cursor::new(&mut buffer[..]);
if let Err(err) = response.write(&mut buffer) { if let Err(err) = response.write_bytes(&mut buffer) {
::log::error!("failed writing response to buffer: {:#}", err); ::log::error!("failed writing response to buffer: {:#}", err);
return; return;

View file

@ -138,7 +138,7 @@ impl RecvHelper {
let addr = CanonicalSocketAddr::new(addr); let addr = CanonicalSocketAddr::new(addr);
let request = Request::from_bytes(msg.payload_data(), self.max_scrape_torrents) let request = Request::parse_bytes(msg.payload_data(), self.max_scrape_torrents)
.map_err(|err| Error::RequestParseError(err, addr))?; .map_err(|err| Error::RequestParseError(err, addr))?;
Ok((request, addr)) Ok((request, addr))

View file

@ -196,7 +196,7 @@ impl SendBuffer {
let mut cursor = Cursor::new(&mut self.bytes[..]); let mut cursor = Cursor::new(&mut self.bytes[..]);
match response.write(&mut cursor) { match response.write_bytes(&mut cursor) {
Ok(()) => { Ok(()) => {
self.iovec.iov_len = cursor.position() as usize; self.iovec.iov_len = cursor.position() as usize;

View file

@ -104,7 +104,7 @@ pub fn request_and_response(
let mut buffer = Cursor::new(&mut buffer[..]); let mut buffer = Cursor::new(&mut buffer[..]);
request request
.write(&mut buffer) .write_bytes(&mut buffer)
.with_context(|| "write request")?; .with_context(|| "write request")?;
let bytes_written = buffer.position() as usize; let bytes_written = buffer.position() as usize;
@ -119,6 +119,6 @@ pub fn request_and_response(
.recv_from(&mut buffer) .recv_from(&mut buffer)
.with_context(|| "recv response")?; .with_context(|| "recv response")?;
Response::from_bytes(&buffer[..bytes_read], true).with_context(|| "parse response") Response::parse_bytes(&buffer[..bytes_read], true).with_context(|| "parse response")
} }
} }

View file

@ -77,7 +77,7 @@ fn no_response(
let mut buffer = Cursor::new(&mut buffer[..]); let mut buffer = Cursor::new(&mut buffer[..]);
request request
.write(&mut buffer) .write_bytes(&mut buffer)
.with_context(|| "write request")?; .with_context(|| "write request")?;
let bytes_written = buffer.position() as usize; let bytes_written = buffer.position() as usize;

View file

@ -71,7 +71,7 @@ impl Worker {
for _ in events.iter() { for _ in events.iter() {
while let Ok(amt) = self.socket.recv(&mut self.buffer) { while let Ok(amt) = self.socket.recv(&mut self.buffer) {
match Response::from_bytes(&self.buffer[0..amt], self.addr.is_ipv4()) { match Response::parse_bytes(&self.buffer[0..amt], self.addr.is_ipv4()) {
Ok(response) => { Ok(response) => {
if let Some(request) = self.process_response(response) { if let Some(request) = self.process_response(response) {
self.send_request(request); self.send_request(request);
@ -288,7 +288,7 @@ impl Worker {
fn send_request(&mut self, request: Request) { fn send_request(&mut self, request: Request) {
let mut cursor = Cursor::new(self.buffer); let mut cursor = Cursor::new(self.buffer);
match request.write(&mut cursor) { match request.write_bytes(&mut cursor) {
Ok(()) => { Ok(()) => {
let position = cursor.position() as usize; let position = cursor.position() as usize;
let inner = cursor.get_ref(); let inner = cursor.get_ref();

View file

@ -1,4 +1,5 @@
# aquatic_udp_protocol: UDP BitTorrent tracker protocol # aquatic_udp_protocol: UDP BitTorrent tracker protocol
[UDP BitTorrent](https://www.bittorrent.org/beps/bep_0015.html) tracker UDP BitTorrent tracker message parsing and serialization.
message parsing and serialization.
Implements [BEP 015](https://www.bittorrent.org/beps/bep_0015.html) ([more details](https://libtorrent.org/udp_tracker_protocol.html)).

View file

@ -19,30 +19,15 @@ pub enum Request {
} }
impl Request { impl Request {
pub fn write(self, bytes: &mut impl Write) -> Result<(), io::Error> { pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
match self { match self {
Request::Connect(r) => { Request::Connect(r) => r.write_bytes(bytes),
bytes.write_i64::<NetworkEndian>(PROTOCOL_IDENTIFIER)?; Request::Announce(r) => r.write_bytes(bytes),
bytes.write_i32::<NetworkEndian>(0)?; Request::Scrape(r) => r.write_bytes(bytes),
bytes.write_all(r.transaction_id.as_bytes())?;
}
Request::Announce(r) => {
bytes.write_all(r.as_bytes())?;
}
Request::Scrape(r) => {
bytes.write_all(r.connection_id.as_bytes())?;
bytes.write_i32::<NetworkEndian>(2)?;
bytes.write_all(r.transaction_id.as_bytes())?;
bytes.write_all((*r.info_hashes.as_slice()).as_bytes())?;
}
} }
Ok(())
} }
pub fn from_bytes(bytes: &[u8], max_scrape_torrents: u8) -> Result<Self, RequestParseError> { pub fn parse_bytes(bytes: &[u8], max_scrape_torrents: u8) -> Result<Self, RequestParseError> {
let action = bytes let action = bytes
.get(8..12) .get(8..12)
.map(|bytes| I32::from_bytes(bytes.try_into().unwrap())) .map(|bytes| I32::from_bytes(bytes.try_into().unwrap()))
@ -145,12 +130,22 @@ impl From<ScrapeRequest> for Request {
} }
} }
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ConnectRequest { pub struct ConnectRequest {
pub transaction_id: TransactionId, pub transaction_id: TransactionId,
} }
#[derive(PartialEq, Eq, Clone, Debug, AsBytes, FromBytes, FromZeroes)] impl ConnectRequest {
pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_i64::<NetworkEndian>(PROTOCOL_IDENTIFIER)?;
bytes.write_i32::<NetworkEndian>(0)?;
bytes.write_all(self.transaction_id.as_bytes())?;
Ok(())
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, AsBytes, FromBytes, FromZeroes)]
#[repr(C, packed)] #[repr(C, packed)]
pub struct AnnounceRequest { pub struct AnnounceRequest {
pub connection_id: ConnectionId, pub connection_id: ConnectionId,
@ -170,6 +165,12 @@ pub struct AnnounceRequest {
pub port: Port, pub port: Port,
} }
impl AnnounceRequest {
pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_all(self.as_bytes())
}
}
/// Note: Request::from_bytes only creates this struct with value 1 /// Note: Request::from_bytes only creates this struct with value 1
#[derive(PartialEq, Eq, Clone, Copy, Debug, AsBytes, FromBytes, FromZeroes)] #[derive(PartialEq, Eq, Clone, Copy, Debug, AsBytes, FromBytes, FromZeroes)]
#[repr(transparent)] #[repr(transparent)]
@ -223,6 +224,17 @@ pub struct ScrapeRequest {
pub info_hashes: Vec<InfoHash>, pub info_hashes: Vec<InfoHash>,
} }
impl ScrapeRequest {
pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_all(self.connection_id.as_bytes())?;
bytes.write_i32::<NetworkEndian>(2)?;
bytes.write_all(self.transaction_id.as_bytes())?;
bytes.write_all((*self.info_hashes.as_slice()).as_bytes())?;
Ok(())
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum RequestParseError { pub enum RequestParseError {
Sendable { Sendable {
@ -323,8 +335,8 @@ mod tests {
fn same_after_conversion(request: Request) -> bool { fn same_after_conversion(request: Request) -> bool {
let mut buf = Vec::new(); let mut buf = Vec::new();
request.clone().write(&mut buf).unwrap(); request.clone().write_bytes(&mut buf).unwrap();
let r2 = Request::from_bytes(&buf[..], ::std::u8::MAX).unwrap(); let r2 = Request::parse_bytes(&buf[..], ::std::u8::MAX).unwrap();
let success = request == r2; let success = request == r2;

View file

@ -18,18 +18,18 @@ pub enum Response {
impl Response { impl Response {
#[inline] #[inline]
pub fn write(&self, bytes: &mut impl Write) -> Result<(), io::Error> { pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
match self { match self {
Response::Connect(r) => r.write(bytes), Response::Connect(r) => r.write_bytes(bytes),
Response::AnnounceIpv4(r) => r.write(bytes), Response::AnnounceIpv4(r) => r.write_bytes(bytes),
Response::AnnounceIpv6(r) => r.write(bytes), Response::AnnounceIpv6(r) => r.write_bytes(bytes),
Response::Scrape(r) => r.write(bytes), Response::Scrape(r) => r.write_bytes(bytes),
Response::Error(r) => r.write(bytes), Response::Error(r) => r.write_bytes(bytes),
} }
} }
#[inline] #[inline]
pub fn from_bytes(mut bytes: &[u8], ipv4: bool) -> Result<Self, io::Error> { pub fn parse_bytes(mut bytes: &[u8], ipv4: bool) -> Result<Self, io::Error> {
let action = read_i32_ne(&mut bytes)?; let action = read_i32_ne(&mut bytes)?;
match action.get() { match action.get() {
@ -128,7 +128,7 @@ impl From<ErrorResponse> for Response {
} }
} }
#[derive(PartialEq, Eq, Clone, Debug, AsBytes, FromBytes, FromZeroes)] #[derive(PartialEq, Eq, Clone, Copy, Debug, AsBytes, FromBytes, FromZeroes)]
#[repr(C, packed)] #[repr(C, packed)]
pub struct ConnectResponse { pub struct ConnectResponse {
pub transaction_id: TransactionId, pub transaction_id: TransactionId,
@ -137,7 +137,7 @@ pub struct ConnectResponse {
impl ConnectResponse { impl ConnectResponse {
#[inline] #[inline]
pub fn write(&self, bytes: &mut impl Write) -> Result<(), io::Error> { pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_i32::<NetworkEndian>(0)?; bytes.write_i32::<NetworkEndian>(0)?;
bytes.write_all(self.as_bytes())?; bytes.write_all(self.as_bytes())?;
@ -160,7 +160,7 @@ impl<I: Ip> AnnounceResponse<I> {
} }
#[inline] #[inline]
pub fn write(&self, bytes: &mut impl Write) -> Result<(), io::Error> { pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_i32::<NetworkEndian>(1)?; bytes.write_i32::<NetworkEndian>(1)?;
bytes.write_all(self.fixed.as_bytes())?; bytes.write_all(self.fixed.as_bytes())?;
bytes.write_all((*self.peers.as_slice()).as_bytes())?; bytes.write_all((*self.peers.as_slice()).as_bytes())?;
@ -169,7 +169,7 @@ impl<I: Ip> AnnounceResponse<I> {
} }
} }
#[derive(PartialEq, Eq, Clone, Debug, AsBytes, FromBytes, FromZeroes)] #[derive(PartialEq, Eq, Clone, Copy, Debug, AsBytes, FromBytes, FromZeroes)]
#[repr(C, packed)] #[repr(C, packed)]
pub struct AnnounceResponseFixedData { pub struct AnnounceResponseFixedData {
pub transaction_id: TransactionId, pub transaction_id: TransactionId,
@ -186,7 +186,7 @@ pub struct ScrapeResponse {
impl ScrapeResponse { impl ScrapeResponse {
#[inline] #[inline]
pub fn write(&self, bytes: &mut impl Write) -> Result<(), io::Error> { pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_i32::<NetworkEndian>(2)?; bytes.write_i32::<NetworkEndian>(2)?;
bytes.write_all(self.transaction_id.as_bytes())?; bytes.write_all(self.transaction_id.as_bytes())?;
bytes.write_all((*self.torrent_stats.as_slice()).as_bytes())?; bytes.write_all((*self.torrent_stats.as_slice()).as_bytes())?;
@ -211,7 +211,7 @@ pub struct ErrorResponse {
impl ErrorResponse { impl ErrorResponse {
#[inline] #[inline]
pub fn write(&self, bytes: &mut impl Write) -> Result<(), io::Error> { pub fn write_bytes(&self, bytes: &mut impl Write) -> Result<(), io::Error> {
bytes.write_i32::<NetworkEndian>(3)?; bytes.write_i32::<NetworkEndian>(3)?;
bytes.write_all(self.transaction_id.as_bytes())?; bytes.write_all(self.transaction_id.as_bytes())?;
bytes.write_all(self.message.as_bytes())?; bytes.write_all(self.message.as_bytes())?;
@ -304,8 +304,8 @@ mod tests {
fn same_after_conversion(response: Response, ipv4: bool) -> bool { fn same_after_conversion(response: Response, ipv4: bool) -> bool {
let mut buf = Vec::new(); let mut buf = Vec::new();
response.clone().write(&mut buf).unwrap(); response.clone().write_bytes(&mut buf).unwrap();
let r2 = Response::from_bytes(&buf[..], ipv4).unwrap(); let r2 = Response::parse_bytes(&buf[..], ipv4).unwrap();
let success = response == r2; let success = response == r2;

View file

@ -12,6 +12,10 @@ rust-version.workspace = true
readme = "./README.md" readme = "./README.md"
[features]
default = ["tungstenite"]
tungstenite = ["dep:tungstenite"]
[lib] [lib]
name = "aquatic_ws_protocol" name = "aquatic_ws_protocol"
@ -26,7 +30,7 @@ hashbrown = { version = "0.14", features = ["serde"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
simd-json = "0.13" simd-json = "0.13"
tungstenite = "0.21" tungstenite = { version = "0.21", optional = true }
[dev-dependencies] [dev-dependencies]
criterion = "0.5" criterion = "0.5"

View file

@ -15,6 +15,7 @@ pub enum InMessage {
ScrapeRequest(ScrapeRequest), ScrapeRequest(ScrapeRequest),
} }
#[cfg(feature = "tungstenite")]
impl InMessage { impl InMessage {
#[inline] #[inline]
pub fn to_ws_message(&self) -> ::tungstenite::Message { pub fn to_ws_message(&self) -> ::tungstenite::Message {

View file

@ -227,6 +227,7 @@ mod tests {
} }
} }
#[cfg(feature = "tungstenite")]
#[quickcheck] #[quickcheck]
fn quickcheck_serde_identity_in_message(in_message_1: InMessage) -> bool { fn quickcheck_serde_identity_in_message(in_message_1: InMessage) -> bool {
let ws_message = in_message_1.to_ws_message(); let ws_message = in_message_1.to_ws_message();
@ -246,6 +247,7 @@ mod tests {
success success
} }
#[cfg(feature = "tungstenite")]
#[quickcheck] #[quickcheck]
fn quickcheck_serde_identity_out_message(out_message_1: OutMessage) -> bool { fn quickcheck_serde_identity_out_message(out_message_1: OutMessage) -> bool {
let ws_message = out_message_1.to_ws_message(); let ws_message = out_message_1.to_ws_message();

View file

@ -23,6 +23,7 @@ pub enum OutMessage {
ErrorResponse(ErrorResponse), ErrorResponse(ErrorResponse),
} }
#[cfg(feature = "tungstenite")]
impl OutMessage { impl OutMessage {
#[inline] #[inline]
pub fn to_ws_message(&self) -> tungstenite::Message { pub fn to_ws_message(&self) -> tungstenite::Message {