mirror of
https://github.com/golang/go
synced 2024-11-24 17:10:03 -07:00
http: Transport hook to register non-http(s) protocols
This permits external packages implementing e.g. FTP or gopher to register themselves with the http.DefaultClient: package ftp func init() { http.DefaultTransport.RegisterProtocol("ftp", &ftp{}) } Client code would look like: import ( _ "github.com/exampleuser/go/gopher" _ "github.com/exampleuser/go/ftp" ) func main() { resp, err := http.Get("ftp://example.com/path.txt") ... } R=dsymonds, rsc CC=golang-dev https://golang.org/cl/4526077
This commit is contained in:
parent
1b6bf88767
commit
0836b86e8b
@ -36,6 +36,7 @@ const DefaultMaxIdleConnsPerHost = 2
|
|||||||
type Transport struct {
|
type Transport struct {
|
||||||
lk sync.Mutex
|
lk sync.Mutex
|
||||||
idleConn map[string][]*persistConn
|
idleConn map[string][]*persistConn
|
||||||
|
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
|
||||||
|
|
||||||
// TODO: tunable on global max cached connections
|
// TODO: tunable on global max cached connections
|
||||||
// TODO: tunable on timeout on cached connections
|
// TODO: tunable on timeout on cached connections
|
||||||
@ -97,7 +98,16 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
||||||
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
|
t.lk.Lock()
|
||||||
|
var rt RoundTripper
|
||||||
|
if t.altProto != nil {
|
||||||
|
rt = t.altProto[req.URL.Scheme]
|
||||||
|
}
|
||||||
|
t.lk.Unlock()
|
||||||
|
if rt == nil {
|
||||||
|
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
|
||||||
|
}
|
||||||
|
return rt.RoundTrip(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
cm, err := t.connectMethodForRequest(req)
|
cm, err := t.connectMethodForRequest(req)
|
||||||
@ -117,6 +127,27 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err os.Error) {
|
|||||||
return pconn.roundTrip(req)
|
return pconn.roundTrip(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterProtocol registers a new protocol with scheme.
|
||||||
|
// The Transport will pass requests using the given scheme to rt.
|
||||||
|
// It is rt's responsibility to simulate HTTP request semantics.
|
||||||
|
//
|
||||||
|
// RegisterProtocol can be used by other packages to provide
|
||||||
|
// implementations of protocol schemes like "ftp" or "file".
|
||||||
|
func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
|
||||||
|
if scheme == "http" || scheme == "https" {
|
||||||
|
panic("protocol " + scheme + " already registered")
|
||||||
|
}
|
||||||
|
t.lk.Lock()
|
||||||
|
defer t.lk.Unlock()
|
||||||
|
if t.altProto == nil {
|
||||||
|
t.altProto = make(map[string]RoundTripper)
|
||||||
|
}
|
||||||
|
if _, exists := t.altProto[scheme]; exists {
|
||||||
|
panic("protocol " + scheme + " already registered")
|
||||||
|
}
|
||||||
|
t.altProto[scheme] = rt
|
||||||
|
}
|
||||||
|
|
||||||
// CloseIdleConnections closes any connections which were previously
|
// CloseIdleConnections closes any connections which were previously
|
||||||
// connected from previous requests but are now sitting idle in
|
// connected from previous requests but are now sitting idle in
|
||||||
// a "keep-alive" state. It does not interrupt any connections currently
|
// a "keep-alive" state. It does not interrupt any connections currently
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -531,6 +532,36 @@ func TestTransportGzipRecursive(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fooProto struct{}
|
||||||
|
|
||||||
|
func (fooProto) RoundTrip(req *Request) (*Response, os.Error) {
|
||||||
|
res := &Response{
|
||||||
|
Status: "200 OK",
|
||||||
|
StatusCode: 200,
|
||||||
|
Header: make(Header),
|
||||||
|
Body: ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTransportAltProto(t *testing.T) {
|
||||||
|
tr := &Transport{}
|
||||||
|
c := &Client{Transport: tr}
|
||||||
|
tr.RegisterProtocol("foo", fooProto{})
|
||||||
|
res, err := c.Get("foo://bar.com/path")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
bodyb, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body := string(bodyb)
|
||||||
|
if e := "You wanted foo://bar.com/path"; body != e {
|
||||||
|
t.Errorf("got response %q, want %q", body, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// rgz is a gzip quine that uncompresses to itself.
|
// rgz is a gzip quine that uncompresses to itself.
|
||||||
var rgz = []byte{
|
var rgz = []byte{
|
||||||
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
|
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
Loading…
Reference in New Issue
Block a user