Go net/http unix 域套接字连接

go package “net/http” 无法直接使用 unix 域套接字侦听的服务,会提示,不支持的协议。

方案

  • 实现自己的 RoundTripper 支持 unix socket 。

  • 伪造拨号功能并将其传递给传输:

1
2
3
4
5
6
7
8
9
func fakeDial(proto, addr string) (conn net.Conn, err error) {
return net.Dial("unix", sock) // 为 sock path
}

tr := &http.Transport{
Dial: fakeDial,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("http://unix/test")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import (
"fmt"
"io"
"net"
"net/http"
"os"
)

func dieIf(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

const sock = "/tmp/dummy.sock"

func server() {
l, err := net.Listen("unix", sock)
dieIf(err)
srv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello: %v\n", r.RequestURI)
}),
}
srv.Serve(l)
}

func fakeDial(proto, addr string) (conn net.Conn, err error) {
return net.Dial("unix", sock)
}

func main() {
defer os.Remove(sock)
go server()
time.Sleep(time.Second)
tr := &http.Transport{
Dial: fakeDial,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("http://unix/test")
dieIf(err)
io.Copy(os.Stdout, resp.Body)
resp.Body.Close()
}

playground

警告,你所有的 client.Get/.Post 调用必须是有效的 url( http://xxxx.xxx/path 不是 unix://… ),域名无关紧要,因为它不会用于连接。

reference

sockets - Go net/http unix 域套接字连接

Go net/http unix domain socket connection

Go:Unix 域套接字

golang 中基于 http 和 unix socket 的通信代码实现(服务端基于 gin 框架)