seanmonstar / reqwest

An easy and powerful Rust HTTP Client
https://docs.rs/reqwest
Apache License 2.0
9.72k stars 1.09k forks source link

Query parameters cannot contain byte strings #1613

Open bk2204 opened 2 years ago

bk2204 commented 2 years ago

Reqwest uses serde_urlencoded for query parameters. However, that is designed to handle only UTF-8 strings, and query parameters may contain arbitrary octets encoded according to the RFC 3986.

I filed nox/serde_urlencoded#104 on the underlying library, which was refused because that is designed for application/x-www-form-urlencoded, which allegedly only deals with UTF-8. However, the query parameters are not so restricted by RFC 3986, so a different library probably needs to be used here.

josecelano commented 1 year ago

I'm implementing a client for a BitTorrent HTTP tracker, and it uses two byte arrays parameters in the URL query.

Is there a way to pass raw GET parameters which have already been percent-encoded? I do not see it.

Banyc commented 11 months ago

This is my current workaround for this:


pub struct TrackerRequest<'caller> {
    pub info_hash: &'caller [u8],
    pub peer_id: &'caller [u8],
    pub port: u16,
    pub uploaded: u64,
    pub downloaded: u64,
    pub left: u64,
    pub compact: bool,
}

impl<'a> TrackerRequest<'a> {
    pub fn url(&'a self, metainfo: &'a Metainfo) -> String {
        let url_encoded_info_hash = urlencoding::encode_binary(metainfo.info().hash());
        let url_encoded_peer_id = urlencoding::encode_binary(self.peer_id);

        let mut url = String::new();
        url.push_str(metainfo.announce());
        url.push('?');
        url.push_str("info_hash=");
        url.push_str(&url_encoded_info_hash);
        url.push('&');
        url.push_str("peer_id=");
        url.push_str(&url_encoded_peer_id);
        url.push('&');
        url.push_str("port=");
        url.push_str(&self.port.to_string());
        url.push('&');
        url.push_str("uploaded=");
        url.push_str(&self.uploaded.to_string());
        url.push('&');
        url.push_str("downloaded=");
        url.push_str(&self.downloaded.to_string());
        url.push('&');
        url.push_str("left=");
        url.push_str(&self.left.to_string());
        url.push('&');
        url.push_str("compact=");
        url.push_str(&(self.compact as u8).to_string());
        url
    }
}

Not ideal but it works for now.