diff options
| author | Shulhan <m.shulhan@gmail.com> | 2020-04-10 14:25:34 +0700 |
|---|---|---|
| committer | Shulhan <m.shulhan@gmail.com> | 2020-04-11 03:50:25 +0700 |
| commit | 33b2a03dfbd90bf53e09fa2210aff8a84bf81fd8 (patch) | |
| tree | 7daf3a3481893ad092cf74a15fe7065f83db0c3f /lib/http | |
| parent | 89b5f1a2d6b1f68adb8f0f5591748fe3e6e6a88d (diff) | |
| download | pakakeh.go-33b2a03dfbd90bf53e09fa2210aff8a84bf81fd8.tar.xz | |
http: implement a simplified HTTP client
Diffstat (limited to 'lib/http')
| -rw-r--r-- | lib/http/client.go | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/lib/http/client.go b/lib/http/client.go new file mode 100644 index 00000000..b027ef5f --- /dev/null +++ b/lib/http/client.go @@ -0,0 +1,206 @@ +// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "mime/multipart" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// +// Client is a wrapper for standard http.Client with simplified +// functionalities. +// +type Client struct { + *http.Client + serverURL string +} + +// +// NewClient create and initialize new Client connection using serverURL to +// minimize repetition. +// The serverURL is any path that is static and will never changes during +// request to server. +// +func NewClient(serverURL string) (client *Client) { + client = &Client{ + serverURL: serverURL, + Client: &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, + }, + } + + return client +} + +// +// Get send the GET request to server using path and params as query +// parameters. +// On success, it will return the response body. +// +func (client *Client) Get(path string, params url.Values) ( + resBody []byte, err error, +) { + if params != nil { + path += "?" + params.Encode() + } + + httpRes, err := client.Client.Get(client.serverURL + path) + if err != nil { + return nil, fmt.Errorf("http: Get: %w", err) + } + + resBody, err = ioutil.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("http: Get: %w", err) + } + + err = httpRes.Body.Close() + if err != nil { + return nil, fmt.Errorf("http: Get: %w", err) + } + + return resBody, nil +} + +// +// PostForm send the POST request to path using +// "application/x-www-form-urlencoded". +// +func (client *Client) PostForm(path string, params url.Values) ( + resBody []byte, err error, +) { + body := strings.NewReader(params.Encode()) + + url := client.serverURL + path + httpRes, err := client.Client.Post(url, ContentTypeForm, body) + if err != nil { + return nil, fmt.Errorf("Post: %w", err) + } + + resBody, err = ioutil.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("Post: %w", err) + } + + err = httpRes.Body.Close() + if err != nil { + return nil, fmt.Errorf("Post: %w", err) + } + + return resBody, nil +} + +// +// PostFormData send the POST request to path with all parameters is send +// using "multipart/form-data". +// +func (client *Client) PostFormData(path string, params map[string][]byte) ( + resBody []byte, err error, +) { + url := client.serverURL + path + + contentType, body, err := generateFormData(params) + if err != nil { + return nil, fmt.Errorf("http: PostFormData: %w", err) + } + + httpRes, err := client.Client.Post(url, contentType, + strings.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("http: PostFormData: %w", err) + } + + resBody, err = ioutil.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("http: PostFormData: %w", err) + } + + err = httpRes.Body.Close() + if err != nil { + return nil, fmt.Errorf("http: PostFormData: %w", err) + } + + return resBody, nil +} + +// +// PostJSON send the POST request with content type set to "application/json" +// and params encoded automatically to JSON. +// +func (client *Client) PostJSON(path string, params interface{}) ( + resBody []byte, err error, +) { + paramsJSON, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("PostJSON: %w", err) + } + + url := client.serverURL + path + + httpRes, err := client.Client.Post(url, ContentTypeJSON, + bytes.NewReader(paramsJSON)) + if err != nil { + return nil, fmt.Errorf("PostJSON: %w", err) + } + + resBody, err = ioutil.ReadAll(httpRes.Body) + if err != nil { + return nil, fmt.Errorf("PostJSON: %w", err) + } + + err = httpRes.Body.Close() + if err != nil { + return nil, fmt.Errorf("PostJSON: %w", err) + } + + return resBody, nil +} + +func generateFormData(params map[string][]byte) ( + contentType, body string, err error, +) { + sb := new(strings.Builder) + w := multipart.NewWriter(sb) + for k, v := range params { + part, err := w.CreateFormField(k) + if err != nil { + return "", "", err + } + _, err = part.Write(v) + if err != nil { + return "", "", err + } + } + + err = w.Close() + if err != nil { + return "", "", err + } + + contentType = w.FormDataContentType() + + return contentType, sb.String(), nil +} |
