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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
Description: net/http: non-keepalive connections close successfully
Connections did not close if Request.Close or Response.Close was true. This
meant that if the user wanted the connection to close, or if the server
requested it via "Connection: close", the connection would not be closed.
Author: James Gray <james@james4k.com>
Bug-Debian: http://bugs.debian.org/683421
Origin: upstream, http://code.google.com/p/go/source/detail?r=820ffde8c396
Forwarded: not-needed
Last-Update: 2012-08-01
---
--- golang-1.0.2.orig/src/pkg/net/http/transport.go
+++ golang-1.0.2/src/pkg/net/http/transport.go
@@ -603,6 +603,10 @@ func (pc *persistConn) readLoop() {
// before we race and peek on the underlying bufio reader.
if waitForBodyRead != nil {
<-waitForBodyRead
+ } else if !alive {
+ // If waitForBodyRead is nil, and we're not alive, we
+ // must close the connection before we leave the loop.
+ pc.close()
}
}
}
--- golang-1.0.2.orig/src/pkg/net/http/transport_test.go
+++ golang-1.0.2/src/pkg/net/http/transport_test.go
@@ -13,6 +13,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net"
. "net/http"
"net/http/httptest"
"net/url"
@@ -20,6 +21,7 @@ import (
"runtime"
"strconv"
"strings"
+ "sync"
"testing"
"time"
)
@@ -35,6 +37,64 @@ var hostPortHandler = HandlerFunc(func(w
w.Write([]byte(r.RemoteAddr))
})
+type testCloseConn struct {
+ net.Conn
+ set *testConnSet
+}
+
+func (conn *testCloseConn) Close() error {
+ conn.set.remove(conn)
+ return conn.Conn.Close()
+}
+
+type testConnSet struct {
+ set map[net.Conn]bool
+ mutex sync.Mutex
+}
+
+func (tcs *testConnSet) insert(c net.Conn) {
+ tcs.mutex.Lock()
+ defer tcs.mutex.Unlock()
+ tcs.set[c] = true
+}
+
+func (tcs *testConnSet) remove(c net.Conn) {
+ tcs.mutex.Lock()
+ defer tcs.mutex.Unlock()
+ // just change to false, so we have a full set of opened connections
+ tcs.set[c] = false
+}
+
+// some tests use this to manage raw tcp connections for later inspection
+func makeTestDial() (*testConnSet, func(n, addr string) (net.Conn, error)) {
+ connSet := &testConnSet{
+ set: make(map[net.Conn]bool),
+ }
+ dial := func(n, addr string) (net.Conn, error) {
+ c, err := net.Dial(n, addr)
+ if err != nil {
+ return nil, err
+ }
+ tc := &testCloseConn{c, connSet}
+ connSet.insert(tc)
+ return tc, nil
+ }
+ return connSet, dial
+}
+
+func (tcs *testConnSet) countClosed() (closed, total int) {
+ tcs.mutex.Lock()
+ defer tcs.mutex.Unlock()
+
+ total = len(tcs.set)
+ for _, open := range tcs.set {
+ if !open {
+ closed += 1
+ }
+ }
+ return
+}
+
// Two subsequent requests and verify their response is the same.
// The response from the server is our own IP:port
func TestTransportKeepAlives(t *testing.T) {
@@ -72,8 +132,12 @@ func TestTransportConnectionCloseOnRespo
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
+ connSet, testDial := makeTestDial()
+
for _, connectionClose := range []bool{false, true} {
- tr := &Transport{}
+ tr := &Transport{
+ Dial: testDial,
+ }
c := &Client{Transport: tr}
fetch := func(n int) string {
@@ -107,6 +171,13 @@ func TestTransportConnectionCloseOnRespo
t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
connectionClose, bodiesDiffer, body1, body2)
}
+
+ tr.CloseIdleConnections()
+ }
+
+ closed, total := connSet.countClosed()
+ if closed < total {
+ t.Errorf("%d out of %d tcp connections were not closed", total-closed, total)
}
}
@@ -114,8 +185,12 @@ func TestTransportConnectionCloseOnReque
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
+ connSet, testDial := makeTestDial()
+
for _, connectionClose := range []bool{false, true} {
- tr := &Transport{}
+ tr := &Transport{
+ Dial: testDial,
+ }
c := &Client{Transport: tr}
fetch := func(n int) string {
@@ -149,6 +224,13 @@ func TestTransportConnectionCloseOnReque
t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
connectionClose, bodiesDiffer, body1, body2)
}
+
+ tr.CloseIdleConnections()
+ }
+
+ closed, total := connSet.countClosed()
+ if closed < total {
+ t.Errorf("%d out of %d tcp connections were not closed", total-closed, total)
}
}
|