1
1
// This package aims to wrap Go HTTP Client's request-response with sane defaults:
2
2
//
3
- // - You are forced to consider timeouts by having to specify Context
4
- // - Instead of not considering non-2xx status codes as a failure, check that by default
5
- // (unless explicitly asked to)
6
- // - Sending and receiving JSON requires much less boilerplate, and on receiving JSON you
7
- // are forced to think whether to "allowUnknownFields"
3
+ // - You are forced to consider timeouts by having to specify Context
4
+ // - Instead of not considering non-2xx status codes as a failure, check that by default
5
+ // (unless explicitly asked to)
6
+ // - Sending and receiving JSON requires much less boilerplate, and on receiving JSON you
7
+ // are forced to think whether to "allowUnknownFields"
8
8
package ezhttp
9
9
10
10
import (
@@ -58,33 +58,46 @@ func (e ResponseStatusError) StatusCode() int {
58
58
return e .statusCode
59
59
}
60
60
61
+ // returns *ResponseStatusError as error if non-2xx response (unless TolerateNon2xxResponse()).
62
+ // error is not *ResponseStatusError for transport-level errors, content (JSON) marshaling errors etc
61
63
func Get (ctx context.Context , url string , confPieces ... ConfigPiece ) (* http.Response , error ) {
62
- return do (ctx , http .MethodGet , url , confPieces ... )
64
+ return newRequest (ctx , http .MethodGet , url , confPieces ... ). Send ( )
63
65
}
64
66
67
+ // returns *ResponseStatusError as error if non-2xx response (unless TolerateNon2xxResponse()).
68
+ // error is not *ResponseStatusError for transport-level errors, content (JSON) marshaling errors etc
65
69
func Post (ctx context.Context , url string , confPieces ... ConfigPiece ) (* http.Response , error ) {
66
- return do (ctx , http .MethodPost , url , confPieces ... )
70
+ return newRequest (ctx , http .MethodPost , url , confPieces ... ). Send ( )
67
71
}
68
72
73
+ // returns *ResponseStatusError as error if non-2xx response (unless TolerateNon2xxResponse()).
74
+ // error is not *ResponseStatusError for transport-level errors, content (JSON) marshaling errors etc
69
75
func Put (ctx context.Context , url string , confPieces ... ConfigPiece ) (* http.Response , error ) {
70
- return do (ctx , http .MethodPut , url , confPieces ... )
76
+ return newRequest (ctx , http .MethodPut , url , confPieces ... ). Send ( )
71
77
}
72
78
79
+ // returns *ResponseStatusError as error if non-2xx response (unless TolerateNon2xxResponse()).
80
+ // error is not *ResponseStatusError for transport-level errors, content (JSON) marshaling errors etc
73
81
func Head (ctx context.Context , url string , confPieces ... ConfigPiece ) (* http.Response , error ) {
74
- return do (ctx , http .MethodHead , url , confPieces ... )
82
+ return newRequest (ctx , http .MethodHead , url , confPieces ... ). Send ( )
75
83
}
76
84
85
+ // returns *ResponseStatusError as error if non-2xx response (unless TolerateNon2xxResponse()).
86
+ // error is not *ResponseStatusError for transport-level errors, content (JSON) marshaling errors etc
77
87
func Del (ctx context.Context , url string , confPieces ... ConfigPiece ) (* http.Response , error ) {
78
- return do (ctx , http .MethodDelete , url , confPieces ... )
88
+ return newRequest (ctx , http .MethodDelete , url , confPieces ... ). Send ( )
79
89
}
80
90
81
- // returns *ResponseStatusError as error if non-2xx response (unless TolerateNon2xxResponse()).
82
- // error is not *ResponseStatusError for transport-level errors, content (JSON) marshaling errors etc
83
- func do (ctx context.Context , method string , url string , confPieces ... ConfigPiece ) (* http.Response , error ) {
91
+ func newRequest (ctx context.Context , method string , url string , confPieces ... ConfigPiece ) * Config {
84
92
conf := & Config {
85
93
Client : http .DefaultClient ,
86
94
}
87
95
96
+ withErr := func (err error ) * Config {
97
+ conf .Abort = err // will be early-error-returned in `Send()`
98
+ return conf
99
+ }
100
+
88
101
for _ , configure := range confPieces {
89
102
if configure .BeforeInit == nil {
90
103
continue
@@ -93,7 +106,7 @@ func do(ctx context.Context, method string, url string, confPieces ...ConfigPiec
93
106
}
94
107
95
108
if conf .Abort != nil {
96
- return nil , conf .Abort
109
+ return withErr ( conf .Abort )
97
110
}
98
111
99
112
// "Request has body = No" for:
@@ -102,15 +115,15 @@ func do(ctx context.Context, method string, url string, confPieces ...ConfigPiec
102
115
if conf .RequestBody != nil && (method == http .MethodGet || method == http .MethodHead ) {
103
116
// Technically, these can have body, but it's usually a mistake so if we need it we'll
104
117
// make it an opt-in flag.
105
- return nil , fmt .Errorf ("ezhttp: %s with non-nil body is usually a mistake" , method )
118
+ return withErr ( fmt .Errorf ("ezhttp: %s with non-nil body is usually a mistake" , method ) )
106
119
}
107
120
108
121
req , err := http .NewRequest (
109
122
method ,
110
123
url ,
111
124
conf .RequestBody )
112
125
if err != nil {
113
- return nil , err
126
+ return withErr ( err )
114
127
}
115
128
116
129
req = req .WithContext (ctx )
@@ -124,17 +137,21 @@ func do(ctx context.Context, method string, url string, confPieces ...ConfigPiec
124
137
configure .AfterInit (conf )
125
138
}
126
139
140
+ return conf
141
+ }
142
+
143
+ func (conf * Config ) Send () (* http.Response , error ) {
127
144
if conf .Abort != nil {
128
145
return nil , conf .Abort
129
146
}
130
147
131
- resp , err := conf .Client .Do (req )
148
+ resp , err := conf .Client .Do (conf . Request )
132
149
if err != nil {
133
150
return resp , err // this is a transport-level error
134
151
}
135
152
136
153
// 304 is an error unless caller is expecting such response by sending caching headers
137
- if resp .StatusCode == http .StatusNotModified && req .Header .Get ("If-None-Match" ) != "" {
154
+ if resp .StatusCode == http .StatusNotModified && conf . Request .Header .Get ("If-None-Match" ) != "" {
138
155
return resp , nil
139
156
}
140
157
0 commit comments