seanmonstar / reqwest

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

Is there a way to use a unix socket? #39

Open bbigras opened 7 years ago

bbigras commented 7 years ago

I would like to do the equivalent of:

curl --unix-socket /var/run/docker.sock http://localhost/containers/json

There's the unix_socket crate but I'm not sure if I can use it with reqwest.

seanmonstar commented 7 years ago

There isn't a way yet, but it definitely makes sense to provide when cfg(unix)!

What makes sense for the best way to provide this? Just adding to the connector to look for the unix:// scheme? There doesn't seem to be a standard way to handle this, as I look up various HTTP libraries.

stephanbuys commented 7 years ago

The unix:// scheme seems to be well established, I'd recommend doing what you propose.

bbigras commented 7 years ago

The url crate seems to support the unix:// scheme (see example).

let url = Url::parse("unix:/run/foo.socket").unwrap();

Would it be possible to include the path with something like this?

let url = Url::parse("unix:/var/run/docker.sock/containers/json").unwrap();

I'm also wondering if localhost is passed to the unix socket. If so, I don't know how it would be possible to specify it (or another vhost) using the unix:// scheme.

According to the Docker Remote API page, older curl version used http://containers/json as the URL so I don't know if the localhost part is used.

seanmonstar commented 7 years ago

So looking around, I see two possible routes.

lucab commented 7 years ago

@seanmonstar in case you were looking for feedback, I definitely prefer the first option because it allows to leave the scheme and host-fqdn part untouched. This is important if at the other end of the unix-socket there is a virtualhost-aware server, or something doing TLS-ALPN (or similar upper protocol detection/negotiation).

mathstuf commented 7 years ago

I've wanted "use a socket for HTTP requests" for a while because setting up a proxy on a port doesn't lock it down to just the current user. Not that that project is ever going to use Rust at its network level anytime soon, but it's not an unheard of use case.

seanmonstar commented 7 years ago

I think I prefer this being Proxy::unix(path). For implementation details, it'd need to be slightly different since it would actually connect a unix stream instead of just give a new URL.

farodin91 commented 6 years ago

Any progress in this area?

mathstuf commented 6 years ago

Doing some research in how it is done elsewhere:

farodin91 commented 6 years ago

I saw a implementation of unix socket in boondock using hyper 0.9.0 https://github.com/faradayio/boondock/blob/master/src/unix.rs

mathstuf commented 6 years ago

This also might not be a non-Windows thing in the future as well: https://blogs.msdn.microsoft.com/commandline/2017/12/19/af_unix-comes-to-windows/

jjl commented 6 years ago

I came across the hyperlocal crate today which might be of interest

Roguelazer commented 6 years ago

Is there any workaround now? I was looking at something with the xmlrpc crate (which uses reqwest by default) but unix sockets are a must-have for my environment and it looked like all the Transport stuff in reqwest is currently hidden away in the async_impl module; there didn't seem to be any hooks in ClientBuilder where I could shove a custom Transport.

doctorCC commented 5 years ago

Has there been any progress on this? I am also looking for a way to use the Docker authorization via the Docker socket, but without going through the loopback address.

snawaz commented 5 years ago

@seanmonstar .. I've designed a solution to support Unix Socket... and we already use it in our codebase. I can make it more generic and reqwesty .. and then submit a PR, in case if you're not already working on it. Please let me know your thoughts.

scottlamb commented 5 years ago

@seanmonstar To be precise, I think you mean something like Proxy::all("unix:/var/run/docker.sock") right? as Proxy::http vs Proxy::https vs Proxy::all vs Proxy::custom appears to be about what should be proxied, not how it's proxied. Looks like the argument determines the latter, within ProxyScheme::parse.

lucasyvas commented 5 years ago

It would be nice if this could work on Windows too via named pipes.

asf-stripe commented 5 years ago

I found this discussion after trying to use reqwest with our unix domain socket-based authentication proxy, and was reminded of this: One of the things that I find that go's http.Transport got right is the way you establish an HTTP-able connection. The Transport has Dial (and, because it's go, DialContext) field that holds a function, which establishes the unencrypted TCP connection.

I kind of wish that reqwest allowed this kind of customization: I'd like to be able to tell the system in what way to establish a network connection. Then, reqwest could be entirely independent of how the proxy wants to be reached: named pipe, unix domain socket, CONNECT proxy over open SMTP relay, etc.

zicklag commented 4 years ago

I would also use this if it was available. I'm making a REST API, but I need to be able to access it by mounting a Unix socket into a container. Reqwest seems pretty de-facto for making HTTP requests so it would be really nice to be able to use it for Unix sockets too.

mathstuf commented 4 years ago

I'll also note that Docker's socket talks HTTP.

vv9k commented 4 years ago

What's the current status of this? As @snawaz mentioned it's already partially ready. Would love some updates as I'd love to talk to docker over a unix socket. Cheers :)

danieleades commented 4 years ago

This is really easy to do with Hyper. Any chance of exposing the necessary methods? Or better yet, creating a Request Client from a Hyper Client?

mathstuf commented 4 years ago

I'm not seeing anything obvious in the docs. Do you have a link to the methods involved (or example code)?

danieleades commented 4 years ago

I'm not seeing anything obvious in the docs. Do you have a link to the methods involved (or example code)?

if you were talking to me, you could take a look at my pull request on hyperlocal for some code examples

kevincox commented 4 years ago

I would recommend the unix: scheme as well because it means that all reqwest users would get it automatically (or with a feature enabled). It would also mean that all tools that use reqwest would have a consistent syntax for specifying UNIX sockets.

Absolucy commented 3 years ago

with the hyper client, it's easy to send http requests over anything that implements AsyncRead and AsyncWrite

let (mut request_sender, connection) = hyper::client::conn::Builder::new()
    .handshake::<MyCustomTcpStream, Body>(stream)
    .await?;

let request = Request::builder()
    .header("Host", "example.com")
    .method("GET")
    .body(Body::from("hello world!"))?;

let response = request_sender.send_request(request).await?;
ghost commented 3 years ago

Would be nice if it was possible to use a unix domain socket as a socks5h proxy (tor provides a socks uds option for example)

jvjfk commented 2 years ago

Hi everyone. Any update on this issue? Since it has been opened for 4 years I wonder if there is anything to do to help :)

ahl commented 2 years ago

This would be a great addition!

Kijewski commented 2 years ago

Apache's ProxyPass uses the syntax unix:/path/to/socket|http://example.com/path?query, i.e. socket path + pipe + URI, which I think is very readable, and has the advantage that you can use it with HTTPS, basic authorization, etc.

insanitybit commented 2 years ago

It seems like there's an open PR that would solve this. Any change to have this merged in?

bchalios commented 2 years ago

It would be really nice to see this PR merged.

insanitybit commented 1 year ago

For anyone running into this, I've found that the hyper-socket crate requires very few changes to work with the latest hyper/ tokio.

https://docs.rs/hyper-socket/latest/hyper_socket/

Doesn't solve the issue for reqwest, but still, it's something.

pythops commented 1 year ago

Would be really great to have :pray:

Mart-Bogdan commented 1 year ago

Would be nice to have.

I agree with @asf-stripe that instead of file name, we can provide callback that creates socket, it can be even linux File descriptor. And on top of it create helper method that accepts file name as String.

Theoretically it can even be some kind of rust stream instead of raw FD, but I don't know inner workings of library to judge.

If out can be some stream, then even TLS part can be build on top of this.

Ambyjkl commented 1 year ago

Here is the format used by nginx, which i think is better than anything unix://: http://unix:{sockfile_path}:/{pathname}?{query}. The protocol is http/https, just instead of an IP address or DNS hostname, it's a unix socket file path

Mart-Bogdan commented 1 year ago

@Ambyjkl you still need to provide domain name somehow.

Domain name is used not only to resolve IP address, but it is also transferred as Host: header.

It can be used by server, if two websites are living on same socket.

And some servers won't return data for incorrect host.

afidegnum commented 10 months ago

So, can i use reqwest with Unix sockets? what's the basic syntax?

7ritn commented 6 months ago

I was struggeling with getting this to work as well. I wanted to perform curl --unix-socket /run/user/1000/podman/podman.sock http://d/v4.0.0/libpod/containers/lldap/logs?stdout=true in rust. I finally got it to work with hyper and axum.

use axum::{
    body::Body,
    http::{Method, Request, StatusCode}
};
use http_body_util::BodyExt;
use hyper_util::rt::TokioIo;

use std::path::PathBuf;
use tokio::net::UnixStream;

pub async fn client(container_name: String) {

    let path = PathBuf::from("/run/user/1000/podman/podman.sock");

    let stream = TokioIo::new(UnixStream::connect(path).await.unwrap());
    let (mut sender, conn) = hyper::client::conn::http1::handshake(stream).await.unwrap();
    tokio::task::spawn(async move {
        if let Err(err) = conn.await {
            println!("Connection failed: {:?}", err);
        }
    });

    let request = Request::builder()
        .method(Method::GET)
        .uri("/v4.0.0/libpod/containers/lldap/logs?stdout=true")
        .header("Host", "d")
        .body(Body::empty())
        .unwrap();

    let response = sender.send_request(request).await.unwrap();

    assert_eq!(response.status(), StatusCode::OK);

    let body = response.collect().await.unwrap().to_bytes();
    let body = String::from_utf8(body.to_vec()).unwrap();
    println!("{}", body);
}

#[tokio::main]
async fn main() {
    client("lldap".to_string()).await;
}
cablehead commented 6 months ago

This is a small example using just hyper (no need for axum), to make requests to a local unix domain socket: https://github.com/cablehead/stacks/blob/main/src-tauri/src/cli.rs#L30

Dropping back to raw hyper though, you lose, http1.1 / http2 negotiation, ssl, and connection pooling.

Although 🤔 .. those things are generally less necessary for the use cases where you'd find yourself wanting a local socket..

danieleades commented 6 months ago

there have been several solutions posted for solving this in different ways using hyper going back as far as 2018. This thread is specifically about using reqwest with a unix socket

sergi0g commented 4 months ago

Is there any update on this?

kczimm commented 4 months ago

+1

kanpov commented 3 months ago

How much work would be necessary to make reqwest fully generic over any type implementing hyper::rt::Read and hyper::rt::Write? This would allow not only unix sockets via tokio's net module, but also vsock and so on.

mikibella commented 3 weeks ago

Any updates on this topic?

frankdavid commented 2 weeks ago

We'd also be interested in using reqwest with a custom Connector. We'd like to customize how the TCP/TLS connection is built up but use reqwest for HTTP communication.

sergi0g commented 2 weeks ago

This is the oldest issue in this repository (8 years old) and it's a shame it's still open. So many people are showing their support and we're still waiting for an answer. The last comment from the maintainer was 7 years ago! A "yes we're working on it" or "no it won't be implemented" is enough.

Ambyjkl commented 2 weeks ago

What I'd like to see: being able to run reqwest over an arbitrary AsyncRead + AsyncWrite, where one can pass in one of our own and just manually handle connecting to stuff like unix socket. I believe hyper lets you do this, so just exposing it to reqwest would be great