request.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. package request
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "reflect"
  9. "strings"
  10. "time"
  11. "github.com/aws/aws-sdk-go/aws"
  12. "github.com/aws/aws-sdk-go/aws/awserr"
  13. "github.com/aws/aws-sdk-go/aws/client/metadata"
  14. "github.com/aws/aws-sdk-go/internal/sdkio"
  15. )
  16. const (
  17. // ErrCodeSerialization is the serialization error code that is received
  18. // during protocol unmarshaling.
  19. ErrCodeSerialization = "SerializationError"
  20. // ErrCodeRead is an error that is returned during HTTP reads.
  21. ErrCodeRead = "ReadError"
  22. // ErrCodeResponseTimeout is the connection timeout error that is received
  23. // during body reads.
  24. ErrCodeResponseTimeout = "ResponseTimeout"
  25. // ErrCodeInvalidPresignExpire is returned when the expire time provided to
  26. // presign is invalid
  27. ErrCodeInvalidPresignExpire = "InvalidPresignExpireError"
  28. // CanceledErrorCode is the error code that will be returned by an
  29. // API request that was canceled. Requests given a aws.Context may
  30. // return this error when canceled.
  31. CanceledErrorCode = "RequestCanceled"
  32. )
  33. // A Request is the service request to be made.
  34. type Request struct {
  35. Config aws.Config
  36. ClientInfo metadata.ClientInfo
  37. Handlers Handlers
  38. Retryer
  39. AttemptTime time.Time
  40. Time time.Time
  41. Operation *Operation
  42. HTTPRequest *http.Request
  43. HTTPResponse *http.Response
  44. Body io.ReadSeeker
  45. BodyStart int64 // offset from beginning of Body that the request body starts
  46. Params interface{}
  47. Error error
  48. Data interface{}
  49. RequestID string
  50. RetryCount int
  51. Retryable *bool
  52. RetryDelay time.Duration
  53. NotHoist bool
  54. SignedHeaderVals http.Header
  55. LastSignedAt time.Time
  56. DisableFollowRedirects bool
  57. // A value greater than 0 instructs the request to be signed as Presigned URL
  58. // You should not set this field directly. Instead use Request's
  59. // Presign or PresignRequest methods.
  60. ExpireTime time.Duration
  61. context aws.Context
  62. built bool
  63. // Need to persist an intermediate body between the input Body and HTTP
  64. // request body because the HTTP Client's transport can maintain a reference
  65. // to the HTTP request's body after the client has returned. This value is
  66. // safe to use concurrently and wrap the input Body for each HTTP request.
  67. safeBody *offsetReader
  68. }
  69. // An Operation is the service API operation to be made.
  70. type Operation struct {
  71. Name string
  72. HTTPMethod string
  73. HTTPPath string
  74. *Paginator
  75. BeforePresignFn func(r *Request) error
  76. }
  77. // New returns a new Request pointer for the service API
  78. // operation and parameters.
  79. //
  80. // Params is any value of input parameters to be the request payload.
  81. // Data is pointer value to an object which the request's response
  82. // payload will be deserialized to.
  83. func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
  84. retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {
  85. method := operation.HTTPMethod
  86. if method == "" {
  87. method = "POST"
  88. }
  89. httpReq, _ := http.NewRequest(method, "", nil)
  90. var err error
  91. httpReq.URL, err = url.Parse(clientInfo.Endpoint + operation.HTTPPath)
  92. if err != nil {
  93. httpReq.URL = &url.URL{}
  94. err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
  95. }
  96. SanitizeHostForHeader(httpReq)
  97. r := &Request{
  98. Config: cfg,
  99. ClientInfo: clientInfo,
  100. Handlers: handlers.Copy(),
  101. Retryer: retryer,
  102. Time: time.Now(),
  103. ExpireTime: 0,
  104. Operation: operation,
  105. HTTPRequest: httpReq,
  106. Body: nil,
  107. Params: params,
  108. Error: err,
  109. Data: data,
  110. }
  111. r.SetBufferBody([]byte{})
  112. return r
  113. }
  114. // A Option is a functional option that can augment or modify a request when
  115. // using a WithContext API operation method.
  116. type Option func(*Request)
  117. // WithGetResponseHeader builds a request Option which will retrieve a single
  118. // header value from the HTTP Response. If there are multiple values for the
  119. // header key use WithGetResponseHeaders instead to access the http.Header
  120. // map directly. The passed in val pointer must be non-nil.
  121. //
  122. // This Option can be used multiple times with a single API operation.
  123. //
  124. // var id2, versionID string
  125. // svc.PutObjectWithContext(ctx, params,
  126. // request.WithGetResponseHeader("x-amz-id-2", &id2),
  127. // request.WithGetResponseHeader("x-amz-version-id", &versionID),
  128. // )
  129. func WithGetResponseHeader(key string, val *string) Option {
  130. return func(r *Request) {
  131. r.Handlers.Complete.PushBack(func(req *Request) {
  132. *val = req.HTTPResponse.Header.Get(key)
  133. })
  134. }
  135. }
  136. // WithGetResponseHeaders builds a request Option which will retrieve the
  137. // headers from the HTTP response and assign them to the passed in headers
  138. // variable. The passed in headers pointer must be non-nil.
  139. //
  140. // var headers http.Header
  141. // svc.PutObjectWithContext(ctx, params, request.WithGetResponseHeaders(&headers))
  142. func WithGetResponseHeaders(headers *http.Header) Option {
  143. return func(r *Request) {
  144. r.Handlers.Complete.PushBack(func(req *Request) {
  145. *headers = req.HTTPResponse.Header
  146. })
  147. }
  148. }
  149. // WithLogLevel is a request option that will set the request to use a specific
  150. // log level when the request is made.
  151. //
  152. // svc.PutObjectWithContext(ctx, params, request.WithLogLevel(aws.LogDebugWithHTTPBody)
  153. func WithLogLevel(l aws.LogLevelType) Option {
  154. return func(r *Request) {
  155. r.Config.LogLevel = aws.LogLevel(l)
  156. }
  157. }
  158. // ApplyOptions will apply each option to the request calling them in the order
  159. // the were provided.
  160. func (r *Request) ApplyOptions(opts ...Option) {
  161. for _, opt := range opts {
  162. opt(r)
  163. }
  164. }
  165. // Context will always returns a non-nil context. If Request does not have a
  166. // context aws.BackgroundContext will be returned.
  167. func (r *Request) Context() aws.Context {
  168. if r.context != nil {
  169. return r.context
  170. }
  171. return aws.BackgroundContext()
  172. }
  173. // SetContext adds a Context to the current request that can be used to cancel
  174. // a in-flight request. The Context value must not be nil, or this method will
  175. // panic.
  176. //
  177. // Unlike http.Request.WithContext, SetContext does not return a copy of the
  178. // Request. It is not safe to use use a single Request value for multiple
  179. // requests. A new Request should be created for each API operation request.
  180. //
  181. // Go 1.6 and below:
  182. // The http.Request's Cancel field will be set to the Done() value of
  183. // the context. This will overwrite the Cancel field's value.
  184. //
  185. // Go 1.7 and above:
  186. // The http.Request.WithContext will be used to set the context on the underlying
  187. // http.Request. This will create a shallow copy of the http.Request. The SDK
  188. // may create sub contexts in the future for nested requests such as retries.
  189. func (r *Request) SetContext(ctx aws.Context) {
  190. if ctx == nil {
  191. panic("context cannot be nil")
  192. }
  193. setRequestContext(r, ctx)
  194. }
  195. // WillRetry returns if the request's can be retried.
  196. func (r *Request) WillRetry() bool {
  197. if !aws.IsReaderSeekable(r.Body) && r.HTTPRequest.Body != NoBody {
  198. return false
  199. }
  200. return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
  201. }
  202. // ParamsFilled returns if the request's parameters have been populated
  203. // and the parameters are valid. False is returned if no parameters are
  204. // provided or invalid.
  205. func (r *Request) ParamsFilled() bool {
  206. return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
  207. }
  208. // DataFilled returns true if the request's data for response deserialization
  209. // target has been set and is a valid. False is returned if data is not
  210. // set, or is invalid.
  211. func (r *Request) DataFilled() bool {
  212. return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
  213. }
  214. // SetBufferBody will set the request's body bytes that will be sent to
  215. // the service API.
  216. func (r *Request) SetBufferBody(buf []byte) {
  217. r.SetReaderBody(bytes.NewReader(buf))
  218. }
  219. // SetStringBody sets the body of the request to be backed by a string.
  220. func (r *Request) SetStringBody(s string) {
  221. r.SetReaderBody(strings.NewReader(s))
  222. }
  223. // SetReaderBody will set the request's body reader.
  224. func (r *Request) SetReaderBody(reader io.ReadSeeker) {
  225. r.Body = reader
  226. r.BodyStart, _ = reader.Seek(0, sdkio.SeekCurrent) // Get the Bodies current offset.
  227. r.ResetBody()
  228. }
  229. // Presign returns the request's signed URL. Error will be returned
  230. // if the signing fails. The expire parameter is only used for presigned Amazon
  231. // S3 API requests. All other AWS services will use a fixed expiration
  232. // time of 15 minutes.
  233. //
  234. // It is invalid to create a presigned URL with a expire duration 0 or less. An
  235. // error is returned if expire duration is 0 or less.
  236. func (r *Request) Presign(expire time.Duration) (string, error) {
  237. r = r.copy()
  238. // Presign requires all headers be hoisted. There is no way to retrieve
  239. // the signed headers not hoisted without this. Making the presigned URL
  240. // useless.
  241. r.NotHoist = false
  242. u, _, err := getPresignedURL(r, expire)
  243. return u, err
  244. }
  245. // PresignRequest behaves just like presign, with the addition of returning a
  246. // set of headers that were signed. The expire parameter is only used for
  247. // presigned Amazon S3 API requests. All other AWS services will use a fixed
  248. // expiration time of 15 minutes.
  249. //
  250. // It is invalid to create a presigned URL with a expire duration 0 or less. An
  251. // error is returned if expire duration is 0 or less.
  252. //
  253. // Returns the URL string for the API operation with signature in the query string,
  254. // and the HTTP headers that were included in the signature. These headers must
  255. // be included in any HTTP request made with the presigned URL.
  256. //
  257. // To prevent hoisting any headers to the query string set NotHoist to true on
  258. // this Request value prior to calling PresignRequest.
  259. func (r *Request) PresignRequest(expire time.Duration) (string, http.Header, error) {
  260. r = r.copy()
  261. return getPresignedURL(r, expire)
  262. }
  263. // IsPresigned returns true if the request represents a presigned API url.
  264. func (r *Request) IsPresigned() bool {
  265. return r.ExpireTime != 0
  266. }
  267. func getPresignedURL(r *Request, expire time.Duration) (string, http.Header, error) {
  268. if expire <= 0 {
  269. return "", nil, awserr.New(
  270. ErrCodeInvalidPresignExpire,
  271. "presigned URL requires an expire duration greater than 0",
  272. nil,
  273. )
  274. }
  275. r.ExpireTime = expire
  276. if r.Operation.BeforePresignFn != nil {
  277. if err := r.Operation.BeforePresignFn(r); err != nil {
  278. return "", nil, err
  279. }
  280. }
  281. if err := r.Sign(); err != nil {
  282. return "", nil, err
  283. }
  284. return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
  285. }
  286. func debugLogReqError(r *Request, stage string, retrying bool, err error) {
  287. if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
  288. return
  289. }
  290. retryStr := "not retrying"
  291. if retrying {
  292. retryStr = "will retry"
  293. }
  294. r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
  295. stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
  296. }
  297. // Build will build the request's object so it can be signed and sent
  298. // to the service. Build will also validate all the request's parameters.
  299. // Any additional build Handlers set on this request will be run
  300. // in the order they were set.
  301. //
  302. // The request will only be built once. Multiple calls to build will have
  303. // no effect.
  304. //
  305. // If any Validate or Build errors occur the build will stop and the error
  306. // which occurred will be returned.
  307. func (r *Request) Build() error {
  308. if !r.built {
  309. r.Handlers.Validate.Run(r)
  310. if r.Error != nil {
  311. debugLogReqError(r, "Validate Request", false, r.Error)
  312. return r.Error
  313. }
  314. r.Handlers.Build.Run(r)
  315. if r.Error != nil {
  316. debugLogReqError(r, "Build Request", false, r.Error)
  317. return r.Error
  318. }
  319. r.built = true
  320. }
  321. return r.Error
  322. }
  323. // Sign will sign the request, returning error if errors are encountered.
  324. //
  325. // Sign will build the request prior to signing. All Sign Handlers will
  326. // be executed in the order they were set.
  327. func (r *Request) Sign() error {
  328. r.Build()
  329. if r.Error != nil {
  330. debugLogReqError(r, "Build Request", false, r.Error)
  331. return r.Error
  332. }
  333. r.Handlers.Sign.Run(r)
  334. return r.Error
  335. }
  336. func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
  337. if r.safeBody != nil {
  338. r.safeBody.Close()
  339. }
  340. r.safeBody = newOffsetReader(r.Body, r.BodyStart)
  341. // Go 1.8 tightened and clarified the rules code needs to use when building
  342. // requests with the http package. Go 1.8 removed the automatic detection
  343. // of if the Request.Body was empty, or actually had bytes in it. The SDK
  344. // always sets the Request.Body even if it is empty and should not actually
  345. // be sent. This is incorrect.
  346. //
  347. // Go 1.8 did add a http.NoBody value that the SDK can use to tell the http
  348. // client that the request really should be sent without a body. The
  349. // Request.Body cannot be set to nil, which is preferable, because the
  350. // field is exported and could introduce nil pointer dereferences for users
  351. // of the SDK if they used that field.
  352. //
  353. // Related golang/go#18257
  354. l, err := aws.SeekerLen(r.Body)
  355. if err != nil {
  356. return nil, awserr.New(ErrCodeSerialization, "failed to compute request body size", err)
  357. }
  358. var body io.ReadCloser
  359. if l == 0 {
  360. body = NoBody
  361. } else if l > 0 {
  362. body = r.safeBody
  363. } else {
  364. // Hack to prevent sending bodies for methods where the body
  365. // should be ignored by the server. Sending bodies on these
  366. // methods without an associated ContentLength will cause the
  367. // request to socket timeout because the server does not handle
  368. // Transfer-Encoding: chunked bodies for these methods.
  369. //
  370. // This would only happen if a aws.ReaderSeekerCloser was used with
  371. // a io.Reader that was not also an io.Seeker, or did not implement
  372. // Len() method.
  373. switch r.Operation.HTTPMethod {
  374. case "GET", "HEAD", "DELETE":
  375. body = NoBody
  376. default:
  377. body = r.safeBody
  378. }
  379. }
  380. return body, nil
  381. }
  382. // GetBody will return an io.ReadSeeker of the Request's underlying
  383. // input body with a concurrency safe wrapper.
  384. func (r *Request) GetBody() io.ReadSeeker {
  385. return r.safeBody
  386. }
  387. // Send will send the request, returning error if errors are encountered.
  388. //
  389. // Send will sign the request prior to sending. All Send Handlers will
  390. // be executed in the order they were set.
  391. //
  392. // Canceling a request is non-deterministic. If a request has been canceled,
  393. // then the transport will choose, randomly, one of the state channels during
  394. // reads or getting the connection.
  395. //
  396. // readLoop() and getConn(req *Request, cm connectMethod)
  397. // https://github.com/golang/go/blob/master/src/net/http/transport.go
  398. //
  399. // Send will not close the request.Request's body.
  400. func (r *Request) Send() error {
  401. defer func() {
  402. // Regardless of success or failure of the request trigger the Complete
  403. // request handlers.
  404. r.Handlers.Complete.Run(r)
  405. }()
  406. if err := r.Error; err != nil {
  407. return err
  408. }
  409. for {
  410. r.Error = nil
  411. r.AttemptTime = time.Now()
  412. if err := r.Sign(); err != nil {
  413. debugLogReqError(r, "Sign Request", false, err)
  414. return err
  415. }
  416. if err := r.sendRequest(); err == nil {
  417. return nil
  418. } else if !shouldRetryCancel(r.Error) {
  419. return err
  420. } else {
  421. r.Handlers.Retry.Run(r)
  422. r.Handlers.AfterRetry.Run(r)
  423. if r.Error != nil || !aws.BoolValue(r.Retryable) {
  424. return r.Error
  425. }
  426. r.prepareRetry()
  427. continue
  428. }
  429. }
  430. }
  431. func (r *Request) prepareRetry() {
  432. if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
  433. r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
  434. r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
  435. }
  436. // The previous http.Request will have a reference to the r.Body
  437. // and the HTTP Client's Transport may still be reading from
  438. // the request's body even though the Client's Do returned.
  439. r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, nil)
  440. r.ResetBody()
  441. // Closing response body to ensure that no response body is leaked
  442. // between retry attempts.
  443. if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
  444. r.HTTPResponse.Body.Close()
  445. }
  446. }
  447. func (r *Request) sendRequest() (sendErr error) {
  448. defer r.Handlers.CompleteAttempt.Run(r)
  449. r.Retryable = nil
  450. r.Handlers.Send.Run(r)
  451. if r.Error != nil {
  452. debugLogReqError(r, "Send Request", r.WillRetry(), r.Error)
  453. return r.Error
  454. }
  455. r.Handlers.UnmarshalMeta.Run(r)
  456. r.Handlers.ValidateResponse.Run(r)
  457. if r.Error != nil {
  458. r.Handlers.UnmarshalError.Run(r)
  459. debugLogReqError(r, "Validate Response", r.WillRetry(), r.Error)
  460. return r.Error
  461. }
  462. r.Handlers.Unmarshal.Run(r)
  463. if r.Error != nil {
  464. debugLogReqError(r, "Unmarshal Response", r.WillRetry(), r.Error)
  465. return r.Error
  466. }
  467. return nil
  468. }
  469. // copy will copy a request which will allow for local manipulation of the
  470. // request.
  471. func (r *Request) copy() *Request {
  472. req := &Request{}
  473. *req = *r
  474. req.Handlers = r.Handlers.Copy()
  475. op := *r.Operation
  476. req.Operation = &op
  477. return req
  478. }
  479. // AddToUserAgent adds the string to the end of the request's current user agent.
  480. func AddToUserAgent(r *Request, s string) {
  481. curUA := r.HTTPRequest.Header.Get("User-Agent")
  482. if len(curUA) > 0 {
  483. s = curUA + " " + s
  484. }
  485. r.HTTPRequest.Header.Set("User-Agent", s)
  486. }
  487. type temporary interface {
  488. Temporary() bool
  489. }
  490. func shouldRetryCancel(err error) bool {
  491. switch err := err.(type) {
  492. case awserr.Error:
  493. if err.Code() == CanceledErrorCode {
  494. return false
  495. }
  496. return shouldRetryCancel(err.OrigErr())
  497. case *url.Error:
  498. if strings.Contains(err.Error(), "connection refused") {
  499. // Refused connections should be retried as the service may not yet
  500. // be running on the port. Go TCP dial considers refused
  501. // connections as not temporary.
  502. return true
  503. }
  504. // *url.Error only implements Temporary after golang 1.6 but since
  505. // url.Error only wraps the error:
  506. return shouldRetryCancel(err.Err)
  507. case temporary:
  508. // If the error is temporary, we want to allow continuation of the
  509. // retry process
  510. return err.Temporary()
  511. case nil:
  512. // `awserr.Error.OrigErr()` can be nil, meaning there was an error but
  513. // because we don't know the cause, it is marked as retriable. See
  514. // TestRequest4xxUnretryable for an example.
  515. return true
  516. default:
  517. switch err.Error() {
  518. case "net/http: request canceled",
  519. "net/http: request canceled while waiting for connection":
  520. // known 1.5 error case when an http request is cancelled
  521. return false
  522. }
  523. // here we don't know the error; so we allow a retry.
  524. return true
  525. }
  526. }
  527. // SanitizeHostForHeader removes default port from host and updates request.Host
  528. func SanitizeHostForHeader(r *http.Request) {
  529. host := getHost(r)
  530. port := portOnly(host)
  531. if port != "" && isDefaultPort(r.URL.Scheme, port) {
  532. r.Host = stripPort(host)
  533. }
  534. }
  535. // Returns host from request
  536. func getHost(r *http.Request) string {
  537. if r.Host != "" {
  538. return r.Host
  539. }
  540. return r.URL.Host
  541. }
  542. // Hostname returns u.Host, without any port number.
  543. //
  544. // If Host is an IPv6 literal with a port number, Hostname returns the
  545. // IPv6 literal without the square brackets. IPv6 literals may include
  546. // a zone identifier.
  547. //
  548. // Copied from the Go 1.8 standard library (net/url)
  549. func stripPort(hostport string) string {
  550. colon := strings.IndexByte(hostport, ':')
  551. if colon == -1 {
  552. return hostport
  553. }
  554. if i := strings.IndexByte(hostport, ']'); i != -1 {
  555. return strings.TrimPrefix(hostport[:i], "[")
  556. }
  557. return hostport[:colon]
  558. }
  559. // Port returns the port part of u.Host, without the leading colon.
  560. // If u.Host doesn't contain a port, Port returns an empty string.
  561. //
  562. // Copied from the Go 1.8 standard library (net/url)
  563. func portOnly(hostport string) string {
  564. colon := strings.IndexByte(hostport, ':')
  565. if colon == -1 {
  566. return ""
  567. }
  568. if i := strings.Index(hostport, "]:"); i != -1 {
  569. return hostport[i+len("]:"):]
  570. }
  571. if strings.Contains(hostport, "]") {
  572. return ""
  573. }
  574. return hostport[colon+len(":"):]
  575. }
  576. // Returns true if the specified URI is using the standard port
  577. // (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)
  578. func isDefaultPort(scheme, port string) bool {
  579. if port == "" {
  580. return true
  581. }
  582. lowerCaseScheme := strings.ToLower(scheme)
  583. if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") {
  584. return true
  585. }
  586. return false
  587. }