mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-03-31 17:55:36 +00:00
Merge pull request #180 from greatest-ape/work-2023-01-29
protocol crates: rename some methods, minor improvements
This commit is contained in:
commit
1807c4a1e9
21 changed files with 103 additions and 82 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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(_) => {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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)).
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue