github.com/cloudwego/hertz@v0.9.3/pkg/protocol/request.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package protocol 43 44 import ( 45 "bytes" 46 "compress/gzip" 47 "encoding/base64" 48 "fmt" 49 "io" 50 "mime/multipart" 51 "net/url" 52 "strings" 53 "sync" 54 55 "github.com/cloudwego/hertz/internal/bytesconv" 56 "github.com/cloudwego/hertz/internal/bytestr" 57 "github.com/cloudwego/hertz/internal/nocopy" 58 "github.com/cloudwego/hertz/pkg/common/bytebufferpool" 59 "github.com/cloudwego/hertz/pkg/common/compress" 60 "github.com/cloudwego/hertz/pkg/common/config" 61 "github.com/cloudwego/hertz/pkg/common/errors" 62 "github.com/cloudwego/hertz/pkg/common/utils" 63 "github.com/cloudwego/hertz/pkg/network" 64 "github.com/cloudwego/hertz/pkg/protocol/consts" 65 ) 66 67 var ( 68 ErrMissingFile = errors.NewPublic("http: no such file") 69 70 responseBodyPool bytebufferpool.Pool 71 requestBodyPool bytebufferpool.Pool 72 73 requestPool sync.Pool 74 ) 75 76 // NoBody is an io.ReadCloser with no bytes. Read always returns EOF 77 // and Close always returns nil. It can be used in an outgoing client 78 // request to explicitly signal that a request has zero bytes. 79 var NoBody = noBody{} 80 81 type noBody struct{} 82 83 func (noBody) Read([]byte) (int, error) { return 0, io.EOF } 84 func (noBody) Close() error { return nil } 85 86 type Request struct { 87 noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used 88 89 Header RequestHeader 90 91 uri URI 92 postArgs Args 93 94 bodyStream io.Reader 95 w requestBodyWriter 96 body *bytebufferpool.ByteBuffer 97 bodyRaw []byte 98 maxKeepBodySize int 99 100 multipartForm *multipart.Form 101 multipartFormBoundary string 102 103 // Group bool members in order to reduce Request object size. 104 parsedURI bool 105 parsedPostArgs bool 106 107 isTLS bool 108 109 multipartFiles []*File 110 multipartFields []*MultipartField 111 112 // Request level options, service discovery options etc. 113 options *config.RequestOptions 114 } 115 116 type requestBodyWriter struct { 117 r *Request 118 } 119 120 // File struct represent file information for multipart request 121 type File struct { 122 Name string 123 ParamName string 124 io.Reader 125 } 126 127 // MultipartField struct represent custom data part for multipart request 128 type MultipartField struct { 129 Param string 130 FileName string 131 ContentType string 132 io.Reader 133 } 134 135 func (w *requestBodyWriter) Write(p []byte) (int, error) { 136 w.r.AppendBody(p) 137 return len(p), nil 138 } 139 140 func (req *Request) Options() *config.RequestOptions { 141 if req.options == nil { 142 req.options = config.NewRequestOptions(nil) 143 } 144 return req.options 145 } 146 147 // AppendBody appends p to request body. 148 // 149 // It is safe re-using p after the function returns. 150 func (req *Request) AppendBody(p []byte) { 151 req.RemoveMultipartFormFiles() 152 req.CloseBodyStream() //nolint:errcheck 153 req.BodyBuffer().Write(p) //nolint:errcheck 154 } 155 156 func (req *Request) BodyBuffer() *bytebufferpool.ByteBuffer { 157 if req.body == nil { 158 req.body = requestBodyPool.Get() 159 } 160 req.bodyRaw = nil 161 return req.body 162 } 163 164 // MayContinue returns true if the request contains 165 // 'Expect: 100-continue' header. 166 // 167 // The caller must do one of the following actions if MayContinue returns true: 168 // 169 // - Either send StatusExpectationFailed response if request headers don't 170 // satisfy the caller. 171 // - Or send StatusContinue response before reading request body 172 // with ContinueReadBody. 173 // - Or close the connection. 174 func (req *Request) MayContinue() bool { 175 return bytes.Equal(req.Header.peek(bytestr.StrExpect), bytestr.Str100Continue) 176 } 177 178 // Scheme returns the scheme of the request. 179 // uri will be parsed in ServeHTTP(before user's process), so that there is no need for uri nil-check. 180 func (req *Request) Scheme() []byte { 181 return req.uri.Scheme() 182 } 183 184 // For keepalive connection reuse. 185 // It is roughly the same as ResetSkipHeader, except that the connection related fields are removed: 186 // - req.isTLS 187 func (req *Request) resetSkipHeaderAndConn() { 188 req.ResetBody() 189 req.uri.Reset() 190 req.parsedURI = false 191 req.parsedPostArgs = false 192 req.postArgs.Reset() 193 } 194 195 func (req *Request) ResetSkipHeader() { 196 req.resetSkipHeaderAndConn() 197 req.isTLS = false 198 } 199 200 func SwapRequestBody(a, b *Request) { 201 a.body, b.body = b.body, a.body 202 a.bodyRaw, b.bodyRaw = b.bodyRaw, a.bodyRaw 203 a.bodyStream, b.bodyStream = b.bodyStream, a.bodyStream 204 a.multipartFields, b.multipartFields = b.multipartFields, a.multipartFields 205 a.multipartFiles, b.multipartFiles = b.multipartFiles, a.multipartFiles 206 } 207 208 // Reset clears request contents. 209 func (req *Request) Reset() { 210 req.Header.Reset() 211 req.ResetSkipHeader() 212 req.CloseBodyStream() 213 214 req.options = nil 215 } 216 217 func (req *Request) IsURIParsed() bool { 218 return req.parsedURI 219 } 220 221 func (req *Request) PostArgString() []byte { 222 return req.postArgs.QueryString() 223 } 224 225 // MultipartForm returns request's multipart form. 226 // 227 // Returns errors.ErrNoMultipartForm if request's Content-Type 228 // isn't 'multipart/form-data'. 229 // 230 // RemoveMultipartFormFiles must be called after returned multipart form 231 // is processed. 232 func (req *Request) MultipartForm() (*multipart.Form, error) { 233 if req.multipartForm != nil { 234 return req.multipartForm, nil 235 } 236 req.multipartFormBoundary = string(req.Header.MultipartFormBoundary()) 237 if len(req.multipartFormBoundary) == 0 { 238 return nil, errors.ErrNoMultipartForm 239 } 240 241 ce := req.Header.peek(bytestr.StrContentEncoding) 242 var err error 243 var f *multipart.Form 244 245 if !req.IsBodyStream() { 246 body := req.BodyBytes() 247 if bytes.Equal(ce, bytestr.StrGzip) { 248 // Do not care about memory usage here. 249 var err error 250 if body, err = compress.AppendGunzipBytes(nil, body); err != nil { 251 return nil, fmt.Errorf("cannot gunzip request body: %s", err) 252 } 253 } else if len(ce) > 0 { 254 return nil, fmt.Errorf("unsupported Content-Encoding: %q", ce) 255 } 256 f, err = ReadMultipartForm(bytes.NewReader(body), req.multipartFormBoundary, len(body), len(body)) 257 } else { 258 bodyStream := req.bodyStream 259 if req.Header.contentLength > 0 { 260 bodyStream = io.LimitReader(bodyStream, int64(req.Header.contentLength)) 261 } 262 if bytes.Equal(ce, bytestr.StrGzip) { 263 // Do not care about memory usage here. 264 if bodyStream, err = gzip.NewReader(bodyStream); err != nil { 265 return nil, fmt.Errorf("cannot gunzip request body: %w", err) 266 } 267 } else if len(ce) > 0 { 268 return nil, fmt.Errorf("unsupported Content-Encoding: %q", ce) 269 } 270 271 mr := multipart.NewReader(bodyStream, req.multipartFormBoundary) 272 273 f, err = mr.ReadForm(8 * 1024) 274 } 275 276 if err != nil { 277 return nil, err 278 } 279 req.multipartForm = f 280 return f, nil 281 } 282 283 // AppendBodyString appends s to request body. 284 func (req *Request) AppendBodyString(s string) { 285 req.RemoveMultipartFormFiles() 286 req.CloseBodyStream() //nolint:errcheck 287 req.BodyBuffer().WriteString(s) //nolint:errcheck 288 } 289 290 // SetRequestURI sets RequestURI. 291 func (req *Request) SetRequestURI(requestURI string) { 292 req.Header.SetRequestURI(requestURI) 293 req.parsedURI = false 294 } 295 296 func (req *Request) SetMaxKeepBodySize(n int) { 297 req.maxKeepBodySize = n 298 } 299 300 // RequestURI returns the RequestURI for the given request. 301 func (req *Request) RequestURI() []byte { 302 return req.Header.RequestURI() 303 } 304 305 // FormFile returns the first file for the provided form key. 306 func (req *Request) FormFile(name string) (*multipart.FileHeader, error) { 307 mf, err := req.MultipartForm() 308 if err != nil { 309 return nil, err 310 } 311 if mf.File == nil { 312 return nil, err 313 } 314 fhh := mf.File[name] 315 if fhh == nil { 316 return nil, ErrMissingFile 317 } 318 return fhh[0], nil 319 } 320 321 // SetHost sets host for the request. 322 func (req *Request) SetHost(host string) { 323 req.URI().SetHost(host) 324 } 325 326 // Host returns the host for the given request. 327 func (req *Request) Host() []byte { 328 return req.URI().Host() 329 } 330 331 // SetIsTLS is used by TLS server to mark whether the request is a TLS request. 332 // Client shouldn't use this method but should depend on the uri.scheme instead. 333 func (req *Request) SetIsTLS(isTLS bool) { 334 req.isTLS = isTLS 335 } 336 337 // SwapBody swaps request body with the given body and returns 338 // the previous request body. 339 // 340 // It is forbidden to use the body passed to SwapBody after 341 // the function returns. 342 func (req *Request) SwapBody(body []byte) []byte { 343 bb := req.BodyBuffer() 344 zw := network.NewWriter(bb) 345 346 if req.IsBodyStream() { 347 bb.Reset() 348 _, err := utils.CopyZeroAlloc(zw, req.bodyStream) 349 req.CloseBodyStream() //nolint:errcheck 350 if err != nil { 351 bb.Reset() 352 bb.SetString(err.Error()) 353 } 354 } 355 356 req.bodyRaw = nil 357 358 oldBody := bb.B 359 bb.B = body 360 return oldBody 361 } 362 363 // CopyTo copies req contents to dst except of body stream. 364 func (req *Request) CopyTo(dst *Request) { 365 req.CopyToSkipBody(dst) 366 if req.bodyRaw != nil { 367 dst.bodyRaw = append(dst.bodyRaw[:0], req.bodyRaw...) 368 if dst.body != nil { 369 dst.body.Reset() 370 } 371 } else if req.body != nil { 372 dst.BodyBuffer().Set(req.body.B) 373 } else if dst.body != nil { 374 dst.body.Reset() 375 } 376 } 377 378 func (req *Request) CopyToSkipBody(dst *Request) { 379 dst.Reset() 380 req.Header.CopyTo(&dst.Header) 381 382 req.uri.CopyTo(&dst.uri) 383 dst.parsedURI = req.parsedURI 384 385 req.postArgs.CopyTo(&dst.postArgs) 386 dst.parsedPostArgs = req.parsedPostArgs 387 dst.isTLS = req.isTLS 388 389 if req.options != nil { 390 dst.options = &config.RequestOptions{} 391 req.options.CopyTo(dst.options) 392 } 393 394 // do not copy multipartForm - it will be automatically 395 // re-created on the first call to MultipartForm. 396 } 397 398 func (req *Request) BodyBytes() []byte { 399 if req.bodyRaw != nil { 400 return req.bodyRaw 401 } 402 if req.body == nil { 403 return nil 404 } 405 return req.body.B 406 } 407 408 // ResetBody resets request body. 409 func (req *Request) ResetBody() { 410 req.bodyRaw = nil 411 req.RemoveMultipartFormFiles() 412 req.CloseBodyStream() //nolint:errcheck 413 if req.body != nil { 414 if req.body.Len() <= req.maxKeepBodySize { 415 req.body.Reset() 416 return 417 } 418 requestBodyPool.Put(req.body) 419 req.body = nil 420 } 421 } 422 423 // SetBodyRaw sets request body, but without copying it. 424 // 425 // From this point onward the body argument must not be changed. 426 func (req *Request) SetBodyRaw(body []byte) { 427 req.ResetBody() 428 req.bodyRaw = body 429 } 430 431 // SetMultipartFormBoundary will set the multipart form boundary for the request. 432 func (req *Request) SetMultipartFormBoundary(b string) { 433 req.multipartFormBoundary = b 434 } 435 436 func (req *Request) MultipartFormBoundary() string { 437 return req.multipartFormBoundary 438 } 439 440 // SetBody sets request body. 441 // 442 // It is safe re-using body argument after the function returns. 443 func (req *Request) SetBody(body []byte) { 444 req.RemoveMultipartFormFiles() 445 req.CloseBodyStream() //nolint:errcheck 446 req.BodyBuffer().Set(body) 447 } 448 449 // SetBodyString sets request body. 450 func (req *Request) SetBodyString(body string) { 451 req.RemoveMultipartFormFiles() 452 req.CloseBodyStream() //nolint:errcheck 453 req.BodyBuffer().SetString(body) 454 } 455 456 // SetQueryString sets query string. 457 func (req *Request) SetQueryString(queryString string) { 458 req.URI().SetQueryString(queryString) 459 } 460 461 // SetFormData sets x-www-form-urlencoded params 462 func (req *Request) SetFormData(data map[string]string) { 463 for k, v := range data { 464 req.postArgs.Add(k, v) 465 } 466 req.parsedPostArgs = true 467 req.Header.SetContentTypeBytes(bytestr.StrPostArgsContentType) 468 } 469 470 // SetFormDataFromValues sets x-www-form-urlencoded params from url values. 471 func (req *Request) SetFormDataFromValues(data url.Values) { 472 for k, v := range data { 473 for _, kv := range v { 474 req.postArgs.Add(k, kv) 475 } 476 } 477 req.parsedPostArgs = true 478 req.Header.SetContentTypeBytes(bytestr.StrPostArgsContentType) 479 } 480 481 // SetFile sets single file field name and its path for multipart upload. 482 func (req *Request) SetFile(param, filePath string) { 483 req.multipartFiles = append(req.multipartFiles, &File{ 484 Name: filePath, 485 ParamName: param, 486 }) 487 } 488 489 // SetFiles sets multiple file field name and its path for multipart upload. 490 func (req *Request) SetFiles(files map[string]string) { 491 for f, fp := range files { 492 req.multipartFiles = append(req.multipartFiles, &File{ 493 Name: fp, 494 ParamName: f, 495 }) 496 } 497 } 498 499 // SetFileReader sets single file using io.Reader for multipart upload. 500 func (req *Request) SetFileReader(param, fileName string, reader io.Reader) { 501 req.multipartFiles = append(req.multipartFiles, &File{ 502 Name: fileName, 503 ParamName: param, 504 Reader: reader, 505 }) 506 } 507 508 // SetMultipartFormData method allows simple form data to be attached to the request as `multipart:form-data` 509 func (req *Request) SetMultipartFormData(data map[string]string) { 510 for k, v := range data { 511 req.SetMultipartField(k, "", "", strings.NewReader(v)) 512 } 513 } 514 515 func (req *Request) MultipartFiles() []*File { 516 return req.multipartFiles 517 } 518 519 // SetMultipartField sets custom data using io.Reader for multipart upload. 520 func (req *Request) SetMultipartField(param, fileName, contentType string, reader io.Reader) { 521 req.multipartFields = append(req.multipartFields, &MultipartField{ 522 Param: param, 523 FileName: fileName, 524 ContentType: contentType, 525 Reader: reader, 526 }) 527 } 528 529 // SetMultipartFields sets multiple data fields using io.Reader for multipart upload. 530 func (req *Request) SetMultipartFields(fields ...*MultipartField) { 531 req.multipartFields = append(req.multipartFields, fields...) 532 } 533 534 func (req *Request) MultipartFields() []*MultipartField { 535 return req.multipartFields 536 } 537 538 // SetBasicAuth sets the basic authentication header in the current HTTP request. 539 func (req *Request) SetBasicAuth(username, password string) { 540 encodeStr := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 541 req.SetHeader(consts.HeaderAuthorization, "Basic "+encodeStr) 542 } 543 544 // BasicAuth can return the username and password in the request's Authorization 545 // header, if the request uses the HTTP Basic Authorization. 546 func (req *Request) BasicAuth() (username, password string, ok bool) { 547 // Using Peek to reduce the cost for type transfer. 548 auth := req.Header.Peek(consts.HeaderAuthorization) 549 if auth == nil { 550 return 551 } 552 553 return parseBasicAuth(auth) 554 } 555 556 var prefix = []byte{'B', 'a', 's', 'i', 'c', ' '} 557 558 // parseBasicAuth can parse an HTTP Basic Authorization string encrypted by base64. 559 // Example: "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true). 560 func parseBasicAuth(auth []byte) (username, password string, ok bool) { 561 if len(auth) < len(prefix) || !bytes.EqualFold(auth[:len(prefix)], prefix) { 562 return 563 } 564 565 decodeLen := base64.StdEncoding.DecodedLen(len(auth[len(prefix):])) 566 // base64.StdEncoding.Decode(dst,rsc []byte) will return less than DecodedLen(len(src))) 567 decodeData := make([]byte, decodeLen) 568 num, err := base64.StdEncoding.Decode(decodeData, auth[len(prefix):]) 569 if err != nil { 570 return 571 } 572 573 cs := bytesconv.B2s(decodeData[:num]) 574 s := strings.IndexByte(cs, ':') 575 576 if s < 0 { 577 return 578 } 579 580 return cs[:s], cs[s+1:], true 581 } 582 583 // SetAuthToken sets the auth token header(Default Scheme: Bearer) in the current HTTP request. Header example: 584 // 585 // Authorization: Bearer <auth-token-value-comes-here> 586 func (req *Request) SetAuthToken(token string) { 587 req.SetHeader(consts.HeaderAuthorization, "Bearer "+token) 588 } 589 590 // SetAuthSchemeToken sets the auth token scheme type in the HTTP request. For Example: 591 // 592 // Authorization: <auth-scheme-value-set-here> <auth-token-value> 593 func (req *Request) SetAuthSchemeToken(scheme, token string) { 594 req.SetHeader(consts.HeaderAuthorization, scheme+" "+token) 595 } 596 597 // SetHeader sets a single header field and its value in the current request. 598 func (req *Request) SetHeader(header, value string) { 599 req.Header.Set(header, value) 600 } 601 602 // SetHeaders sets multiple header field and its value in the current request. 603 func (req *Request) SetHeaders(headers map[string]string) { 604 for h, v := range headers { 605 req.Header.Set(h, v) 606 } 607 } 608 609 // SetCookie appends a single cookie in the current request instance. 610 func (req *Request) SetCookie(key, value string) { 611 req.Header.SetCookie(key, value) 612 } 613 614 // SetCookies sets an array of cookies in the current request instance. 615 func (req *Request) SetCookies(hc map[string]string) { 616 for k, v := range hc { 617 req.Header.SetCookie(k, v) 618 } 619 } 620 621 // SetMethod sets http method for this request. 622 func (req *Request) SetMethod(method string) { 623 req.Header.SetMethod(method) 624 } 625 626 func (req *Request) OnlyMultipartForm() bool { 627 return req.multipartForm != nil && (req.body == nil || len(req.body.B) == 0) 628 } 629 630 func (req *Request) HasMultipartForm() bool { 631 return req.multipartForm != nil 632 } 633 634 // IsBodyStream returns true if body is set via SetBodyStream* 635 func (req *Request) IsBodyStream() bool { 636 return req.bodyStream != nil && req.bodyStream != NoBody 637 } 638 639 func (req *Request) BodyStream() io.Reader { 640 if req.bodyStream == nil { 641 req.bodyStream = NoBody 642 } 643 return req.bodyStream 644 } 645 646 // SetBodyStream sets request body stream and, optionally body size. 647 // 648 // If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes 649 // before returning io.EOF. 650 // 651 // If bodySize < 0, then bodyStream is read until io.EOF. 652 // 653 // bodyStream.Close() is called after finishing reading all body data 654 // if it implements io.Closer. 655 // 656 // Note that GET and HEAD requests cannot have body. 657 // 658 // See also SetBodyStreamWriter. 659 func (req *Request) SetBodyStream(bodyStream io.Reader, bodySize int) { 660 req.ResetBody() 661 req.bodyStream = bodyStream 662 req.Header.SetContentLength(bodySize) 663 } 664 665 func (req *Request) ConstructBodyStream(body *bytebufferpool.ByteBuffer, bodyStream io.Reader) { 666 req.body = body 667 req.bodyStream = bodyStream 668 } 669 670 // BodyWriter returns writer for populating request body. 671 func (req *Request) BodyWriter() io.Writer { 672 req.w.r = req 673 return &req.w 674 } 675 676 // PostArgs returns POST arguments. 677 func (req *Request) PostArgs() *Args { 678 req.parsePostArgs() 679 return &req.postArgs 680 } 681 682 func (req *Request) parsePostArgs() { 683 if req.parsedPostArgs { 684 return 685 } 686 req.parsedPostArgs = true 687 688 if !bytes.HasPrefix(req.Header.ContentType(), bytestr.StrPostArgsContentType) { 689 return 690 } 691 req.postArgs.ParseBytes(req.Body()) 692 } 693 694 // BodyE returns request body. 695 func (req *Request) BodyE() ([]byte, error) { 696 if req.bodyRaw != nil { 697 return req.bodyRaw, nil 698 } 699 if req.IsBodyStream() { 700 bodyBuf := req.BodyBuffer() 701 bodyBuf.Reset() 702 zw := network.NewWriter(bodyBuf) 703 _, err := utils.CopyZeroAlloc(zw, req.bodyStream) 704 req.CloseBodyStream() //nolint:errcheck 705 if err != nil { 706 return nil, err 707 } 708 return req.BodyBytes(), nil 709 } 710 if req.OnlyMultipartForm() { 711 body, err := MarshalMultipartForm(req.multipartForm, req.multipartFormBoundary) 712 if err != nil { 713 return nil, err 714 } 715 return body, nil 716 } 717 return req.BodyBytes(), nil 718 } 719 720 // Body returns request body. 721 // if get body failed, returns nil. 722 func (req *Request) Body() []byte { 723 body, _ := req.BodyE() 724 return body 725 } 726 727 // BodyWriteTo writes request body to w. 728 func (req *Request) BodyWriteTo(w io.Writer) error { 729 if req.IsBodyStream() { 730 zw := network.NewWriter(w) 731 _, err := utils.CopyZeroAlloc(zw, req.bodyStream) 732 req.CloseBodyStream() //nolint:errcheck 733 return err 734 } 735 if req.OnlyMultipartForm() { 736 return WriteMultipartForm(w, req.multipartForm, req.multipartFormBoundary) 737 } 738 _, err := w.Write(req.BodyBytes()) 739 return err 740 } 741 742 func (req *Request) CloseBodyStream() error { 743 if req.bodyStream == nil { 744 return nil 745 } 746 747 var err error 748 if bsc, ok := req.bodyStream.(io.Closer); ok { 749 err = bsc.Close() 750 } 751 req.bodyStream = nil 752 return err 753 } 754 755 // URI returns request URI 756 func (req *Request) URI() *URI { 757 req.ParseURI() 758 return &req.uri 759 } 760 761 func (req *Request) ParseURI() { 762 if req.parsedURI { 763 return 764 } 765 req.parsedURI = true 766 767 req.uri.parse(req.Header.Host(), req.Header.RequestURI(), req.isTLS) 768 } 769 770 // RemoveMultipartFormFiles removes multipart/form-data temporary files 771 // associated with the request. 772 func (req *Request) RemoveMultipartFormFiles() { 773 if req.multipartForm != nil { 774 // Do not check for error, since these files may be deleted or moved 775 // to new places by user code. 776 req.multipartForm.RemoveAll() //nolint:errcheck 777 req.multipartForm = nil 778 } 779 req.multipartFormBoundary = "" 780 req.multipartFiles = nil 781 req.multipartFields = nil 782 } 783 784 func AddMultipartFormField(w *multipart.Writer, mf *MultipartField) error { 785 partWriter, err := w.CreatePart(CreateMultipartHeader(mf.Param, mf.FileName, mf.ContentType)) 786 if err != nil { 787 return err 788 } 789 790 _, err = io.Copy(partWriter, mf.Reader) 791 return err 792 } 793 794 // Method returns request method 795 func (req *Request) Method() []byte { 796 return req.Header.Method() 797 } 798 799 // Path returns request path 800 func (req *Request) Path() []byte { 801 return req.URI().Path() 802 } 803 804 // QueryString returns request query 805 func (req *Request) QueryString() []byte { 806 return req.URI().QueryString() 807 } 808 809 // SetOptions is used to set request options. 810 // These options can be used to do something in middlewares such as service discovery. 811 func (req *Request) SetOptions(opts ...config.RequestOption) { 812 req.Options().Apply(opts) 813 } 814 815 // ConnectionClose returns true if 'Connection: close' header is set. 816 func (req *Request) ConnectionClose() bool { 817 return req.Header.ConnectionClose() 818 } 819 820 // SetConnectionClose sets 'Connection: close' header. 821 func (req *Request) SetConnectionClose() { 822 req.Header.SetConnectionClose(true) 823 } 824 825 func (req *Request) ResetWithoutConn() { 826 req.Header.Reset() 827 req.resetSkipHeaderAndConn() 828 req.CloseBodyStream() 829 830 req.options = nil 831 } 832 833 // AcquireRequest returns an empty Request instance from request pool. 834 // 835 // The returned Request instance may be passed to ReleaseRequest when it is 836 // no longer needed. This allows Request recycling, reduces GC pressure 837 // and usually improves performance. 838 func AcquireRequest() *Request { 839 v := requestPool.Get() 840 if v == nil { 841 return &Request{} 842 } 843 return v.(*Request) 844 } 845 846 // ReleaseRequest returns req acquired via AcquireRequest to request pool. 847 // 848 // It is forbidden accessing req and/or its members after returning 849 // it to request pool. 850 func ReleaseRequest(req *Request) { 851 req.Reset() 852 requestPool.Put(req) 853 } 854 855 // NewRequest makes a new Request given a method, URL, and 856 // optional body. 857 // 858 // # Method's default value is GET 859 // 860 // Url must contain fully qualified uri, i.e. with scheme and host, 861 // and http is assumed if scheme is omitted. 862 // 863 // Protocol version is always HTTP/1.1 864 // 865 // NewRequest just uses for unit-testing. Use AcquireRequest() in other cases. 866 func NewRequest(method, url string, body io.Reader) *Request { 867 if method == "" { 868 method = consts.MethodGet 869 } 870 871 req := new(Request) 872 req.SetRequestURI(url) 873 req.SetIsTLS(bytes.HasPrefix(bytesconv.S2b(url), bytestr.StrHTTPS)) 874 req.ParseURI() 875 req.SetMethod(method) 876 req.Header.SetHost(string(req.URI().Host())) 877 req.Header.SetRequestURIBytes(req.URI().RequestURI()) 878 879 if !req.Header.IgnoreBody() { 880 req.SetBodyStream(body, -1) 881 switch v := req.BodyStream().(type) { 882 case *bytes.Buffer: 883 req.Header.SetContentLength(v.Len()) 884 case *bytes.Reader: 885 req.Header.SetContentLength(v.Len()) 886 case *strings.Reader: 887 req.Header.SetContentLength(v.Len()) 888 default: 889 } 890 } 891 892 return req 893 }