connect-es icon indicating copy to clipboard operation
connect-es copied to clipboard

Unix domain socket

Open jacobwgillespie opened this issue 1 year ago • 5 comments

Is your feature request related to a problem? Please describe.

With other gRPC libraries, I'm able to connect to a gRPC server listening on a unix domain socket (e.g. unix:///path/to/server.sock).

Describe the solution you'd like

I don't think this is possible with createGrpcTransport() at the moment, but it would be awesome if it was.

Describe alternatives you've considered

At the moment, I have to use another gRPC client library - I'd prefer to use @bufbuild/connect instead.

jacobwgillespie avatar Aug 14 '23 10:08 jacobwgillespie

Unfortunately, the Node.js http2 module does not support UDS out of the box. I assume that it's possible to support this, but it's likely not completely trivial.

In general, we aim to not replicate every gRPC feature, and rely on the standard library of the underlying platform. If Node.js were to support UDS in http2.connect, we'd be happy to make any necessary changes (I assume we'll need to treat baseUrl a bit differently).

If someone comes up with a reliable alternative to http2.connect - demonstrated with a code example - we would also be happy to integrate it, or accept contributions.

timostamm avatar Aug 30 '23 18:08 timostamm

I may be missing something, but I believe http2 supports UDS. I copied this example from the docs and changed it to bind to a file instead of a port:

import * as http2 from 'node:http2'

const server = http2.createServer()

server.on('stream', (stream, headers) => {
  stream.respond({
    'content-type': 'text/html charset=utf-8',
    ':status': 200,
  })
  stream.end('<h1>Hello World</h1>')
})

// server.listen(8000)
server.listen('./example.sock')
$ curl -v --http2-prior-knowledge --unix-socket ./example.sock http://test
*   Trying ./example.sock:0...
* Connected to test (./example.sock) port 80 (#0)
* h2h3 [:method: GET]
* h2h3 [:path: /]
* h2h3 [:scheme: http]
* h2h3 [:authority: test]
* h2h3 [user-agent: curl/7.88.1]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x13d80a800)
> GET / HTTP/2
> Host: test
> user-agent: curl/7.88.1
> accept: */*
>
< HTTP/2 200
< content-type: text/html; charset=utf-8
< date: Thu, 31 Aug 2023 08:56:53 GMT
<
* Connection #0 to host test left intact
<h1>Hello World</h1>

jacobwgillespie avatar Aug 31 '23 08:08 jacobwgillespie

I'm glad you brought up the example, Jacob - I should have been more precise. Yes, you can listen to an UDS with an http2 server (it's a method inherited from net). There is no equivalent for H2 clients though - http2.connect only accepts URLs with the http or https scheme out of the box, see here.

timostamm avatar Aug 31 '23 11:08 timostamm

Ah, fun. So it looks like createConnection exists in http2.ClientSessionOptions, this appears to work:

http2.connect('http://example', {
  createConnection: (authority, option) => {
    return net.connect('./example.sock');
  },
})

It looks like it's also possible to pass that as an option to new Http2SessionManager(..., ..., {createConnection}) - perhaps that would just work?

jacobwgillespie avatar Aug 31 '23 11:08 jacobwgillespie

Yup, I can confirm that this works on Linux. Using @connectrpc/connect-node version 2.0.0-alpha1

import { createGrpcTransport } from "@connectrpc/connect-node";
import { connect as nodeConnect } from "node:net";

const socket = "/path/to/socket"
const transport = createGrpcTransport({
  httpVersion: "2",
  baseUrl: "http://socket.localhost", // not a real URL, socket is in nodeOptions.createConnection
  nodeOptions: {
    createConnection: () => {
      return nodeConnect(socket)
    },
  },
})

caleblloyd avatar Aug 22 '24 14:08 caleblloyd