ipdatainfo 0.1.0

Official Rust client for the ipdata.info IP geolocation, ASN, and threat-intelligence API
Documentation
//! Lightweight single-shot HTTP mock server used by the integration tests.
//! Avoids pulling in a heavy dev-dependency: it accepts one raw TCP
//! connection, replies with the canned status/body, and reports the request
//! line + whether an `X-Api-Key` header was sent.

use std::io::{Read, Write};
use std::net::TcpListener;
use std::sync::mpsc::{self, Receiver};
use std::thread;

/// Captured details of the single request the mock server received.
pub struct CapturedRequest {
    pub request_line: String,
    pub had_api_key_header: bool,
}

/// Starts a background thread listening on an ephemeral localhost port that
/// accepts exactly one connection, sends `status_line`/`body`, then exits.
/// Returns the server's base URL and a channel to receive the captured
/// request once the client has connected.
pub fn spawn(
    status_line: &'static str,
    body: &'static str,
) -> (String, Receiver<CapturedRequest>) {
    let listener = TcpListener::bind("127.0.0.1:0").expect("bind mock server");
    let addr = listener.local_addr().expect("mock server local_addr");
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        if let Ok((mut stream, _)) = listener.accept() {
            let mut buf = [0u8; 8192];
            let n = stream.read(&mut buf).unwrap_or(0);
            let request = String::from_utf8_lossy(&buf[..n]).to_string();
            let request_line = request.lines().next().unwrap_or("").to_string();
            let had_api_key_header = request.to_ascii_lowercase().contains("x-api-key:");
            let _ = tx.send(CapturedRequest {
                request_line,
                had_api_key_header,
            });

            let response = format!(
                "{status_line}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
                body.len()
            );
            let _ = stream.write_all(response.as_bytes());
            let _ = stream.flush();
        }
    });

    (format!("http://{addr}"), rx)
}