Write configuration to output JSON (#135)
* Config to json output, filters and matchers * optRange marshaling * Add CHANGELOG entry
This commit is contained in:
parent
1b45085191
commit
ac2b447dfd
@ -10,7 +10,7 @@
|
|||||||
- Regexp matching and filtering (-mr/-fr) allow using keywords in patterns
|
- Regexp matching and filtering (-mr/-fr) allow using keywords in patterns
|
||||||
- Take 429 responses into account when -sa (stop on all error cases) is used
|
- Take 429 responses into account when -sa (stop on all error cases) is used
|
||||||
- Remove -k flag support, convert to dummy flag #134
|
- Remove -k flag support, convert to dummy flag #134
|
||||||
|
- Write configuration to output JSON
|
||||||
|
|
||||||
- v0.12
|
- v0.12
|
||||||
- New
|
- New
|
||||||
|
|||||||
7
main.go
7
main.go
@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -326,11 +325,11 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||||||
|
|
||||||
// Verify proxy url format
|
// Verify proxy url format
|
||||||
if len(parseOpts.proxyURL) > 0 {
|
if len(parseOpts.proxyURL) > 0 {
|
||||||
pu, err := url.Parse(parseOpts.proxyURL)
|
_, err := url.Parse(parseOpts.proxyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs.Add(fmt.Errorf("Bad proxy url (-x) format: %s", err))
|
errs.Add(fmt.Errorf("Bad proxy url (-x) format: %s", err))
|
||||||
} else {
|
} else {
|
||||||
conf.ProxyURL = http.ProxyURL(pu)
|
conf.ProxyURL = parseOpts.proxyURL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,7 +350,9 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto-calibration strings
|
// Auto-calibration strings
|
||||||
|
if len(parseOpts.AutoCalibrationStrings) > 0 {
|
||||||
conf.AutoCalibrationStrings = parseOpts.AutoCalibrationStrings
|
conf.AutoCalibrationStrings = parseOpts.AutoCalibrationStrings
|
||||||
|
}
|
||||||
// Using -acc implies -ac
|
// Using -acc implies -ac
|
||||||
if len(conf.AutoCalibrationStrings) > 0 {
|
if len(conf.AutoCalibrationStrings) > 0 {
|
||||||
conf.AutoCalibration = true
|
conf.AutoCalibration = true
|
||||||
|
|||||||
@ -2,60 +2,49 @@ package ffuf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//optRange stores either a single float, in which case the value is stored in min and IsRange is false,
|
|
||||||
//or a range of floats, in which case IsRange is true
|
|
||||||
type optRange struct {
|
|
||||||
Min float64
|
|
||||||
Max float64
|
|
||||||
IsRange bool
|
|
||||||
HasDelay bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Headers map[string]string
|
Headers map[string]string `json:"headers"`
|
||||||
Extensions []string
|
Extensions []string `json:"extensions"`
|
||||||
DirSearchCompat bool
|
DirSearchCompat bool `json:"dirsearch_compatibility"`
|
||||||
Method string
|
Method string `json:"method"`
|
||||||
Url string
|
Url string `json:"url"`
|
||||||
Data string
|
Data string `json:"postdata"`
|
||||||
Quiet bool
|
Quiet bool `json:"quiet"`
|
||||||
Colors bool
|
Colors bool `json:"colors"`
|
||||||
InputProviders []InputProviderConfig
|
InputProviders []InputProviderConfig `json:"inputproviders"`
|
||||||
CommandKeywords []string
|
CommandKeywords []string `json:"-"`
|
||||||
InputNum int
|
InputNum int `json:"cmd_inputnum"`
|
||||||
InputMode string
|
InputMode string `json:"inputmode"`
|
||||||
OutputDirectory string
|
OutputDirectory string `json:"outputdirectory"`
|
||||||
OutputFile string
|
OutputFile string `json:"outputfile"`
|
||||||
OutputFormat string
|
OutputFormat string `json:"outputformat"`
|
||||||
StopOn403 bool
|
StopOn403 bool `json:"stop_403"`
|
||||||
StopOnErrors bool
|
StopOnErrors bool `json:"stop_errors"`
|
||||||
StopOnAll bool
|
StopOnAll bool `json:"stop_all"`
|
||||||
FollowRedirects bool
|
FollowRedirects bool `json:"follow_redirects"`
|
||||||
AutoCalibration bool
|
AutoCalibration bool `json:"autocalibration"`
|
||||||
AutoCalibrationStrings []string
|
AutoCalibrationStrings []string `json:"autocalibration_strings"`
|
||||||
Timeout int
|
Timeout int `json:"timeout"`
|
||||||
ProgressFrequency int
|
ProgressFrequency int `json:"-"`
|
||||||
Delay optRange
|
Delay optRange `json:"delay"`
|
||||||
Filters []FilterProvider
|
Filters map[string]FilterProvider `json:"filters"`
|
||||||
Matchers []FilterProvider
|
Matchers map[string]FilterProvider `json:"matchers"`
|
||||||
Threads int
|
Threads int `json:"threads"`
|
||||||
Context context.Context
|
Context context.Context `json:"-"`
|
||||||
ProxyURL func(*http.Request) (*url.URL, error)
|
ProxyURL string `json:"proxyurl"`
|
||||||
CommandLine string
|
CommandLine string `json:"cmdline"`
|
||||||
Verbose bool
|
Verbose bool `json:"verbose"`
|
||||||
MaxTime int
|
MaxTime int `json:"maxtime"`
|
||||||
Recursion bool
|
Recursion bool `json:"recursion"`
|
||||||
RecursionDepth int
|
RecursionDepth int `json:"recursion_depth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InputProviderConfig struct {
|
type InputProviderConfig struct {
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
Keyword string
|
Keyword string `json:"keyword"`
|
||||||
Value string
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig(ctx context.Context) Config {
|
func NewConfig(ctx context.Context) Config {
|
||||||
@ -72,10 +61,12 @@ func NewConfig(ctx context.Context) Config {
|
|||||||
conf.FollowRedirects = false
|
conf.FollowRedirects = false
|
||||||
conf.InputProviders = make([]InputProviderConfig, 0)
|
conf.InputProviders = make([]InputProviderConfig, 0)
|
||||||
conf.CommandKeywords = make([]string, 0)
|
conf.CommandKeywords = make([]string, 0)
|
||||||
|
conf.AutoCalibrationStrings = make([]string, 0)
|
||||||
conf.InputNum = 0
|
conf.InputNum = 0
|
||||||
conf.InputMode = "clusterbomb"
|
conf.InputMode = "clusterbomb"
|
||||||
conf.ProxyURL = http.ProxyFromEnvironment
|
conf.ProxyURL = ""
|
||||||
conf.Filters = make([]FilterProvider, 0)
|
conf.Filters = make(map[string]FilterProvider)
|
||||||
|
conf.Matchers = make(map[string]FilterProvider)
|
||||||
conf.Delay = optRange{0, 0, false, false}
|
conf.Delay = optRange{0, 0, false, false}
|
||||||
conf.Extensions = make([]string, 0)
|
conf.Extensions = make([]string, 0)
|
||||||
conf.Timeout = 10
|
conf.Timeout = 10
|
||||||
|
|||||||
67
pkg/ffuf/optrange.go
Normal file
67
pkg/ffuf/optrange.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package ffuf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//optRange stores either a single float, in which case the value is stored in min and IsRange is false,
|
||||||
|
//or a range of floats, in which case IsRange is true
|
||||||
|
type optRange struct {
|
||||||
|
Min float64
|
||||||
|
Max float64
|
||||||
|
IsRange bool
|
||||||
|
HasDelay bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type optRangeJSON struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *optRange) MarshalJSON() ([]byte, error) {
|
||||||
|
value := ""
|
||||||
|
if o.Min == o.Max {
|
||||||
|
value = fmt.Sprintf("%.2f", o.Min)
|
||||||
|
} else {
|
||||||
|
value = fmt.Sprintf("%.2f-%.2f", o.Min, o.Max)
|
||||||
|
}
|
||||||
|
return json.Marshal(&optRangeJSON{
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *optRange) UnmarshalJSON(b []byte) error {
|
||||||
|
var inc optRangeJSON
|
||||||
|
err := json.Unmarshal(b, &inc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return o.Initialize(inc.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Initialize sets up the optRange from string value
|
||||||
|
func (o *optRange) Initialize(value string) error {
|
||||||
|
var err, err2 error
|
||||||
|
d := strings.Split(value, "-")
|
||||||
|
if len(d) > 2 {
|
||||||
|
return fmt.Errorf("Delay needs to be either a single float: \"0.1\" or a range of floats, delimited by dash: \"0.1-0.8\"")
|
||||||
|
} else if len(d) == 2 {
|
||||||
|
o.IsRange = true
|
||||||
|
o.HasDelay = true
|
||||||
|
o.Min, err = strconv.ParseFloat(d[0], 64)
|
||||||
|
o.Max, err2 = strconv.ParseFloat(d[1], 64)
|
||||||
|
if err != nil || err2 != nil {
|
||||||
|
return fmt.Errorf("Delay range min and max values need to be valid floats. For example: 0.1-0.5")
|
||||||
|
}
|
||||||
|
} else if len(value) > 0 {
|
||||||
|
o.IsRange = false
|
||||||
|
o.HasDelay = true
|
||||||
|
o.Min, err = strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Delay needs to be either a single float: \"0.1\" or a range of floats, delimited by dash: \"0.1-0.8\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ func NewFilterByName(name string, value string) (ffuf.FilterProvider, error) {
|
|||||||
func AddFilter(conf *ffuf.Config, name string, option string) error {
|
func AddFilter(conf *ffuf.Config, name string, option string) error {
|
||||||
newf, err := NewFilterByName(name, option)
|
newf, err := NewFilterByName(name, option)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
conf.Filters = append(conf.Filters, newf)
|
conf.Filters[name] = newf
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ func AddFilter(conf *ffuf.Config, name string, option string) error {
|
|||||||
func AddMatcher(conf *ffuf.Config, name string, option string) error {
|
func AddMatcher(conf *ffuf.Config, name string, option string) error {
|
||||||
newf, err := NewFilterByName(name, option)
|
newf, err := NewFilterByName(name, option)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
conf.Matchers = append(conf.Matchers, newf)
|
conf.Matchers[name] = newf
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -24,6 +25,22 @@ func NewLineFilter(value string) (ffuf.FilterProvider, error) {
|
|||||||
return &LineFilter{Value: intranges}, nil
|
return &LineFilter{Value: intranges}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *LineFilter) MarshalJSON() ([]byte, error) {
|
||||||
|
value := make([]string, 0)
|
||||||
|
for _, v := range f.Value {
|
||||||
|
if v.Min == v.Max {
|
||||||
|
value = append(value, strconv.FormatInt(v.Min, 10))
|
||||||
|
} else {
|
||||||
|
value = append(value, fmt.Sprintf("%d-%d", v.Min, v.Max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}{
|
||||||
|
Value: strings.Join(value, ","),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *LineFilter) Filter(response *ffuf.Response) (bool, error) {
|
func (f *LineFilter) Filter(response *ffuf.Response) (bool, error) {
|
||||||
linesSize := len(strings.Split(string(response.Data), "\n"))
|
linesSize := len(strings.Split(string(response.Data), "\n"))
|
||||||
for _, iv := range f.Value {
|
for _, iv := range f.Value {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -21,6 +22,14 @@ func NewRegexpFilter(value string) (ffuf.FilterProvider, error) {
|
|||||||
return &RegexpFilter{Value: re, valueRaw: value}, nil
|
return &RegexpFilter{Value: re, valueRaw: value}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *RegexpFilter) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}{
|
||||||
|
Value: f.valueRaw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *RegexpFilter) Filter(response *ffuf.Response) (bool, error) {
|
func (f *RegexpFilter) Filter(response *ffuf.Response) (bool, error) {
|
||||||
matchheaders := ""
|
matchheaders := ""
|
||||||
for k, v := range response.Headers {
|
for k, v := range response.Headers {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -25,6 +26,22 @@ func NewSizeFilter(value string) (ffuf.FilterProvider, error) {
|
|||||||
return &SizeFilter{Value: intranges}, nil
|
return &SizeFilter{Value: intranges}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *SizeFilter) MarshalJSON() ([]byte, error) {
|
||||||
|
value := make([]string, 0)
|
||||||
|
for _, v := range f.Value {
|
||||||
|
if v.Min == v.Max {
|
||||||
|
value = append(value, strconv.FormatInt(v.Min, 10))
|
||||||
|
} else {
|
||||||
|
value = append(value, fmt.Sprintf("%d-%d", v.Min, v.Max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}{
|
||||||
|
Value: strings.Join(value, ","),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *SizeFilter) Filter(response *ffuf.Response) (bool, error) {
|
func (f *SizeFilter) Filter(response *ffuf.Response) (bool, error) {
|
||||||
for _, iv := range f.Value {
|
for _, iv := range f.Value {
|
||||||
if iv.Min <= response.ContentLength && response.ContentLength <= iv.Max {
|
if iv.Min <= response.ContentLength && response.ContentLength <= iv.Max {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -30,6 +31,26 @@ func NewStatusFilter(value string) (ffuf.FilterProvider, error) {
|
|||||||
return &StatusFilter{Value: intranges}, nil
|
return &StatusFilter{Value: intranges}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *StatusFilter) MarshalJSON() ([]byte, error) {
|
||||||
|
value := make([]string, 0)
|
||||||
|
for _, v := range f.Value {
|
||||||
|
if v.Min == 0 && v.Max == 0 {
|
||||||
|
value = append(value, "all")
|
||||||
|
} else {
|
||||||
|
if v.Min == v.Max {
|
||||||
|
value = append(value, strconv.FormatInt(v.Min, 10))
|
||||||
|
} else {
|
||||||
|
value = append(value, fmt.Sprintf("%d-%d", v.Min, v.Max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}{
|
||||||
|
Value: strings.Join(value, ","),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *StatusFilter) Filter(response *ffuf.Response) (bool, error) {
|
func (f *StatusFilter) Filter(response *ffuf.Response) (bool, error) {
|
||||||
for _, iv := range f.Value {
|
for _, iv := range f.Value {
|
||||||
if iv.Min == AllStatuses && iv.Max == AllStatuses {
|
if iv.Min == AllStatuses && iv.Max == AllStatuses {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -24,6 +25,22 @@ func NewWordFilter(value string) (ffuf.FilterProvider, error) {
|
|||||||
return &WordFilter{Value: intranges}, nil
|
return &WordFilter{Value: intranges}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *WordFilter) MarshalJSON() ([]byte, error) {
|
||||||
|
value := make([]string, 0)
|
||||||
|
for _, v := range f.Value {
|
||||||
|
if v.Min == v.Max {
|
||||||
|
value = append(value, strconv.FormatInt(v.Min, 10))
|
||||||
|
} else {
|
||||||
|
value = append(value, fmt.Sprintf("%d-%d", v.Min, v.Max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}{
|
||||||
|
Value: strings.Join(value, ","),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *WordFilter) Filter(response *ffuf.Response) (bool, error) {
|
func (f *WordFilter) Filter(response *ffuf.Response) (bool, error) {
|
||||||
wordsSize := len(strings.Split(string(response.Data), " "))
|
wordsSize := len(strings.Split(string(response.Data), " "))
|
||||||
for _, iv := range f.Value {
|
for _, iv := range f.Value {
|
||||||
|
|||||||
@ -30,6 +30,7 @@ type jsonFileOutput struct {
|
|||||||
CommandLine string `json:"commandline"`
|
CommandLine string `json:"commandline"`
|
||||||
Time string `json:"time"`
|
Time string `json:"time"`
|
||||||
Results []JsonResult `json:"results"`
|
Results []JsonResult `json:"results"`
|
||||||
|
Config *ffuf.Config `json:"config"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeEJSON(config *ffuf.Config, res []Result) error {
|
func writeEJSON(config *ffuf.Config, res []Result) error {
|
||||||
@ -75,6 +76,7 @@ func writeJSON(config *ffuf.Config, res []Result) error {
|
|||||||
CommandLine: config.CommandLine,
|
CommandLine: config.CommandLine,
|
||||||
Time: t.Format(time.RFC3339),
|
Time: t.Format(time.RFC3339),
|
||||||
Results: jsonRes,
|
Results: jsonRes,
|
||||||
|
Config: config,
|
||||||
}
|
}
|
||||||
outBytes, err := json.Marshal(outJSON)
|
outBytes, err := json.Marshal(outJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -24,12 +25,21 @@ type SimpleRunner struct {
|
|||||||
|
|
||||||
func NewSimpleRunner(conf *ffuf.Config) ffuf.RunnerProvider {
|
func NewSimpleRunner(conf *ffuf.Config) ffuf.RunnerProvider {
|
||||||
var simplerunner SimpleRunner
|
var simplerunner SimpleRunner
|
||||||
|
proxyURL := http.ProxyFromEnvironment
|
||||||
|
|
||||||
|
if len(conf.ProxyURL) > 0 {
|
||||||
|
pu, err := url.Parse(conf.ProxyURL)
|
||||||
|
if err == nil {
|
||||||
|
proxyURL = http.ProxyURL(pu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
simplerunner.config = conf
|
simplerunner.config = conf
|
||||||
simplerunner.client = &http.Client{
|
simplerunner.client = &http.Client{
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
|
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
|
||||||
Timeout: time.Duration(time.Duration(conf.Timeout) * time.Second),
|
Timeout: time.Duration(time.Duration(conf.Timeout) * time.Second),
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Proxy: conf.ProxyURL,
|
Proxy: proxyURL,
|
||||||
MaxIdleConns: 1000,
|
MaxIdleConns: 1000,
|
||||||
MaxIdleConnsPerHost: 500,
|
MaxIdleConnsPerHost: 500,
|
||||||
MaxConnsPerHost: 500,
|
MaxConnsPerHost: 500,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user