Skip to content

Commit 554250b

Browse files
author
Nate Smith
authored
Merge pull request cli#3779 from jgold-stripe/unix
Add ability to dial API via unix socket
2 parents 0474ba6 + 666ed2f commit 554250b

File tree

7 files changed

+73
-5
lines changed

7 files changed

+73
-5
lines changed

internal/config/config_type.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ var configOptions = []ConfigOption{
5050
Description: "the terminal pager program to send standard output to",
5151
DefaultValue: "",
5252
},
53+
{
54+
Key: "http_unix_socket",
55+
Description: "the path to a unix socket through which to make HTTP connection",
56+
DefaultValue: "",
57+
},
5358
}
5459

5560
func ConfigOptions() []ConfigOption {
@@ -179,6 +184,15 @@ func NewBlankRoot() *yaml.Node {
179184
},
180185
},
181186
},
187+
{
188+
HeadComment: "The path to a unix socket through which send HTTP connections. If blank, HTTP traffic will be handled by net/http.DefaultTransport.",
189+
Kind: yaml.ScalarNode,
190+
Value: "http_unix_socket",
191+
},
192+
{
193+
Kind: yaml.ScalarNode,
194+
Value: "",
195+
},
182196
},
183197
},
184198
},

internal/config/config_type_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ func Test_defaultConfig(t *testing.T) {
5050
# Aliases allow you to create nicknames for gh commands
5151
aliases:
5252
co: pr checkout
53+
# The path to a unix socket through which send HTTP connections. If blank, HTTP traffic will be handled by net/http.DefaultTransport.
54+
http_unix_socket:
5355
`)
5456
assert.Equal(t, expected, mainBuf.String())
5557
assert.Equal(t, "", hostsBuf.String())
@@ -81,6 +83,9 @@ func Test_ValidateValue(t *testing.T) {
8183

8284
err = ValidateValue("got", "123")
8385
assert.NoError(t, err)
86+
87+
err = ValidateValue("http_unix_socket", "really_anything/is/allowed/and/net.Dial\\(...\\)/will/ultimately/validate")
88+
assert.NoError(t, err)
8489
}
8590

8691
func Test_ValidateKey(t *testing.T) {
@@ -98,4 +103,7 @@ func Test_ValidateKey(t *testing.T) {
98103

99104
err = ValidateKey("pager")
100105
assert.NoError(t, err)
106+
107+
err = ValidateKey("http_unix_socket")
108+
assert.NoError(t, err)
101109
}

internal/httpunix/transport.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// package httpunix provides an http.RoundTripper which dials a server via a unix socket.
2+
package httpunix
3+
4+
import (
5+
"net"
6+
"net/http"
7+
)
8+
9+
// NewRoundTripper returns an http.RoundTripper which sends requests via a unix
10+
// socket at socketPath.
11+
func NewRoundTripper(socketPath string) http.RoundTripper {
12+
dial := func(network, addr string) (net.Conn, error) {
13+
return net.Dial("unix", socketPath)
14+
}
15+
16+
return &http.Transport{
17+
Dial: dial,
18+
DialTLS: dial,
19+
DisableKeepAlives: true,
20+
}
21+
}

pkg/cmd/factory/default.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func httpClientFunc(f *cmdutil.Factory, appVersion string) func() (*http.Client,
8585
if err != nil {
8686
return nil, err
8787
}
88-
return NewHTTPClient(io, cfg, appVersion, true), nil
88+
return NewHTTPClient(io, cfg, appVersion, true)
8989
}
9090
}
9191

pkg/cmd/factory/http.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/cli/cli/api"
1111
"github.com/cli/cli/internal/ghinstance"
12+
"github.com/cli/cli/internal/httpunix"
1213
"github.com/cli/cli/pkg/iostreams"
1314
)
1415

@@ -57,8 +58,31 @@ type configGetter interface {
5758
}
5859

5960
// generic authenticated HTTP client for commands
60-
func NewHTTPClient(io *iostreams.IOStreams, cfg configGetter, appVersion string, setAccept bool) *http.Client {
61+
func NewHTTPClient(io *iostreams.IOStreams, cfg configGetter, appVersion string, setAccept bool) (*http.Client, error) {
6162
var opts []api.ClientOption
63+
64+
// We need to check and potentially add the unix socket roundtripper option
65+
// before adding any other options, since if we are going to use the unix
66+
// socket transport, it needs to form the base of the transport chain
67+
// represented by invocations of opts...
68+
//
69+
// Another approach might be to change the signature of api.NewHTTPClient to
70+
// take an explicit base http.RoundTripper as its first parameter (it
71+
// currently defaults internally to http.DefaultTransport), or add another
72+
// variant like api.NewHTTPClientWithBaseRoundTripper. But, the only caller
73+
// which would use that non-default behavior is right here, and it doesn't
74+
// seem worth the cognitive overhead everywhere else just to serve this one
75+
// use case.
76+
unixSocket, err := cfg.Get("", "http_unix_socket")
77+
if err != nil {
78+
return nil, err
79+
}
80+
if unixSocket != "" {
81+
opts = append(opts, api.ClientOption(func(http.RoundTripper) http.RoundTripper {
82+
return httpunix.NewRoundTripper(unixSocket)
83+
}))
84+
}
85+
6286
if verbose := os.Getenv("DEBUG"); verbose != "" {
6387
logTraffic := strings.Contains(verbose, "api")
6488
opts = append(opts, api.VerboseLog(io.ErrOut, logTraffic, io.IsStderrTTY()))
@@ -98,7 +122,7 @@ func NewHTTPClient(io *iostreams.IOStreams, cfg configGetter, appVersion string,
98122
)
99123
}
100124

101-
return api.NewHTTPClient(opts...)
125+
return api.NewHTTPClient(opts...), nil
102126
}
103127

104128
func getHost(r *http.Request) string {

pkg/cmd/factory/http_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ func TestNewHTTPClient(t *testing.T) {
135135
})
136136

137137
io, _, _, stderr := iostreams.Test()
138-
client := NewHTTPClient(io, tt.args.config, tt.args.appVersion, tt.args.setAccept)
138+
client, err := NewHTTPClient(io, tt.args.config, tt.args.appVersion, tt.args.setAccept)
139+
require.NoError(t, err)
139140

140141
req, err := http.NewRequest("GET", ts.URL, nil)
141142
req.Host = tt.host

pkg/cmd/root/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,6 @@ func bareHTTPClient(f *cmdutil.Factory, version string) func() (*http.Client, er
118118
if err != nil {
119119
return nil, err
120120
}
121-
return factory.NewHTTPClient(f.IOStreams, cfg, version, false), nil
121+
return factory.NewHTTPClient(f.IOStreams, cfg, version, false)
122122
}
123123
}

0 commit comments

Comments
 (0)