Skip to content

Commit bc9b425

Browse files
authored
Merge pull request #11 from lxzan/dev
Add ProtoBuf/Yaml Codec
2 parents 8588531 + 84e3a20 commit bc9b425

23 files changed

+858
-177
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ test/
1616
debug
1717
vendor/
1818
examples/
19-
bin/
19+
bin/
20+
go.work*

Makefile

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
test:
2-
go test -count 1 -timeout 30s -run ^Test ./...
2+
go test -timeout 30s -run ^Test ./...
3+
go test -timeout 30s -run ^Test ./contrib/pb/...
4+
go test -timeout 30s -run ^Test ./contrib/yaml/...
35

46
bench:
57
go test -benchmem -run=^$$ -bench . github.com/lxzan/hasaki
68

79
cover:
810
go test -coverprofile=bin/cover.out --cover ./...
11+
go test -coverprofile=bin/pb.out --cover ./contrib/pb/...
12+
go test -coverprofile=bin/yaml.out --cover ./contrib/yaml/...
13+
14+
install:
15+
go mod tidy
16+
go generate ./contrib/pb/codec.go
17+
go generate ./contrib/yaml/codec.go

README.md

+29-69
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
# Hasaki
22

3-
http request library for golang
3+
Http Request Library for Go
44

55
[![Build Status][1]][2] [![codecov][3]][4]
66

77
[1]: https://github.com/lxzan/hasaki/workflows/Go%20Test/badge.svg?branch=master
8-
98
[2]: https://github.com/lxzan/hasaki/actions?query=branch%3Amaster
10-
119
[3]: https://codecov.io/gh/lxzan/hasaki/graph/badge.svg?token=0VY55RLS3G
12-
1310
[4]: https://codecov.io/gh/lxzan/hasaki
1411

15-
- [Hasaki](#hasaki)
16-
- [Features](#features)
17-
- [Install](#install)
18-
- [Usage](#usage)
19-
- [Get](#get)
20-
- [Post](#post)
21-
- [Stream](#stream)
22-
- [Error Stack](#error-stack)
23-
- [Middleware](#middleware)
24-
- [Debug](#debug)
12+
- [Hasaki](#hasaki)
13+
- [Features](#features)
14+
- [Install](#install)
15+
- [Usage](#usage)
16+
- [Get](#get)
17+
- [Post](#post)
18+
- [Stream](#stream)
19+
- [Error Stack](#error-stack)
20+
- [Middleware](#middleware)
21+
- [How to get request latency in simple way](#how-to-get-request-latency-in-simple-way)
2522

2623
### Features
2724

28-
- [x] Buffer Pool
29-
- [x] Trace the Error Stack
30-
- [x] JSON / WWWForm Encoder
31-
- [x] Request Before and After Middleware
32-
- [x] Export CURL Command
25+
- [x] Buffer Pool
26+
- [x] Trace the Error Stack
27+
- [x] Request Encoder Bind: JSON, YAML, Form, Stream
28+
- [x] Response Decoder Bind: JSON, YAML, XML
29+
- [x] Request Before and After Middleware
30+
- [x] Export CURL Command in Debug Mode
3331

3432
### Install
3533

@@ -63,23 +61,6 @@ resp := hasaki.
6361
Send(nil)
6462
```
6563

66-
```go
67-
// GET https://api.example.com/search?q=hasaki&page=1
68-
// Send get request, with Query parameter, encoded with struct
69-
70-
type Req struct {
71-
Q string `form:"q"`
72-
Page int `form:"page"`
73-
}
74-
resp := hasaki.
75-
Get("https://api.example.com/search").
76-
SetQuery(Req{
77-
Q: "hasaki",
78-
Page: 1,
79-
}).
80-
Send(nil)
81-
```
82-
8364
#### Post
8465

8566
```go
@@ -102,16 +83,12 @@ resp := hasaki.
10283
// POST https://api.example.com/search
10384
// Send post request, encoded with www-form
10485

105-
type Req struct {
106-
Q string `form:"q"`
107-
Page int `form:"page"`
108-
}
10986
resp := hasaki.
11087
Post("https://api.example.com/search").
11188
SetEncoder(hasaki.FormEncoder).
112-
Send(Req{
113-
Q: "hasaki",
114-
Page: 1,
89+
Send(url.Values{
90+
"q": []string{"hasaki"},
91+
"page": []string{"1"},
11592
})
11693
```
11794

@@ -145,14 +122,20 @@ if err != nil {
145122

146123
#### Middleware
147124

125+
Very useful middleware, you can use it to do something before and after the request is sent.
126+
127+
The middleware is a function, it receives a context and a request or response object, and returns a context and an error.
128+
129+
Under code is a simple middleware example , record the request latency.
130+
148131
```go
149-
// Statistics on time spent on requests
132+
// You can use the before and after middleware to do something before and after the request is sent
150133

151-
before := hasaki.WithBefore(func (ctx context.Context, request *http.Request) (context.Context, error) {
134+
before := hasaki.WithBefore(func(ctx context.Context, request *http.Request) (context.Context, error) {
152135
return context.WithValue(ctx, "t0", time.Now()), nil
153136
})
154137

155-
after := hasaki.WithAfter(func (ctx context.Context, response *http.Response) (context.Context, error) {
138+
after := hasaki.WithAfter(func(ctx context.Context, response *http.Response) (context.Context, error) {
156139
t0 := ctx.Value("t0").(time.Time)
157140
log.Printf("latency=%s", time.Since(t0).String())
158141
return ctx, nil
@@ -161,27 +144,4 @@ after := hasaki.WithAfter(func (ctx context.Context, response *http.Response) (c
161144
var url = "https://api.github.com/search/repositories"
162145
cli, _ := hasaki.NewClient(before, after)
163146
cli.Get(url).Send(nil)
164-
```
165-
166-
#### Debug
167-
168-
```go
169-
hasaki.
170-
Post("http://127.0.0.1:3000").
171-
Debug().
172-
SetHeader("x-username", "123").
173-
SetHeader("user-agent", "hasaki").
174-
SetEncoder(hasaki.FormEncoder).
175-
Send(url.Values{
176-
"name": []string{"洪荒"},
177-
"age": []string{"456"},
178-
})
179-
```
180-
181-
```bash
182-
curl -X POST 'http://127.0.0.1:3000' \
183-
--header 'User-Agent: hasaki' \
184-
--header 'Content-Type: application/x-www-form-urlencoded' \
185-
--header 'X-Username: 123' \
186-
--data-raw 'age=456&name=%E6%B4%AA%E8%8D%92'
187147
```

client.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ type Client struct {
1414
// NewClient 新建一个客户端
1515
// Create a new client
1616
func NewClient(options ...Option) (*Client, error) {
17-
var c = new(config)
18-
options = append(options, withInitialize())
17+
var conf = new(config)
1918
for _, f := range options {
20-
f(c)
19+
f(conf)
2120
}
22-
var client = &Client{config: c}
21+
withInitialize()(conf)
22+
var client = &Client{config: conf}
2323
return client, nil
2424
}
2525

@@ -39,17 +39,34 @@ func (c *Client) Delete(url string, args ...any) *Request {
3939
return c.Request(http.MethodDelete, url, args...)
4040
}
4141

42+
func (c *Client) Head(url string, args ...any) *Request {
43+
return c.Request(http.MethodHead, url, args...)
44+
}
45+
46+
func (c *Client) Options(url string, args ...any) *Request {
47+
return c.Request(http.MethodOptions, url, args...)
48+
}
49+
50+
func (c *Client) Patch(url string, args ...any) *Request {
51+
return c.Request(http.MethodPatch, url, args...)
52+
}
53+
4254
func (c *Client) Request(method string, url string, args ...any) *Request {
4355
if len(args) > 0 {
4456
url = fmt.Sprintf(url, args...)
4557
}
46-
return (&Request{
58+
59+
r := &Request{
4760
ctx: context.Background(),
4861
client: c.config.HTTPClient,
4962
method: strings.ToUpper(method),
5063
url: url,
5164
before: c.config.BeforeFunc,
5265
after: c.config.AfterFunc,
5366
headers: http.Header{},
54-
}).SetEncoder(JsonEncoder)
67+
}
68+
69+
r.SetEncoder(JsonEncoder)
70+
71+
return r
5572
}

client_test.go

+40-29
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ func TestClient(t *testing.T) {
5959
Name string `form:"name"`
6060
}
6161
req := c.Get("http://%s", addr).SetQuery(Req{Name: "xxx"})
62-
exp := fmt.Sprintf("http://%s?name=xxx", addr)
63-
assert.Equal(t, req.url, exp)
62+
assert.True(t, errors.Is(req.err, errUnsupportedData))
6463
}
6564
}
6665

@@ -96,6 +95,18 @@ func TestRequest(t *testing.T) {
9695
resp := Delete("http://%s", addr).Send(nil)
9796
assert.NoError(t, resp.Err())
9897
}
98+
{
99+
resp := Patch("http://%s", addr).Send(nil)
100+
assert.NoError(t, resp.Err())
101+
}
102+
{
103+
resp := Head("http://%s", addr).Send(nil)
104+
assert.NoError(t, resp.Err())
105+
}
106+
{
107+
resp := Options("http://%s", addr).Send(nil)
108+
assert.NoError(t, resp.Err())
109+
}
99110
{
100111
resp := NewRequest(http.MethodDelete, "http://%s", addr).Send(nil)
101112
assert.NoError(t, resp.Err())
@@ -109,19 +120,6 @@ func TestRequest(t *testing.T) {
109120
}
110121
}
111122

112-
func TestRequest_Header(t *testing.T) {
113-
{
114-
var req = Post("http://%s", nextAddr()).SetEncoder(FormEncoder)
115-
var typ = req.Header().Get("Content-Type")
116-
assert.Equal(t, typ, MimeForm)
117-
}
118-
{
119-
var req = Get("http://%s", nextAddr())
120-
var typ = req.Header().Get("Content-Type")
121-
assert.Equal(t, typ, MimeJson)
122-
}
123-
}
124-
125123
func TestRequest_SetContext(t *testing.T) {
126124
addr := nextAddr()
127125
srv := &http.Server{Addr: addr}
@@ -166,14 +164,6 @@ func TestRequest_SetQuery(t *testing.T) {
166164
})
167165
assert.Equal(t, req.url, "http://"+addr+"?name=xxx")
168166
})
169-
170-
t.Run("", func(t *testing.T) {
171-
type Req struct {
172-
Name string `form:"name"`
173-
}
174-
req := Get("http://%s", addr).SetQuery(Req{Name: "xxx"})
175-
assert.Equal(t, req.url, "http://"+addr+"?name=xxx")
176-
})
177167
}
178168

179169
func TestRequest_Send(t *testing.T) {
@@ -290,9 +280,18 @@ func TestResponse(t *testing.T) {
290280
case "/greet":
291281
writer.WriteHeader(http.StatusOK)
292282
writer.Write([]byte("hello"))
293-
case "/test":
283+
case "/json":
294284
writer.WriteHeader(http.StatusOK)
295285
writer.Write([]byte(`{"name":"caster"}`))
286+
case "/yaml":
287+
writer.WriteHeader(http.StatusOK)
288+
writer.Write([]byte(`name: caster`))
289+
case "/xml":
290+
writer.WriteHeader(http.StatusOK)
291+
writer.Write([]byte(`<A><name>caster</name></A>`))
292+
case "/proto":
293+
writer.WriteHeader(http.StatusOK)
294+
writer.Write([]byte{10, 6, 99, 97, 115, 116, 101, 114})
296295
case "/204":
297296
writer.WriteHeader(http.StatusNoContent)
298297
default:
@@ -311,35 +310,35 @@ func TestResponse(t *testing.T) {
311310
})
312311

313312
t.Run("read body error 1", func(t *testing.T) {
314-
resp := Post("http://%s/test", nextAddr()).Send(nil)
313+
resp := Post("http://%s/json", nextAddr()).Send(nil)
315314
_, err := resp.ReadBody()
316315
assert.Error(t, err)
317316
})
318317

319318
t.Run("read body error 2", func(t *testing.T) {
320-
resp := Post("http://%s/test", addr).Send(nil)
319+
resp := Post("http://%s/json", addr).Send(nil)
321320
resp.Body = nil
322321
_, err := resp.ReadBody()
323322
assert.Error(t, err)
324323
})
325324

326325
t.Run("bind json ok", func(t *testing.T) {
327-
resp := Post("http://%s/test", addr).Send(nil)
326+
resp := Post("http://%s/json", addr).Send(nil)
328327
input := struct{ Name string }{}
329328
err := resp.BindJSON(&input)
330329
assert.NoError(t, err)
331330
assert.Equal(t, input.Name, "caster")
332331
})
333332

334333
t.Run("bind json error 1", func(t *testing.T) {
335-
resp := Post("http://%s/test", nextAddr()).Send(nil)
334+
resp := Post("http://%s/json", nextAddr()).Send(nil)
336335
inputs := struct{ Name string }{}
337336
err := resp.BindJSON(&inputs)
338337
assert.Error(t, err)
339338
})
340339

341340
t.Run("bind json error 2", func(t *testing.T) {
342-
resp := Post("http://%s/test", addr).Send(map[string]any{
341+
resp := Post("http://%s/json", addr).Send(map[string]any{
343342
"name": "xxx",
344343
})
345344
resp.Body = nil
@@ -373,3 +372,15 @@ func TestResponse(t *testing.T) {
373372
assert.Error(t, resp.Err())
374373
})
375374
}
375+
376+
func TestRequest_SetHeaders(t *testing.T) {
377+
var h = http.Header{}
378+
h.Set("Content-Type", MimeJson)
379+
h.Set("cookie", "123")
380+
var req = Get("https://api.github.com").
381+
SetHeader("Cookie", "456").
382+
SetHeader("encoding", "none").
383+
SetHeaders(h)
384+
assert.Equal(t, req.headers.Get("cookie"), "123")
385+
assert.Equal(t, req.headers.Get("encoding"), "none")
386+
}

0 commit comments

Comments
 (0)