Skip to content

Commit

Permalink
Merge pull request #58 from dyweb/requests/default
Browse files Browse the repository at this point in the history
[requests] Use new default transport and http client
  • Loading branch information
at15 authored Feb 23, 2018
2 parents 7360581 + 3bcf758 commit 7a8a931
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 57 deletions.
2 changes: 2 additions & 0 deletions noodle/fs_embed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
)

func TestGenerateEmbed(t *testing.T) {
t.Skip("modification time result in different binary data")

b, err := GenerateEmbed("_examples/embed/assets")
if err != nil {
t.Fatal(err)
Expand Down
6 changes: 5 additions & 1 deletion requests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ Originally from [xephon-b](https://github.com/xephonhq/xephon-b)

## Usage

TODO: it is mainly used for building config for http client to use socks5 proxy
TODO:

- it was mainly used for building config for http client to use socks5 proxy
- [ ] https://github.com/hashicorp/go-getter like features

## Changelog

- use same config as `DefaultTransport` in `net/http`
- add client struct and default client which wraps `http.Client`
- support socks5 proxy https://github.com/dyweb/gommon/issues/18
- rename `GetJSON` to get `GetJSONStringMap`, this should break old Xephon-B code, which is now in tsdb-proxy
3 changes: 1 addition & 2 deletions requests/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
"github.com/pkg/errors"
)

// TODO: it is already in net/http https://golang.org/pkg/net/http/#Request.BasicAuth

// NOTE: net/http already implemented it https://golang.org/pkg/net/http/#Request.BasicAuth
const AuthorizationHeader = "Authorization"

func GenerateBasicAuth(username string, password string) string {
Expand Down
4 changes: 2 additions & 2 deletions requests/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

// TODO: might switch to functional options https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
// TODO: http proxy? might just use environment variable? https://stackoverflow.com/questions/14661511/setting-up-proxy-for-http-client
// TransportBuilder is the initial builder, its method return a new copy for chaining and keep itself unchanged
// TransportBuilder is the initial builder, its method use value receiver and return a new copy for chaining and keep itself unchanged
var TransportBuilder transportBuilder

type transportBuilder struct {
Expand Down Expand Up @@ -40,7 +40,7 @@ func (b transportBuilder) UseSocks5(host string, username string, password strin
}

func (b transportBuilder) Build() (*http.Transport, error) {
transport := &http.Transport{}
transport := NewDefaultTransport()
if b.skipKeyVerify {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
Expand Down
33 changes: 33 additions & 0 deletions requests/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package requests

import (
"net"
"net/http"
"time"
)

// Default Transport Client that is same as https://golang.org/src/net/http/transport.go
// It's similar to https://github.com/hashicorp/go-cleanhttp

// NewDefaultTransport is copied from net/http/transport.go
func NewDefaultTransport() *http.Transport {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}

// NewDefaultClient returns a client using NewDefaultTransport
func NewDefaultClient() *http.Client {
return &http.Client{
Transport: NewDefaultTransport(),
}
}
2 changes: 0 additions & 2 deletions requests/doc.go

This file was deleted.

1 change: 1 addition & 0 deletions requests/pkg.go
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// Package requests is a pythonic HTTP library for Gopher
package requests
78 changes: 40 additions & 38 deletions requests/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,46 @@
package requests

import (
"bytes"
"io"
"io/ioutil"
"net/http"
"strings"

"bytes"
"github.com/pkg/errors"
"io"
"strings"
)

const (
ShadowSocksLocal = "127.0.0.1:1080"
ContentJSON = "application/json"
)

var (
defaultClient = &Client{http: &http.Client{Transport: &http.Transport{}}}
)
var defaultClient = &Client{h: NewDefaultClient(), content: ContentJSON}

type Client struct {
http *http.Client
h *http.Client
content string
}

func NewClient(options ...func(h *http.Client)) (*Client, error) {
c := &Client{http: &http.Client{Transport: &http.Transport{}}}
func NewClient(options ...func(h *http.Client)) *Client {
c := &Client{h: NewDefaultClient(), content: ContentJSON}
for _, option := range options {
option(c.http)
option(c.h)
}
return c, nil
return c
}

func makeRequest(method string, url string, body io.Reader) (*Response, error) {
return defaultClient.makeRequest(method, url, body)
}

func (client *Client) makeRequest(method string, url string, body io.Reader) (*Response, error) {
if client.http == nil {
func (c *Client) makeRequest(method string, url string, body io.Reader) (*Response, error) {
if c.h == nil {
return nil, errors.New("http client is not initialized")
}
var res *http.Response
var err error
switch method {
case http.MethodGet:
res, err = client.http.Get(url)
res, err = c.h.Get(url)
case http.MethodPost:
// TODO: we only support JSON for now
res, err = client.http.Post(url, ContentJSON, body)
res, err = c.h.Post(url, c.content, body)
}
response := &Response{}
if err != nil {
Expand All @@ -60,42 +54,50 @@ func (client *Client) makeRequest(method string, url string, body io.Reader) (*R
return response, errors.Wrap(err, "error reading body")
}
response.Res = res
response.Text = resContent
response.StatusCode = res.StatusCode
response.Data = resContent
return response, nil
}

func Get(url string) (*Response, error) {
return makeRequest(http.MethodGet, url, nil)
// TODO: this defaultish wrapping methods should be generated by gommon generator
func Post(url string, data io.Reader) (*Response, error) {
return defaultClient.Post(url, data)
}

func (client *Client) Get(url string) (*Response, error) {
return client.makeRequest(http.MethodGet, url, nil)
func (c *Client) Post(url string, data io.Reader) (*Response, error) {
return c.makeRequest(http.MethodPost, url, data)
}

// TODO: accept io.reader
func PostString(url string, data string) (*Response, error) {
return defaultClient.PostString(url, data)
}

func PostJSONString(url string, data string) (*Response, error) {
return makeRequest(http.MethodPost, url, ioutil.NopCloser(strings.NewReader(data)))
func (c *Client) PostString(url string, data string) (*Response, error) {
return c.makeRequest(http.MethodPost, url, ioutil.NopCloser(strings.NewReader(data)))
}

func (client *Client) PostJSONString(url string, data string) (*Response, error) {
return client.makeRequest(http.MethodPost, url, ioutil.NopCloser(strings.NewReader(data)))
func PostBytes(url string, data []byte) (*Response, error) {
return defaultClient.PostBytes(url, data)
}

func PostJSONBytes(url string, data []byte) (*Response, error) {
return makeRequest(http.MethodPost, url, ioutil.NopCloser(bytes.NewReader(data)))
func (c *Client) PostBytes(url string, data []byte) (*Response, error) {
return c.makeRequest(http.MethodPost, url, ioutil.NopCloser(bytes.NewReader(data)))
}

func Get(url string) (*Response, error) {
return defaultClient.Get(url)
}

func (client *Client) PostJSONBytes(url string, data []byte) (*Response, error) {
return client.makeRequest(http.MethodPost, url, ioutil.NopCloser(bytes.NewReader(data)))
func (c *Client) Get(url string) (*Response, error) {
return c.makeRequest(http.MethodGet, url, nil)
}

func GetJSON(url string, data interface{}) error {
return defaultClient.GetJSON(url, data)
}

func (client *Client) GetJSON(url string, data interface{}) error {
res, err := client.Get(url)
func (c *Client) GetJSON(url string, data interface{}) error {
res, err := c.Get(url)
if err != nil {
return errors.Wrap(err, "error getting response")
}
Expand All @@ -110,9 +112,9 @@ func GetJSONStringMap(url string) (map[string]string, error) {
return defaultClient.GetJSONStringMap(url)
}

func (client *Client) GetJSONStringMap(url string) (map[string]string, error) {
func (c *Client) GetJSONStringMap(url string) (map[string]string, error) {
var data map[string]string
res, err := client.Get(url)
res, err := c.Get(url)
if err != nil {
return data, errors.Wrap(err, "error getting response")
}
Expand Down
35 changes: 29 additions & 6 deletions requests/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package requests

import (
"fmt"
asst "github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

asst "github.com/stretchr/testify/assert"
)

func TestRequestsE2E(t *testing.T) {
Expand Down Expand Up @@ -37,37 +38,59 @@ func TestRequestsE2E(t *testing.T) {
versionURL := testServer.URL + "/version"
echoURL := testServer.URL + "/echo"

c := NewClient()
t.Run("c.Get", func(t *testing.T) {
assert := asst.New(t)
res, err := c.Get(versionURL)
assert.Nil(err)
assert.Equal(http.StatusOK, res.Res.StatusCode)
})

t.Run("Get", func(t *testing.T) {
assert := asst.New(t)
res, err := Get(versionURL)
assert.Nil(err)
assert.Equal(http.StatusOK, res.Res.StatusCode)
})

t.Run("c.GetJSONStringMap", func(t *testing.T) {
assert := asst.New(t)
data, err := c.GetJSONStringMap(versionURL)
assert.Nil(err)
assert.Equal(version, data["version"])
})

t.Run("GetJSONStringMap", func(t *testing.T) {
assert := asst.New(t)
data, err := GetJSONStringMap(versionURL)
assert.Nil(err)
assert.Equal(version, data["version"])
})

t.Run("PostJSONString", func(t *testing.T) {
t.Run("c.PostString", func(t *testing.T) {
assert := asst.New(t)
payload := fmt.Sprintf("{\"version\": \"%s\"}", version)
res, err := PostJSONString(echoURL, payload)
res, err := c.PostString(echoURL, payload)
assert.Nil(err)
assert.Equal(payload, string(res.Text))
assert.Equal(payload, string(res.Data))
})

t.Run("c.PostString", func(t *testing.T) {
assert := asst.New(t)
payload := fmt.Sprintf("{\"version\": \"%s\"}", version)
res, err := PostString(echoURL, payload)
assert.Nil(err)
assert.Equal(payload, string(res.Data))
})
}

func TestNewClient(t *testing.T) {
assert := asst.New(t)
tr, err := TransportBuilder.UseShadowSocks().Build()
assert.Nil(err)
c, err := NewClient(func(h *http.Client) {
c := NewClient(func(h *http.Client) {
h.Transport = tr
})
assert.Nil(err)
assert.NotNil(c)
// uncomment the following if you have local socks 5 proxy running
//_, err = c.Get("https://google.com")
Expand Down
11 changes: 5 additions & 6 deletions requests/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,27 @@ package requests

import (
"encoding/json"

"net/http"

"github.com/pkg/errors"
)

type Response struct {
Res *http.Response
Text []byte
Res *http.Response
StatusCode int
Data []byte
}

func (res *Response) JSON(data interface{}) error {
// TODO: may change to decoder
if err := json.Unmarshal(res.Text, &data); err != nil {
if err := json.Unmarshal(res.Data, &data); err != nil {
return errors.Wrap(err, "error unmarshal json using map[string]string")
}
return nil
}

func (res *Response) JSONStringMap() (map[string]string, error) {
var data map[string]string
if err := json.Unmarshal(res.Text, &data); err != nil {
if err := json.Unmarshal(res.Data, &data); err != nil {
return data, errors.Wrap(err, "error unmarshal json using map[string]string")
}
return data, nil
Expand Down

0 comments on commit 7a8a931

Please sign in to comment.