-
Notifications
You must be signed in to change notification settings - Fork 182
/
client.go
134 lines (123 loc) · 3.07 KB
/
client.go
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package vpnkit
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"time"
"github.com/moby/vpnkit/go/pkg/vpnkit/transport"
)
const (
ListPath = "/forwards/list"
ExposePortPath = "/forwards/expose/port"
ExposePipePath = "/forwards/expose/pipe"
UnexposePortPath = "/forwards/unexpose/port"
UnexposePipePath = "/forwards/unexpose/pipe"
DumpStatePath = "/forwards/dump"
)
const httpTimeout = 120 * time.Second
func NewClient(path string) (Client, error) {
t := transport.Choose(path)
return &httpClient{
client: http.Client{
Timeout: httpTimeout,
Transport: &http.Transport{
DialContext: func(c context.Context, _, _ string) (net.Conn, error) {
return t.Dial(c, path)
},
},
},
}, nil
}
type httpClient struct {
client http.Client
}
// ExposeError should be reported through to the user
type ExposeError struct {
Message string `json:"message"`
}
func (e *ExposeError) Error() string {
return e.Message
}
func (h *httpClient) Expose(_ context.Context, port *Port) error {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
if err := enc.Encode(port); err != nil {
return err
}
path := ExposePortPath
if port.Proto == Unix {
path = ExposePipePath
}
res, err := h.client.Post("http://unix"+path, "application/json", &buf)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode == 400 {
var exposeError ExposeError
dec := json.NewDecoder(res.Body)
if err := dec.Decode(&exposeError); err != nil {
fmt.Printf("failed to decode: %v\n", err)
return err
}
return &exposeError
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf(path+" returned unexpected status: %d", res.StatusCode)
}
return nil
}
func (h *httpClient) Unexpose(_ context.Context, port *Port) error {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
if err := enc.Encode(port); err != nil {
return err
}
path := UnexposePortPath
if port.Proto == Unix {
path = UnexposePipePath
}
res, err := h.client.Post("http://unix"+path, "application/json", &buf)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return fmt.Errorf(path+" returned unexpected status: %d", res.StatusCode)
}
return nil
}
func (h *httpClient) ListExposed(context.Context) ([]Port, error) {
res, err := h.client.Get("http://unix" + ListPath)
if err != nil {
fmt.Printf("GET failed with %v\n", err)
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf(ListPath+" returned unexpected status: %d", res.StatusCode)
}
dec := json.NewDecoder(res.Body)
var ports []Port
if err := dec.Decode(&ports); err != nil {
return nil, err
}
return ports, nil
}
func (h *httpClient) DumpState(_ context.Context, w io.Writer) error {
res, err := h.client.Get("http://unix" + DumpStatePath)
if err != nil {
fmt.Printf("GET failed with %v\n", err)
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return fmt.Errorf(DumpStatePath+" returned unexpected status: %d", res.StatusCode)
}
_, err = io.Copy(w, res.Body)
return err
}