-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
143 lines (112 loc) · 3.31 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
135
136
137
138
139
140
141
142
143
package reverseip
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
)
const (
libraryVersion = "1.0.0"
userAgent = "reverse-ip-go/" + libraryVersion
mediaType = "application/json"
)
// defaultReverseIPURL is the default Reverse IP/DNS API URL.
const defaultReverseIPURL = `https://dns-history.whoisxmlapi.com/api/v1`
// ClientParams is used to create Client. None of parameters are mandatory and
// leaving this struct empty works just fine for most cases.
type ClientParams struct {
// HTTPClient is the client used to access API endpoint
// If it's nil then value API client uses http.DefaultClient
HTTPClient *http.Client
// ReverseIPBaseURL is the endpoint for 'Reverse IP/DNS API' service
ReverseIPBaseURL *url.URL
}
// NewBasicClient creates Client with recommended parameters.
func NewBasicClient(apiKey string) *Client {
return NewClient(apiKey, ClientParams{})
}
// NewClient creates Client with specified parameters.
func NewClient(apiKey string, params ClientParams) *Client {
var err error
apiBaseURL := params.ReverseIPBaseURL
if apiBaseURL == nil {
apiBaseURL, err = url.Parse(defaultReverseIPURL)
if err != nil {
panic(err)
}
}
httpClient := http.DefaultClient
if params.HTTPClient != nil {
httpClient = params.HTTPClient
}
client := &Client{
client: httpClient,
userAgent: userAgent,
apiKey: apiKey,
}
client.ReverseIP = &reverseIPServiceOp{client: client, baseURL: apiBaseURL}
return client
}
// Client is the client for Reverse IP/DNS API services.
type Client struct {
client *http.Client
userAgent string
apiKey string
// ReverseIP is an interface for Reverse IP/DNS API
ReverseIP
}
// NewRequest creates a basic API request.
func (c *Client) NewRequest(method string, u *url.URL, body io.Reader) (*http.Request, error) {
var err error
var req *http.Request
req, err = http.NewRequest(method, u.String(), body)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", mediaType)
req.Header.Add("Accept", mediaType)
req.Header.Add("User-Agent", c.userAgent)
return req, nil
}
// Do sends the API request and returns the API response.
func (c *Client) Do(ctx context.Context, req *http.Request, v io.Writer) (response *http.Response, err error) {
req = req.WithContext(ctx)
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("cannot execute request: %w", err)
}
defer func() {
if rerr := resp.Body.Close(); err == nil && rerr != nil {
err = fmt.Errorf("cannot close response: %w", rerr)
}
}()
_, err = io.Copy(v, resp.Body)
if err != nil {
return resp, fmt.Errorf("cannot read response: %w", err)
}
return resp, err
}
// ErrorResponse is returned when the response status code is not 2xx.
type ErrorResponse struct {
Response *http.Response
Message string
}
// Error returns error message as a string.
func (e *ErrorResponse) Error() string {
if e.Message != "" {
return "API failed with status code: " + strconv.Itoa(e.Response.StatusCode) + " (" + e.Message + ")"
}
return "API failed with status code: " + strconv.Itoa(e.Response.StatusCode)
}
// checkResponse checks if the response status code is not 2xx.
func checkResponse(r *http.Response) error {
if c := r.StatusCode; c >= 200 && c <= 299 {
return nil
}
var errorResponse = ErrorResponse{
Response: r,
}
return &errorResponse
}