1package mailgun 2 3import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "mime/multipart" 10 "net/http" 11 "net/url" 12 "os" 13 "path" 14 "strings" 15) 16 17type httpRequest struct { 18 URL string 19 Parameters map[string][]string 20 Headers map[string]string 21 BasicAuthUser string 22 BasicAuthPassword string 23 Client *http.Client 24} 25 26type httpResponse struct { 27 Code int 28 Data []byte 29} 30 31type payload interface { 32 getPayloadBuffer() (*bytes.Buffer, error) 33 getContentType() string 34 getValues() []keyValuePair 35} 36 37type keyValuePair struct { 38 key string 39 value string 40} 41 42type keyNameRC struct { 43 key string 44 name string 45 value io.ReadCloser 46} 47 48type formDataPayload struct { 49 contentType string 50 Values []keyValuePair 51 Files []keyValuePair 52 ReadClosers []keyNameRC 53} 54 55type urlEncodedPayload struct { 56 Values []keyValuePair 57} 58 59func newHTTPRequest(url string) *httpRequest { 60 return &httpRequest{URL: url, Client: http.DefaultClient} 61} 62 63func (r *httpRequest) addParameter(name, value string) { 64 if r.Parameters == nil { 65 r.Parameters = make(map[string][]string) 66 } 67 r.Parameters[name] = append(r.Parameters[name], value) 68} 69 70func (r *httpRequest) setClient(c *http.Client) { 71 r.Client = c 72} 73 74func (r *httpRequest) setBasicAuth(user, password string) { 75 r.BasicAuthUser = user 76 r.BasicAuthPassword = password 77} 78 79func newUrlEncodedPayload() *urlEncodedPayload { 80 return &urlEncodedPayload{} 81} 82 83func (f *urlEncodedPayload) addValue(key, value string) { 84 f.Values = append(f.Values, keyValuePair{key: key, value: value}) 85} 86 87func (f *urlEncodedPayload) getPayloadBuffer() (*bytes.Buffer, error) { 88 data := url.Values{} 89 for _, keyVal := range f.Values { 90 data.Add(keyVal.key, keyVal.value) 91 } 92 return bytes.NewBufferString(data.Encode()), nil 93} 94 95func (f *urlEncodedPayload) getContentType() string { 96 return "application/x-www-form-urlencoded" 97} 98 99func (f *urlEncodedPayload) getValues() []keyValuePair { 100 return f.Values 101} 102 103func (r *httpResponse) parseFromJSON(v interface{}) error { 104 return json.Unmarshal(r.Data, v) 105} 106 107func newFormDataPayload() *formDataPayload { 108 return &formDataPayload{} 109} 110 111func (f *formDataPayload) getValues() []keyValuePair { 112 return f.Values 113} 114 115func (f *formDataPayload) addValue(key, value string) { 116 f.Values = append(f.Values, keyValuePair{key: key, value: value}) 117} 118 119func (f *formDataPayload) addFile(key, file string) { 120 f.Files = append(f.Files, keyValuePair{key: key, value: file}) 121} 122 123func (f *formDataPayload) addReadCloser(key, name string, rc io.ReadCloser) { 124 f.ReadClosers = append(f.ReadClosers, keyNameRC{key: key, name: name, value: rc}) 125} 126 127func (f *formDataPayload) getPayloadBuffer() (*bytes.Buffer, error) { 128 data := &bytes.Buffer{} 129 writer := multipart.NewWriter(data) 130 defer writer.Close() 131 132 for _, keyVal := range f.Values { 133 if tmp, err := writer.CreateFormField(keyVal.key); err == nil { 134 tmp.Write([]byte(keyVal.value)) 135 } else { 136 return nil, err 137 } 138 } 139 140 for _, file := range f.Files { 141 if tmp, err := writer.CreateFormFile(file.key, path.Base(file.value)); err == nil { 142 if fp, err := os.Open(file.value); err == nil { 143 defer fp.Close() 144 io.Copy(tmp, fp) 145 } else { 146 return nil, err 147 } 148 } else { 149 return nil, err 150 } 151 } 152 153 for _, file := range f.ReadClosers { 154 if tmp, err := writer.CreateFormFile(file.key, file.name); err == nil { 155 defer file.value.Close() 156 io.Copy(tmp, file.value) 157 } else { 158 return nil, err 159 } 160 } 161 162 f.contentType = writer.FormDataContentType() 163 164 return data, nil 165} 166 167func (f *formDataPayload) getContentType() string { 168 if f.contentType == "" { 169 f.getPayloadBuffer() 170 } 171 return f.contentType 172} 173 174func (r *httpRequest) addHeader(name, value string) { 175 if r.Headers == nil { 176 r.Headers = make(map[string]string) 177 } 178 r.Headers[name] = value 179} 180 181func (r *httpRequest) makeGetRequest() (*httpResponse, error) { 182 return r.makeRequest("GET", nil) 183} 184 185func (r *httpRequest) makePostRequest(payload payload) (*httpResponse, error) { 186 return r.makeRequest("POST", payload) 187} 188 189func (r *httpRequest) makePutRequest(payload payload) (*httpResponse, error) { 190 return r.makeRequest("PUT", payload) 191} 192 193func (r *httpRequest) makeDeleteRequest() (*httpResponse, error) { 194 return r.makeRequest("DELETE", nil) 195} 196 197func (r *httpRequest) makeRequest(method string, payload payload) (*httpResponse, error) { 198 url, err := r.generateUrlWithParameters() 199 if err != nil { 200 return nil, err 201 } 202 203 var body io.Reader 204 if payload != nil { 205 if body, err = payload.getPayloadBuffer(); err != nil { 206 return nil, err 207 } 208 } else { 209 body = nil 210 } 211 212 req, err := http.NewRequest(method, url, body) 213 if err != nil { 214 return nil, err 215 } 216 217 if payload != nil && payload.getContentType() != "" { 218 req.Header.Add("Content-Type", payload.getContentType()) 219 } 220 221 if r.BasicAuthUser != "" && r.BasicAuthPassword != "" { 222 req.SetBasicAuth(r.BasicAuthUser, r.BasicAuthPassword) 223 } 224 225 for header, value := range r.Headers { 226 req.Header.Add(header, value) 227 } 228 229 if Debug { 230 fmt.Println(r.curlString(req, payload)) 231 } 232 233 response := httpResponse{} 234 235 resp, err := r.Client.Do(req) 236 if resp != nil { 237 response.Code = resp.StatusCode 238 } 239 if err != nil { 240 return nil, err 241 } 242 243 defer resp.Body.Close() 244 responseBody, err := ioutil.ReadAll(resp.Body) 245 if err != nil { 246 return nil, err 247 } 248 249 response.Data = responseBody 250 return &response, nil 251} 252 253func (r *httpRequest) generateUrlWithParameters() (string, error) { 254 url, err := url.Parse(r.URL) 255 if err != nil { 256 return "", err 257 } 258 q := url.Query() 259 if r.Parameters != nil && len(r.Parameters) > 0 { 260 for name, values := range r.Parameters { 261 for _, value := range values { 262 q.Add(name, value) 263 } 264 } 265 } 266 url.RawQuery = q.Encode() 267 268 return url.String(), nil 269} 270 271func (r *httpRequest) curlString(req *http.Request, p payload) string { 272 273 parts := []string{"curl", "-i", "-X", req.Method, req.URL.String()} 274 for key, value := range req.Header { 275 parts = append(parts, fmt.Sprintf("-H \"%s: %s\"", key, value[0])) 276 } 277 278 //parts = append(parts, fmt.Sprintf(" --user '%s:%s'", r.BasicAuthUser, r.BasicAuthPassword)) 279 280 if p != nil { 281 for _, param := range p.getValues() { 282 parts = append(parts, fmt.Sprintf(" -F %s='%s'", param.key, param.value)) 283 } 284 } 285 return strings.Join(parts, " ") 286} 287