Skip to content

Commit

Permalink
Merge pull request #3 from dr4g0n369/main
Browse files Browse the repository at this point in the history
Added HTTP/2 Support
  • Loading branch information
dmdhrumilmistry authored Oct 17, 2024
2 parents 5357292 + d5580ce commit 3150c4f
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 9 deletions.
2 changes: 2 additions & 0 deletions cmd/offat/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type FlagConfig struct {
// Report
AvoidImmuneFilter *bool
OutputFilePath *string

HTTP2 *bool
}

// Custom type for headers
Expand Down
17 changes: 13 additions & 4 deletions cmd/offat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"time"

c "github.com/dmdhrumilmistry/fasthttpclient/client"
"github.com/owasp-offat/offat/pkg/http"
_ "github.com/owasp-offat/offat/pkg/logging"
"github.com/owasp-offat/offat/pkg/parser"
Expand Down Expand Up @@ -70,6 +71,8 @@ func main() {
config.OutputFilePath = flag.String("o", "output.json", "JSON report output file path. default: output.json")
config.AvoidImmuneFilter = flag.Bool("ai", true, "does not filter immune endpoint from results if used. usage: -ai=true/false")

config.HTTP2 = flag.Bool("http2", false, "enable HTTP/2 support")

flag.Parse()

// Start Timer
Expand Down Expand Up @@ -112,12 +115,18 @@ func main() {
parser.FuzzDocHttpParams()

// Create http client and config
httpCfg := http.NewConfig(config.RequestsPerSecond, config.SkipTlsVerification, config.Proxy)
hc := http.NewHttp(httpCfg)
var hc c.ClientInterface // changed hc to interface
if config.HTTP2 != nil && *config.HTTP2 {
hc = http.NewConfigHttp2(config.RequestsPerSecond, config.SkipTlsVerification, config.Proxy)
} else {
httpCfg := http.NewConfig(config.RequestsPerSecond, config.SkipTlsVerification, config.Proxy)
hc = http.NewHttp(httpCfg).Client.FHClient
}

// Test server connectivity
url := *parser.Doc.GetBaseUrl()
resp, err := hc.Client.FHClient.Do(url, fasthttp.MethodGet, nil, nil, nil)

resp, err := hc.Do(url, fasthttp.MethodGet, nil, nil, nil)
if err != nil {
log.Error().Stack().Err(err).Msg("cannot connect to server")
os.Exit(1)
Expand Down Expand Up @@ -176,7 +185,7 @@ func main() {
}

// run api tests
trunner.RunApiTests(&apiTestHandler, hc, hc.Client.FHClient, apiTests)
trunner.RunApiTests(&apiTestHandler, hc, apiTests)
log.Info().Msgf("Total Requests: %d", len(apiTests))

// **## Run Post Tests processors ##**
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/li-jin-gou/http2curl v0.1.2 // indirect
github.com/li-jin-gou/http2curl v0.1.2
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand All @@ -36,8 +36,8 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/net v0.29.0
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/time v0.6.0
)
154 changes: 154 additions & 0 deletions pkg/http/http2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package http

import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/http"
"net/url"
"time"

"github.com/dmdhrumilmistry/fasthttpclient/client"
"github.com/li-jin-gou/http2curl"
"github.com/rs/zerolog/log"
"golang.org/x/net/http2"
"golang.org/x/time/rate"
)

type ThrottledTransport struct {
roundTripperWrap http.RoundTripper
ratelimiter *rate.Limiter
}

type CustomClient struct {
Requests []*client.Request
Responses []*client.ConcurrentResponse
Client *http.Client
}

func GetResponseHeaders(resp *http.Response) map[string]string {
headers := make(map[string]string)
for header, value := range resp.Header {
headers[header] = value[0]
}
return headers
}

func SetRequestBody(body interface{}, req *http.Request) error {
if body == nil {
return nil
}

bodyBytes, ok := body.([]byte)
if !ok {
return fmt.Errorf("body only supports []byte type")
} else {
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
}
return nil
}

func (c *CustomClient) Do(uri string, method string, queryParams any, headers any, reqBody any) (*client.Response, error) {
req, err := http.NewRequest(method, uri, nil)
if err != nil {
return nil, err
}

queryParamsMap, ok := queryParams.(map[string]string)
if !ok && queryParams != nil {
return nil, fmt.Errorf("queryParams must be a map[string]string")
} else {
for key, value := range queryParamsMap {
req.Header.Add(key, value)
}
}

err = SetRequestBody(reqBody, req)
if err != nil {
return nil, err
}

curlCmd := "error generating curl command"
curlCmdObj, err := http2curl.GetCurlCommand(req)
if err == nil {
curlCmd = curlCmdObj.String()
}

now := time.Now()
resp, err := c.Client.Do(req)
if err != nil {
return nil, err
}
elapsed := time.Since(now)

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
respHeaders := GetResponseHeaders(resp)
statusCode := resp.StatusCode

return &client.Response{StatusCode: statusCode, Body: body, Headers: respHeaders, CurlCommand: curlCmd, TimeElapsed: elapsed}, nil
}

// RoundTrip method implements the rate limiting logic for HTTP requests
func (c *ThrottledTransport) RoundTrip(r *http.Request) (*http.Response, error) {
err := c.ratelimiter.Wait(r.Context()) // This is a blocking call. Honors the rate limit
if err != nil {
return nil, err
}
return c.roundTripperWrap.RoundTrip(r)
}

// NewThrottledTransport wraps the provided transport with a rate limiter
func NewThrottledTransport(limitPeriod time.Duration, requestCount int, transportWrap http.RoundTripper) http.RoundTripper {
return &ThrottledTransport{
roundTripperWrap: transportWrap,
ratelimiter: rate.NewLimiter(rate.Every(limitPeriod), requestCount),
}
}

func NewConfigHttp2(requestsPerSecond *int, skipTlsVerification *bool, proxy *string) *CustomClient {
tlsConfig := &tls.Config{
InsecureSkipVerify: *skipTlsVerification,
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_AES_128_GCM_SHA256, // TLS 1.3
tls.TLS_AES_256_GCM_SHA384, // TLS 1.3
tls.TLS_CHACHA20_POLY1305_SHA256, // TLS 1.3
},
PreferServerCipherSuites: true,
}

var transport *http.Transport
if *proxy != "" {
proxy_url, _ := url.Parse(*proxy)
transport = &http.Transport{
TLSClientConfig: tlsConfig,
Proxy: http.ProxyURL(proxy_url),
}
} else {
transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}

err := http2.ConfigureTransport(transport)
if err != nil {
log.Error().Msgf("failed to configure transport: %v", err)
}

rateLimitedTransport := NewThrottledTransport(1*time.Second, *requestsPerSecond, transport)

client := http.Client{
Transport: rateLimitedTransport,
}

return &CustomClient{
Client: &client,
}
}
33 changes: 33 additions & 0 deletions pkg/http/http2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package http_test

import (
"testing"

"github.com/owasp-offat/offat/pkg/http"

c "github.com/dmdhrumilmistry/fasthttpclient/client"
"github.com/valyala/fasthttp"
)

func TestHttp2Client(t *testing.T) {
// http2 client
requestsPerSecond := 10
skipTlsVerification := false
proxy := ""
hc := http.NewConfigHttp2(&requestsPerSecond, &skipTlsVerification, &proxy)

url := "https://example.com"
hc.Requests = append(hc.Requests, c.NewRequest(url, fasthttp.MethodGet, nil, nil, nil))
hc.Requests = append(hc.Requests, c.NewRequest(url, fasthttp.MethodGet, nil, nil, nil))

t.Run("Concurrent Requests Test", func(t *testing.T) {
hc.Responses = c.MakeConcurrentRequests(hc.Requests, hc)

for _, connResp := range hc.Responses {
if connResp.Error != nil {
t.Fatalf("failed to make concurrent request: %v\n", connResp.Error)
}
}
})

}
3 changes: 1 addition & 2 deletions pkg/trunner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import (

c "github.com/dmdhrumilmistry/fasthttpclient/client"
"github.com/k0kubun/go-ansi"
"github.com/owasp-offat/offat/pkg/http"
"github.com/owasp-offat/offat/pkg/tgen"
"github.com/schollz/progressbar/v3"
"golang.org/x/term"
)

// Runs API Tests
func RunApiTests(t *tgen.TGenHandler, hc *http.Http, client c.ClientInterface, apiTests []*tgen.ApiTest) {
func RunApiTests(t *tgen.TGenHandler, client c.ClientInterface, apiTests []*tgen.ApiTest) {
var wg sync.WaitGroup

// Get the terminal size
Expand Down

0 comments on commit 3150c4f

Please sign in to comment.