-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathngrok.go
173 lines (151 loc) · 4.76 KB
/
ngrok.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Package ngrok simplifies interaction with the ngrok API.
//
// This client is not endpoint compatible with the API - many endpoints are
// missing. Still, it should be trivial to add new *Service objects and
// endpoints, following the existing pattern.
//
// For more information on the ngrok API, see the documentation:
//
// https://ngrok.com/docs/ngrok-link#service-api
package ngrok
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"runtime"
"strings"
"github.com/kevinburke/rest"
"github.com/kevinburke/rest/restclient"
)
var defaultUserAgent string
const BaseURL = "https://api.ngrok.com"
const Version = "0.1"
func init() {
gv := strings.Replace(runtime.Version(), "go", "", 1)
defaultUserAgent = fmt.Sprintf("ngrok-go/%s go/%s (%s/%s)", Version, gv,
runtime.GOOS, runtime.GOARCH)
}
type Client struct {
*rest.Client
userAgent string
token string
Creds *CredService
ReservedAddrs *ReservedAddrService
}
const accept = "application/json"
func New(baseURL string, token string) *Client {
restclient := restclient.New("", "", baseURL)
restclient.ErrorParser = errorParser
c := &Client{
Client: restclient,
token: token,
userAgent: defaultUserAgent,
}
c.Creds = &CredService{c}
c.ReservedAddrs = &ReservedAddrService{c}
return c
}
type Region string
const RegionUS Region = "us"
const RegionEU Region = "eu"
const RegionAP Region = "ap"
const RegionAU Region = "au"
type ngrokError struct {
ErrorCode string `json:"error_code"`
Message string `json:"msg"`
StatusCode int `json:"status_code"`
Details json.RawMessage `json:"details"`
}
// errorParser tries to return a *rest.Error
func errorParser(resp *http.Response) error {
resBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()
rerr := new(ngrokError)
err = json.Unmarshal(resBody, rerr)
if err != nil {
return fmt.Errorf("invalid response body: %q", string(resBody))
}
if rerr.Message == "" {
return fmt.Errorf("invalid response body: %q", string(resBody))
}
restError := &rest.Error{
Title: rerr.Message,
Status: rerr.StatusCode,
ID: rerr.ErrorCode,
Detail: string(rerr.Details),
}
return restError
}
// NewRequest creates a new signed request. The base URL will be prepended to
// the path, and the user-agent will also be attached.
func (c *Client) NewRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, c.Client.Base+path, body)
if err != nil {
return nil, err
}
if ua := req.Header.Get("User-Agent"); ua == "" {
req.Header.Set("User-Agent", c.userAgent)
} else {
req.Header.Set("User-Agent", c.userAgent+" "+ua)
}
req.Header.Set("Accept", accept)
if body != nil {
// ngrok doesn't allow charset
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
req.Header.Set("Authorization", "Bearer "+c.token)
req.Header.Set("X-Ngrok-Version", "1")
return req, nil
}
// from kevinburke/twilio-go/http.go
// GetResource retrieves an instance resource with the given path part (e.g.
// "/Messages") and sid (e.g. "MM123").
func (c *Client) GetResource(ctx context.Context, pathPart string, sid string, v interface{}) error {
sidPart := strings.Join([]string{pathPart, sid}, "/")
return c.MakeRequest(ctx, "GET", sidPart, nil, v)
}
// CreateResource makes a POST request to the given resource.
func (c *Client) CreateResource(ctx context.Context, pathPart string, data url.Values, v interface{}) error {
return c.MakeRequest(ctx, "POST", pathPart, data, v)
}
func (c *Client) UpdateResource(ctx context.Context, pathPart string, sid string, data url.Values, v interface{}) error {
sidPart := strings.Join([]string{pathPart, sid}, "/")
return c.MakeRequest(ctx, "POST", sidPart, data, v)
}
func (c *Client) DeleteResource(ctx context.Context, pathPart string, sid string) error {
sidPart := strings.Join([]string{pathPart, sid}, "/")
err := c.MakeRequest(ctx, "DELETE", sidPart, nil, nil)
if err == nil {
return nil
}
rerr, ok := err.(*rest.Error)
if ok && rerr.Status == http.StatusNotFound {
return nil
}
return err
}
func (c *Client) ListResource(ctx context.Context, pathPart string, data url.Values, v interface{}) error {
return c.MakeRequest(ctx, "GET", pathPart, data, v)
}
// Make a request to the ngrok API.
func (c *Client) MakeRequest(ctx context.Context, method string, pathPart string, data url.Values, v interface{}) error {
rb := new(strings.Reader)
if data != nil && (method == "POST" || method == "PUT") {
rb = strings.NewReader(data.Encode())
}
if method == "GET" && data != nil {
pathPart = pathPart + "?" + data.Encode()
}
req, err := c.NewRequest(ctx, method, pathPart, rb)
if err != nil {
return err
}
return c.Do(req, &v)
}