aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-02-16 00:24:47 +0700
committerShulhan <ms@kilabit.info>2024-02-16 01:38:51 +0700
commit2b6dc0730aa3c30787e440c1090b8041ff6b4ca8 (patch)
tree588439eafdb7cdae7d5d469e7f688e6fd206c00a
parent372d10a3e1ea01f8d03e44e6ab8be673d05c0773 (diff)
downloadgorankusu-2b6dc0730aa3c30787e440c1090b8041ff6b4ca8.tar.xz
all: set default HTTPTarget Attack if its not set
Previously, the function for Attack need to be coded manually. This changes introduce new function DefaultHTTPAttack that generate HTTPAttackHandler based on the HTTPTarget method, request type, and Params; if AllowAttack is true and Attack is nil. Implements: https://todo.sr.ht/~shulhan/gorankusu/4
-rw-r--r--errors.go8
-rw-r--r--example.go89
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--gorankusu.go3
-rw-r--r--http_attack_handler.go82
-rw-r--r--http_target.go15
7 files changed, 99 insertions, 104 deletions
diff --git a/errors.go b/errors.go
index 42781ea..b2ff902 100644
--- a/errors.go
+++ b/errors.go
@@ -35,14 +35,6 @@ func errAttackNotAllowed() error {
return res
}
-var errAttackHandlerNotSet = libhttp.EndpointResponse{
- E: liberrors.E{
- Code: http.StatusNotAcceptable,
- Message: `the Attack handler is not set`,
- Name: `ERR_ATTACK_HANDLER_NOT_SET`,
- },
-}
-
func errInternal(err error) error {
res := &libhttp.EndpointResponse{
E: liberrors.E{
diff --git a/example.go b/example.go
index 16fa47b..cf53ae9 100644
--- a/example.go
+++ b/example.go
@@ -17,7 +17,6 @@ import (
libhttp "github.com/shuLhan/share/lib/http"
"github.com/shuLhan/share/lib/mlog"
"github.com/shuLhan/share/lib/websocket"
- vegeta "github.com/tsenart/vegeta/v12/lib"
)
const (
@@ -52,10 +51,6 @@ type requestResponse struct {
type Example struct {
*Gorankusu
wsServer *websocket.Server
-
- targetExampleErrorGet vegeta.Target
- targetExampleGet vegeta.Target
- targetExamplePostForm vegeta.Target
}
// NewExample create, initialize, and setup an example of Gorankusu.
@@ -239,8 +234,6 @@ func (ex *Example) registerTargetHTTP() (err error) {
},
Run: ex.runExampleGet,
AllowAttack: true,
- Attack: ex.attackExampleGet,
- PreAttack: ex.preattackExampleGet,
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
}, {
@@ -268,8 +261,6 @@ func (ex *Example) registerTargetHTTP() (err error) {
},
Run: ex.runExampleGet,
AllowAttack: true,
- Attack: ex.attackExampleErrorGet,
- PreAttack: ex.preattackExampleErrorGet,
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
}, {
@@ -303,13 +294,11 @@ func (ex *Example) registerTargetHTTP() (err error) {
},
Run: ex.runExamplePostForm,
AllowAttack: true,
- PreAttack: ex.preattackExamplePostForm,
- Attack: ex.attackExamplePostForm,
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
}, {
ID: `http_free_form`,
- Name: `HTTP free form`,
+ Name: `HTTP Free Form`,
Hint: fmt.Sprintf(`Test endpoint %q using custom HTTP method and/or content type.`, pathExample),
Method: libhttp.RequestMethodGet,
Path: pathExample,
@@ -333,6 +322,7 @@ func (ex *Example) registerTargetHTTP() (err error) {
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
IsCustomizable: true,
+ AllowAttack: true,
}, {
ID: `http_post_path_binding`,
Name: `HTTP Post path binding`,
@@ -354,6 +344,7 @@ func (ex *Example) registerTargetHTTP() (err error) {
},
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
+ AllowAttack: true,
}, {
ID: `http_rawbody_json`,
Name: `HTTP raw body - JSON`,
@@ -364,6 +355,7 @@ func (ex *Example) registerTargetHTTP() (err error) {
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
WithRawBody: true,
+ AllowAttack: true,
}, {
ID: `http_upload`,
Name: `HTTP upload`,
@@ -391,6 +383,7 @@ func (ex *Example) registerTargetHTTP() (err error) {
},
RequestDumper: requestDumperWithoutDate,
ResponseDumper: responseDumperWithoutDate,
+ AllowAttack: true,
}},
}
@@ -586,54 +579,6 @@ func (ex *Example) runExampleGet(req *RunRequest) (res *RunResponse, err error)
return res, nil
}
-func (ex *Example) preattackExampleErrorGet(rr *RunRequest) {
- ex.targetExampleErrorGet = vegeta.Target{
- Method: rr.HTTPTarget.Method.String(),
- URL: fmt.Sprintf(`%s%s`, rr.Target.BaseURL, rr.HTTPTarget.Path),
- Header: rr.HTTPTarget.Headers.ToHTTPHeader(),
- }
-
- var q = rr.HTTPTarget.Params.ToURLValues().Encode()
- if len(q) > 0 {
- ex.targetExampleErrorGet.URL += `?` + q
- }
-
- fmt.Printf("preattackExampleErrorGet: %+v\n", ex.targetExampleErrorGet)
-}
-
-func (ex *Example) preattackExampleGet(rr *RunRequest) {
- ex.targetExampleGet = vegeta.Target{
- Method: rr.HTTPTarget.Method.String(),
- URL: fmt.Sprintf(`%s%s`, rr.Target.BaseURL, rr.HTTPTarget.Path),
- Header: rr.HTTPTarget.Headers.ToHTTPHeader(),
- }
-
- var q = rr.HTTPTarget.Params.ToURLValues().Encode()
- if len(q) > 0 {
- ex.targetExampleGet.URL += `?` + q
- }
-
- fmt.Printf("preattackExampleGet: %+v\n", ex.targetExampleGet)
-}
-
-func (ex *Example) attackExampleErrorGet(rr *RunRequest) vegeta.Targeter {
- return func(tgt *vegeta.Target) error {
- rr.HTTPTarget.Lock()
- *tgt = ex.targetExampleErrorGet
- rr.HTTPTarget.Unlock()
- return nil
- }
-}
-
-func (ex *Example) attackExampleGet(rr *RunRequest) vegeta.Targeter {
- return func(tgt *vegeta.Target) error {
- rr.HTTPTarget.Lock()
- *tgt = ex.targetExampleGet
- rr.HTTPTarget.Unlock()
- return nil
- }
-}
-
func (ex *Example) runExamplePostForm(req *RunRequest) (res *RunResponse, err error) {
if req.Target.HTTPClient == nil {
var httpcOpts = &libhttp.ClientOptions{
@@ -683,30 +628,6 @@ func (ex *Example) runExamplePostForm(req *RunRequest) (res *RunResponse, err er
return res, nil
}
-func (ex *Example) preattackExamplePostForm(rr *RunRequest) {
- ex.targetExamplePostForm = vegeta.Target{
- Method: rr.HTTPTarget.Method.String(),
- URL: fmt.Sprintf(`%s%s`, rr.Target.BaseURL, rr.HTTPTarget.Path),
- Header: rr.HTTPTarget.Headers.ToHTTPHeader(),
- }
-
- var q = rr.HTTPTarget.Params.ToURLValues().Encode()
- if len(q) > 0 {
- ex.targetExamplePostForm.Body = []byte(q)
- }
-
- fmt.Printf("preattackExamplePostForm: %+v\n", ex.targetExamplePostForm)
-}
-
-func (ex *Example) attackExamplePostForm(rr *RunRequest) vegeta.Targeter {
- return func(tgt *vegeta.Target) error {
- rr.HTTPTarget.Lock()
- *tgt = ex.targetExamplePostForm
- rr.HTTPTarget.Unlock()
- return nil
- }
-}
-
func (ex *Example) handleWSExampleGet(_ context.Context, req *websocket.Request) (res websocket.Response) {
res.ID = req.ID
res.Code = http.StatusOK
diff --git a/go.mod b/go.mod
index 46042f0..7d18ecd 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ go 1.20
require (
git.sr.ht/~shulhan/ciigo v0.11.0
- github.com/shuLhan/share v0.53.0
+ github.com/shuLhan/share v0.53.1-0.20240215151058-c14c0aada881
github.com/tsenart/vegeta/v12 v12.11.1
)
diff --git a/go.sum b/go.sum
index b708445..36e21d7 100644
--- a/go.sum
+++ b/go.sum
@@ -26,8 +26,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
-github.com/shuLhan/share v0.53.0 h1:hxQQbUWKav0pGaVarakEBX7hqJsyfd5jGDG3l6mcAsU=
-github.com/shuLhan/share v0.53.0/go.mod h1:97/BcWdLau8i+xeFvPHdyqph1HgxVBSVhQEUIyCmgRc=
+github.com/shuLhan/share v0.53.1-0.20240215151058-c14c0aada881 h1:SGgdg7934FRJZZ1G+ZZNEdwSL7UgUCFRohtJnwSNrDI=
+github.com/shuLhan/share v0.53.1-0.20240215151058-c14c0aada881/go.mod h1:97/BcWdLau8i+xeFvPHdyqph1HgxVBSVhQEUIyCmgRc=
github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d h1:X4+kt6zM/OVO6gbJdAfJR60MGPsqCzbtXNnjoGqdfAs=
github.com/tsenart/vegeta/v12 v12.11.1 h1:Rbwe7Zxr7sJ+BDTReemeQalYPvKiSV+O7nwmUs20B3E=
github.com/tsenart/vegeta/v12 v12.11.1/go.mod h1:swiFmrgpqj2llHURgHYFRFN0tfrIrlnspg01HjwOnSQ=
diff --git a/gorankusu.go b/gorankusu.go
index f3d120e..c807336 100644
--- a/gorankusu.go
+++ b/gorankusu.go
@@ -103,9 +103,6 @@ func (gorankusu *Gorankusu) AttackHTTP(req *RunRequest) (err error) {
if !origHTTPTarget.AllowAttack {
return errAttackNotAllowed()
}
- if origHTTPTarget.Attack == nil {
- return fmt.Errorf(`%s: %w`, logp, &errAttackHandlerNotSet)
- }
req = generateRunRequest(gorankusu.Env, req, origTarget, origHTTPTarget)
diff --git a/http_attack_handler.go b/http_attack_handler.go
new file mode 100644
index 0000000..8a70416
--- /dev/null
+++ b/http_attack_handler.go
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package gorankusu
+
+import (
+ "encoding/json"
+ "fmt"
+
+ libhttp "github.com/shuLhan/share/lib/http"
+ vegeta "github.com/tsenart/vegeta/v12/lib"
+)
+
+// HTTPAttackHandler define the function type that will be called when client
+// send request to attack HTTP target.
+type HTTPAttackHandler func(rr *RunRequest) vegeta.Targeter
+
+// DefaultHTTPAttack define the default value for [HTTPTarget.Attack] handler
+// that return [vegeta.Targeter].
+func DefaultHTTPAttack() HTTPAttackHandler {
+ return func(rr *RunRequest) vegeta.Targeter {
+ var err error
+
+ rr.HTTPTarget.Lock()
+ defer rr.HTTPTarget.Unlock()
+
+ rr.HTTPTarget.paramsToPath()
+
+ var vegetaTarget = vegeta.Target{
+ Method: rr.HTTPTarget.Method.String(),
+ URL: fmt.Sprintf(`%s%s`, rr.Target.BaseURL, rr.HTTPTarget.Path),
+ Header: rr.HTTPTarget.Headers.ToHTTPHeader(),
+ }
+
+ var contentType = rr.HTTPTarget.RequestType.String()
+ if len(contentType) != 0 {
+ vegetaTarget.Header.Set(libhttp.HeaderContentType, contentType)
+ }
+
+ if rr.HTTPTarget.WithRawBody {
+ vegetaTarget.Body = rr.HTTPTarget.RawBody
+ } else {
+ switch rr.HTTPTarget.RequestType {
+ case libhttp.RequestTypeQuery:
+ var q = rr.HTTPTarget.Params.ToURLValues().Encode()
+ if len(q) > 0 {
+ vegetaTarget.URL += `?` + q
+ }
+
+ case libhttp.RequestTypeForm:
+ var form = rr.HTTPTarget.Params.ToURLValues().Encode()
+ vegetaTarget.Body = []byte(form)
+
+ case libhttp.RequestTypeJSON:
+ var mapStringAny = rr.HTTPTarget.Params.ToJSONObject()
+
+ vegetaTarget.Body, err = json.Marshal(mapStringAny)
+
+ case libhttp.RequestTypeMultipartForm:
+ var (
+ params map[string][]byte
+ body string
+ )
+
+ params = rr.HTTPTarget.Params.ToMultipartFormData()
+ contentType, body, err = libhttp.GenerateFormData(params)
+ if err == nil {
+ vegetaTarget.Body = []byte(body)
+ vegetaTarget.Header.Set(libhttp.HeaderContentType, contentType)
+ }
+ }
+ }
+
+ return func(tgt *vegeta.Target) error {
+ if err != nil {
+ return fmt.Errorf(`DefaultHTTPAttack: %w`, err)
+ }
+ *tgt = vegetaTarget
+ return nil
+ }
+ }
+}
diff --git a/http_target.go b/http_target.go
index b62156a..262f8ec 100644
--- a/http_target.go
+++ b/http_target.go
@@ -15,7 +15,6 @@ import (
libhttp "github.com/shuLhan/share/lib/http"
"github.com/shuLhan/share/lib/mlog"
libpath "github.com/shuLhan/share/lib/path"
- vegeta "github.com/tsenart/vegeta/v12/lib"
)
// HTTPConvertParams is a handler that will be called inside the Run handler
@@ -26,10 +25,6 @@ type HTTPConvertParams func(target *HTTPTarget) (interface{}, error)
// send request to run the HTTP target.
type HTTPRunHandler func(rr *RunRequest) (runres *RunResponse, err error)
-// HTTPAttackHandler define the function type that will be called when client
-// send request to attack HTTP target.
-type HTTPAttackHandler func(rr *RunRequest) vegeta.Targeter
-
// HTTPPreAttackHandler define the function type that will be called before
// the actual Attack being called.
type HTTPPreAttackHandler func(rr *RunRequest)
@@ -43,7 +38,10 @@ type HTTPTarget struct {
Run HTTPRunHandler `json:"-"`
PreAttack HTTPPreAttackHandler `json:"-"`
- Attack HTTPAttackHandler `json:"-"`
+
+ // Attack define custom handler to generate [vegeta.Attacker].
+ // This field is optional default to [DefaultAttack].
+ Attack HTTPAttackHandler `json:"-"`
// RequestDumper define the handler to store [http.Request] after
// Run into [RunRequest] DumpRequest.
@@ -135,6 +133,11 @@ func (ht *HTTPTarget) init() (err error) {
if len(ht.Path) == 0 {
ht.Path = "/"
}
+
+ if ht.AllowAttack && ht.Attack == nil {
+ ht.Attack = DefaultHTTPAttack()
+ }
+
if ht.RequestDumper == nil {
ht.RequestDumper = DumpHTTPRequest
}