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  }