jsonrpc/http/
simple_http.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! This module implements a minimal and non standard conforming HTTP 1.0
4//! round-tripper that works with the bitcoind RPC server. This can be used
5//! if minimal dependencies are a goal and synchronous communication is ok.
6
7#[cfg(feature = "proxy")]
8use socks::Socks5Stream;
9use std::io::{BufRead, BufReader, Read, Write};
10#[cfg(not(jsonrpc_fuzz))]
11use std::net::TcpStream;
12use std::net::{SocketAddr, ToSocketAddrs};
13use std::sync::{Arc, Mutex, MutexGuard};
14use std::time::Duration;
15use std::{error, fmt, io, net, num};
16
17use crate::client::Transport;
18use crate::http::DEFAULT_PORT;
19#[cfg(feature = "proxy")]
20use crate::http::DEFAULT_PROXY_PORT;
21use crate::{Request, Response};
22
23/// Absolute maximum content length allowed before cutting off the response.
24const FINAL_RESP_ALLOC: u64 = 1024 * 1024 * 1024;
25
26#[cfg(not(jsonrpc_fuzz))]
27const DEFAULT_TIMEOUT: Duration = Duration::from_secs(15);
28
29#[cfg(jsonrpc_fuzz)]
30const DEFAULT_TIMEOUT: Duration = Duration::from_millis(1);
31
32/// Simple HTTP transport that implements the necessary subset of HTTP for
33/// running a bitcoind RPC client.
34#[derive(Clone, Debug)]
35pub struct SimpleHttpTransport {
36    addr: net::SocketAddr,
37    path: String,
38    timeout: Duration,
39    /// The value of the `Authorization` HTTP header.
40    basic_auth: Option<String>,
41    #[cfg(feature = "proxy")]
42    proxy_addr: net::SocketAddr,
43    #[cfg(feature = "proxy")]
44    proxy_auth: Option<(String, String)>,
45    sock: Arc<Mutex<Option<BufReader<TcpStream>>>>,
46}
47
48impl Default for SimpleHttpTransport {
49    fn default() -> Self {
50        SimpleHttpTransport {
51            addr: net::SocketAddr::new(
52                net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)),
53                DEFAULT_PORT,
54            ),
55            path: "/".to_owned(),
56            timeout: DEFAULT_TIMEOUT,
57            basic_auth: None,
58            #[cfg(feature = "proxy")]
59            proxy_addr: net::SocketAddr::new(
60                net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)),
61                DEFAULT_PROXY_PORT,
62            ),
63            #[cfg(feature = "proxy")]
64            proxy_auth: None,
65            sock: Arc::new(Mutex::new(None)),
66        }
67    }
68}
69
70impl SimpleHttpTransport {
71    /// Constructs a new [`SimpleHttpTransport`] with default parameters.
72    pub fn new() -> Self {
73        SimpleHttpTransport::default()
74    }
75
76    /// Returns a builder for [`SimpleHttpTransport`].
77    pub fn builder() -> Builder {
78        Builder::new()
79    }
80
81    /// Replaces the URL of the transport.
82    pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
83        let url = check_url(url)?;
84        self.addr = url.0;
85        self.path = url.1;
86        Ok(())
87    }
88
89    /// Replaces only the path part of the URL.
90    pub fn set_url_path(&mut self, path: String) {
91        self.path = path;
92    }
93
94    fn request<R>(&self, req: impl serde::Serialize) -> Result<R, Error>
95    where
96        R: for<'a> serde::de::Deserialize<'a>,
97    {
98        match self.try_request(req) {
99            Ok(response) => Ok(response),
100            Err(err) => {
101                // No part of this codebase should panic, so unwrapping a mutex lock is fine
102                *self.sock.lock().expect("poisoned mutex") = None;
103                Err(err)
104            }
105        }
106    }
107
108    #[cfg(feature = "proxy")]
109    fn fresh_socket(&self) -> Result<TcpStream, Error> {
110        let stream = if let Some((username, password)) = &self.proxy_auth {
111            Socks5Stream::connect_with_password(
112                self.proxy_addr,
113                self.addr,
114                username.as_str(),
115                password.as_str(),
116            )?
117        } else {
118            Socks5Stream::connect(self.proxy_addr, self.addr)?
119        };
120        Ok(stream.into_inner())
121    }
122
123    #[cfg(not(feature = "proxy"))]
124    fn fresh_socket(&self) -> Result<TcpStream, Error> {
125        let stream = TcpStream::connect_timeout(&self.addr, self.timeout)?;
126        stream.set_read_timeout(Some(self.timeout))?;
127        stream.set_write_timeout(Some(self.timeout))?;
128        Ok(stream)
129    }
130
131    fn try_request<R>(&self, req: impl serde::Serialize) -> Result<R, Error>
132    where
133        R: for<'a> serde::de::Deserialize<'a>,
134    {
135        // No part of this codebase should panic, so unwrapping a mutex lock is fine
136        let mut sock_lock: MutexGuard<Option<_>> = self.sock.lock().expect("poisoned mutex");
137        if sock_lock.is_none() {
138            *sock_lock = Some(BufReader::new(self.fresh_socket()?));
139        };
140        // In the immediately preceding block, we made sure that `sock` is non-`None`,
141        // so unwrapping here is fine.
142        let sock: &mut BufReader<_> = sock_lock.as_mut().unwrap();
143
144        // Serialize the body first so we can set the Content-Length header.
145        let body = serde_json::to_vec(&req)?;
146
147        let mut request_bytes = Vec::new();
148
149        request_bytes.write_all(b"POST ")?;
150        request_bytes.write_all(self.path.as_bytes())?;
151        request_bytes.write_all(b" HTTP/1.1\r\n")?;
152        // Write headers
153        request_bytes.write_all(b"host: ")?;
154        request_bytes.write_all(self.addr.to_string().as_bytes())?;
155        request_bytes.write_all(b"\r\n")?;
156        request_bytes.write_all(b"Content-Type: application/json\r\n")?;
157        request_bytes.write_all(b"Content-Length: ")?;
158        request_bytes.write_all(body.len().to_string().as_bytes())?;
159        request_bytes.write_all(b"\r\n")?;
160        if let Some(ref auth) = self.basic_auth {
161            request_bytes.write_all(b"Authorization: ")?;
162            request_bytes.write_all(auth.as_ref())?;
163            request_bytes.write_all(b"\r\n")?;
164        }
165        // Write body
166        request_bytes.write_all(b"\r\n")?;
167        request_bytes.write_all(&body)?;
168
169        // Send HTTP request
170        let write_success = sock.get_mut().write_all(request_bytes.as_slice()).is_ok()
171            && sock.get_mut().flush().is_ok();
172
173        // This indicates the socket is broken so let's retry the send once with a fresh socket
174        if !write_success {
175            *sock.get_mut() = self.fresh_socket()?;
176            sock.get_mut().write_all(request_bytes.as_slice())?;
177            sock.get_mut().flush()?;
178        }
179
180        // Parse first HTTP response header line
181        let mut header_buf = String::new();
182        let read_success = sock.read_line(&mut header_buf).is_ok();
183
184        // This is another possible indication that the socket is broken so let's retry the send once
185        // with a fresh socket IF the write attempt has not already experienced a failure
186        if (!read_success || header_buf.is_empty()) && write_success {
187            *sock.get_mut() = self.fresh_socket()?;
188            sock.get_mut().write_all(request_bytes.as_slice())?;
189            sock.get_mut().flush()?;
190
191            sock.read_line(&mut header_buf)?;
192        }
193
194        if header_buf.len() < 12 {
195            return Err(Error::HttpResponseTooShort {
196                actual: header_buf.len(),
197                needed: 12,
198            });
199        }
200        if !header_buf.as_bytes()[..12].is_ascii() {
201            return Err(Error::HttpResponseNonAsciiHello(header_buf.as_bytes()[..12].to_vec()));
202        }
203        if !header_buf.starts_with("HTTP/1.1 ") {
204            return Err(Error::HttpResponseBadHello {
205                actual: header_buf[0..9].into(),
206                expected: "HTTP/1.1 ".into(),
207            });
208        }
209        let response_code = match header_buf[9..12].parse::<u16>() {
210            Ok(n) => n,
211            Err(e) => return Err(Error::HttpResponseBadStatus(header_buf[9..12].into(), e)),
212        };
213
214        // Parse response header fields
215        let mut content_length = None;
216        loop {
217            header_buf.clear();
218            sock.read_line(&mut header_buf)?;
219            if header_buf == "\r\n" {
220                break;
221            }
222            header_buf.make_ascii_lowercase();
223
224            const CONTENT_LENGTH: &str = "content-length: ";
225            if let Some(s) = header_buf.strip_prefix(CONTENT_LENGTH) {
226                content_length = Some(
227                    s.trim()
228                        .parse::<u64>()
229                        .map_err(|e| Error::HttpResponseBadContentLength(s.into(), e))?,
230                );
231            }
232        }
233
234        if response_code == 401 {
235            // There is no body in a 401 response, so don't try to read it
236            return Err(Error::HttpErrorCode(response_code));
237        }
238
239        // Read up to `content_length` bytes. Note that if there is no content-length
240        // header, we will assume an effectively infinite content length, i.e. we will
241        // just keep reading from the socket until it is closed.
242        let mut reader = match content_length {
243            None => sock.take(FINAL_RESP_ALLOC),
244            Some(n) if n > FINAL_RESP_ALLOC => {
245                return Err(Error::HttpResponseContentLengthTooLarge {
246                    length: n,
247                    max: FINAL_RESP_ALLOC,
248                });
249            }
250            Some(n) => sock.take(n),
251        };
252
253        // Attempt to parse the response. Don't check the HTTP error code until
254        // after parsing, since Bitcoin Core will often return a descriptive JSON
255        // error structure which is more useful than the error code.
256        match serde_json::from_reader(&mut reader) {
257            Ok(s) => {
258                if content_length.is_some() {
259                    reader.bytes().count(); // consume any trailing bytes
260                }
261                Ok(s)
262            }
263            Err(e) => {
264                // If the response was not 200, assume the parse failed because of that
265                if response_code != 200 {
266                    Err(Error::HttpErrorCode(response_code))
267                } else {
268                    // If it was 200 then probably it was legitimately a parse error
269                    Err(e.into())
270                }
271            }
272        }
273    }
274}
275
276/// Does some very basic manual URL parsing because the uri/url crates
277/// all have unicode-normalization as a dependency and that's broken.
278fn check_url(url: &str) -> Result<(SocketAddr, String), Error> {
279    // The fallback port in case no port was provided.
280    // This changes when the http or https scheme was provided.
281    let mut fallback_port = DEFAULT_PORT;
282
283    // We need to get the hostname and the port.
284    // (1) Split scheme
285    let after_scheme = {
286        let mut split = url.splitn(2, "://");
287        let s = split.next().unwrap();
288        match split.next() {
289            None => s, // no scheme present
290            Some(after) => {
291                // Check if the scheme is http or https.
292                if s == "http" {
293                    fallback_port = 80;
294                } else if s == "https" {
295                    fallback_port = 443;
296                } else {
297                    return Err(Error::url(url, "scheme should be http or https"));
298                }
299                after
300            }
301        }
302    };
303    // (2) split off path
304    let (before_path, path) = {
305        if let Some(slash) = after_scheme.find('/') {
306            (&after_scheme[0..slash], &after_scheme[slash..])
307        } else {
308            (after_scheme, "/")
309        }
310    };
311    // (3) split off auth part
312    let after_auth = {
313        let mut split = before_path.splitn(2, '@');
314        let s = split.next().unwrap();
315        split.next().unwrap_or(s)
316    };
317
318    // (4) Parse into socket address.
319    // At this point we either have <host_name> or <host_name_>:<port>
320    // `std::net::ToSocketAddrs` requires `&str` to have <host_name_>:<port> format.
321    let mut addr = match after_auth.to_socket_addrs() {
322        Ok(addr) => addr,
323        Err(_) => {
324            // Invalid socket address. Try to add port.
325            format!("{}:{}", after_auth, fallback_port).to_socket_addrs()?
326        }
327    };
328
329    match addr.next() {
330        Some(a) => Ok((a, path.to_owned())),
331        None => Err(Error::url(url, "invalid hostname: error extracting socket address")),
332    }
333}
334
335impl Transport for SimpleHttpTransport {
336    fn send_request(&self, req: Request) -> Result<Response, crate::Error> {
337        Ok(self.request(req)?)
338    }
339
340    fn send_batch(&self, reqs: &[Request]) -> Result<Vec<Response>, crate::Error> {
341        Ok(self.request(reqs)?)
342    }
343
344    fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result {
345        write!(f, "http://{}:{}{}", self.addr.ip(), self.addr.port(), self.path)
346    }
347}
348
349/// Builder for simple bitcoind [`SimpleHttpTransport`].
350#[derive(Clone, Debug)]
351pub struct Builder {
352    tp: SimpleHttpTransport,
353}
354
355impl Builder {
356    /// Constructs a new [`Builder`] with default configuration.
357    pub fn new() -> Builder {
358        Builder {
359            tp: SimpleHttpTransport::new(),
360        }
361    }
362
363    /// Sets the timeout after which requests will abort if they aren't finished.
364    pub fn timeout(mut self, timeout: Duration) -> Self {
365        self.tp.timeout = timeout;
366        self
367    }
368
369    /// Sets the URL of the server to the transport.
370    pub fn url(mut self, url: &str) -> Result<Self, Error> {
371        self.tp.set_url(url)?;
372        Ok(self)
373    }
374
375    /// Adds authentication information to the transport.
376    pub fn auth<S: AsRef<str>>(mut self, user: S, pass: Option<S>) -> Self {
377        let mut auth = user.as_ref().to_owned();
378        auth.push(':');
379        if let Some(ref pass) = pass {
380            auth.push_str(pass.as_ref());
381        }
382        self.tp.basic_auth = Some(format!("Basic {}", &base64::encode(auth.as_bytes())));
383        self
384    }
385
386    /// Adds authentication information to the transport using a cookie string ('user:pass').
387    pub fn cookie_auth<S: AsRef<str>>(mut self, cookie: S) -> Self {
388        self.tp.basic_auth = Some(format!("Basic {}", &base64::encode(cookie.as_ref().as_bytes())));
389        self
390    }
391
392    /// Adds proxy address to the transport for SOCKS5 proxy.
393    #[cfg(feature = "proxy")]
394    pub fn proxy_addr<S: AsRef<str>>(mut self, proxy_addr: S) -> Result<Self, Error> {
395        // We don't expect path in proxy address.
396        self.tp.proxy_addr = check_url(proxy_addr.as_ref())?.0;
397        Ok(self)
398    }
399
400    /// Adds optional proxy authentication as ('username', 'password').
401    #[cfg(feature = "proxy")]
402    pub fn proxy_auth<S: AsRef<str>>(mut self, user: S, pass: S) -> Self {
403        self.tp.proxy_auth =
404            Some((user, pass)).map(|(u, p)| (u.as_ref().to_string(), p.as_ref().to_string()));
405        self
406    }
407
408    /// Builds the final [`SimpleHttpTransport`].
409    pub fn build(self) -> SimpleHttpTransport {
410        self.tp
411    }
412}
413
414impl Default for Builder {
415    fn default() -> Self {
416        Builder::new()
417    }
418}
419
420impl crate::Client {
421    /// Creates a new JSON-RPC client using a bare-minimum HTTP transport.
422    pub fn simple_http(
423        url: &str,
424        user: Option<String>,
425        pass: Option<String>,
426    ) -> Result<crate::Client, Error> {
427        let mut builder = Builder::new().url(url)?;
428        if let Some(user) = user {
429            builder = builder.auth(user, pass);
430        }
431        Ok(crate::Client::with_transport(builder.build()))
432    }
433
434    /// Creates a new JSON_RPC client using a HTTP-Socks5 proxy transport.
435    #[cfg(feature = "proxy")]
436    pub fn http_proxy(
437        url: &str,
438        user: Option<String>,
439        pass: Option<String>,
440        proxy_addr: &str,
441        proxy_auth: Option<(&str, &str)>,
442    ) -> Result<crate::Client, Error> {
443        let mut builder = Builder::new().url(url)?;
444        if let Some(user) = user {
445            builder = builder.auth(user, pass);
446        }
447        builder = builder.proxy_addr(proxy_addr)?;
448        if let Some((user, pass)) = proxy_auth {
449            builder = builder.proxy_auth(user, pass);
450        }
451        let tp = builder.build();
452        Ok(crate::Client::with_transport(tp))
453    }
454}
455
456/// Error that can happen when sending requests.
457#[derive(Debug)]
458pub enum Error {
459    /// An invalid URL was passed.
460    InvalidUrl {
461        /// The URL passed.
462        url: String,
463        /// The reason the URL is invalid.
464        reason: &'static str,
465    },
466    /// An error occurred on the socket layer.
467    SocketError(io::Error),
468    /// The HTTP response was too short to even fit a HTTP 1.1 header.
469    HttpResponseTooShort {
470        /// The total length of the response.
471        actual: usize,
472        /// Minimum length we can parse.
473        needed: usize,
474    },
475    /// The HTTP response started with a HTTP/1.1 line which was not ASCII.
476    HttpResponseNonAsciiHello(Vec<u8>),
477    /// The HTTP response did not start with HTTP/1.1
478    HttpResponseBadHello {
479        /// Actual HTTP-whatever string.
480        actual: String,
481        /// The hello string of the HTTP version we support.
482        expected: String,
483    },
484    /// Could not parse the status value as a number.
485    HttpResponseBadStatus(String, num::ParseIntError),
486    /// Could not parse the status value as a number.
487    HttpResponseBadContentLength(String, num::ParseIntError),
488    /// The indicated content-length header exceeded our maximum.
489    HttpResponseContentLengthTooLarge {
490        /// The length indicated in the content-length header.
491        length: u64,
492        /// Our hard maximum on number of bytes we'll try to read.
493        max: u64,
494    },
495    /// Unexpected HTTP error code (non-200).
496    HttpErrorCode(u16),
497    /// Received EOF before getting as many bytes as were indicated by the content-length header.
498    IncompleteResponse {
499        /// The content-length header.
500        content_length: u64,
501        /// The number of bytes we actually read.
502        n_read: u64,
503    },
504    /// JSON parsing error.
505    Json(serde_json::Error),
506}
507
508impl Error {
509    /// Utility method to create [`Error::InvalidUrl`] variants.
510    fn url<U: Into<String>>(url: U, reason: &'static str) -> Error {
511        Error::InvalidUrl {
512            url: url.into(),
513            reason,
514        }
515    }
516}
517
518impl fmt::Display for Error {
519    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
520        use Error::*;
521
522        match *self {
523            InvalidUrl {
524                ref url,
525                ref reason,
526            } => write!(f, "invalid URL '{}': {}", url, reason),
527            SocketError(ref e) => write!(f, "Couldn't connect to host: {}", e),
528            HttpResponseTooShort {
529                ref actual,
530                ref needed,
531            } => {
532                write!(f, "HTTP response too short: length {}, needed {}.", actual, needed)
533            }
534            HttpResponseNonAsciiHello(ref bytes) => {
535                write!(f, "HTTP response started with non-ASCII {:?}", bytes)
536            }
537            HttpResponseBadHello {
538                ref actual,
539                ref expected,
540            } => {
541                write!(f, "HTTP response started with `{}`; expected `{}`.", actual, expected)
542            }
543            HttpResponseBadStatus(ref status, ref err) => {
544                write!(f, "HTTP response had bad status code `{}`: {}.", status, err)
545            }
546            HttpResponseBadContentLength(ref len, ref err) => {
547                write!(f, "HTTP response had bad content length `{}`: {}.", len, err)
548            }
549            HttpResponseContentLengthTooLarge {
550                length,
551                max,
552            } => {
553                write!(f, "HTTP response content length {} exceeds our max {}.", length, max)
554            }
555            HttpErrorCode(c) => write!(f, "unexpected HTTP code: {}", c),
556            IncompleteResponse {
557                content_length,
558                n_read,
559            } => {
560                write!(
561                    f,
562                    "read {} bytes but HTTP response content-length header was {}.",
563                    n_read, content_length
564                )
565            }
566            Json(ref e) => write!(f, "JSON error: {}", e),
567        }
568    }
569}
570
571impl error::Error for Error {
572    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
573        use self::Error::*;
574
575        match *self {
576            InvalidUrl {
577                ..
578            }
579            | HttpResponseTooShort {
580                ..
581            }
582            | HttpResponseNonAsciiHello(..)
583            | HttpResponseBadHello {
584                ..
585            }
586            | HttpResponseBadStatus(..)
587            | HttpResponseBadContentLength(..)
588            | HttpResponseContentLengthTooLarge {
589                ..
590            }
591            | HttpErrorCode(_)
592            | IncompleteResponse {
593                ..
594            } => None,
595            SocketError(ref e) => Some(e),
596            Json(ref e) => Some(e),
597        }
598    }
599}
600
601impl From<io::Error> for Error {
602    fn from(e: io::Error) -> Self {
603        Error::SocketError(e)
604    }
605}
606
607impl From<serde_json::Error> for Error {
608    fn from(e: serde_json::Error) -> Self {
609        Error::Json(e)
610    }
611}
612
613impl From<Error> for crate::Error {
614    fn from(e: Error) -> crate::Error {
615        match e {
616            Error::Json(e) => crate::Error::Json(e),
617            e => crate::Error::Transport(Box::new(e)),
618        }
619    }
620}
621
622/// Global mutex used by the fuzzing harness to inject data into the read end of the TCP stream.
623#[cfg(jsonrpc_fuzz)]
624pub static FUZZ_TCP_SOCK: Mutex<Option<io::Cursor<Vec<u8>>>> = Mutex::new(None);
625
626#[cfg(jsonrpc_fuzz)]
627#[derive(Clone, Debug)]
628struct TcpStream;
629
630#[cfg(jsonrpc_fuzz)]
631mod impls {
632    use super::*;
633    impl Read for TcpStream {
634        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
635            match *FUZZ_TCP_SOCK.lock().unwrap() {
636                Some(ref mut cursor) => io::Read::read(cursor, buf),
637                None => Ok(0),
638            }
639        }
640    }
641    impl Write for TcpStream {
642        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
643            io::sink().write(buf)
644        }
645        fn flush(&mut self) -> io::Result<()> {
646            Ok(())
647        }
648    }
649
650    impl TcpStream {
651        pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<Self> {
652            Ok(TcpStream)
653        }
654        pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
655            Ok(())
656        }
657        pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
658            Ok(())
659        }
660    }
661}
662
663#[cfg(test)]
664mod tests {
665    use std::net;
666    #[cfg(feature = "proxy")]
667    use std::str::FromStr;
668
669    use super::*;
670    use crate::Client;
671
672    #[test]
673    fn test_urls() {
674        let addr: net::SocketAddr = ("localhost", 22).to_socket_addrs().unwrap().next().unwrap();
675        let urls = [
676            "localhost:22",
677            "http://localhost:22/",
678            "https://localhost:22/walletname/stuff?it=working",
679            "http://me:weak@localhost:22/wallet",
680        ];
681        for u in &urls {
682            let tp = Builder::new().url(u).unwrap().build();
683            assert_eq!(tp.addr, addr);
684        }
685
686        // Default port and 80 and 443 fill-in.
687        let addr: net::SocketAddr = ("localhost", 80).to_socket_addrs().unwrap().next().unwrap();
688        let tp = Builder::new().url("http://localhost/").unwrap().build();
689        assert_eq!(tp.addr, addr);
690        let addr: net::SocketAddr = ("localhost", 443).to_socket_addrs().unwrap().next().unwrap();
691        let tp = Builder::new().url("https://localhost/").unwrap().build();
692        assert_eq!(tp.addr, addr);
693        let addr: net::SocketAddr =
694            ("localhost", super::DEFAULT_PORT).to_socket_addrs().unwrap().next().unwrap();
695        let tp = Builder::new().url("localhost").unwrap().build();
696        assert_eq!(tp.addr, addr);
697
698        let valid_urls = [
699            "localhost",
700            "127.0.0.1:8080",
701            "http://127.0.0.1:8080/",
702            "http://127.0.0.1:8080/rpc/test",
703            "https://127.0.0.1/rpc/test",
704            "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8300",
705            "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
706        ];
707        for u in &valid_urls {
708            let (addr, path) = check_url(u).unwrap();
709            let builder = Builder::new().url(u).unwrap_or_else(|_| panic!("error for: {}", u));
710            assert_eq!(builder.tp.addr, addr);
711            assert_eq!(builder.tp.path, path);
712            assert_eq!(builder.tp.timeout, DEFAULT_TIMEOUT);
713            assert_eq!(builder.tp.basic_auth, None);
714            #[cfg(feature = "proxy")]
715            assert_eq!(builder.tp.proxy_addr, SocketAddr::from_str("127.0.0.1:9050").unwrap());
716        }
717
718        let invalid_urls = [
719            "127.0.0.1.0:8080",
720            "httpx://127.0.0.1:8080/",
721            "ftp://127.0.0.1:8080/rpc/test",
722            "http://127.0.0./rpc/test",
723            // NB somehow, Rust's IpAddr accepts "127.0.0" and adds the extra 0..
724        ];
725        for u in &invalid_urls {
726            if let Ok(b) = Builder::new().url(u) {
727                let tp = b.build();
728                panic!("expected error for url {}, got {:?}", u, tp);
729            }
730        }
731    }
732
733    #[test]
734    fn construct() {
735        let tp = Builder::new()
736            .timeout(Duration::from_millis(100))
737            .url("localhost:22")
738            .unwrap()
739            .auth("user", None)
740            .build();
741        let _ = Client::with_transport(tp);
742
743        let _ = Client::simple_http("localhost:22", None, None).unwrap();
744    }
745
746    #[cfg(feature = "proxy")]
747    #[test]
748    fn construct_with_proxy() {
749        let tp = Builder::new()
750            .timeout(Duration::from_millis(100))
751            .url("localhost:22")
752            .unwrap()
753            .auth("user", None)
754            .proxy_addr("127.0.0.1:9050")
755            .unwrap()
756            .build();
757        let _ = Client::with_transport(tp);
758
759        let _ = Client::http_proxy(
760            "localhost:22",
761            None,
762            None,
763            "127.0.0.1:9050",
764            Some(("user", "password")),
765        )
766        .unwrap();
767    }
768
769    /// Test that the client will detect that a socket is closed and open a fresh one before sending
770    /// the request
771    #[cfg(all(not(feature = "proxy"), not(jsonrpc_fuzz)))]
772    #[test]
773    fn request_to_closed_socket() {
774        use serde_json::{Number, Value};
775        use std::net::{Shutdown, TcpListener};
776        use std::sync::mpsc;
777        use std::thread;
778
779        let (tx, rx) = mpsc::sync_channel(1);
780
781        thread::spawn(move || {
782            let server = TcpListener::bind("localhost:0").expect("Binding a Tcp Listener");
783            tx.send(server.local_addr().unwrap().port()).unwrap();
784            for (request_id, stream) in server.incoming().enumerate() {
785                let mut stream = stream.unwrap();
786
787                let buf_reader = BufReader::new(&mut stream);
788
789                let _http_request: Vec<_> = buf_reader
790                    .lines()
791                    .map(|result| result.unwrap())
792                    .take_while(|line| !line.is_empty())
793                    .collect();
794
795                let response = Response {
796                    result: None,
797                    error: None,
798                    id: Value::Number(Number::from(request_id)),
799                    jsonrpc: Some(String::from("2.0")),
800                };
801                let response_str = serde_json::to_string(&response).unwrap();
802
803                stream.write_all(b"HTTP/1.1 200\r\n").unwrap();
804                stream.write_all(b"Content-Length: ").unwrap();
805                stream.write_all(response_str.len().to_string().as_bytes()).unwrap();
806                stream.write_all(b"\r\n").unwrap();
807                stream.write_all(b"\r\n").unwrap();
808                stream.write_all(response_str.as_bytes()).unwrap();
809                stream.flush().unwrap();
810
811                stream.shutdown(Shutdown::Both).unwrap();
812            }
813        });
814
815        // Give the server thread a second to start up and listen
816        thread::sleep(Duration::from_secs(1));
817
818        let port = rx.recv().unwrap();
819        let client =
820            Client::simple_http(format!("localhost:{}", port).as_str(), None, None).unwrap();
821        let request = client.build_request("test_request", &[]);
822        let result = client.send_request(request).unwrap();
823        assert_eq!(result.id, Value::Number(Number::from(0)));
824        thread::sleep(Duration::from_secs(1));
825        let request = client.build_request("test_request2", &[]);
826        let result2 = client.send_request(request)
827            .expect("This second request should not be an Err like `Err(Transport(HttpResponseTooShort { actual: 0, needed: 12 }))`");
828        assert_eq!(result2.id, Value::Number(Number::from(1)));
829    }
830}