github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/fshttp/http.go (about) 1 // Package fshttp contains the common http parts of the config, Transport and Client 2 package fshttp 3 4 import ( 5 "bytes" 6 "context" 7 "crypto/tls" 8 "crypto/x509" 9 "io/ioutil" 10 "log" 11 "net" 12 "net/http" 13 "net/http/cookiejar" 14 "net/http/httputil" 15 "reflect" 16 "sync" 17 "time" 18 19 "github.com/ncw/rclone/fs" 20 "golang.org/x/net/publicsuffix" 21 "golang.org/x/time/rate" 22 ) 23 24 const ( 25 separatorReq = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" 26 separatorResp = "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" 27 ) 28 29 var ( 30 transport http.RoundTripper 31 noTransport = new(sync.Once) 32 tpsBucket *rate.Limiter // for limiting number of http transactions per second 33 cookieJar, _ = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) 34 ) 35 36 // StartHTTPTokenBucket starts the token bucket if necessary 37 func StartHTTPTokenBucket() { 38 if fs.Config.TPSLimit > 0 { 39 tpsBurst := fs.Config.TPSLimitBurst 40 if tpsBurst < 1 { 41 tpsBurst = 1 42 } 43 tpsBucket = rate.NewLimiter(rate.Limit(fs.Config.TPSLimit), tpsBurst) 44 fs.Infof(nil, "Starting HTTP transaction limiter: max %g transactions/s with burst %d", fs.Config.TPSLimit, tpsBurst) 45 } 46 } 47 48 // A net.Conn that sets a deadline for every Read or Write operation 49 type timeoutConn struct { 50 net.Conn 51 timeout time.Duration 52 } 53 54 // create a timeoutConn using the timeout 55 func newTimeoutConn(conn net.Conn, timeout time.Duration) (c *timeoutConn, err error) { 56 c = &timeoutConn{ 57 Conn: conn, 58 timeout: timeout, 59 } 60 err = c.nudgeDeadline() 61 return 62 } 63 64 // Nudge the deadline for an idle timeout on by c.timeout if non-zero 65 func (c *timeoutConn) nudgeDeadline() (err error) { 66 if c.timeout == 0 { 67 return nil 68 } 69 when := time.Now().Add(c.timeout) 70 return c.Conn.SetDeadline(when) 71 } 72 73 // readOrWrite bytes doing idle timeouts 74 func (c *timeoutConn) readOrWrite(f func([]byte) (int, error), b []byte) (n int, err error) { 75 n, err = f(b) 76 // Don't nudge if no bytes or an error 77 if n == 0 || err != nil { 78 return 79 } 80 // Nudge the deadline on successful Read or Write 81 err = c.nudgeDeadline() 82 return 83 } 84 85 // Read bytes doing idle timeouts 86 func (c *timeoutConn) Read(b []byte) (n int, err error) { 87 return c.readOrWrite(c.Conn.Read, b) 88 } 89 90 // Write bytes doing idle timeouts 91 func (c *timeoutConn) Write(b []byte) (n int, err error) { 92 return c.readOrWrite(c.Conn.Write, b) 93 } 94 95 // setDefaults for a from b 96 // 97 // Copy the public members from b to a. We can't just use a struct 98 // copy as Transport contains a private mutex. 99 func setDefaults(a, b interface{}) { 100 pt := reflect.TypeOf(a) 101 t := pt.Elem() 102 va := reflect.ValueOf(a).Elem() 103 vb := reflect.ValueOf(b).Elem() 104 for i := 0; i < t.NumField(); i++ { 105 aField := va.Field(i) 106 // Set a from b if it is public 107 if aField.CanSet() { 108 bField := vb.Field(i) 109 aField.Set(bField) 110 } 111 } 112 } 113 114 // dial with context and timeouts 115 func dialContextTimeout(ctx context.Context, network, address string, ci *fs.ConfigInfo) (net.Conn, error) { 116 dialer := NewDialer(ci) 117 c, err := dialer.DialContext(ctx, network, address) 118 if err != nil { 119 return c, err 120 } 121 return newTimeoutConn(c, ci.Timeout) 122 } 123 124 // ResetTransport resets the existing transport, allowing it to take new settings. 125 // Should only be used for testing. 126 func ResetTransport() { 127 noTransport = new(sync.Once) 128 } 129 130 // NewTransport returns an http.RoundTripper with the correct timeouts 131 func NewTransport(ci *fs.ConfigInfo) http.RoundTripper { 132 (*noTransport).Do(func() { 133 // Start with a sensible set of defaults then override. 134 // This also means we get new stuff when it gets added to go 135 t := new(http.Transport) 136 setDefaults(t, http.DefaultTransport.(*http.Transport)) 137 t.Proxy = http.ProxyFromEnvironment 138 t.MaxIdleConnsPerHost = 2 * (ci.Checkers + ci.Transfers + 1) 139 t.MaxIdleConns = 2 * t.MaxIdleConnsPerHost 140 t.TLSHandshakeTimeout = ci.ConnectTimeout 141 t.ResponseHeaderTimeout = ci.Timeout 142 143 // TLS Config 144 t.TLSClientConfig = &tls.Config{ 145 InsecureSkipVerify: ci.InsecureSkipVerify, 146 } 147 148 // Load client certs 149 if ci.ClientCert != "" || ci.ClientKey != "" { 150 if ci.ClientCert == "" || ci.ClientKey == "" { 151 log.Fatalf("Both --client-cert and --client-key must be set") 152 } 153 cert, err := tls.LoadX509KeyPair(ci.ClientCert, ci.ClientKey) 154 if err != nil { 155 log.Fatalf("Failed to load --client-cert/--client-key pair: %v", err) 156 } 157 t.TLSClientConfig.Certificates = []tls.Certificate{cert} 158 t.TLSClientConfig.BuildNameToCertificate() 159 } 160 161 // Load CA cert 162 if ci.CaCert != "" { 163 caCert, err := ioutil.ReadFile(ci.CaCert) 164 if err != nil { 165 log.Fatalf("Failed to read --ca-cert: %v", err) 166 } 167 caCertPool := x509.NewCertPool() 168 ok := caCertPool.AppendCertsFromPEM(caCert) 169 if !ok { 170 log.Fatalf("Failed to add certificates from --ca-cert") 171 } 172 t.TLSClientConfig.RootCAs = caCertPool 173 } 174 175 t.DisableCompression = ci.NoGzip 176 t.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { 177 return dialContextTimeout(ctx, network, addr, ci) 178 } 179 t.IdleConnTimeout = 60 * time.Second 180 t.ExpectContinueTimeout = ci.ConnectTimeout 181 // Wrap that http.Transport in our own transport 182 transport = newTransport(ci, t) 183 }) 184 return transport 185 } 186 187 // NewClient returns an http.Client with the correct timeouts 188 func NewClient(ci *fs.ConfigInfo) *http.Client { 189 transport := &http.Client{ 190 Transport: NewTransport(ci), 191 } 192 if ci.Cookie { 193 transport.Jar = cookieJar 194 } 195 return transport 196 } 197 198 // Transport is a our http Transport which wraps an http.Transport 199 // * Sets the User Agent 200 // * Does logging 201 type Transport struct { 202 *http.Transport 203 dump fs.DumpFlags 204 filterRequest func(req *http.Request) 205 userAgent string 206 } 207 208 // newTransport wraps the http.Transport passed in and logs all 209 // roundtrips including the body if logBody is set. 210 func newTransport(ci *fs.ConfigInfo, transport *http.Transport) *Transport { 211 return &Transport{ 212 Transport: transport, 213 dump: ci.Dump, 214 userAgent: ci.UserAgent, 215 } 216 } 217 218 // SetRequestFilter sets a filter to be used on each request 219 func (t *Transport) SetRequestFilter(f func(req *http.Request)) { 220 t.filterRequest = f 221 } 222 223 // A mutex to protect this map 224 var checkedHostMu sync.RWMutex 225 226 // A map of servers we have checked for time 227 var checkedHost = make(map[string]struct{}, 1) 228 229 // Check the server time is the same as ours, once for each server 230 func checkServerTime(req *http.Request, resp *http.Response) { 231 host := req.URL.Host 232 if req.Host != "" { 233 host = req.Host 234 } 235 checkedHostMu.RLock() 236 _, ok := checkedHost[host] 237 checkedHostMu.RUnlock() 238 if ok { 239 return 240 } 241 dateString := resp.Header.Get("Date") 242 if dateString == "" { 243 return 244 } 245 date, err := http.ParseTime(dateString) 246 if err != nil { 247 fs.Debugf(nil, "Couldn't parse Date: from server %s: %q: %v", host, dateString, err) 248 return 249 } 250 dt := time.Since(date) 251 const window = 5 * 60 * time.Second 252 if dt > window || dt < -window { 253 fs.Logf(nil, "Time may be set wrong - time from %q is %v different from this computer", host, dt) 254 } 255 checkedHostMu.Lock() 256 checkedHost[host] = struct{}{} 257 checkedHostMu.Unlock() 258 } 259 260 // cleanAuth gets rid of one authBuf header within the first 4k 261 func cleanAuth(buf, authBuf []byte) []byte { 262 // Find how much buffer to check 263 n := 4096 264 if len(buf) < n { 265 n = len(buf) 266 } 267 // See if there is an Authorization: header 268 i := bytes.Index(buf[:n], authBuf) 269 if i < 0 { 270 return buf 271 } 272 i += len(authBuf) 273 // Overwrite the next 4 chars with 'X' 274 for j := 0; i < len(buf) && j < 4; j++ { 275 if buf[i] == '\n' { 276 break 277 } 278 buf[i] = 'X' 279 i++ 280 } 281 // Snip out to the next '\n' 282 j := bytes.IndexByte(buf[i:], '\n') 283 if j < 0 { 284 return buf[:i] 285 } 286 n = copy(buf[i:], buf[i+j:]) 287 return buf[:i+n] 288 } 289 290 var authBufs = [][]byte{ 291 []byte("Authorization: "), 292 []byte("X-Auth-Token: "), 293 } 294 295 // cleanAuths gets rid of all the possible Auth headers 296 func cleanAuths(buf []byte) []byte { 297 for _, authBuf := range authBufs { 298 buf = cleanAuth(buf, authBuf) 299 } 300 return buf 301 } 302 303 // RoundTrip implements the RoundTripper interface. 304 func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { 305 // Get transactions per second token first if limiting 306 if tpsBucket != nil { 307 tbErr := tpsBucket.Wait(req.Context()) 308 if tbErr != nil { 309 fs.Errorf(nil, "HTTP token bucket error: %v", err) 310 } 311 } 312 // Force user agent 313 req.Header.Set("User-Agent", t.userAgent) 314 // Filter the request if required 315 if t.filterRequest != nil { 316 t.filterRequest(req) 317 } 318 // Logf request 319 if t.dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpAuth|fs.DumpRequests|fs.DumpResponses) != 0 { 320 buf, _ := httputil.DumpRequestOut(req, t.dump&(fs.DumpBodies|fs.DumpRequests) != 0) 321 if t.dump&fs.DumpAuth == 0 { 322 buf = cleanAuths(buf) 323 } 324 fs.Debugf(nil, "%s", separatorReq) 325 fs.Debugf(nil, "%s (req %p)", "HTTP REQUEST", req) 326 fs.Debugf(nil, "%s", string(buf)) 327 fs.Debugf(nil, "%s", separatorReq) 328 } 329 // Do round trip 330 resp, err = t.Transport.RoundTrip(req) 331 // Logf response 332 if t.dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpAuth|fs.DumpRequests|fs.DumpResponses) != 0 { 333 fs.Debugf(nil, "%s", separatorResp) 334 fs.Debugf(nil, "%s (req %p)", "HTTP RESPONSE", req) 335 if err != nil { 336 fs.Debugf(nil, "Error: %v", err) 337 } else { 338 buf, _ := httputil.DumpResponse(resp, t.dump&(fs.DumpBodies|fs.DumpResponses) != 0) 339 fs.Debugf(nil, "%s", string(buf)) 340 } 341 fs.Debugf(nil, "%s", separatorResp) 342 } 343 if err == nil { 344 checkServerTime(req, resp) 345 } 346 return resp, err 347 } 348 349 // NewDialer creates a net.Dialer structure with Timeout, Keepalive 350 // and LocalAddr set from rclone flags. 351 func NewDialer(ci *fs.ConfigInfo) *net.Dialer { 352 dialer := &net.Dialer{ 353 Timeout: ci.ConnectTimeout, 354 KeepAlive: 30 * time.Second, 355 } 356 if ci.BindAddr != nil { 357 dialer.LocalAddr = &net.TCPAddr{IP: ci.BindAddr} 358 } 359 return dialer 360 }