mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
Merge pull request #1223 from bbx-winner/master
feat: support HTTP/2 protocol
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
- feat: add `--profile` flag for har2case to support overwrite headers/cookies with specified yaml/json profile file
|
||||
- feat: support run testcases in specified folder path, including testcases in sub folders
|
||||
- feat: support HTTP/2 protocol
|
||||
- change: integrate [sentry sdk][sentry sdk] for panic reporting and analysis
|
||||
- change: lock funplugin version when creating scaffold project
|
||||
- fix: call referenced api/testcase with relative path
|
||||
|
||||
1
go.mod
1
go.mod
@@ -19,6 +19,7 @@ require (
|
||||
github.com/rs/zerolog v1.26.1
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ func newResponseObject(t *testing.T, parser *Parser, resp *http.Response) (*resp
|
||||
}
|
||||
|
||||
respObjMeta := respObjMeta{
|
||||
Proto: resp.Proto,
|
||||
StatusCode: resp.StatusCode,
|
||||
Headers: headers,
|
||||
Cookies: cookies,
|
||||
@@ -74,6 +75,7 @@ func newResponseObject(t *testing.T, parser *Parser, resp *http.Response) (*resp
|
||||
}
|
||||
|
||||
type respObjMeta struct {
|
||||
Proto string `json:"proto"`
|
||||
StatusCode int `json:"status_code"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
Cookies map[string]string `json:"cookies"`
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/httprunner/httprunner/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/hrp/internal/sdk"
|
||||
@@ -31,12 +32,18 @@ func NewRunner(t *testing.T) *HRPRunner {
|
||||
t: t,
|
||||
failfast: true, // default to failfast
|
||||
genHTMLReport: false,
|
||||
client: &http.Client{
|
||||
httpClient: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
http2Client: &http.Client{
|
||||
Transport: &http2.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +54,18 @@ type HRPRunner struct {
|
||||
pluginLogOn bool
|
||||
saveTests bool
|
||||
genHTMLReport bool
|
||||
client *http.Client
|
||||
httpClient *http.Client
|
||||
http2Client *http.Client
|
||||
}
|
||||
|
||||
// SetClientTransport configures transport of http client for high concurrency load testing
|
||||
func (r *HRPRunner) SetClientTransport(maxConns int, disableKeepAlive bool, disableCompression bool) *HRPRunner {
|
||||
log.Info().Int("maxConns", maxConns).Msg("[init] SetClientTransport")
|
||||
r.client.Transport = &http.Transport{
|
||||
log.Info().
|
||||
Int("maxConns", maxConns).
|
||||
Bool("disableKeepAlive", disableKeepAlive).
|
||||
Bool("disableCompression", disableCompression).
|
||||
Msg("[init] SetClientTransport")
|
||||
r.httpClient.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DialContext: (&net.Dialer{}).DialContext,
|
||||
MaxIdleConns: 0,
|
||||
@@ -61,6 +73,10 @@ func (r *HRPRunner) SetClientTransport(maxConns int, disableKeepAlive bool, disa
|
||||
DisableKeepAlives: disableKeepAlive,
|
||||
DisableCompression: disableCompression,
|
||||
}
|
||||
r.http2Client.Transport = &http2.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DisableCompression: disableCompression,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -93,7 +109,7 @@ func (r *HRPRunner) SetProxyUrl(proxyUrl string) *HRPRunner {
|
||||
log.Error().Err(err).Str("proxyUrl", proxyUrl).Msg("[init] invalid proxyUrl")
|
||||
return r
|
||||
}
|
||||
r.client.Transport = &http.Transport{
|
||||
r.httpClient.Transport = &http.Transport{
|
||||
Proxy: http.ProxyURL(p),
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
@@ -86,16 +86,6 @@ func isPreRendezvousAllReleased(rendezvous *Rendezvous, testCase *TCase) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Rendezvous creates a new rendezvous
|
||||
func (s *StepRequest) Rendezvous(name string) *StepRendezvous {
|
||||
s.step.Rendezvous = &Rendezvous{
|
||||
Name: name,
|
||||
}
|
||||
return &StepRendezvous{
|
||||
step: s.step,
|
||||
}
|
||||
}
|
||||
|
||||
// WithUserNumber sets the user number needed to release the current rendezvous
|
||||
func (s *StepRendezvous) WithUserNumber(number int64) *StepRendezvous {
|
||||
s.step.Rendezvous.Number = number
|
||||
|
||||
@@ -16,26 +16,26 @@ func TestRunCaseWithRendezvous(t *testing.T) {
|
||||
}),
|
||||
TestSteps: []IStep{
|
||||
NewStep("test negative number").
|
||||
Rendezvous("test negative number").
|
||||
SetRendezvous("test negative number").
|
||||
WithUserNumber(-1),
|
||||
NewStep("test overflow number").
|
||||
Rendezvous("test overflow number").
|
||||
SetRendezvous("test overflow number").
|
||||
WithUserNumber(1000000),
|
||||
NewStep("test negative percent").
|
||||
Rendezvous("test very low percent").
|
||||
SetRendezvous("test very low percent").
|
||||
WithUserPercent(-0.5),
|
||||
NewStep("test very low percent").
|
||||
Rendezvous("test very low percent").
|
||||
SetRendezvous("test very low percent").
|
||||
WithUserPercent(0.00001),
|
||||
NewStep("test overflow percent").
|
||||
Rendezvous("test overflow percent").
|
||||
SetRendezvous("test overflow percent").
|
||||
WithUserPercent(1.5),
|
||||
NewStep("test conflict params").
|
||||
Rendezvous("test conflict params").
|
||||
SetRendezvous("test conflict params").
|
||||
WithUserNumber(1).
|
||||
WithUserPercent(0.123),
|
||||
NewStep("test negative timeout").
|
||||
Rendezvous("test negative timeout").
|
||||
SetRendezvous("test negative timeout").
|
||||
WithTimeout(-1000),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ const (
|
||||
type Request struct {
|
||||
Method HTTPMethod `json:"method" yaml:"method"` // required
|
||||
URL string `json:"url" yaml:"url"` // required
|
||||
HTTP2 bool `json:"http2,omitempty" yaml:"http2,omitempty"`
|
||||
Params map[string]interface{} `json:"params,omitempty" yaml:"params,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||
Cookies map[string]string `json:"cookies,omitempty" yaml:"cookies,omitempty"`
|
||||
@@ -56,17 +57,23 @@ func newRequestBuilder(parser *Parser, config *TConfig, stepRequest *Request) *r
|
||||
var requestMap map[string]interface{}
|
||||
_ = json.Unmarshal(jsonRequest, &requestMap)
|
||||
|
||||
request := &http.Request{
|
||||
Header: make(http.Header),
|
||||
}
|
||||
if stepRequest.HTTP2 {
|
||||
request.ProtoMajor = 2
|
||||
request.ProtoMinor = 0
|
||||
} else {
|
||||
request.ProtoMajor = 1
|
||||
request.ProtoMinor = 1
|
||||
}
|
||||
|
||||
return &requestBuilder{
|
||||
stepRequest: stepRequest,
|
||||
req: &http.Request{
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
},
|
||||
config: config,
|
||||
parser: parser,
|
||||
requestMap: requestMap,
|
||||
req: request,
|
||||
config: config,
|
||||
parser: parser,
|
||||
requestMap: requestMap,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +313,13 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
|
||||
|
||||
// do request action
|
||||
start := time.Now()
|
||||
resp, err := r.hrpRunner.client.Do(rb.req)
|
||||
var resp *http.Response
|
||||
if step.Request.HTTP2 {
|
||||
resp, err = r.hrpRunner.http2Client.Do(rb.req)
|
||||
} else {
|
||||
resp, err = r.hrpRunner.httpClient.Do(rb.req)
|
||||
}
|
||||
|
||||
stepResult.Elapsed = time.Since(start).Milliseconds()
|
||||
if err != nil {
|
||||
return stepResult, errors.Wrap(err, "do request failed")
|
||||
@@ -465,11 +478,24 @@ func (s *StepRequest) SetupHook(hook string) *StepRequest {
|
||||
return s
|
||||
}
|
||||
|
||||
// HTTP2 enables HTTP/2 protocol
|
||||
func (s *StepRequest) HTTP2() *StepRequest {
|
||||
s.step.Request = &Request{
|
||||
HTTP2: true,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GET makes a HTTP GET request.
|
||||
func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpGET,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpGET
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpGET,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -478,9 +504,14 @@ func (s *StepRequest) GET(url string) *StepRequestWithOptionalArgs {
|
||||
|
||||
// HEAD makes a HTTP HEAD request.
|
||||
func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpHEAD,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpHEAD
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpHEAD,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -489,9 +520,14 @@ func (s *StepRequest) HEAD(url string) *StepRequestWithOptionalArgs {
|
||||
|
||||
// POST makes a HTTP POST request.
|
||||
func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpPOST,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpPOST
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpPOST,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -500,9 +536,14 @@ func (s *StepRequest) POST(url string) *StepRequestWithOptionalArgs {
|
||||
|
||||
// PUT makes a HTTP PUT request.
|
||||
func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpPUT,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpPUT
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpPUT,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -511,9 +552,14 @@ func (s *StepRequest) PUT(url string) *StepRequestWithOptionalArgs {
|
||||
|
||||
// DELETE makes a HTTP DELETE request.
|
||||
func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpDELETE,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpDELETE
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpDELETE,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -522,9 +568,14 @@ func (s *StepRequest) DELETE(url string) *StepRequestWithOptionalArgs {
|
||||
|
||||
// OPTIONS makes a HTTP OPTIONS request.
|
||||
func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpOPTIONS,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpOPTIONS
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpOPTIONS,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -533,9 +584,14 @@ func (s *StepRequest) OPTIONS(url string) *StepRequestWithOptionalArgs {
|
||||
|
||||
// PATCH makes a HTTP PATCH request.
|
||||
func (s *StepRequest) PATCH(url string) *StepRequestWithOptionalArgs {
|
||||
s.step.Request = &Request{
|
||||
Method: httpPATCH,
|
||||
URL: url,
|
||||
if s.step.Request != nil {
|
||||
s.step.Request.Method = httpPATCH
|
||||
s.step.Request.URL = url
|
||||
} else {
|
||||
s.step.Request = &Request{
|
||||
Method: httpPATCH,
|
||||
URL: url,
|
||||
}
|
||||
}
|
||||
return &StepRequestWithOptionalArgs{
|
||||
step: s.step,
|
||||
@@ -600,6 +656,16 @@ func (s *StepRequest) SetThinkTime(time float64) *StepThinkTime {
|
||||
}
|
||||
}
|
||||
|
||||
// SetRendezvous creates a new rendezvous
|
||||
func (s *StepRequest) SetRendezvous(name string) *StepRendezvous {
|
||||
s.step.Rendezvous = &Rendezvous{
|
||||
Name: name,
|
||||
}
|
||||
return &StepRendezvous{
|
||||
step: s.step,
|
||||
}
|
||||
}
|
||||
|
||||
// StepRequestWithOptionalArgs implements IStep interface.
|
||||
type StepRequestWithOptionalArgs struct {
|
||||
step *TStep
|
||||
|
||||
54
hrp/tests/protocol_test.go
Normal file
54
hrp/tests/protocol_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/hrp"
|
||||
)
|
||||
|
||||
func TestProtocol(t *testing.T) {
|
||||
testcase := &hrp.TestCase{
|
||||
Config: hrp.NewConfig("run request with different protocol types").
|
||||
SetBaseURL("https://postman-echo.com"),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("HTTP/1.1 get").
|
||||
GET("/get").
|
||||
WithParams(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertEqual("proto", "HTTP/1.1", "check protocol type").
|
||||
AssertLengthEqual("body.args.foo1", 4, "check param foo1"),
|
||||
hrp.NewStep("HTTP/1.1 post").
|
||||
POST("/post").
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
WithBody(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertEqual("proto", "HTTP/1.1", "check protocol type").
|
||||
AssertLengthEqual("body.json.foo1", 4, "check body foo1"),
|
||||
hrp.NewStep("HTTP/2 get").
|
||||
HTTP2().
|
||||
GET("/get").
|
||||
WithParams(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertEqual("proto", "HTTP/2.0", "check protocol type").
|
||||
AssertLengthEqual("body.args.foo1", 4, "check param foo1"),
|
||||
hrp.NewStep("HTTP/2 post").
|
||||
HTTP2().
|
||||
POST("/post").
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
WithBody(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertEqual("proto", "HTTP/2.0", "check protocol type").
|
||||
AssertLengthEqual("body.json.foo1", 4, "check body foo1"),
|
||||
},
|
||||
}
|
||||
err := hrp.NewRunner(t).Run(testcase)
|
||||
if err != nil {
|
||||
t.Fatalf("run testcase error: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -6,50 +6,47 @@ import (
|
||||
"github.com/httprunner/httprunner/hrp"
|
||||
)
|
||||
|
||||
const rendezvousTestJSONPath = "rendezvous_test.json"
|
||||
|
||||
var rendezvousTestcase = &hrp.TestCase{
|
||||
Config: hrp.NewConfig("run request with functions").
|
||||
SetBaseURL("https://postman-echo.com").
|
||||
WithVariables(map[string]interface{}{
|
||||
"n": 5,
|
||||
"a": 12.3,
|
||||
"b": 3.45,
|
||||
}),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("waiting for all users in the beginning").
|
||||
Rendezvous("rendezvous0"),
|
||||
hrp.NewStep("rendezvous before get").
|
||||
Rendezvous("rendezvous1").
|
||||
WithUserNumber(50).
|
||||
WithTimeout(3000),
|
||||
hrp.NewStep("get with params").
|
||||
GET("/get").
|
||||
WithParams(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
Extract().
|
||||
WithJmesPath("body.args.foo1", "varFoo1").
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code"),
|
||||
hrp.NewStep("rendezvous before post").
|
||||
Rendezvous("rendezvous2").
|
||||
WithUserNumber(20).
|
||||
WithTimeout(2000),
|
||||
hrp.NewStep("post json data with functions").
|
||||
POST("/post").
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
WithBody(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertLengthEqual("body.json.foo1", 4, "check args foo1").
|
||||
AssertEqual("body.json.foo2", "foo2", "check args foo2"),
|
||||
hrp.NewStep("waiting for all users in the end").
|
||||
Rendezvous("rendezvous3"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestRendezvous(t *testing.T) {
|
||||
err := hrp.NewRunner(t).Run(rendezvousTestcase)
|
||||
testcase := &hrp.TestCase{
|
||||
Config: hrp.NewConfig("run request with rendezvous").
|
||||
SetBaseURL("https://postman-echo.com").
|
||||
WithVariables(map[string]interface{}{
|
||||
"n": 5,
|
||||
"a": 12.3,
|
||||
"b": 3.45,
|
||||
}),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("waiting for all users in the beginning").
|
||||
SetRendezvous("rendezvous0"),
|
||||
hrp.NewStep("rendezvous before get").
|
||||
SetRendezvous("rendezvous1").
|
||||
WithUserNumber(50).
|
||||
WithTimeout(3000),
|
||||
hrp.NewStep("get with params").
|
||||
GET("/get").
|
||||
WithParams(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
Extract().
|
||||
WithJmesPath("body.args.foo1", "varFoo1").
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code"),
|
||||
hrp.NewStep("rendezvous before post").
|
||||
SetRendezvous("rendezvous2").
|
||||
WithUserNumber(20).
|
||||
WithTimeout(2000),
|
||||
hrp.NewStep("post json data with functions").
|
||||
POST("/post").
|
||||
WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
|
||||
WithBody(map[string]interface{}{"foo1": "foo1", "foo2": "foo2"}).
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertLengthEqual("body.json.foo1", 4, "check args foo1").
|
||||
AssertEqual("body.json.foo2", "foo2", "check args foo2"),
|
||||
hrp.NewStep("waiting for all users in the end").
|
||||
SetRendezvous("rendezvous3"),
|
||||
},
|
||||
}
|
||||
err := hrp.NewRunner(t).Run(testcase)
|
||||
if err != nil {
|
||||
t.Fatalf("run testcase error: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user