github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/net/http/transport.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // HTTP client implementation. See RFC 2616. 6 // 7 // This is the low-level Transport implementation of RoundTripper. 8 // The high-level interface is in client.go. 9 10 package http 11 12 import ( 13 "bufio" 14 "compress/gzip" 15 "crypto/tls" 16 "errors" 17 "fmt" 18 "io" 19 "log" 20 "net" 21 "net/url" 22 "os" 23 "strings" 24 "sync" 25 "time" 26 ) 27 28 // DefaultTransport is the default implementation of Transport and is 29 // used by DefaultClient. It establishes network connections as needed 30 // and caches them for reuse by subsequent calls. It uses HTTP proxies 31 // as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and 32 // $no_proxy) environment variables. 33 var DefaultTransport RoundTripper = &Transport{ 34 Proxy: ProxyFromEnvironment, 35 Dial: (&net.Dialer{ 36 Timeout: 30 * time.Second, 37 KeepAlive: 30 * time.Second, 38 }).Dial, 39 TLSHandshakeTimeout: 10 * time.Second, 40 } 41 42 // DefaultMaxIdleConnsPerHost is the default value of Transport's 43 // MaxIdleConnsPerHost. 44 const DefaultMaxIdleConnsPerHost = 2 45 46 // Transport is an implementation of RoundTripper that supports HTTP, 47 // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT). 48 // Transport can also cache connections for future re-use. 49 type Transport struct { 50 idleMu sync.Mutex 51 wantIdle bool // user has requested to close all idle conns 52 idleConn map[connectMethodKey][]*persistConn 53 idleConnCh map[connectMethodKey]chan *persistConn 54 55 reqMu sync.Mutex 56 reqCanceler map[*Request]func() 57 58 altMu sync.RWMutex 59 altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper 60 61 // Proxy specifies a function to return a proxy for a given 62 // Request. If the function returns a non-nil error, the 63 // request is aborted with the provided error. 64 // If Proxy is nil or returns a nil *URL, no proxy is used. 65 Proxy func(*Request) (*url.URL, error) 66 67 // Dial specifies the dial function for creating unencrypted 68 // TCP connections. 69 // If Dial is nil, net.Dial is used. 70 Dial func(network, addr string) (net.Conn, error) 71 72 // DialTLS specifies an optional dial function for creating 73 // TLS connections for non-proxied HTTPS requests. 74 // 75 // If DialTLS is nil, Dial and TLSClientConfig are used. 76 // 77 // If DialTLS is set, the Dial hook is not used for HTTPS 78 // requests and the TLSClientConfig and TLSHandshakeTimeout 79 // are ignored. The returned net.Conn is assumed to already be 80 // past the TLS handshake. 81 DialTLS func(network, addr string) (net.Conn, error) 82 83 // TLSClientConfig specifies the TLS configuration to use with 84 // tls.Client. If nil, the default configuration is used. 85 TLSClientConfig *tls.Config 86 87 // TLSHandshakeTimeout specifies the maximum amount of time waiting to 88 // wait for a TLS handshake. Zero means no timeout. 89 TLSHandshakeTimeout time.Duration 90 91 // DisableKeepAlives, if true, prevents re-use of TCP connections 92 // between different HTTP requests. 93 DisableKeepAlives bool 94 95 // DisableCompression, if true, prevents the Transport from 96 // requesting compression with an "Accept-Encoding: gzip" 97 // request header when the Request contains no existing 98 // Accept-Encoding value. If the Transport requests gzip on 99 // its own and gets a gzipped response, it's transparently 100 // decoded in the Response.Body. However, if the user 101 // explicitly requested gzip it is not automatically 102 // uncompressed. 103 DisableCompression bool 104 105 // MaxIdleConnsPerHost, if non-zero, controls the maximum idle 106 // (keep-alive) to keep per-host. If zero, 107 // DefaultMaxIdleConnsPerHost is used. 108 MaxIdleConnsPerHost int 109 110 // ResponseHeaderTimeout, if non-zero, specifies the amount of 111 // time to wait for a server's response headers after fully 112 // writing the request (including its body, if any). This 113 // time does not include the time to read the response body. 114 ResponseHeaderTimeout time.Duration 115 116 // TODO: tunable on global max cached connections 117 // TODO: tunable on timeout on cached connections 118 } 119 120 // ProxyFromEnvironment returns the URL of the proxy to use for a 121 // given request, as indicated by the environment variables 122 // HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions 123 // thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https 124 // requests. 125 // 126 // The environment values may be either a complete URL or a 127 // "host[:port]", in which case the "http" scheme is assumed. 128 // An error is returned if the value is a different form. 129 // 130 // A nil URL and nil error are returned if no proxy is defined in the 131 // environment, or a proxy should not be used for the given request, 132 // as defined by NO_PROXY. 133 // 134 // As a special case, if req.URL.Host is "localhost" (with or without 135 // a port number), then a nil URL and nil error will be returned. 136 func ProxyFromEnvironment(req *Request) (*url.URL, error) { 137 var proxy string 138 if req.URL.Scheme == "https" { 139 proxy = httpsProxyEnv.Get() 140 } 141 if proxy == "" { 142 proxy = httpProxyEnv.Get() 143 } 144 if proxy == "" { 145 return nil, nil 146 } 147 if !useProxy(canonicalAddr(req.URL)) { 148 return nil, nil 149 } 150 proxyURL, err := url.Parse(proxy) 151 if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") { 152 // proxy was bogus. Try prepending "http://" to it and 153 // see if that parses correctly. If not, we fall 154 // through and complain about the original one. 155 if proxyURL, err := url.Parse("http://" + proxy); err == nil { 156 return proxyURL, nil 157 } 158 } 159 if err != nil { 160 return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) 161 } 162 return proxyURL, nil 163 } 164 165 // ProxyURL returns a proxy function (for use in a Transport) 166 // that always returns the same URL. 167 func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) { 168 return func(*Request) (*url.URL, error) { 169 return fixedURL, nil 170 } 171 } 172 173 // transportRequest is a wrapper around a *Request that adds 174 // optional extra headers to write. 175 type transportRequest struct { 176 *Request // original request, not to be mutated 177 extra Header // extra headers to write, or nil 178 } 179 180 func (tr *transportRequest) extraHeaders() Header { 181 if tr.extra == nil { 182 tr.extra = make(Header) 183 } 184 return tr.extra 185 } 186 187 // RoundTrip implements the RoundTripper interface. 188 // 189 // For higher-level HTTP client support (such as handling of cookies 190 // and redirects), see Get, Post, and the Client type. 191 func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) { 192 if req.URL == nil { 193 req.closeBody() 194 return nil, errors.New("http: nil Request.URL") 195 } 196 if req.Header == nil { 197 req.closeBody() 198 return nil, errors.New("http: nil Request.Header") 199 } 200 if req.URL.Scheme != "http" && req.URL.Scheme != "https" { 201 t.altMu.RLock() 202 var rt RoundTripper 203 if t.altProto != nil { 204 rt = t.altProto[req.URL.Scheme] 205 } 206 t.altMu.RUnlock() 207 if rt == nil { 208 req.closeBody() 209 return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} 210 } 211 return rt.RoundTrip(req) 212 } 213 if req.URL.Host == "" { 214 req.closeBody() 215 return nil, errors.New("http: no Host in request URL") 216 } 217 treq := &transportRequest{Request: req} 218 cm, err := t.connectMethodForRequest(treq) 219 if err != nil { 220 req.closeBody() 221 return nil, err 222 } 223 224 // Get the cached or newly-created connection to either the 225 // host (for http or https), the http proxy, or the http proxy 226 // pre-CONNECTed to https server. In any case, we'll be ready 227 // to send it requests. 228 pconn, err := t.getConn(req, cm) 229 if err != nil { 230 t.setReqCanceler(req, nil) 231 req.closeBody() 232 return nil, err 233 } 234 235 return pconn.roundTrip(treq) 236 } 237 238 // RegisterProtocol registers a new protocol with scheme. 239 // The Transport will pass requests using the given scheme to rt. 240 // It is rt's responsibility to simulate HTTP request semantics. 241 // 242 // RegisterProtocol can be used by other packages to provide 243 // implementations of protocol schemes like "ftp" or "file". 244 func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { 245 if scheme == "http" || scheme == "https" { 246 panic("protocol " + scheme + " already registered") 247 } 248 t.altMu.Lock() 249 defer t.altMu.Unlock() 250 if t.altProto == nil { 251 t.altProto = make(map[string]RoundTripper) 252 } 253 if _, exists := t.altProto[scheme]; exists { 254 panic("protocol " + scheme + " already registered") 255 } 256 t.altProto[scheme] = rt 257 } 258 259 // CloseIdleConnections closes any connections which were previously 260 // connected from previous requests but are now sitting idle in 261 // a "keep-alive" state. It does not interrupt any connections currently 262 // in use. 263 func (t *Transport) CloseIdleConnections() { 264 t.idleMu.Lock() 265 m := t.idleConn 266 t.idleConn = nil 267 t.idleConnCh = nil 268 t.wantIdle = true 269 t.idleMu.Unlock() 270 for _, conns := range m { 271 for _, pconn := range conns { 272 pconn.close() 273 } 274 } 275 } 276 277 // CancelRequest cancels an in-flight request by closing its connection. 278 // CancelRequest should only be called after RoundTrip has returned. 279 func (t *Transport) CancelRequest(req *Request) { 280 t.reqMu.Lock() 281 cancel := t.reqCanceler[req] 282 delete(t.reqCanceler, req) 283 t.reqMu.Unlock() 284 if cancel != nil { 285 cancel() 286 } 287 } 288 289 // 290 // Private implementation past this point. 291 // 292 293 var ( 294 httpProxyEnv = &envOnce{ 295 names: []string{"HTTP_PROXY", "http_proxy"}, 296 } 297 httpsProxyEnv = &envOnce{ 298 names: []string{"HTTPS_PROXY", "https_proxy"}, 299 } 300 noProxyEnv = &envOnce{ 301 names: []string{"NO_PROXY", "no_proxy"}, 302 } 303 ) 304 305 // envOnce looks up an environment variable (optionally by multiple 306 // names) once. It mitigates expensive lookups on some platforms 307 // (e.g. Windows). 308 type envOnce struct { 309 names []string 310 once sync.Once 311 val string 312 } 313 314 func (e *envOnce) Get() string { 315 e.once.Do(e.init) 316 return e.val 317 } 318 319 func (e *envOnce) init() { 320 for _, n := range e.names { 321 e.val = os.Getenv(n) 322 if e.val != "" { 323 return 324 } 325 } 326 } 327 328 // reset is used by tests 329 func (e *envOnce) reset() { 330 e.once = sync.Once{} 331 e.val = "" 332 } 333 334 func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectMethod, err error) { 335 cm.targetScheme = treq.URL.Scheme 336 cm.targetAddr = canonicalAddr(treq.URL) 337 if t.Proxy != nil { 338 cm.proxyURL, err = t.Proxy(treq.Request) 339 } 340 return cm, err 341 } 342 343 // proxyAuth returns the Proxy-Authorization header to set 344 // on requests, if applicable. 345 func (cm *connectMethod) proxyAuth() string { 346 if cm.proxyURL == nil { 347 return "" 348 } 349 if u := cm.proxyURL.User; u != nil { 350 username := u.Username() 351 password, _ := u.Password() 352 return "Basic " + basicAuth(username, password) 353 } 354 return "" 355 } 356 357 // putIdleConn adds pconn to the list of idle persistent connections awaiting 358 // a new request. 359 // If pconn is no longer needed or not in a good state, putIdleConn 360 // returns false. 361 func (t *Transport) putIdleConn(pconn *persistConn) bool { 362 if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 { 363 pconn.close() 364 return false 365 } 366 if pconn.isBroken() { 367 return false 368 } 369 key := pconn.cacheKey 370 max := t.MaxIdleConnsPerHost 371 if max == 0 { 372 max = DefaultMaxIdleConnsPerHost 373 } 374 t.idleMu.Lock() 375 376 waitingDialer := t.idleConnCh[key] 377 select { 378 case waitingDialer <- pconn: 379 // We're done with this pconn and somebody else is 380 // currently waiting for a conn of this type (they're 381 // actively dialing, but this conn is ready 382 // first). Chrome calls this socket late binding. See 383 // https://insouciant.org/tech/connection-management-in-chromium/ 384 t.idleMu.Unlock() 385 return true 386 default: 387 if waitingDialer != nil { 388 // They had populated this, but their dial won 389 // first, so we can clean up this map entry. 390 delete(t.idleConnCh, key) 391 } 392 } 393 if t.wantIdle { 394 t.idleMu.Unlock() 395 pconn.close() 396 return false 397 } 398 if t.idleConn == nil { 399 t.idleConn = make(map[connectMethodKey][]*persistConn) 400 } 401 if len(t.idleConn[key]) >= max { 402 t.idleMu.Unlock() 403 pconn.close() 404 return false 405 } 406 for _, exist := range t.idleConn[key] { 407 if exist == pconn { 408 log.Fatalf("dup idle pconn %p in freelist", pconn) 409 } 410 } 411 t.idleConn[key] = append(t.idleConn[key], pconn) 412 t.idleMu.Unlock() 413 return true 414 } 415 416 // getIdleConnCh returns a channel to receive and return idle 417 // persistent connection for the given connectMethod. 418 // It may return nil, if persistent connections are not being used. 419 func (t *Transport) getIdleConnCh(cm connectMethod) chan *persistConn { 420 if t.DisableKeepAlives { 421 return nil 422 } 423 key := cm.key() 424 t.idleMu.Lock() 425 defer t.idleMu.Unlock() 426 t.wantIdle = false 427 if t.idleConnCh == nil { 428 t.idleConnCh = make(map[connectMethodKey]chan *persistConn) 429 } 430 ch, ok := t.idleConnCh[key] 431 if !ok { 432 ch = make(chan *persistConn) 433 t.idleConnCh[key] = ch 434 } 435 return ch 436 } 437 438 func (t *Transport) getIdleConn(cm connectMethod) (pconn *persistConn) { 439 key := cm.key() 440 t.idleMu.Lock() 441 defer t.idleMu.Unlock() 442 if t.idleConn == nil { 443 return nil 444 } 445 for { 446 pconns, ok := t.idleConn[key] 447 if !ok { 448 return nil 449 } 450 if len(pconns) == 1 { 451 pconn = pconns[0] 452 delete(t.idleConn, key) 453 } else { 454 // 2 or more cached connections; pop last 455 // TODO: queue? 456 pconn = pconns[len(pconns)-1] 457 t.idleConn[key] = pconns[:len(pconns)-1] 458 } 459 if !pconn.isBroken() { 460 return 461 } 462 } 463 } 464 465 func (t *Transport) setReqCanceler(r *Request, fn func()) { 466 t.reqMu.Lock() 467 defer t.reqMu.Unlock() 468 if t.reqCanceler == nil { 469 t.reqCanceler = make(map[*Request]func()) 470 } 471 if fn != nil { 472 t.reqCanceler[r] = fn 473 } else { 474 delete(t.reqCanceler, r) 475 } 476 } 477 478 // replaceReqCanceler replaces an existing cancel function. If there is no cancel function 479 // for the request, we don't set the function and return false. 480 // Since CancelRequest will clear the canceler, we can use the return value to detect if 481 // the request was canceled since the last setReqCancel call. 482 func (t *Transport) replaceReqCanceler(r *Request, fn func()) bool { 483 t.reqMu.Lock() 484 defer t.reqMu.Unlock() 485 _, ok := t.reqCanceler[r] 486 if !ok { 487 return false 488 } 489 if fn != nil { 490 t.reqCanceler[r] = fn 491 } else { 492 delete(t.reqCanceler, r) 493 } 494 return true 495 } 496 497 func (t *Transport) dial(network, addr string) (c net.Conn, err error) { 498 if t.Dial != nil { 499 return t.Dial(network, addr) 500 } 501 return net.Dial(network, addr) 502 } 503 504 // Testing hooks: 505 var prePendingDial, postPendingDial func() 506 507 // getConn dials and creates a new persistConn to the target as 508 // specified in the connectMethod. This includes doing a proxy CONNECT 509 // and/or setting up TLS. If this doesn't return an error, the persistConn 510 // is ready to write requests to. 511 func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error) { 512 if pc := t.getIdleConn(cm); pc != nil { 513 // set request canceler to some non-nil function so we 514 // can detect whether it was cleared between now and when 515 // we enter roundTrip 516 t.setReqCanceler(req, func() {}) 517 return pc, nil 518 } 519 520 type dialRes struct { 521 pc *persistConn 522 err error 523 } 524 dialc := make(chan dialRes) 525 526 // Copy these hooks so we don't race on the postPendingDial in 527 // the goroutine we launch. Issue 11136. 528 prePendingDial := prePendingDial 529 postPendingDial := postPendingDial 530 531 handlePendingDial := func() { 532 if prePendingDial != nil { 533 prePendingDial() 534 } 535 go func() { 536 if v := <-dialc; v.err == nil { 537 t.putIdleConn(v.pc) 538 } 539 if postPendingDial != nil { 540 postPendingDial() 541 } 542 }() 543 } 544 545 cancelc := make(chan struct{}) 546 t.setReqCanceler(req, func() { close(cancelc) }) 547 548 go func() { 549 pc, err := t.dialConn(cm) 550 dialc <- dialRes{pc, err} 551 }() 552 553 idleConnCh := t.getIdleConnCh(cm) 554 select { 555 case v := <-dialc: 556 // Our dial finished. 557 return v.pc, v.err 558 case pc := <-idleConnCh: 559 // Another request finished first and its net.Conn 560 // became available before our dial. Or somebody 561 // else's dial that they didn't use. 562 // But our dial is still going, so give it away 563 // when it finishes: 564 handlePendingDial() 565 return pc, nil 566 case <-req.Cancel: 567 handlePendingDial() 568 return nil, errors.New("net/http: request canceled while waiting for connection") 569 case <-cancelc: 570 handlePendingDial() 571 return nil, errors.New("net/http: request canceled while waiting for connection") 572 } 573 } 574 575 func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) { 576 pconn := &persistConn{ 577 t: t, 578 cacheKey: cm.key(), 579 reqch: make(chan requestAndChan, 1), 580 writech: make(chan writeRequest, 1), 581 closech: make(chan struct{}), 582 writeErrCh: make(chan error, 1), 583 } 584 tlsDial := t.DialTLS != nil && cm.targetScheme == "https" && cm.proxyURL == nil 585 if tlsDial { 586 var err error 587 pconn.conn, err = t.DialTLS("tcp", cm.addr()) 588 if err != nil { 589 return nil, err 590 } 591 if tc, ok := pconn.conn.(*tls.Conn); ok { 592 cs := tc.ConnectionState() 593 pconn.tlsState = &cs 594 } 595 } else { 596 conn, err := t.dial("tcp", cm.addr()) 597 if err != nil { 598 if cm.proxyURL != nil { 599 err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err) 600 } 601 return nil, err 602 } 603 pconn.conn = conn 604 } 605 606 // Proxy setup. 607 switch { 608 case cm.proxyURL == nil: 609 // Do nothing. Not using a proxy. 610 case cm.targetScheme == "http": 611 pconn.isProxy = true 612 if pa := cm.proxyAuth(); pa != "" { 613 pconn.mutateHeaderFunc = func(h Header) { 614 h.Set("Proxy-Authorization", pa) 615 } 616 } 617 case cm.targetScheme == "https": 618 conn := pconn.conn 619 connectReq := &Request{ 620 Method: "CONNECT", 621 URL: &url.URL{Opaque: cm.targetAddr}, 622 Host: cm.targetAddr, 623 Header: make(Header), 624 } 625 if pa := cm.proxyAuth(); pa != "" { 626 connectReq.Header.Set("Proxy-Authorization", pa) 627 } 628 connectReq.Write(conn) 629 630 // Read response. 631 // Okay to use and discard buffered reader here, because 632 // TLS server will not speak until spoken to. 633 br := bufio.NewReader(conn) 634 resp, err := ReadResponse(br, connectReq) 635 if err != nil { 636 conn.Close() 637 return nil, err 638 } 639 if resp.StatusCode != 200 { 640 f := strings.SplitN(resp.Status, " ", 2) 641 conn.Close() 642 return nil, errors.New(f[1]) 643 } 644 } 645 646 if cm.targetScheme == "https" && !tlsDial { 647 // Initiate TLS and check remote host name against certificate. 648 cfg := cloneTLSClientConfig(t.TLSClientConfig) 649 if cfg.ServerName == "" { 650 cfg.ServerName = cm.tlsHost() 651 } 652 plainConn := pconn.conn 653 tlsConn := tls.Client(plainConn, cfg) 654 errc := make(chan error, 2) 655 var timer *time.Timer // for canceling TLS handshake 656 if d := t.TLSHandshakeTimeout; d != 0 { 657 timer = time.AfterFunc(d, func() { 658 errc <- tlsHandshakeTimeoutError{} 659 }) 660 } 661 go func() { 662 err := tlsConn.Handshake() 663 if timer != nil { 664 timer.Stop() 665 } 666 errc <- err 667 }() 668 if err := <-errc; err != nil { 669 plainConn.Close() 670 return nil, err 671 } 672 if !cfg.InsecureSkipVerify { 673 if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { 674 plainConn.Close() 675 return nil, err 676 } 677 } 678 cs := tlsConn.ConnectionState() 679 pconn.tlsState = &cs 680 pconn.conn = tlsConn 681 } 682 683 pconn.br = bufio.NewReader(noteEOFReader{pconn.conn, &pconn.sawEOF}) 684 pconn.bw = bufio.NewWriter(pconn.conn) 685 go pconn.readLoop() 686 go pconn.writeLoop() 687 return pconn, nil 688 } 689 690 // useProxy reports whether requests to addr should use a proxy, 691 // according to the NO_PROXY or no_proxy environment variable. 692 // addr is always a canonicalAddr with a host and port. 693 func useProxy(addr string) bool { 694 if len(addr) == 0 { 695 return true 696 } 697 host, _, err := net.SplitHostPort(addr) 698 if err != nil { 699 return false 700 } 701 if host == "localhost" { 702 return false 703 } 704 if ip := net.ParseIP(host); ip != nil { 705 if ip.IsLoopback() { 706 return false 707 } 708 } 709 710 no_proxy := noProxyEnv.Get() 711 if no_proxy == "*" { 712 return false 713 } 714 715 addr = strings.ToLower(strings.TrimSpace(addr)) 716 if hasPort(addr) { 717 addr = addr[:strings.LastIndex(addr, ":")] 718 } 719 720 for _, p := range strings.Split(no_proxy, ",") { 721 p = strings.ToLower(strings.TrimSpace(p)) 722 if len(p) == 0 { 723 continue 724 } 725 if hasPort(p) { 726 p = p[:strings.LastIndex(p, ":")] 727 } 728 if addr == p { 729 return false 730 } 731 if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { 732 // no_proxy ".foo.com" matches "bar.foo.com" or "foo.com" 733 return false 734 } 735 if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { 736 // no_proxy "foo.com" matches "bar.foo.com" 737 return false 738 } 739 } 740 return true 741 } 742 743 // connectMethod is the map key (in its String form) for keeping persistent 744 // TCP connections alive for subsequent HTTP requests. 745 // 746 // A connect method may be of the following types: 747 // 748 // Cache key form Description 749 // ----------------- ------------------------- 750 // |http|foo.com http directly to server, no proxy 751 // |https|foo.com https directly to server, no proxy 752 // http://proxy.com|https|foo.com http to proxy, then CONNECT to foo.com 753 // http://proxy.com|http http to proxy, http to anywhere after that 754 // 755 // Note: no support to https to the proxy yet. 756 // 757 type connectMethod struct { 758 proxyURL *url.URL // nil for no proxy, else full proxy URL 759 targetScheme string // "http" or "https" 760 targetAddr string // Not used if proxy + http targetScheme (4th example in table) 761 } 762 763 func (cm *connectMethod) key() connectMethodKey { 764 proxyStr := "" 765 targetAddr := cm.targetAddr 766 if cm.proxyURL != nil { 767 proxyStr = cm.proxyURL.String() 768 if cm.targetScheme == "http" { 769 targetAddr = "" 770 } 771 } 772 return connectMethodKey{ 773 proxy: proxyStr, 774 scheme: cm.targetScheme, 775 addr: targetAddr, 776 } 777 } 778 779 // addr returns the first hop "host:port" to which we need to TCP connect. 780 func (cm *connectMethod) addr() string { 781 if cm.proxyURL != nil { 782 return canonicalAddr(cm.proxyURL) 783 } 784 return cm.targetAddr 785 } 786 787 // tlsHost returns the host name to match against the peer's 788 // TLS certificate. 789 func (cm *connectMethod) tlsHost() string { 790 h := cm.targetAddr 791 if hasPort(h) { 792 h = h[:strings.LastIndex(h, ":")] 793 } 794 return h 795 } 796 797 // connectMethodKey is the map key version of connectMethod, with a 798 // stringified proxy URL (or the empty string) instead of a pointer to 799 // a URL. 800 type connectMethodKey struct { 801 proxy, scheme, addr string 802 } 803 804 func (k connectMethodKey) String() string { 805 // Only used by tests. 806 return fmt.Sprintf("%s|%s|%s", k.proxy, k.scheme, k.addr) 807 } 808 809 // persistConn wraps a connection, usually a persistent one 810 // (but may be used for non-keep-alive requests as well) 811 type persistConn struct { 812 t *Transport 813 cacheKey connectMethodKey 814 conn net.Conn 815 tlsState *tls.ConnectionState 816 br *bufio.Reader // from conn 817 sawEOF bool // whether we've seen EOF from conn; owned by readLoop 818 bw *bufio.Writer // to conn 819 reqch chan requestAndChan // written by roundTrip; read by readLoop 820 writech chan writeRequest // written by roundTrip; read by writeLoop 821 closech chan struct{} // closed when conn closed 822 isProxy bool 823 // writeErrCh passes the request write error (usually nil) 824 // from the writeLoop goroutine to the readLoop which passes 825 // it off to the res.Body reader, which then uses it to decide 826 // whether or not a connection can be reused. Issue 7569. 827 writeErrCh chan error 828 829 lk sync.Mutex // guards following fields 830 numExpectedResponses int 831 closed bool // whether conn has been closed 832 broken bool // an error has happened on this connection; marked broken so it's not reused. 833 canceled bool // whether this conn was broken due a CancelRequest 834 // mutateHeaderFunc is an optional func to modify extra 835 // headers on each outbound request before it's written. (the 836 // original Request given to RoundTrip is not modified) 837 mutateHeaderFunc func(Header) 838 } 839 840 // isBroken reports whether this connection is in a known broken state. 841 func (pc *persistConn) isBroken() bool { 842 pc.lk.Lock() 843 b := pc.broken 844 pc.lk.Unlock() 845 return b 846 } 847 848 // isCanceled reports whether this connection was closed due to CancelRequest. 849 func (pc *persistConn) isCanceled() bool { 850 pc.lk.Lock() 851 defer pc.lk.Unlock() 852 return pc.canceled 853 } 854 855 func (pc *persistConn) cancelRequest() { 856 pc.lk.Lock() 857 defer pc.lk.Unlock() 858 pc.canceled = true 859 pc.closeLocked() 860 } 861 862 func (pc *persistConn) readLoop() { 863 // eofc is used to block http.Handler goroutines reading from Response.Body 864 // at EOF until this goroutines has (potentially) added the connection 865 // back to the idle pool. 866 eofc := make(chan struct{}) 867 defer close(eofc) // unblock reader on errors 868 869 // Read this once, before loop starts. (to avoid races in tests) 870 testHookMu.Lock() 871 testHookReadLoopBeforeNextRead := testHookReadLoopBeforeNextRead 872 testHookMu.Unlock() 873 874 alive := true 875 for alive { 876 pb, err := pc.br.Peek(1) 877 878 pc.lk.Lock() 879 if pc.numExpectedResponses == 0 { 880 if !pc.closed { 881 pc.closeLocked() 882 if len(pb) > 0 { 883 log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", 884 string(pb), err) 885 } 886 } 887 pc.lk.Unlock() 888 return 889 } 890 pc.lk.Unlock() 891 892 rc := <-pc.reqch 893 894 var resp *Response 895 if err == nil { 896 resp, err = ReadResponse(pc.br, rc.req) 897 if err == nil && resp.StatusCode == 100 { 898 // Skip any 100-continue for now. 899 // TODO(bradfitz): if rc.req had "Expect: 100-continue", 900 // actually block the request body write and signal the 901 // writeLoop now to begin sending it. (Issue 2184) For now we 902 // eat it, since we're never expecting one. 903 resp, err = ReadResponse(pc.br, rc.req) 904 } 905 } 906 907 if resp != nil { 908 resp.TLS = pc.tlsState 909 } 910 911 hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0 912 913 if err != nil { 914 pc.close() 915 } else { 916 if rc.addedGzip && hasBody && resp.Header.Get("Content-Encoding") == "gzip" { 917 resp.Header.Del("Content-Encoding") 918 resp.Header.Del("Content-Length") 919 resp.ContentLength = -1 920 resp.Body = &gzipReader{body: resp.Body} 921 } 922 resp.Body = &bodyEOFSignal{body: resp.Body} 923 } 924 925 if err != nil || resp.Close || rc.req.Close || resp.StatusCode <= 199 { 926 // Don't do keep-alive on error if either party requested a close 927 // or we get an unexpected informational (1xx) response. 928 // StatusCode 100 is already handled above. 929 alive = false 930 } 931 932 var waitForBodyRead chan bool // channel is nil when there's no body 933 if hasBody { 934 waitForBodyRead = make(chan bool, 2) 935 resp.Body.(*bodyEOFSignal).earlyCloseFn = func() error { 936 waitForBodyRead <- false 937 return nil 938 } 939 resp.Body.(*bodyEOFSignal).fn = func(err error) error { 940 isEOF := err == io.EOF 941 waitForBodyRead <- isEOF 942 if isEOF { 943 <-eofc // see comment at top 944 } else if err != nil && pc.isCanceled() { 945 return errRequestCanceled 946 } 947 return err 948 } 949 } else { 950 // Before send on rc.ch, as client might re-use the 951 // same *Request pointer, and we don't want to set this 952 // on t from this persistConn while the Transport 953 // potentially spins up a different persistConn for the 954 // caller's subsequent request. 955 pc.t.setReqCanceler(rc.req, nil) 956 } 957 958 pc.lk.Lock() 959 pc.numExpectedResponses-- 960 pc.lk.Unlock() 961 962 // The connection might be going away when we put the 963 // idleConn below. When that happens, we close the response channel to signal 964 // to roundTrip that the connection is gone. roundTrip waits for 965 // both closing and a response in a select, so it might choose 966 // the close channel, rather than the response. 967 // We send the response first so that roundTrip can check 968 // if there is a pending one with a non-blocking select 969 // on the response channel before erroring out. 970 rc.ch <- responseAndError{resp, err} 971 972 if hasBody { 973 // To avoid a race, wait for the just-returned 974 // response body to be fully consumed before peek on 975 // the underlying bufio reader. 976 select { 977 case <-rc.req.Cancel: 978 alive = false 979 pc.t.CancelRequest(rc.req) 980 case bodyEOF := <-waitForBodyRead: 981 pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool 982 alive = alive && 983 bodyEOF && 984 !pc.sawEOF && 985 pc.wroteRequest() && 986 pc.t.putIdleConn(pc) 987 if bodyEOF { 988 eofc <- struct{}{} 989 } 990 case <-pc.closech: 991 alive = false 992 } 993 } else { 994 alive = alive && 995 !pc.sawEOF && 996 pc.wroteRequest() && 997 pc.t.putIdleConn(pc) 998 } 999 1000 if hook := testHookReadLoopBeforeNextRead; hook != nil { 1001 hook() 1002 } 1003 } 1004 pc.close() 1005 } 1006 1007 func (pc *persistConn) writeLoop() { 1008 for { 1009 select { 1010 case wr := <-pc.writech: 1011 if pc.isBroken() { 1012 wr.ch <- errors.New("http: can't write HTTP request on broken connection") 1013 continue 1014 } 1015 err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra) 1016 if err == nil { 1017 err = pc.bw.Flush() 1018 } 1019 if err != nil { 1020 pc.markBroken() 1021 wr.req.Request.closeBody() 1022 } 1023 pc.writeErrCh <- err // to the body reader, which might recycle us 1024 wr.ch <- err // to the roundTrip function 1025 case <-pc.closech: 1026 return 1027 } 1028 } 1029 } 1030 1031 // wroteRequest is a check before recycling a connection that the previous write 1032 // (from writeLoop above) happened and was successful. 1033 func (pc *persistConn) wroteRequest() bool { 1034 select { 1035 case err := <-pc.writeErrCh: 1036 // Common case: the write happened well before the response, so 1037 // avoid creating a timer. 1038 return err == nil 1039 default: 1040 // Rare case: the request was written in writeLoop above but 1041 // before it could send to pc.writeErrCh, the reader read it 1042 // all, processed it, and called us here. In this case, give the 1043 // write goroutine a bit of time to finish its send. 1044 // 1045 // Less rare case: We also get here in the legitimate case of 1046 // Issue 7569, where the writer is still writing (or stalled), 1047 // but the server has already replied. In this case, we don't 1048 // want to wait too long, and we want to return false so this 1049 // connection isn't re-used. 1050 select { 1051 case err := <-pc.writeErrCh: 1052 return err == nil 1053 case <-time.After(50 * time.Millisecond): 1054 return false 1055 } 1056 } 1057 } 1058 1059 type responseAndError struct { 1060 res *Response 1061 err error 1062 } 1063 1064 type requestAndChan struct { 1065 req *Request 1066 ch chan responseAndError 1067 1068 // did the Transport (as opposed to the client code) add an 1069 // Accept-Encoding gzip header? only if it we set it do 1070 // we transparently decode the gzip. 1071 addedGzip bool 1072 } 1073 1074 // A writeRequest is sent by the readLoop's goroutine to the 1075 // writeLoop's goroutine to write a request while the read loop 1076 // concurrently waits on both the write response and the server's 1077 // reply. 1078 type writeRequest struct { 1079 req *transportRequest 1080 ch chan<- error 1081 } 1082 1083 type httpError struct { 1084 err string 1085 timeout bool 1086 } 1087 1088 func (e *httpError) Error() string { return e.err } 1089 func (e *httpError) Timeout() bool { return e.timeout } 1090 func (e *httpError) Temporary() bool { return true } 1091 1092 var errTimeout error = &httpError{err: "net/http: timeout awaiting response headers", timeout: true} 1093 var errClosed error = &httpError{err: "net/http: transport closed before response was received"} 1094 var errRequestCanceled = errors.New("net/http: request canceled") 1095 1096 // nil except for tests 1097 var ( 1098 testHookPersistConnClosedGotRes func() 1099 testHookEnterRoundTrip func() 1100 testHookMu sync.Locker = fakeLocker{} // guards following 1101 testHookReadLoopBeforeNextRead func() 1102 ) 1103 1104 func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { 1105 if hook := testHookEnterRoundTrip; hook != nil { 1106 hook() 1107 } 1108 if !pc.t.replaceReqCanceler(req.Request, pc.cancelRequest) { 1109 pc.t.putIdleConn(pc) 1110 return nil, errRequestCanceled 1111 } 1112 pc.lk.Lock() 1113 pc.numExpectedResponses++ 1114 headerFn := pc.mutateHeaderFunc 1115 pc.lk.Unlock() 1116 1117 if headerFn != nil { 1118 headerFn(req.extraHeaders()) 1119 } 1120 1121 // Ask for a compressed version if the caller didn't set their 1122 // own value for Accept-Encoding. We only attempt to 1123 // uncompress the gzip stream if we were the layer that 1124 // requested it. 1125 requestedGzip := false 1126 if !pc.t.DisableCompression && 1127 req.Header.Get("Accept-Encoding") == "" && 1128 req.Header.Get("Range") == "" && 1129 req.Method != "HEAD" { 1130 // Request gzip only, not deflate. Deflate is ambiguous and 1131 // not as universally supported anyway. 1132 // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 1133 // 1134 // Note that we don't request this for HEAD requests, 1135 // due to a bug in nginx: 1136 // http://trac.nginx.org/nginx/ticket/358 1137 // https://golang.org/issue/5522 1138 // 1139 // We don't request gzip if the request is for a range, since 1140 // auto-decoding a portion of a gzipped document will just fail 1141 // anyway. See https://golang.org/issue/8923 1142 requestedGzip = true 1143 req.extraHeaders().Set("Accept-Encoding", "gzip") 1144 } 1145 1146 if pc.t.DisableKeepAlives { 1147 req.extraHeaders().Set("Connection", "close") 1148 } 1149 1150 // Write the request concurrently with waiting for a response, 1151 // in case the server decides to reply before reading our full 1152 // request body. 1153 writeErrCh := make(chan error, 1) 1154 pc.writech <- writeRequest{req, writeErrCh} 1155 1156 resc := make(chan responseAndError, 1) 1157 pc.reqch <- requestAndChan{req.Request, resc, requestedGzip} 1158 1159 var re responseAndError 1160 var respHeaderTimer <-chan time.Time 1161 cancelChan := req.Request.Cancel 1162 WaitResponse: 1163 for { 1164 select { 1165 case err := <-writeErrCh: 1166 if isNetWriteError(err) { 1167 // Issue 11745. If we failed to write the request 1168 // body, it's possible the server just heard enough 1169 // and already wrote to us. Prioritize the server's 1170 // response over returning a body write error. 1171 select { 1172 case re = <-resc: 1173 pc.close() 1174 break WaitResponse 1175 case <-time.After(50 * time.Millisecond): 1176 // Fall through. 1177 } 1178 } 1179 if err != nil { 1180 re = responseAndError{nil, err} 1181 pc.close() 1182 break WaitResponse 1183 } 1184 if d := pc.t.ResponseHeaderTimeout; d > 0 { 1185 timer := time.NewTimer(d) 1186 defer timer.Stop() // prevent leaks 1187 respHeaderTimer = timer.C 1188 } 1189 case <-pc.closech: 1190 // The persist connection is dead. This shouldn't 1191 // usually happen (only with Connection: close responses 1192 // with no response bodies), but if it does happen it 1193 // means either a) the remote server hung up on us 1194 // prematurely, or b) the readLoop sent us a response & 1195 // closed its closech at roughly the same time, and we 1196 // selected this case first. If we got a response, readLoop makes sure 1197 // to send it before it puts the conn and closes the channel. 1198 // That way, we can fetch the response, if there is one, 1199 // with a non-blocking receive. 1200 select { 1201 case re = <-resc: 1202 if fn := testHookPersistConnClosedGotRes; fn != nil { 1203 fn() 1204 } 1205 default: 1206 re = responseAndError{err: errClosed} 1207 if pc.isCanceled() { 1208 re = responseAndError{err: errRequestCanceled} 1209 } 1210 } 1211 break WaitResponse 1212 case <-respHeaderTimer: 1213 pc.close() 1214 re = responseAndError{err: errTimeout} 1215 break WaitResponse 1216 case re = <-resc: 1217 break WaitResponse 1218 case <-cancelChan: 1219 pc.t.CancelRequest(req.Request) 1220 cancelChan = nil 1221 } 1222 } 1223 1224 if re.err != nil { 1225 pc.t.setReqCanceler(req.Request, nil) 1226 } 1227 return re.res, re.err 1228 } 1229 1230 // markBroken marks a connection as broken (so it's not reused). 1231 // It differs from close in that it doesn't close the underlying 1232 // connection for use when it's still being read. 1233 func (pc *persistConn) markBroken() { 1234 pc.lk.Lock() 1235 defer pc.lk.Unlock() 1236 pc.broken = true 1237 } 1238 1239 func (pc *persistConn) close() { 1240 pc.lk.Lock() 1241 defer pc.lk.Unlock() 1242 pc.closeLocked() 1243 } 1244 1245 func (pc *persistConn) closeLocked() { 1246 pc.broken = true 1247 if !pc.closed { 1248 pc.conn.Close() 1249 pc.closed = true 1250 close(pc.closech) 1251 } 1252 pc.mutateHeaderFunc = nil 1253 } 1254 1255 var portMap = map[string]string{ 1256 "http": "80", 1257 "https": "443", 1258 } 1259 1260 // canonicalAddr returns url.Host but always with a ":port" suffix 1261 func canonicalAddr(url *url.URL) string { 1262 addr := url.Host 1263 if !hasPort(addr) { 1264 return addr + ":" + portMap[url.Scheme] 1265 } 1266 return addr 1267 } 1268 1269 // bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most 1270 // once, right before its final (error-producing) Read or Close call 1271 // returns. fn should return the new error to return from Read or Close. 1272 // 1273 // If earlyCloseFn is non-nil and Close is called before io.EOF is 1274 // seen, earlyCloseFn is called instead of fn, and its return value is 1275 // the return value from Close. 1276 type bodyEOFSignal struct { 1277 body io.ReadCloser 1278 mu sync.Mutex // guards following 4 fields 1279 closed bool // whether Close has been called 1280 rerr error // sticky Read error 1281 fn func(error) error // err will be nil on Read io.EOF 1282 earlyCloseFn func() error // optional alt Close func used if io.EOF not seen 1283 } 1284 1285 func (es *bodyEOFSignal) Read(p []byte) (n int, err error) { 1286 es.mu.Lock() 1287 closed, rerr := es.closed, es.rerr 1288 es.mu.Unlock() 1289 if closed { 1290 return 0, errors.New("http: read on closed response body") 1291 } 1292 if rerr != nil { 1293 return 0, rerr 1294 } 1295 1296 n, err = es.body.Read(p) 1297 if err != nil { 1298 es.mu.Lock() 1299 defer es.mu.Unlock() 1300 if es.rerr == nil { 1301 es.rerr = err 1302 } 1303 err = es.condfn(err) 1304 } 1305 return 1306 } 1307 1308 func (es *bodyEOFSignal) Close() error { 1309 es.mu.Lock() 1310 defer es.mu.Unlock() 1311 if es.closed { 1312 return nil 1313 } 1314 es.closed = true 1315 if es.earlyCloseFn != nil && es.rerr != io.EOF { 1316 return es.earlyCloseFn() 1317 } 1318 err := es.body.Close() 1319 return es.condfn(err) 1320 } 1321 1322 // caller must hold es.mu. 1323 func (es *bodyEOFSignal) condfn(err error) error { 1324 if es.fn == nil { 1325 return err 1326 } 1327 err = es.fn(err) 1328 es.fn = nil 1329 return err 1330 } 1331 1332 // gzipReader wraps a response body so it can lazily 1333 // call gzip.NewReader on the first call to Read 1334 type gzipReader struct { 1335 body io.ReadCloser // underlying Response.Body 1336 zr io.Reader // lazily-initialized gzip reader 1337 } 1338 1339 func (gz *gzipReader) Read(p []byte) (n int, err error) { 1340 if gz.zr == nil { 1341 gz.zr, err = gzip.NewReader(gz.body) 1342 if err != nil { 1343 return 0, err 1344 } 1345 } 1346 return gz.zr.Read(p) 1347 } 1348 1349 func (gz *gzipReader) Close() error { 1350 return gz.body.Close() 1351 } 1352 1353 type readerAndCloser struct { 1354 io.Reader 1355 io.Closer 1356 } 1357 1358 type tlsHandshakeTimeoutError struct{} 1359 1360 func (tlsHandshakeTimeoutError) Timeout() bool { return true } 1361 func (tlsHandshakeTimeoutError) Temporary() bool { return true } 1362 func (tlsHandshakeTimeoutError) Error() string { return "net/http: TLS handshake timeout" } 1363 1364 type noteEOFReader struct { 1365 r io.Reader 1366 sawEOF *bool 1367 } 1368 1369 func (nr noteEOFReader) Read(p []byte) (n int, err error) { 1370 n, err = nr.r.Read(p) 1371 if err == io.EOF { 1372 *nr.sawEOF = true 1373 } 1374 return 1375 } 1376 1377 // fakeLocker is a sync.Locker which does nothing. It's used to guard 1378 // test-only fields when not under test, to avoid runtime atomic 1379 // overhead. 1380 type fakeLocker struct{} 1381 1382 func (fakeLocker) Lock() {} 1383 func (fakeLocker) Unlock() {} 1384 1385 func isNetWriteError(err error) bool { 1386 switch e := err.(type) { 1387 case *url.Error: 1388 return isNetWriteError(e.Err) 1389 case *net.OpError: 1390 return e.Op == "write" 1391 default: 1392 return false 1393 } 1394 } 1395 1396 // cloneTLSConfig returns a shallow clone of the exported 1397 // fields of cfg, ignoring the unexported sync.Once, which 1398 // contains a mutex and must not be copied. 1399 // 1400 // The cfg must not be in active use by tls.Server, or else 1401 // there can still be a race with tls.Server updating SessionTicketKey 1402 // and our copying it, and also a race with the server setting 1403 // SessionTicketsDisabled=false on failure to set the random 1404 // ticket key. 1405 // 1406 // If cfg is nil, a new zero tls.Config is returned. 1407 func cloneTLSConfig(cfg *tls.Config) *tls.Config { 1408 if cfg == nil { 1409 return &tls.Config{} 1410 } 1411 return &tls.Config{ 1412 Rand: cfg.Rand, 1413 Time: cfg.Time, 1414 Certificates: cfg.Certificates, 1415 NameToCertificate: cfg.NameToCertificate, 1416 GetCertificate: cfg.GetCertificate, 1417 RootCAs: cfg.RootCAs, 1418 NextProtos: cfg.NextProtos, 1419 ServerName: cfg.ServerName, 1420 ClientAuth: cfg.ClientAuth, 1421 ClientCAs: cfg.ClientCAs, 1422 InsecureSkipVerify: cfg.InsecureSkipVerify, 1423 CipherSuites: cfg.CipherSuites, 1424 PreferServerCipherSuites: cfg.PreferServerCipherSuites, 1425 SessionTicketsDisabled: cfg.SessionTicketsDisabled, 1426 SessionTicketKey: cfg.SessionTicketKey, 1427 ClientSessionCache: cfg.ClientSessionCache, 1428 MinVersion: cfg.MinVersion, 1429 MaxVersion: cfg.MaxVersion, 1430 CurvePreferences: cfg.CurvePreferences, 1431 } 1432 } 1433 1434 // cloneTLSClientConfig is like cloneTLSConfig but omits 1435 // the fields SessionTicketsDisabled and SessionTicketKey. 1436 // This makes it safe to call cloneTLSClientConfig on a config 1437 // in active use by a server. 1438 func cloneTLSClientConfig(cfg *tls.Config) *tls.Config { 1439 if cfg == nil { 1440 return &tls.Config{} 1441 } 1442 return &tls.Config{ 1443 Rand: cfg.Rand, 1444 Time: cfg.Time, 1445 Certificates: cfg.Certificates, 1446 NameToCertificate: cfg.NameToCertificate, 1447 GetCertificate: cfg.GetCertificate, 1448 RootCAs: cfg.RootCAs, 1449 NextProtos: cfg.NextProtos, 1450 ServerName: cfg.ServerName, 1451 ClientAuth: cfg.ClientAuth, 1452 ClientCAs: cfg.ClientCAs, 1453 InsecureSkipVerify: cfg.InsecureSkipVerify, 1454 CipherSuites: cfg.CipherSuites, 1455 PreferServerCipherSuites: cfg.PreferServerCipherSuites, 1456 ClientSessionCache: cfg.ClientSessionCache, 1457 MinVersion: cfg.MinVersion, 1458 MaxVersion: cfg.MaxVersion, 1459 CurvePreferences: cfg.CurvePreferences, 1460 } 1461 }