This commit is contained in:
bxd
2023-11-14 16:49:44 +08:00
parent b2006334e2
commit 09fefdb994
19 changed files with 587 additions and 38 deletions

View File

@@ -19,44 +19,30 @@ Requests is a fully featured HTTP client library for Golang. Network requests ca
## Features
* GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS, etc.
* [Simple for settings and request](https://github.com/gospider007/requests#quickly-send-requests)
* [Request](https://pkg.go.dev/github.com/gospider007/requests#RequestOption) Body can be `string`, `[]byte`, `struct`, `map`, `slice` and `io.Reader` too
* Auto detects `Content-Type`
* Buffer less processing for `io.Reader`
* [Flow request](https://github.com/gospider007/requests/blob/master/test/stream_test.go)
* Response object gives you more possibility
* [Return whether to reuse connections](https://github.com/gospider007/requests/blob/master/test/isNewConn_test.go)
* Automatic marshal and unmarshal for content
* Easy to upload one or more file(s) via `multipart/form-data`
* Auto detects file content type
* Request URL Path Params (aka URI Params)
* Backoff Retry Mechanism with retry condition function
* Optionally allows GET request with payload
* Request design
* Have client level settings & options and also override at Request level if you want to
* Request and Response middleware
* goroutine concurrent safe
* Gzip - Go does it automatically also requests has fallback handling too
* Works fine with `HTTP/2` and `HTTP/1.1`
* [Request](https://github.com/gospider007/requests/tree/master/test/request)
* [Json request](https://github.com/gospider007/requests/blob/master/test/request/json_test.go) with `application/json`
* [Data request](https://github.com/gospider007/requests/blob/master/test/request/data_test.go) with `application/x-www-form-urlencoded`
* [Form request](https://github.com/gospider007/requests/blob/master/test/request/form_test.go) with `multipart/form-data`
* [File request](https://github.com/gospider007/requests/blob/master/test/request/file_test.go) with `multipart/form-data`
* [Flow request](https://github.com/gospider007/requests/blob/master/test/request/stream_test.go)
* [Request URL Path Params](https://github.com/gospider007/requests/blob/master/test/request/params_test.go)
* [Response](https://github.com/gospider007/requests/tree/master/test/response)
* [Return whether to reuse connections](https://github.com/gospider007/requests/blob/master/test/response/isNewConn_test.go)
* [Middleware](https://github.com/gospider007/requests/tree/master/test/middleware)
* [Option Callback Method](https://github.com/gospider007/requests/blob/master/test/middleware/optionltCallBack_test.go)
* [Result Callback Method](https://github.com/gospider007/requests/blob/master/test/middleware/resultCallBack_test.go)
* [Error Callback Method](https://github.com/gospider007/requests/blob/master/test/middleware/errCallBack_test.go)
* [Retry](https://github.com/gospider007/requests/blob/master/test/middleware/try_test.go)
* [Protocol](https://github.com/gospider007/requests/tree/master/test/protocol)
* [HTTP1](https://github.com/gospider007/requests/blob/master/test/protocol/http1_test.go)
* [HTTP2](https://github.com/gospider007/requests/blob/master/test/protocol/http2_test.go)
* [WebSocket](https://github.com/gospider007/requests/blob/master/test/protocol/websocket_test.go)
* [Session](https://github.com/gospider007/requests/blob/master/test/session_test.go)
* [IPv4, IPv6 Address Control Parsing](https://github.com/gospider007/requests/blob/master/test/addType_test.go)
* [DNS Settings](https://github.com/gospider007/requests/blob/master/test/dns_test.go)
* [Fingerprint](https://github.com/gospider007/requests/blob/master/test/ja3_test.go)
* JA3
* HTTP2
* JA4
* OrderHeaders
* Request header capitalization
* [Proxy](https://github.com/gospider007/requests/blob/master/test/proxy_test.go)
* HTTP
* HTTPS
* SOCKS5
* Protocol
* HTTP
* HTTPS
* [WebSocket](https://github.com/gospider007/requests/blob/master/test/websocket_test.go)
* SSE
* Well tested client library
* [Well tested client library](https://github.com/gospider007/requests/tree/master/test)
## Supported Go Versions
Recommended to use `go1.21.3` and above.
Initially Requests started supporting `go modules`

View File

@@ -65,17 +65,17 @@ func (obj *RequestOption) newBody(val any, valType bodyType, dataMap map[string]
}
case string:
switch valType {
case jsonType, textType, dataType, rawType:
case jsonType, textType, rawType:
return bytes.NewReader(tools.StringToBytes(value)), nil
case formType, paramsType:
case formType, paramsType, dataType:
default:
return nil, fmt.Errorf("unknow content-type%d", valType)
}
case []byte:
switch valType {
case jsonType, textType, dataType, rawType:
case jsonType, textType, rawType:
return bytes.NewReader(value), nil
case formType, paramsType:
case formType, paramsType, dataType:
default:
return nil, fmt.Errorf("unknow content-type%d", valType)
}

2
go.mod
View File

@@ -5,7 +5,7 @@ go 1.21.3
require (
github.com/gospider007/bar v0.0.0-20231024075629-3f50832a4cbf
github.com/gospider007/bs4 v0.0.0-20231024075735-6bbdac929d8b
github.com/gospider007/gson v0.0.0-20231110081735-fa6816715498
github.com/gospider007/gson v0.0.0-20231114084834-650b26cdd864
github.com/gospider007/gtls v0.0.0-20231109073720-32fe741c06d3
github.com/gospider007/ja3 v0.0.0-20231029025157-38fc2f8f2d91
github.com/gospider007/net v0.0.0-20231028084010-313c148cf0a1

2
go.sum
View File

@@ -37,6 +37,8 @@ github.com/gospider007/gson v0.0.0-20231108025125-ff62d4066dc4 h1:5s6Ird9YHFd858
github.com/gospider007/gson v0.0.0-20231108025125-ff62d4066dc4/go.mod h1:CGrR2aJGWEd96xr9TVCSOePEFWX1F3sBVaf4ZBvCbMI=
github.com/gospider007/gson v0.0.0-20231110081735-fa6816715498 h1:NDZlnVcLScXmCeS4a7kugrNUWzioVBDX/kdcGURxcj8=
github.com/gospider007/gson v0.0.0-20231110081735-fa6816715498/go.mod h1:r8uL+sPUwP8+6n8GZFa2iz2bJe0qNS6EKwkqmiZhHqI=
github.com/gospider007/gson v0.0.0-20231114084834-650b26cdd864 h1:san+x97YVi5o580y/0e64mrs2b7JkG7dGlsS4+bjw44=
github.com/gospider007/gson v0.0.0-20231114084834-650b26cdd864/go.mod h1:r8uL+sPUwP8+6n8GZFa2iz2bJe0qNS6EKwkqmiZhHqI=
github.com/gospider007/gtls v0.0.0-20231024092712-01193b9f0404 h1:Qp/o+l3KgWmviEsy0OXlFSjA0lbB8YtlcIOxN1xXyoI=
github.com/gospider007/gtls v0.0.0-20231024092712-01193b9f0404/go.mod h1:fLcidMDKVv8b9NvLy0P/ZclltTaXJvTHANWiPCgDbSI=
github.com/gospider007/gtls v0.0.0-20231108025158-443489ca9953 h1:CN2nz4q88S3BhNR1QSmXubOgMGnxzWwdDL2OIASwYBs=

View File

@@ -93,6 +93,11 @@ func (obj *Response) Location() (*url.URL, error) {
return obj.response.Location()
}
// return response Proto
func (obj *Response) Proto() string {
return obj.response.Proto
}
// return response cookies
func (obj *Response) Cookies() Cookies {
if obj.filePath != "" {

View File

@@ -0,0 +1,32 @@
package main
import (
"context"
"errors"
"testing"
"github.com/gospider007/requests"
)
func TestErrCallBack(t *testing.T) {
n := 0
_, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
TryNum: 3,
ResultCallBack: func(ctx context.Context, client *requests.Client, response *requests.Response) error {
return errors.New("try")
},
ErrCallBack: func(ctx context.Context, client *requests.Client, response *requests.Response, err error) error {
if n == 0 {
n++
return nil
}
return errors.New("test")
},
})
if err == nil {
t.Error("callback error")
}
if n != 1 {
t.Error("n!=1")
}
}

View File

@@ -0,0 +1,30 @@
package main
import (
"context"
"testing"
"github.com/gospider007/requests"
)
func TestOptionCallBack(t *testing.T) {
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
OptionCallBack: func(ctx context.Context, client *requests.Client, option *requests.RequestOption) error {
option.Params = map[string]string{"name": "test"}
return nil
},
})
if err != nil {
t.Error(err)
}
if resp.StatusCode() != 200 {
t.Error("resp.StatusCode!= 200")
}
jsonData, err := resp.Json()
if err != nil {
t.Error(err)
}
if jsonData.Get("args.name").String() != "test" {
t.Error("jsonData.Get(\"args.name\").String()!= test")
}
}

View File

@@ -0,0 +1,28 @@
package main
import (
"context"
"errors"
"testing"
"github.com/gospider007/requests"
)
func TestResultCallBack(t *testing.T) {
var code int
_, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
ResultCallBack: func(ctx context.Context, client *requests.Client, response *requests.Response) error {
if response.StatusCode() != 200 {
return errors.New("resp.StatusCode!= 200")
}
code = response.StatusCode()
return nil
},
})
if err != nil {
t.Error(err)
}
if code != 200 {
t.Error("code!= 200")
}
}

View File

@@ -0,0 +1,32 @@
package main
import (
"context"
"errors"
"testing"
"github.com/gospider007/requests"
)
func TestTryNum(t *testing.T) {
n := 0
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
TryNum: 3,
ResultCallBack: func(ctx context.Context, client *requests.Client, response *requests.Response) error {
if n == 0 {
n++
return errors.New("try")
}
return nil
},
})
if err != nil {
t.Error(err)
}
if resp.StatusCode() != 200 {
t.Error("resp.StatusCode!= 200")
}
if n != 1 {
t.Error("n!=1")
}
}

View File

@@ -0,0 +1,22 @@
package main
import (
"testing"
"github.com/gospider007/requests"
)
func TestHttp1(t *testing.T) {
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
ForceHttp1: true,
})
if err != nil {
t.Error(err)
}
if resp.StatusCode() != 200 {
t.Error("resp.StatusCode!= 200")
}
if resp.Proto() != "HTTP/1.1" {
t.Error("resp.Proto!= HTTP/1.1")
}
}

View File

@@ -0,0 +1,20 @@
package main
import (
"testing"
"github.com/gospider007/requests"
)
func TestHttp2(t *testing.T) {
resp, err := requests.Get(nil, "https://httpbin.org/anything")
if err != nil {
t.Error(err)
}
if resp.StatusCode() != 200 {
t.Error("resp.StatusCode!= 200")
}
if resp.Proto() != "HTTP/2.0" {
t.Error("resp.Proto!= HTTP/2.0")
}
}

90
test/request/data_test.go Normal file
View File

@@ -0,0 +1,90 @@
package main
import (
"testing"
"github.com/gospider007/gson"
"github.com/gospider007/requests"
)
func TestSendDataWithMap(t *testing.T) {
dataBody := map[string]any{
"name": "test",
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Data: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/x-www-form-urlencoded" {
t.Fatal("json data error")
}
if jsonData.Get("form.name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendDataWithString(t *testing.T) {
dataBody := `{"name":"test"}`
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Data: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/x-www-form-urlencoded" {
t.Fatal("json data error")
}
if jsonData.Get("form.name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendDataWithStruct(t *testing.T) {
dataBody := struct{ Name string }{"test"}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Data: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/x-www-form-urlencoded" {
t.Fatal("json data error")
}
if jsonData.Get("form.Name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendDataWithGson(t *testing.T) {
dataBody, err := gson.Decode(struct{ Name string }{"test"})
if err != nil {
t.Fatal(err)
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Data: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/x-www-form-urlencoded" {
t.Fatal("json data error")
}
if jsonData.Get("form.Name").String() != "test" {
t.Fatal("json data error")
}
}

35
test/request/file_test.go Normal file
View File

@@ -0,0 +1,35 @@
package main
import (
"strings"
"testing"
"github.com/gospider007/requests"
)
func TestSendFile(t *testing.T) {
fileData := []requests.File{
{
Key: "file",
Val: []byte("test"),
FileName: "test.txt",
ContentType: "text/plain",
},
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Files: fileData,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(jsonData.Get("headers.Content-Type").String(), "multipart/form-data") {
t.Fatal("json data error")
}
if jsonData.Get("files.file").String() != "test" {
t.Fatal("json data error")
}
}

91
test/request/form_test.go Normal file
View File

@@ -0,0 +1,91 @@
package main
import (
"strings"
"testing"
"github.com/gospider007/gson"
"github.com/gospider007/requests"
)
func TestSendFormWithMap(t *testing.T) {
dataBody := map[string]any{
"name": "test",
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Form: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(jsonData.Get("headers.Content-Type").String(), "multipart/form-data") {
t.Fatal("json data error")
}
if jsonData.Get("form.name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendFormWithString(t *testing.T) {
dataBody := `{"name":"test"}`
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Form: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(jsonData.Get("headers.Content-Type").String(), "multipart/form-data") {
t.Fatal("json data error")
}
if jsonData.Get("form.name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendFormWithStruct(t *testing.T) {
dataBody := struct{ Name string }{"test"}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Form: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(jsonData.Get("headers.Content-Type").String(), "multipart/form-data") {
t.Fatal("json data error")
}
if jsonData.Get("form.Name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendFormWithGson(t *testing.T) {
dataBody, err := gson.Decode(struct{ Name string }{"test"})
if err != nil {
t.Fatal(err)
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Form: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(jsonData.Get("headers.Content-Type").String(), "multipart/form-data") {
t.Fatal("json data error")
}
if jsonData.Get("form.Name").String() != "test" {
t.Fatal("json data error")
}
}

98
test/request/json_test.go Normal file
View File

@@ -0,0 +1,98 @@
package main
import (
"testing"
"github.com/gospider007/gson"
"github.com/gospider007/requests"
)
func TestSendJsonWithMap(t *testing.T) {
jsonBody := map[string]any{
"name": "test",
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Json: jsonBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/json" {
t.Fatal("json data error")
}
bodyJson, err := gson.Decode(jsonBody)
if err != nil {
t.Fatal(err)
}
if bodyJson.String() != jsonData.Get("data").String() {
t.Fatal("json data error")
}
}
func TestSendJsonWithString(t *testing.T) {
jsonBody := `{"name":"test"}`
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Json: jsonBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/json" {
t.Fatal("json data error")
}
if jsonBody != jsonData.Get("data").String() {
t.Fatal("json data error")
}
}
func TestSendJsonWithStruct(t *testing.T) {
jsonBody := struct{ Name string }{"test"}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Json: jsonBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/json" {
t.Fatal("json data error")
}
bodyJson, err := gson.Decode(jsonBody)
if err != nil {
t.Fatal(err)
}
if bodyJson.String() != jsonData.Get("data").String() {
t.Fatal("json data error")
}
}
func TestSendJsonWithGson(t *testing.T) {
bodyJson, err := gson.Decode(struct{ Name string }{"test"})
if err != nil {
t.Fatal(err)
}
resp, err := requests.Post(nil, "https://httpbin.org/anything", requests.RequestOption{
Json: bodyJson,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("headers.Content-Type").String() != "application/json" {
t.Fatal("json data error")
}
if bodyJson.String() != jsonData.Get("data").String() {
t.Fatal("json data error")
}
}

View File

@@ -0,0 +1,78 @@
package main
import (
"testing"
"github.com/gospider007/gson"
"github.com/gospider007/requests"
)
func TestSendParamsWithMap(t *testing.T) {
dataBody := map[string]any{
"name": "test",
}
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
Params: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("args.name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendParamsWithString(t *testing.T) {
dataBody := `{"name":"test"}`
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
Params: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("args.name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendParamsWithStruct(t *testing.T) {
dataBody := struct{ Name string }{"test"}
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
Params: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("args.Name").String() != "test" {
t.Fatal("json data error")
}
}
func TestSendParamsWithGson(t *testing.T) {
dataBody, err := gson.Decode(struct{ Name string }{"test"})
if err != nil {
t.Fatal(err)
}
resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{
Params: dataBody,
})
if err != nil {
t.Fatal(err)
}
jsonData, err := resp.Json()
if err != nil {
t.Fatal(err)
}
if jsonData.Get("args.Name").String() != "test" {
t.Fatal("json data error")
}
}