aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
}