Skip to content

Commit 78bf9c3

Browse files
committed
restoring original logic + limiting read to 512Mb + lint
1 parent 8569576 commit 78bf9c3

File tree

8 files changed

+50
-36
lines changed

8 files changed

+50
-36
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ OPTIMIZATIONS:
254254
-retries int number of retries
255255
-timeout int timeout in seconds (default 10)
256256
-delay value duration between each http request (eg: 200ms, 1s) (default -1ns)
257-
-rsts, -response-size-to-save int max response size to save in bytes (default 10485760)
258-
-rstr, -response-size-to-read int max response size to read in bytes (default 10485760)
257+
-rsts, -response-size-to-save int max response size to save in bytes (default 2147483647)
258+
-rstr, -response-size-to-read int max response size to read in bytes (default 2147483647)
259259

260260
CLOUD:
261261
-auth configure projectdiscovery cloud (pdcp) api key (default true)
@@ -307,4 +307,4 @@ Probing feature is inspired by [@tomnomnom/httprobe](https://github.com/tomnomno
307307

308308
<a href="https://discord.gg/projectdiscovery"><img src="https://raw.githubusercontent.com/projectdiscovery/nuclei-burp-plugin/main/static/join-discord.png" width="300" alt="Join Discord"></a>
309309

310-
</div>
310+
</div>

cmd/httpx/resume.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
resume_from=http://localhost:8000/endless
2+
index=1

common/httpx/httpx.go

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"net"
99
"net/http"
10-
"net/http/httputil"
1110
"net/url"
1211
"os"
1312
"strconv"
@@ -236,49 +235,47 @@ get_response:
236235

237236
resp.Headers = httpresp.Header.Clone()
238237

239-
// Dump headers only (does not consume body)
240-
headers, err := httputil.DumpResponse(httpresp, false)
238+
if h.Options.MaxResponseBodySizeToRead > 0 {
239+
httpresp.Body = io.NopCloser(io.LimitReader(httpresp.Body, h.Options.MaxResponseBodySizeToRead))
240+
defer func() {
241+
_, _ = io.Copy(io.Discard, httpresp.Body)
242+
_ = httpresp.Body.Close()
243+
}()
244+
}
245+
246+
// httputil.DumpResponse does not handle websockets
247+
headers, rawResp, err := pdhttputil.DumpResponseHeadersAndRaw(httpresp)
241248
if err != nil {
242249
if stringsutil.ContainsAny(err.Error(), "tls: user canceled") {
243250
shouldIgnoreErrors = true
244251
shouldIgnoreBodyErrors = true
245252
}
253+
254+
// Edge case - some servers respond with gzip encoding header but uncompressed body, in this case the standard library configures the reader as gzip, triggering an error when read.
255+
// The bytes slice is not accessible because of abstraction, therefore we need to perform the request again tampering the Accept-Encoding header
256+
if !gzipRetry && strings.Contains(err.Error(), "gzip: invalid header") {
257+
gzipRetry = true
258+
req.Header.Set("Accept-Encoding", "identity")
259+
goto get_response
260+
}
246261
if !shouldIgnoreErrors {
247262
return nil, err
248263
}
249264
}
250-
265+
resp.Raw = string(rawResp)
251266
resp.RawHeaders = string(headers)
252-
253267
var respbody []byte
254268
// body shouldn't be read with the following status codes
255269
// 101 - Switching Protocols => websockets don't have a readable body
256270
// 304 - Not Modified => no body the response terminates with latest header newline
257271
if !generic.EqualsAny(httpresp.StatusCode, http.StatusSwitchingProtocols, http.StatusNotModified) {
258-
272+
var err error
259273
respbody, err = io.ReadAll(io.LimitReader(httpresp.Body, h.Options.MaxResponseBodySizeToRead))
260-
if err != nil {
261-
// Edge case: some servers respond with gzip encoding header but uncompressed body.
262-
// Retry request with identity encoding.
263-
if !gzipRetry && strings.Contains(err.Error(), "gzip: invalid header") {
264-
gzipRetry = true
265-
req.Header.Set("Accept-Encoding", "identity")
266-
goto get_response
267-
}
268-
if !shouldIgnoreBodyErrors {
269-
return nil, err
270-
}
274+
if err != nil && !shouldIgnoreBodyErrors {
275+
return nil, err
271276
}
272277
}
273278

274-
// Build bounded raw response: headers + capped body
275-
// NOTE: resp.Raw must be constructed from a capped body to avoid OOM on infinite streams.
276-
277-
raw := make([]byte, 0, len(headers)+len(respbody))
278-
raw = append(raw, headers...)
279-
raw = append(raw, respbody...)
280-
resp.Raw = string(raw)
281-
282279
closeErr := httpresp.Body.Close()
283280
if closeErr != nil && !shouldIgnoreBodyErrors {
284281
return nil, closeErr

common/httpx/option.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,19 @@ import (
55
"strings"
66
"time"
77

8+
"github.com/dustin/go-humanize"
89
"github.com/projectdiscovery/cdncheck"
910
"github.com/projectdiscovery/networkpolicy"
1011
)
1112

13+
// DefaultMaxResponseBodySize is the default maximum response body size (4GB)
14+
var DefaultMaxResponseBodySize int64
15+
16+
func init() {
17+
maxResponseBodySize, _ := humanize.ParseBytes("512Mb")
18+
DefaultMaxResponseBodySize = int64(maxResponseBodySize)
19+
}
20+
1221
// Options contains configuration options for the client
1322
type Options struct {
1423
RandomAgent bool
@@ -66,7 +75,7 @@ var DefaultOptions = Options{
6675
Unsafe: false,
6776
CdnCheck: "true",
6877
ExcludeCdn: false,
69-
MaxResponseBodySizeToRead: 1024 * 1024 * 10,
78+
MaxResponseBodySizeToRead: DefaultMaxResponseBodySize,
7079
// VHOSTs options
7180
VHostIgnoreStatusCode: false,
7281
VHostIgnoreContentLength: true,

common/stringz/stringz.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ func AddURLDefaultPort(rawURL string) string {
8585
}
8686
// Force default port to be added if not present
8787
if u.Port() == "" {
88-
if u.Scheme == urlutil.HTTP {
88+
switch u.Scheme {
89+
case urlutil.HTTP:
8990
u.UpdatePort("80")
90-
} else if u.Scheme == urlutil.HTTPS {
91+
case urlutil.HTTPS:
9192
u.UpdatePort("443")
9293
}
9394
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ require (
5151

5252
require (
5353
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.0
54+
github.com/dustin/go-humanize v1.0.1
5455
github.com/go-viper/mapstructure/v2 v2.4.0
5556
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
5657
github.com/weppos/publicsuffix-go v0.50.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
116116
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
117117
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
118118
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
119+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
120+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
119121
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
120122
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
121123
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=

runner/options.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"github.com/projectdiscovery/httpx/common/customlist"
2323
customport "github.com/projectdiscovery/httpx/common/customports"
2424
fileutilz "github.com/projectdiscovery/httpx/common/fileutil"
25-
"github.com/projectdiscovery/httpx/common/httpx"
25+
httpxcommon "github.com/projectdiscovery/httpx/common/httpx"
2626
"github.com/projectdiscovery/httpx/common/stringz"
2727
"github.com/projectdiscovery/networkpolicy"
2828
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
@@ -540,9 +540,11 @@ func ParseOptions() *Options {
540540
flagSet.IntVar(&options.Retries, "retries", 0, "number of retries"),
541541
flagSet.IntVar(&options.Timeout, "timeout", 10, "timeout in seconds"),
542542
flagSet.DurationVar(&options.Delay, "delay", -1, "duration between each http request (eg: 200ms, 1s)"),
543-
// 10MB max response size matches common/httpx default
544-
flagSet.IntVarP(&options.MaxResponseBodySizeToSave, "response-size-to-save", "rsts", (10*1024*1024), "max response size to save in bytes"),
545-
flagSet.IntVarP(&options.MaxResponseBodySizeToRead, "response-size-to-read", "rstr", (10*1024*1024), "max response size to read in bytes"),
543+
)
544+
545+
flagSet.CreateGroup("response", "Response",
546+
flagSet.IntVarP(&options.MaxResponseBodySizeToSave, "response-size-to-save", "rsts", int(httpxcommon.DefaultMaxResponseBodySize), "max response size to save in bytes"),
547+
flagSet.IntVarP(&options.MaxResponseBodySizeToRead, "response-size-to-read", "rstr", int(httpxcommon.DefaultMaxResponseBodySize), "max response size to read in bytes"),
546548
)
547549

548550
flagSet.CreateGroup("cloud", "Cloud",
@@ -772,7 +774,7 @@ func (options *Options) ValidateOptions() error {
772774
options.OutputCDN = "true"
773775
}
774776

775-
if !stringsutil.EqualFoldAny(options.Protocol, string(httpx.UNKNOWN), string(httpx.HTTP11)) {
777+
if !stringsutil.EqualFoldAny(options.Protocol, string(httpxcommon.UNKNOWN), string(httpxcommon.HTTP11)) {
776778
return fmt.Errorf("invalid protocol: %s", options.Protocol)
777779
}
778780

0 commit comments

Comments
 (0)