1// Package httpunix provides a HTTP transport (net/http.RoundTripper) 2// that uses Unix domain sockets instead of HTTP. 3// 4// This is useful for non-browser connections within the same host, as 5// it allows using the file system for credentials of both client 6// and server, and guaranteeing unique names. 7// 8// The URLs look like this: 9// 10// http+unix://LOCATION/PATH_ETC 11// 12// where LOCATION is translated to a file system path with 13// Transport.RegisterLocation, and PATH_ETC follow normal http: scheme 14// conventions. 15package httpunix 16 17import ( 18 "bufio" 19 "errors" 20 "net" 21 "net/http" 22 "sync" 23 "time" 24) 25 26// Scheme is the URL scheme used for HTTP over UNIX domain sockets. 27const Scheme = "http+unix" 28 29// Transport is a http.RoundTripper that connects to Unix domain 30// sockets. 31type Transport struct { 32 DialTimeout time.Duration 33 RequestTimeout time.Duration 34 ResponseHeaderTimeout time.Duration 35 36 mu sync.Mutex 37 // map a URL "hostname" to a UNIX domain socket path 38 loc map[string]string 39} 40 41// RegisterLocation registers an URL location and maps it to the given 42// file system path. 43// 44// Calling RegisterLocation twice for the same location is a 45// programmer error, and causes a panic. 46func (t *Transport) RegisterLocation(loc string, path string) { 47 t.mu.Lock() 48 defer t.mu.Unlock() 49 if t.loc == nil { 50 t.loc = make(map[string]string) 51 } 52 if _, exists := t.loc[loc]; exists { 53 panic("location " + loc + " already registered") 54 } 55 t.loc[loc] = path 56} 57 58var _ http.RoundTripper = (*Transport)(nil) 59 60// RoundTrip executes a single HTTP transaction. See 61// net/http.RoundTripper. 62func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { 63 if req.URL == nil { 64 return nil, errors.New("http+unix: nil Request.URL") 65 } 66 if req.URL.Scheme != Scheme { 67 return nil, errors.New("unsupported protocol scheme: " + req.URL.Scheme) 68 } 69 if req.URL.Host == "" { 70 return nil, errors.New("http+unix: no Host in request URL") 71 } 72 t.mu.Lock() 73 path, ok := t.loc[req.URL.Host] 74 t.mu.Unlock() 75 if !ok { 76 return nil, errors.New("unknown location: " + req.Host) 77 } 78 79 c, err := net.DialTimeout("unix", path, t.DialTimeout) 80 if err != nil { 81 return nil, err 82 } 83 r := bufio.NewReader(c) 84 if t.RequestTimeout > 0 { 85 c.SetWriteDeadline(time.Now().Add(t.RequestTimeout)) 86 } 87 if err := req.Write(c); err != nil { 88 return nil, err 89 } 90 if t.ResponseHeaderTimeout > 0 { 91 c.SetReadDeadline(time.Now().Add(t.ResponseHeaderTimeout)) 92 } 93 resp, err := http.ReadResponse(r, req) 94 return resp, err 95} 96