Skip to content

Commit ec69820

Browse files
authored
Merge pull request #507 from projectdiscovery/dwisiswant0/feat/add-TLS-session-cache-for-conn-resumption
feat: add TLS session cache for conn resumption
2 parents dab4f56 + 6d31f5e commit ec69820

File tree

6 files changed

+63
-32
lines changed

6 files changed

+63
-32
lines changed

buggyhttp/buggyhttp.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func messyEncoding(w http.ResponseWriter, req *http.Request) {
6767
}
6868

6969
for _, oneencodingfrommany := range soManyEncodings {
70-
fmt.Fprint(w, oneencodingfrommany)
70+
_, _ = fmt.Fprint(w, oneencodingfrommany)
7171
}
7272
}
7373

@@ -77,7 +77,7 @@ func superSlow(w http.ResponseWriter, req *http.Request) {
7777
z := w.(http.Flusher)
7878
for name, headers := range req.Header {
7979
for _, h := range headers {
80-
fmt.Fprintf(w, "%v: %v\n", name, h)
80+
_, _ = fmt.Fprintf(w, "%v: %v\n", name, h)
8181
z.Flush()
8282
time.Sleep(250 * time.Millisecond)
8383
}
@@ -98,7 +98,9 @@ func superSlow(w http.ResponseWriter, req *http.Request) {
9898
func emptyResponse(w http.ResponseWriter, req *http.Request) {
9999
hj, _ := w.(http.Hijacker)
100100
conn, _, _ := hj.Hijack()
101-
defer conn.Close()
101+
defer func() {
102+
_ = conn.Close()
103+
}()
102104
}
103105

104106
// SO MANY REDIRECTS
@@ -111,7 +113,9 @@ func infiniteRedirects(w http.ResponseWriter, req *http.Request) {
111113
func unexpectedEOF(w http.ResponseWriter, req *http.Request) {
112114
hj, _ := w.(http.Hijacker)
113115
conn, bufrw, _ := hj.Hijack()
114-
defer conn.Close()
116+
defer func() {
117+
_ = conn.Close()
118+
}()
115119
// reply with bogus data - this should either crash the client or trigger a recoverable error on
116120
// default retryablehttp requests
117121
_, _ = bufrw.WriteString("HTTP/1.1 200 OK\n" +
@@ -121,18 +125,18 @@ func unexpectedEOF(w http.ResponseWriter, req *http.Request) {
121125
// "Content-Length: -124" +
122126
// "Content-Type: whatzdacontenttype" +
123127
// "Connection: drunk")
124-
bufrw.Flush()
128+
_ = bufrw.Flush()
125129
}
126130

127131
// Simulate normal 200 answer with body
128132
func foo(w http.ResponseWriter, req *http.Request) {
129-
fmt.Fprintf(w, "foo")
133+
_, _ = fmt.Fprintf(w, "foo")
130134
}
131135

132136
// generates recoverable errors until SuccessAfter attempts => after it 200 + body
133137
var count int // as of now a local horrible variable suffice
134138
func successAfter(w http.ResponseWriter, req *http.Request) {
135-
var successAfter int = defaultSuccessAfterThreshold
139+
successAfter := defaultSuccessAfterThreshold
136140
if req.FormValue("successAfter") != "" {
137141
if i, err := strconv.Atoi(req.FormValue("successAfter")); err == nil {
138142
successAfter = i
@@ -143,7 +147,9 @@ func successAfter(w http.ResponseWriter, req *http.Request) {
143147
if count <= successAfter {
144148
hj, _ := w.(http.Hijacker)
145149
conn, bufrw, _ := hj.Hijack()
146-
defer conn.Close()
150+
defer func() {
151+
_ = conn.Close()
152+
}()
147153
// reply with bogus data - this should either crash the client or trigger a recoverable error on
148154
// default retryablehttp requests
149155
_, _ = bufrw.WriteString("HHHTTP\\1,.1 -500 MAYBEOK\n" +
@@ -153,17 +159,15 @@ func successAfter(w http.ResponseWriter, req *http.Request) {
153159
"Content-Length: -124\n" +
154160
"Content-Type: whatzdacontenttype\n" +
155161
"Connection: drunk")
156-
bufrw.Flush()
162+
_ = bufrw.Flush()
157163
return
158164
}
159165

160166
// zeroes attempts and return 200 + valid body
161167
count = 0
162-
fmt.Fprintf(w, "foo")
168+
_, _ = fmt.Fprintf(w, "foo")
163169
}
164170

165-
166-
167171
var (
168172
server *http.Server
169173
serverTLS *http.Server

client.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,30 +68,36 @@ type Options struct {
6868
WrapTransport func(http.RoundTripper) http.RoundTripper
6969
// Trace enables tracing of the HTTP request
7070
Trace bool
71+
// TLSSessionCacheSize specifies the size of the TLS session cache.
72+
//
73+
// If less than or equal to zero, defaults to 1024.
74+
TLSSessionCacheSize int
7175
}
7276

7377
// DefaultOptionsSpraying contains the default options for host spraying
7478
// scenarios where lots of requests need to be sent to different hosts.
7579
var DefaultOptionsSpraying = Options{
76-
RetryWaitMin: 1 * time.Second,
77-
RetryWaitMax: 30 * time.Second,
78-
Timeout: 30 * time.Second,
79-
RetryMax: 5,
80-
RespReadLimit: 4096,
81-
KillIdleConn: true,
82-
NoAdjustTimeout: true,
80+
RetryWaitMin: 1 * time.Second,
81+
RetryWaitMax: 30 * time.Second,
82+
Timeout: 30 * time.Second,
83+
RetryMax: 5,
84+
RespReadLimit: 4096,
85+
KillIdleConn: true,
86+
NoAdjustTimeout: true,
87+
TLSSessionCacheSize: 1024,
8388
}
8489

8590
// DefaultOptionsSingle contains the default options for host bruteforce
8691
// scenarios where lots of requests need to be sent to a single host.
8792
var DefaultOptionsSingle = Options{
88-
RetryWaitMin: 1 * time.Second,
89-
RetryWaitMax: 30 * time.Second,
90-
Timeout: 30 * time.Second,
91-
RetryMax: 5,
92-
RespReadLimit: 4096,
93-
KillIdleConn: false,
94-
NoAdjustTimeout: true,
93+
RetryWaitMin: 1 * time.Second,
94+
RetryWaitMax: 30 * time.Second,
95+
Timeout: 30 * time.Second,
96+
RetryMax: 5,
97+
RespReadLimit: 4096,
98+
KillIdleConn: false,
99+
NoAdjustTimeout: true,
100+
TLSSessionCacheSize: 1024,
95101
}
96102

97103
// NewClient creates a new Client with default settings.
@@ -110,6 +116,11 @@ func NewClient(options Options) *Client {
110116
return nil
111117
}
112118

119+
if options.HttpClient == nil && options.TLSSessionCacheSize > 0 {
120+
applyTLSSessionCache(httpclient, options.TLSSessionCacheSize)
121+
applyTLSSessionCache(httpclient2, options.TLSSessionCacheSize)
122+
}
123+
113124
// Apply transport wrapper if provided
114125
if options.WrapTransport != nil {
115126
httpclient.Transport = options.WrapTransport(httpclient.Transport)

examples/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func main() {
4545

4646
router := httprouter.New()
4747
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
48-
fmt.Fprintf(w, "this is a test")
48+
_, _ = fmt.Fprintf(w, "this is a test")
4949
})
5050
ts := httptest.NewServer(router)
5151
defer ts.Close()
@@ -63,8 +63,8 @@ func main() {
6363
log.Println(err)
6464
continue
6565
}
66-
io.Copy(io.Discard, resp.Body)
67-
resp.Body.Close()
66+
_, _ = io.Copy(io.Discard, resp.Body)
67+
_ = resp.Body.Close()
6868
}
6969
}()
7070
}

http.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func DefaultReusePooledTransport() *http.Transport {
4141
MaxIdleConnsPerHost: 100,
4242
MaxResponseHeaderBytes: 4096, // net/http default is 10Mb
4343
TLSClientConfig: &tls.Config{
44+
ClientSessionCache: tls.NewLRUClientSessionCache(1024),
4445
Renegotiation: tls.RenegotiateOnceAsClient, // Renegotiation is not supported in TLS 1.3 as per docs
4546
InsecureSkipVerify: true,
4647
MinVersion: tls.VersionTLS10,
@@ -76,6 +77,19 @@ func DefaultPooledClient() *http.Client {
7677
}
7778
}
7879

80+
// applyTLSSessionCache configures the TLS session cache size for an
81+
// [http.Client].
82+
//
83+
// It only applies if the client has an [http.Transport] with a
84+
// TLSClientConfig.
85+
func applyTLSSessionCache(client *http.Client, size int) {
86+
if transport, ok := client.Transport.(*http.Transport); ok {
87+
if transport.TLSClientConfig != nil {
88+
transport.TLSClientConfig.ClientSessionCache = tls.NewLRUClientSessionCache(size)
89+
}
90+
}
91+
}
92+
7993
var (
8094
fdInit = &sync.Once{}
8195
fd *fastdialer.Dialer

request.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ func (r *Request) SetBodyString(body string) error {
159159
// read into the reusable reader.
160160
func (r *Request) SetBodyStream(bodyStream io.Reader, bodySize int64) error {
161161
if closer, ok := bodyStream.(io.Closer); ok {
162-
defer closer.Close()
162+
defer func() {
163+
_ = closer.Close()
164+
}()
163165

164166
// Wrap in NopCloser to prevent NewReusableReadCloser from closing it,
165167
// since we are handling the close via defer.

request_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ func verifyGetBody(t *testing.T, req *retryablehttp.Request, expected []byte) {
376376
t.Fatalf("GetBody failed: %v", err)
377377
}
378378
data1, _ := io.ReadAll(rc1)
379-
rc1.Close()
379+
_ = rc1.Close()
380380

381381
if !bytes.Equal(data1, expected) {
382382
t.Errorf("Read 1 mismatch. Got %s, want %s", string(data1), string(expected))
@@ -388,7 +388,7 @@ func verifyGetBody(t *testing.T, req *retryablehttp.Request, expected []byte) {
388388
t.Fatalf("GetBody failed 2nd time: %v", err)
389389
}
390390
data2, _ := io.ReadAll(rc2)
391-
rc2.Close()
391+
_ = rc2.Close()
392392

393393
if !bytes.Equal(data2, expected) {
394394
t.Errorf("Read 2 mismatch. Got %s, want %s", string(data2), string(expected))

0 commit comments

Comments
 (0)