-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added initial http client * added timeout constants * add Authentication Headers * improved TestDefaultHTTPClientAuth with more descriptive asserts * dropped ConfigStore from test and use SetACSToken()
- Loading branch information
Showing
3 changed files
with
153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package dcos | ||
|
||
import ( | ||
"crypto/tls" | ||
"fmt" | ||
"net" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
const ( | ||
// Set a 10 seconds timeout for the connection to be established. | ||
DefaultHTTPClientDialContextTimeout = 10 * time.Second | ||
// Set it to 10 seconds as well for the TLS handshake when using HTTPS. | ||
DefaultHTTPClientTLSHandshakeTimeout = 10 * time.Second | ||
// The client will be dealing with a single host (the one in baseURL), | ||
// set max idle connections to 30 regardless of the host. | ||
DefaultHTTPClientMaxIdleConns = 30 | ||
DefaultHTTPClientMaxIdleConnsPerHost = 30 | ||
) | ||
|
||
// DefaultTransport is a http.RoundTripper that adds authentication based on Config | ||
type DefaultTransport struct { | ||
Config *Config | ||
Base http.RoundTripper | ||
} | ||
|
||
func (t *DefaultTransport) base() http.RoundTripper { | ||
if t.Base != nil { | ||
return t.Base | ||
} | ||
return http.DefaultTransport | ||
} | ||
|
||
// RoundTrip authorizes requests to DC/OS by adding dcos_acs_token to Authorization header | ||
func (t *DefaultTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||
// meet the requirements of RoundTripper and only modify a copy | ||
req2 := cloneRequest(req) | ||
req2.Header.Set("Authorization", fmt.Sprintf("token=%s", t.Config.ACSToken())) | ||
|
||
return t.base().RoundTrip(req2) | ||
} | ||
|
||
func cloneRequest(req *http.Request) *http.Request { | ||
req2 := new(http.Request) | ||
*req2 = *req | ||
|
||
// until now we only clone headers as we only modify those. | ||
req2.Header = make(http.Header, len(req.Header)) | ||
for k, s := range req.Header { | ||
req2.Header[k] = append([]string(nil), s...) | ||
} | ||
|
||
return req2 | ||
} | ||
|
||
// NewHTTPClient provides a http.Client able to communicate to dcos in an authenticated way | ||
func NewHTTPClient(config *Config) *http.Client { | ||
client := &http.Client{} | ||
client.Transport = &http.Transport{ | ||
|
||
// Allow http_proxy, https_proxy, and no_proxy. | ||
Proxy: http.ProxyFromEnvironment, | ||
|
||
DialContext: (&net.Dialer{ | ||
Timeout: DefaultHTTPClientDialContextTimeout, | ||
}).DialContext, | ||
|
||
TLSHandshakeTimeout: DefaultHTTPClientTLSHandshakeTimeout, | ||
TLSClientConfig: &tls.Config{ | ||
InsecureSkipVerify: config.TLS().Insecure, | ||
RootCAs: config.TLS().RootCAs, | ||
}, | ||
|
||
MaxIdleConns: DefaultHTTPClientMaxIdleConns, | ||
MaxIdleConnsPerHost: DefaultHTTPClientMaxIdleConnsPerHost, | ||
} | ||
|
||
return AddTransportHTTPClient(client, config) | ||
} | ||
|
||
// AddTransportHTTPClient adds dcos.DefaultTransport to http.Client to add dcos authentication | ||
func AddTransportHTTPClient(client *http.Client, config *Config) *http.Client { | ||
transport := DefaultTransport{ | ||
Config: config, | ||
Base: client.Transport, | ||
} | ||
|
||
client.Transport = &transport | ||
|
||
return client | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package dcos | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestDefaultTransportBaseNil(t *testing.T) { | ||
c := &DefaultTransport{ | ||
Base: nil, | ||
} | ||
rt := c.base() | ||
|
||
assert.ObjectsAreEqual(http.DefaultTransport, rt) | ||
} | ||
|
||
func TestDefaultTransportBase(t *testing.T) { | ||
c := &DefaultTransport{ | ||
Base: &DefaultTransport{Base: nil}, | ||
} | ||
rt := c.base() | ||
|
||
assert.IsType(t, &DefaultTransport{}, rt) | ||
} | ||
|
||
func TestDefaultHTTPClientAuth(t *testing.T) { | ||
tokenValue := "TestDefaultHTTPClientAuth-token" | ||
config := NewConfig(nil) | ||
config.SetACSToken(tokenValue) | ||
|
||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if r.Header.Get("Authorization") == "token="+tokenValue { | ||
w.WriteHeader(http.StatusOK) | ||
w.Write([]byte("TestDefaultHTTPClientAuth - Success")) | ||
} else { | ||
w.WriteHeader(http.StatusUnauthorized) | ||
w.Write([]byte("TestDefaultHTTPClientAuth - Forbidden")) | ||
} | ||
})) | ||
defer s.Close() | ||
|
||
c := NewHTTPClient(config) | ||
|
||
resp, err := c.Get(s.URL) | ||
assert.NoError(t, err) | ||
assert.Equal(t, 200, resp.StatusCode, "using the dcos.NewHTTPClient should respond with 200") | ||
|
||
respDflt, err := http.DefaultClient.Get(s.URL) | ||
assert.NoError(t, err) | ||
assert.Equal(t, 401, respDflt.StatusCode, "expect a forbidden state with http.DefaultClient") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package dcos | ||
|
||
// Version represents the client library version | ||
const Version = "0.0.1" | ||
|
||
// ClientName represents the libraries name | ||
const ClientName = "dcos-client-go" |