mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
remove go-httpclient
This commit is contained in:
parent
91daf1da86
commit
65f7833c22
@ -1,17 +0,0 @@
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1,39 +0,0 @@
|
||||
## go-httpclient
|
||||
|
||||
**requires Go 1.1+** as of `v0.4.0` the API has been completely re-written for Go 1.1 (for a Go
|
||||
1.0.x compatible release see [1adef50](https://github.com/mreiferson/go-httpclient/tree/1adef50))
|
||||
|
||||
[](http://travis-ci.org/mreiferson/go-httpclient)
|
||||
|
||||
Provides an HTTP Transport that implements the `RoundTripper` interface and
|
||||
can be used as a built in replacement for the standard library's, providing:
|
||||
|
||||
* connection timeouts
|
||||
* request timeouts
|
||||
|
||||
This is a thin wrapper around `http.Transport` that sets dial timeouts and uses
|
||||
Go's internal timer scheduler to call the Go 1.1+ `CancelRequest()` API.
|
||||
|
||||
### Example
|
||||
|
||||
```go
|
||||
transport := &httpclient.Transport{
|
||||
ConnectTimeout: 1*time.Second,
|
||||
RequestTimeout: 10*time.Second,
|
||||
ResponseHeaderTimeout: 5*time.Second,
|
||||
}
|
||||
defer transport.Close()
|
||||
|
||||
client := &http.Client{Transport: transport}
|
||||
req, _ := http.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
```
|
||||
|
||||
### Reference Docs
|
||||
|
||||
For API docs see [godoc](http://godoc.org/github.com/mreiferson/go-httpclient).
|
@ -1,210 +0,0 @@
|
||||
/*
|
||||
Provides an HTTP Transport that implements the `RoundTripper` interface and
|
||||
can be used as a built in replacement for the standard library's, providing:
|
||||
|
||||
* connection timeouts
|
||||
* request timeouts
|
||||
|
||||
This is a thin wrapper around `http.Transport` that sets dial timeouts and uses
|
||||
Go's internal timer scheduler to call the Go 1.1+ `CancelRequest()` API.
|
||||
*/
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// returns the current version of the package
|
||||
func Version() string {
|
||||
return "0.4.1"
|
||||
}
|
||||
|
||||
// Transport implements the RoundTripper interface and can be used as a replacement
|
||||
// for Go's built in http.Transport implementing end-to-end request timeouts.
|
||||
//
|
||||
// transport := &httpclient.Transport{
|
||||
// ConnectTimeout: 1*time.Second,
|
||||
// ResponseHeaderTimeout: 5*time.Second,
|
||||
// RequestTimeout: 10*time.Second,
|
||||
// }
|
||||
// defer transport.Close()
|
||||
//
|
||||
// client := &http.Client{Transport: transport}
|
||||
// req, _ := http.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||
// resp, err := client.Do(req)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer resp.Body.Close()
|
||||
//
|
||||
type Transport struct {
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
// *http.Request. If the function returns a non-nil error, the
|
||||
// request is aborted with the provided error.
|
||||
// If Proxy is nil or returns a nil *url.URL, no proxy is used.
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
|
||||
// Dial specifies the dial function for creating TCP
|
||||
// connections. This will override the Transport's ConnectTimeout and
|
||||
// ReadWriteTimeout settings.
|
||||
// If Dial is nil, a dialer is generated on demand matching the Transport's
|
||||
// options.
|
||||
Dial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with
|
||||
// tls.Client. If nil, the default configuration is used.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// DisableKeepAlives, if true, prevents re-use of TCP connections
|
||||
// between different HTTP requests.
|
||||
DisableKeepAlives bool
|
||||
|
||||
// DisableCompression, if true, prevents the Transport from
|
||||
// requesting compression with an "Accept-Encoding: gzip"
|
||||
// request header when the Request contains no existing
|
||||
// Accept-Encoding value. If the Transport requests gzip on
|
||||
// its own and gets a gzipped response, it's transparently
|
||||
// decoded in the Response.Body. However, if the user
|
||||
// explicitly requested gzip it is not automatically
|
||||
// uncompressed.
|
||||
DisableCompression bool
|
||||
|
||||
// MaxIdleConnsPerHost, if non-zero, controls the maximum idle
|
||||
// (keep-alive) to keep per-host. If zero,
|
||||
// http.DefaultMaxIdleConnsPerHost is used.
|
||||
MaxIdleConnsPerHost int
|
||||
|
||||
// ConnectTimeout, if non-zero, is the maximum amount of time a dial will wait for
|
||||
// a connect to complete.
|
||||
ConnectTimeout time.Duration
|
||||
|
||||
// ResponseHeaderTimeout, if non-zero, specifies the amount of
|
||||
// time to wait for a server's response headers after fully
|
||||
// writing the request (including its body, if any). This
|
||||
// time does not include the time to read the response body.
|
||||
ResponseHeaderTimeout time.Duration
|
||||
|
||||
// RequestTimeout, if non-zero, specifies the amount of time for the entire
|
||||
// request to complete (including all of the above timeouts + entire response body).
|
||||
// This should never be less than the sum total of the above two timeouts.
|
||||
RequestTimeout time.Duration
|
||||
|
||||
// ReadWriteTimeout, if non-zero, will set a deadline for every Read and
|
||||
// Write operation on the request connection.
|
||||
ReadWriteTimeout time.Duration
|
||||
|
||||
starter sync.Once
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
// Close cleans up the Transport, currently a no-op
|
||||
func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) lazyStart() {
|
||||
if t.Dial == nil {
|
||||
t.Dial = func(netw, addr string) (net.Conn, error) {
|
||||
c, err := net.DialTimeout(netw, addr, t.ConnectTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t.ReadWriteTimeout > 0 {
|
||||
timeoutConn := &rwTimeoutConn{
|
||||
TCPConn: c.(*net.TCPConn),
|
||||
rwTimeout: t.ReadWriteTimeout,
|
||||
}
|
||||
return timeoutConn, nil
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
t.transport = &http.Transport{
|
||||
Dial: t.Dial,
|
||||
Proxy: t.Proxy,
|
||||
TLSClientConfig: t.TLSClientConfig,
|
||||
DisableKeepAlives: t.DisableKeepAlives,
|
||||
DisableCompression: t.DisableCompression,
|
||||
MaxIdleConnsPerHost: t.MaxIdleConnsPerHost,
|
||||
ResponseHeaderTimeout: t.ResponseHeaderTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) CancelRequest(req *http.Request) {
|
||||
t.starter.Do(t.lazyStart)
|
||||
|
||||
t.transport.CancelRequest(req)
|
||||
}
|
||||
|
||||
func (t *Transport) CloseIdleConnections() {
|
||||
t.starter.Do(t.lazyStart)
|
||||
|
||||
t.transport.CloseIdleConnections()
|
||||
}
|
||||
|
||||
func (t *Transport) RegisterProtocol(scheme string, rt http.RoundTripper) {
|
||||
t.starter.Do(t.lazyStart)
|
||||
|
||||
t.transport.RegisterProtocol(scheme, rt)
|
||||
}
|
||||
|
||||
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
t.starter.Do(t.lazyStart)
|
||||
|
||||
if t.RequestTimeout > 0 {
|
||||
timer := time.AfterFunc(t.RequestTimeout, func() {
|
||||
t.transport.CancelRequest(req)
|
||||
})
|
||||
|
||||
resp, err = t.transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
timer.Stop()
|
||||
} else {
|
||||
resp.Body = &bodyCloseInterceptor{ReadCloser: resp.Body, timer: timer}
|
||||
}
|
||||
} else {
|
||||
resp, err = t.transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type bodyCloseInterceptor struct {
|
||||
io.ReadCloser
|
||||
timer *time.Timer
|
||||
}
|
||||
|
||||
func (bci *bodyCloseInterceptor) Close() error {
|
||||
bci.timer.Stop()
|
||||
return bci.ReadCloser.Close()
|
||||
}
|
||||
|
||||
// A net.Conn that sets a deadline for every Read or Write operation
|
||||
type rwTimeoutConn struct {
|
||||
*net.TCPConn
|
||||
rwTimeout time.Duration
|
||||
}
|
||||
|
||||
func (c *rwTimeoutConn) Read(b []byte) (int, error) {
|
||||
err := c.TCPConn.SetReadDeadline(time.Now().Add(c.rwTimeout))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.TCPConn.Read(b)
|
||||
}
|
||||
|
||||
func (c *rwTimeoutConn) Write(b []byte) (int, error) {
|
||||
err := c.TCPConn.SetWriteDeadline(time.Now().Add(c.rwTimeout))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.TCPConn.Write(b)
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var starter sync.Once
|
||||
var addr net.Addr
|
||||
|
||||
func testHandler(w http.ResponseWriter, req *http.Request) {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
io.WriteString(w, "hello, world!\n")
|
||||
}
|
||||
|
||||
func postHandler(w http.ResponseWriter, req *http.Request) {
|
||||
ioutil.ReadAll(req.Body)
|
||||
w.Header().Set("Content-Length", "2")
|
||||
io.WriteString(w, "OK")
|
||||
}
|
||||
|
||||
func closeHandler(w http.ResponseWriter, req *http.Request) {
|
||||
hj, _ := w.(http.Hijacker)
|
||||
conn, bufrw, _ := hj.Hijack()
|
||||
defer conn.Close()
|
||||
bufrw.WriteString("HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n")
|
||||
bufrw.Flush()
|
||||
}
|
||||
|
||||
func redirectHandler(w http.ResponseWriter, req *http.Request) {
|
||||
ioutil.ReadAll(req.Body)
|
||||
http.Redirect(w, req, "/post", 302)
|
||||
}
|
||||
|
||||
func redirect2Handler(w http.ResponseWriter, req *http.Request) {
|
||||
ioutil.ReadAll(req.Body)
|
||||
http.Redirect(w, req, "/redirect", 302)
|
||||
}
|
||||
|
||||
func slowHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
io.WriteString(w, "START\n")
|
||||
f := w.(http.Flusher)
|
||||
f.Flush()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
io.WriteString(w, "WORKING\n")
|
||||
f.Flush()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
io.WriteString(w, "DONE\n")
|
||||
return
|
||||
}
|
||||
|
||||
func setupMockServer(t *testing.T) {
|
||||
http.HandleFunc("/test", testHandler)
|
||||
http.HandleFunc("/post", postHandler)
|
||||
http.HandleFunc("/redirect", redirectHandler)
|
||||
http.HandleFunc("/redirect2", redirect2Handler)
|
||||
http.HandleFunc("/close", closeHandler)
|
||||
http.HandleFunc("/slow", slowHandler)
|
||||
ln, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen - %s", err.Error())
|
||||
}
|
||||
go func() {
|
||||
err = http.Serve(ln, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start HTTP server - %s", err.Error())
|
||||
}
|
||||
}()
|
||||
addr = ln.Addr()
|
||||
}
|
||||
|
||||
func TestHttpsConnection(t *testing.T) {
|
||||
transport := &Transport{
|
||||
ConnectTimeout: 1 * time.Second,
|
||||
RequestTimeout: 2 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
defer transport.Close()
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://httpbin.org/ip", nil)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("1st request failed - %s", err.Error())
|
||||
}
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("1st failed to read body - %s", err.Error())
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
req2, _ := http.NewRequest("GET", "https://httpbin.org/delay/5", nil)
|
||||
_, err = client.Do(req2)
|
||||
if err == nil {
|
||||
t.Fatalf("HTTPS request should have timed out")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHttpClient(t *testing.T) {
|
||||
starter.Do(func() { setupMockServer(t) })
|
||||
|
||||
transport := &Transport{
|
||||
ConnectTimeout: 1 * time.Second,
|
||||
RequestTimeout: 5 * time.Second,
|
||||
ReadWriteTimeout: 3 * time.Second,
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("1st request failed - %s", err.Error())
|
||||
}
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("1st failed to read body - %s", err.Error())
|
||||
}
|
||||
resp.Body.Close()
|
||||
transport.Close()
|
||||
|
||||
transport = &Transport{
|
||||
ConnectTimeout: 25 * time.Millisecond,
|
||||
RequestTimeout: 50 * time.Millisecond,
|
||||
ReadWriteTimeout: 50 * time.Millisecond,
|
||||
}
|
||||
client = &http.Client{Transport: transport}
|
||||
|
||||
req2, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
||||
resp, err = client.Do(req2)
|
||||
if err == nil {
|
||||
t.Fatal("2nd request should have timed out")
|
||||
}
|
||||
transport.Close()
|
||||
|
||||
transport = &Transport{
|
||||
ConnectTimeout: 25 * time.Millisecond,
|
||||
RequestTimeout: 250 * time.Millisecond,
|
||||
ReadWriteTimeout: 250 * time.Millisecond,
|
||||
}
|
||||
client = &http.Client{Transport: transport}
|
||||
|
||||
req3, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
||||
resp, err = client.Do(req3)
|
||||
if err != nil {
|
||||
t.Fatal("3rd request should not have timed out")
|
||||
}
|
||||
resp.Body.Close()
|
||||
transport.Close()
|
||||
}
|
||||
|
||||
func TestSlowServer(t *testing.T) {
|
||||
starter.Do(func() { setupMockServer(t) })
|
||||
|
||||
transport := &Transport{
|
||||
ConnectTimeout: 25 * time.Millisecond,
|
||||
RequestTimeout: 500 * time.Millisecond,
|
||||
ReadWriteTimeout: 250 * time.Millisecond,
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://"+addr.String()+"/slow", nil)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("1st request failed - %s", err)
|
||||
}
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("1st failed to read body - %s", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
transport.Close()
|
||||
|
||||
transport = &Transport{
|
||||
ConnectTimeout: 25 * time.Millisecond,
|
||||
RequestTimeout: 500 * time.Millisecond,
|
||||
ReadWriteTimeout: 100 * time.Millisecond,
|
||||
}
|
||||
client = &http.Client{Transport: transport}
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://"+addr.String()+"/slow", nil)
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("2nd request failed - %s", err)
|
||||
}
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
netErr, ok := err.(net.Error)
|
||||
if !ok {
|
||||
t.Fatalf("2nd request dind't return a net.Error - %s", netErr)
|
||||
}
|
||||
|
||||
if !netErr.Timeout() {
|
||||
t.Fatalf("2nd request should have timed out - %s", netErr)
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
transport.Close()
|
||||
}
|
||||
|
||||
func TestMultipleRequests(t *testing.T) {
|
||||
starter.Do(func() { setupMockServer(t) })
|
||||
|
||||
transport := &Transport{
|
||||
ConnectTimeout: 1 * time.Second,
|
||||
RequestTimeout: 5 * time.Second,
|
||||
ReadWriteTimeout: 3 * time.Second,
|
||||
ResponseHeaderTimeout: 400 * time.Millisecond,
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
||||
for i := 0; i < 10; i++ {
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("%d request failed - %s", i, err.Error())
|
||||
}
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("%d failed to read body - %s", i, err.Error())
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
transport.Close()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user