-
Notifications
You must be signed in to change notification settings - Fork 159
Expand file tree
/
Copy pathhttp.go
More file actions
76 lines (66 loc) · 2.59 KB
/
Copy pathhttp.go
File metadata and controls
76 lines (66 loc) · 2.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package github
import (
"context"
"net"
"net/http"
"time"
)
// Github's HTTP endpoint for asset uploads is flaky. Often it will drop a
// TCP connection *. The HTTP POST will just hang indefinitely. To solve
// this without using absolute deadlines (which would be hard to predict as
// it depends on the size of the asset and the speed of the users'
// connection), we use a modified http.Client which resets a timeout every
// time the kernel accepts a read/write on the socket. Doing so basically
// creates a sort of "inactivity watcher".
//
// We can't use the http.Client.Timeout field, as that's an absolute timeout
// which doesn't get reset whenever there's some activity.
//
// * At least that's what I think is happening, I have never observed this
// myself since I never upload big assets, see issue
// http://github.com/aktau/github-release/issues/26.
// HTTPReadWriteTimeout is the read/write timeout after which connections
// will be closed.
const HTTPReadWriteTimeout = 1 * time.Minute
// dialer for use by the transport below, initialized like the net/http
// dialer.
var dialer = &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
// transport for use by the http.Client below.
//
// TODO: enable HTTP/2 transport as documented in net/http.
var transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: func(ctx context.Context, netw, addr string) (net.Conn, error) {
return newWatchdogConn(dialer.DialContext(ctx, netw, addr))
},
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ResponseHeaderTimeout: 1 * time.Minute,
}
// client is an HTTP client suited for use with GitHub. It includes watchdog
// functionality that will break of an upload/download attempt after
// HTTPReadWriteTimeout.
var client = &http.Client{Transport: transport}
// watchdogConn wraps a net.Conn, on every read and write it sets a timeout.
// If no bytes are read or written in that time, the connection is closed.
type watchdogConn struct {
net.Conn
timeout time.Duration // The amount of time to wait between reads/writes on the connection before cancelling it.
}
func newWatchdogConn(conn net.Conn, err error) (net.Conn, error) {
return &watchdogConn{Conn: conn, timeout: HTTPReadWriteTimeout}, err
}
func (c *watchdogConn) Read(b []byte) (n int, err error) {
c.SetReadDeadline(time.Now().Add(c.timeout))
return c.Conn.Read(b)
}
func (c *watchdogConn) Write(b []byte) (n int, err error) {
c.SetWriteDeadline(time.Now().Add(c.timeout))
return c.Conn.Write(b)
}