gitee.com/h79/goutils@v1.22.10/common/http/http.go (about) 1 package http 2 3 import ( 4 "bytes" 5 "context" 6 "flag" 7 "fmt" 8 "gitee.com/h79/goutils/common/json" 9 "gitee.com/h79/goutils/common/logger" 10 "io" 11 "mime/multipart" 12 "net/http" 13 "net/http/httptrace" 14 "net/url" 15 "strconv" 16 "strings" 17 "sync/atomic" 18 "time" 19 ) 20 21 const ( 22 kTimeOut = time.Second * 10 23 KContentType = "Content-Type" 24 KContentDisposition = "Content-Disposition" 25 KContentLength = "Content-Length" 26 ) 27 28 type HeaderFunc func(h *http.Header) 29 30 type RequestOption func(req *http.Request) (*http.Request, error) 31 32 func Method(m string) RequestOption { 33 return func(req *http.Request) (*http.Request, error) { 34 req.Method = strings.ToUpper(m) 35 return req, nil 36 } 37 } 38 39 var ( 40 HEAD = Method("HEAD") 41 GET = Method("GET") 42 POST = Method("POST") 43 PUT = Method("PUT") 44 DELETE = Method("DELETE") 45 ) 46 47 func URL(u *url.URL) RequestOption { 48 return func(req *http.Request) (*http.Request, error) { 49 req.URL = u 50 return req, nil 51 } 52 } 53 54 func URLString(rawUrl string) RequestOption { 55 return func(req *http.Request) (*http.Request, error) { 56 u, err := url.Parse(rawUrl) 57 if err != nil { 58 return nil, err 59 } 60 req.URL = u 61 return req, nil 62 } 63 } 64 65 func Path(path string) RequestOption { 66 return func(req *http.Request) (*http.Request, error) { 67 if req.URL != nil { 68 u, err := req.URL.Parse(path) 69 if err != nil { 70 return nil, err 71 } 72 req.URL = u 73 return req, nil 74 } 75 u, err := url.Parse(path) 76 if err != nil { 77 return nil, err 78 } 79 req.URL = u 80 return req, nil 81 } 82 } 83 84 func Params(params map[string]interface{}) RequestOption { 85 return func(req *http.Request) (*http.Request, error) { 86 u := req.URL.String() 87 req.URL = URLWithParams(u, params) 88 return req, nil 89 } 90 } 91 92 func StringParams(params map[string]string) RequestOption { 93 return func(req *http.Request) (*http.Request, error) { 94 q := req.URL.Query() 95 for k, v := range params { 96 q.Set(k, v) 97 } 98 req.URL.RawQuery = q.Encode() 99 return req, nil 100 } 101 } 102 103 func Body(r io.Reader) RequestOption { 104 return func(req *http.Request) (*http.Request, error) { 105 if r == nil { 106 req.Body = http.NoBody 107 req.ContentLength = 0 108 return req, nil 109 } 110 111 if rc, ok := r.(io.ReadCloser); ok { 112 req.Body = rc 113 } else { 114 req.Body = io.NopCloser(r) 115 } 116 117 if v, ok := r.(interface{ Len() int }); ok { 118 req.ContentLength = int64(v.Len()) 119 } else if v, ok := r.(interface{ Size() int64 }); ok { 120 req.ContentLength = v.Size() 121 } 122 123 return req, nil 124 } 125 } 126 127 func JsonBody(body interface{}) RequestOption { 128 return func(req *http.Request) (*http.Request, error) { 129 b, err := json.DumpBytes(body) 130 if err != nil { 131 return nil, err 132 } 133 req.Body = io.NopCloser(bytes.NewBuffer(b)) 134 req.ContentLength = int64(len(b)) 135 req.Header.Set(KContentType, "application/json; charset=utf-8") 136 return req, nil 137 } 138 } 139 140 func FormBody(params map[string]interface{}) RequestOption { 141 return func(req *http.Request) (*http.Request, error) { 142 u := WithParams(params) 143 r := strings.NewReader(u.Encode()) 144 req.Body = io.NopCloser(r) 145 req.ContentLength = int64(r.Len()) 146 req.Header.Set(KContentType, MimeForm) 147 return req, nil 148 } 149 } 150 151 func Accept(ct string) RequestOption { 152 return func(req *http.Request) (*http.Request, error) { 153 req.Header.Set("Accept", ct) 154 return req, nil 155 } 156 } 157 158 func ContentType(ct string) RequestOption { 159 return func(req *http.Request) (*http.Request, error) { 160 req.Header.Set(KContentType, ct) 161 return req, nil 162 } 163 } 164 165 func ContentLength(l int64) RequestOption { 166 return func(req *http.Request) (*http.Request, error) { 167 if l >= 0 { 168 req.ContentLength = l 169 } 170 return req, nil 171 } 172 } 173 174 func Header(headers map[string]string) RequestOption { 175 return func(req *http.Request) (*http.Request, error) { 176 for k, v := range headers { 177 if strings.ToLower(k) == KContentLength { 178 if l, err := strconv.Atoi(v); err == nil && req.ContentLength <= 0 { 179 req.ContentLength = int64(l) 180 } 181 } else if v == "" { 182 req.Header.Del(k) 183 } else { 184 req.Header.Set(k, v) 185 } 186 } 187 188 return req, nil 189 } 190 } 191 192 func Context(ctx context.Context) RequestOption { 193 return func(req *http.Request) (*http.Request, error) { 194 return req.WithContext(ctx), nil 195 } 196 } 197 198 func Trace(tracer *httptrace.ClientTrace) RequestOption { 199 return func(req *http.Request) (*http.Request, error) { 200 return req.WithContext(httptrace.WithClientTrace(req.Context(), tracer)), nil 201 } 202 } 203 204 // Response 请求回应 205 type Response struct { 206 StatusCode int 207 err error 208 contentDisposition string 209 contentType string 210 contentLength int64 211 body []byte 212 } 213 214 func (res *Response) GetContentDisposition() string { 215 return res.contentDisposition 216 } 217 218 func (res *Response) IsFile() bool { 219 return strings.HasPrefix(res.contentDisposition, "attachment;") 220 } 221 222 func (res *Response) FileName() string { 223 filename := strings.TrimPrefix(res.contentDisposition, "attachment;") 224 filename = strings.TrimPrefix(filename, " ") 225 if strings.HasPrefix(filename, "filename=") { 226 filename = strings.TrimPrefix(filename, "filename=") 227 } 228 filename = strings.TrimPrefix(filename, "\"") 229 filename = strings.TrimSuffix(filename, "\"") 230 f := strings.Split(filename, "/") 231 if len(f) > 0 { 232 filename = f[len(f)-1] 233 } 234 if filename == "*" { 235 filename = "" 236 } 237 return filename 238 } 239 func (res *Response) Error() error { 240 return res.err 241 } 242 243 func (res *Response) GetContentLength() int64 { 244 return res.contentLength 245 } 246 247 func (res *Response) GetContentType() string { 248 return res.contentType 249 } 250 251 func (res *Response) GetBody() []byte { 252 return res.body 253 } 254 255 func (res *Response) Reset() { 256 res.err = nil 257 res.StatusCode = 0 258 res.contentLength = 0 259 res.contentType = "" 260 res.contentDisposition = "" 261 res.body = []byte{} 262 } 263 264 var EnableLog bool = true 265 266 func init() { 267 flag.BoolVar(&EnableLog, "httpLog", true, "http log") 268 } 269 270 type NewHttpClientFunc func(h *Http, timeout time.Duration) *http.Client 271 272 var defaultNewHttpClient = func(h *Http, timeout time.Duration) *http.Client { 273 return &http.Client{ 274 Timeout: timeout, 275 } 276 } 277 278 //Http 279 280 type Http struct { 281 DisableLog bool 282 SeqNo string 283 TimeOut time.Duration 284 newClient NewHttpClientFunc 285 response Response 286 } 287 288 func (hp *Http) SetNewHttpClient(fn NewHttpClientFunc) { 289 hp.newClient = fn 290 } 291 292 func (hp *Http) LogEnabled() bool { 293 return !hp.DisableLog && EnableLog 294 } 295 296 func (hp *Http) GetResponse() *Response { 297 return &hp.response 298 } 299 300 func (hp *Http) WithTimeout(duration time.Duration) *Http { 301 hp.TimeOut = duration 302 return hp 303 } 304 305 func (hp *Http) DoString(method string, url string, buf []byte, reqOpts ...RequestOption) (string, error) { 306 ret, err := hp.DoBytes(method, url, buf, reqOpts...) 307 if err != nil { 308 return "", err 309 } 310 return string(ret), nil 311 } 312 313 func (hp *Http) DoBytes(method string, url string, buf []byte, reqOpts ...RequestOption) ([]byte, error) { 314 return hp.DoWithHead(method, url, buf, nil, reqOpts...) 315 } 316 317 func (hp *Http) DoWithHead(method string, url string, buf []byte, headerFunc HeaderFunc, opts ...RequestOption) ([]byte, error) { 318 body, err := hp.DoV2(method, url, bytes.NewBuffer(buf), func(h *http.Header) { 319 h.Set(KContentType, MimeJSONUtf8) 320 if headerFunc != nil { 321 headerFunc(h) 322 } 323 }, func(in bool, method string, url string, seqNo string, no int64, res *Response) { 324 if in { 325 logger.N("Http", "REQ method= '%s', url= '%s', seqNo= '%s', no= '%d', body= '%s'", method, url, seqNo, no, logger.Byte2(logger.NDebugLevel, buf)) 326 return 327 } 328 logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err) 329 }, opts...) 330 if err != nil { 331 return nil, err 332 } 333 return body, nil 334 } 335 336 func (hp *Http) DoMultiForm(url string, reqOpts ...RequestOption) ([]byte, error) { 337 sp := strings.SplitN(url, "?", 2) 338 if len(sp) <= 1 { 339 return hp.DoFormWith(sp[0], "") 340 } 341 return hp.DoFormWith(sp[0], sp[1], reqOpts...) 342 } 343 344 func (hp *Http) DoMultiFormWith(rawUrl, query string, reqOpts ...RequestOption) ([]byte, error) { 345 val, er := url.ParseQuery(query) 346 if er != nil { 347 return nil, er 348 } 349 return hp.DoMultiFormWithValues(rawUrl, val, reqOpts...) 350 } 351 352 func (hp *Http) DoMultiFormWithValues(url string, form url.Values, opts ...RequestOption) ([]byte, error) { 353 body := new(bytes.Buffer) 354 w := multipart.NewWriter(body) 355 for k, v := range form { 356 _ = w.WriteField(k, v[0]) 357 } 358 _ = w.Close() 359 return hp.DoV2("POST", url, body, func(h *http.Header) { 360 h.Set(KContentType, w.FormDataContentType()) 361 }, func(in bool, method string, url string, seqNo string, no int64, res *Response) { 362 if in { 363 logger.N("Http", "REQ method= '%s', url= '%s', seqNo= '%s', no= '%d', body= '%s'", method, url, seqNo, no, logger.Byte2(logger.NDebugLevel, body.Bytes())) 364 return 365 } 366 logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err) 367 }, opts...) 368 } 369 370 // DoForm 371 // url http://api.xxx.com/ssddd?key=ddd&ddd=sfsf 372 func (hp *Http) DoForm(url string, reqOpts ...RequestOption) ([]byte, error) { 373 sp := strings.SplitN(url, "?", 2) 374 if len(sp) <= 1 { 375 return hp.DoFormWith(sp[0], "") 376 } 377 return hp.DoFormWith(sp[0], sp[1], reqOpts...) 378 } 379 380 // DoFormWith 381 // query ssddd=sdsfs&sfssfs=sfsssfs&sfsfsfs& 382 func (hp *Http) DoFormWith(rawUrl, query string, reqOpts ...RequestOption) ([]byte, error) { 383 val, er := url.ParseQuery(query) 384 if er != nil { 385 return nil, er 386 } 387 return hp.DoFormWithValues(rawUrl, val, reqOpts...) 388 } 389 390 func (hp *Http) DoFormWithValues(url string, values url.Values, opts ...RequestOption) ([]byte, error) { 391 body := values.Encode() 392 return hp.DoV2("POST", url, strings.NewReader(body), func(h *http.Header) { 393 h.Set(KContentType, MimeForm) 394 }, func(in bool, method string, url string, seqNo string, no int64, res *Response) { 395 if in { 396 logger.N("Http", "REQ method= '%s', url= '%s', seqNo= '%s', no= '%d', body= '%s'", method, url, seqNo, no, body) 397 return 398 } 399 logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err) 400 }, opts...) 401 } 402 403 var httpNo = int64(0) 404 405 func (hp *Http) Do(method string, url string, body io.Reader, headerFunc HeaderFunc, opts ...RequestOption) ([]byte, error) { 406 return hp.DoV2(method, url, body, headerFunc, DefaultLog, opts...) 407 } 408 409 type LogFunc func(in bool, method string, url string, seqNo string, no int64, res *Response) 410 411 func DefaultLog(in bool, method string, url string, seqNo string, no int64, res *Response) { 412 if in { 413 logger.N("Http", "REQ method= '%s',url= '%s', seqNo= '%s', no= '%d'", method, url, seqNo, no) 414 return 415 } 416 logger.N("Http", "RESP status= %d, seqNo= '%s', no= '%d', body= '%s', err= '%v'", res.StatusCode, seqNo, no, logger.Byte2(logger.NDebugLevel, res.body), res.err) 417 } 418 419 func (hp *Http) DoV2(method string, url string, body io.Reader, headerFunc HeaderFunc, logger LogFunc, opts ...RequestOption) ([]byte, error) { 420 var no = httpNo 421 if hp.LogEnabled() && logger != nil { 422 no = atomic.AddInt64(&httpNo, 1) 423 logger(true, method, url, hp.SeqNo, no, nil) 424 } 425 hp.response.Reset() 426 hp.response.StatusCode = 200 427 // Now that you have a form, you can submit it to your handler. 428 req, err := http.NewRequest(method, url, body) 429 if err != nil { 430 fmt.Printf("Http:Do url= '%s', err= '%s',", url, err) 431 return nil, err 432 } 433 if headerFunc != nil { 434 headerFunc(&req.Header) 435 } 436 for i := range opts { 437 req, err = opts[i](req) 438 if err != nil { 439 return nil, err 440 } 441 } 442 timeout := kTimeOut 443 if hp.TimeOut > 0 { 444 timeout = time.Second * hp.TimeOut 445 } 446 var fn = hp.newClient 447 if fn == nil { 448 fn = defaultNewHttpClient 449 } 450 // Submit the request 451 client := fn(hp, timeout) 452 res, err := client.Do(req) 453 if err != nil { 454 fmt.Printf("Http:Do url= '%s', err= '%s',", url, err) 455 return nil, err 456 } 457 defer res.Body.Close() 458 459 hp.response.StatusCode = res.StatusCode 460 hp.response.contentDisposition = res.Header.Get(KContentDisposition) 461 hp.response.contentType = res.Header.Get(KContentType) 462 hp.response.contentLength = res.ContentLength 463 464 hp.response.body, hp.response.err = io.ReadAll(res.Body) 465 if hp.LogEnabled() && logger != nil { 466 logger(false, method, url, hp.SeqNo, no, &hp.response) 467 } 468 return hp.response.body, err 469 }