1// Copyright 2014 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
5package oauth2
6
7import (
8	"errors"
9	"log"
10	"net/http"
11	"sync"
12)
13
14// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
15// wrapping a base RoundTripper and adding an Authorization header
16// with a token from the supplied Sources.
17//
18// Transport is a low-level mechanism. Most code will use the
19// higher-level Config.Client method instead.
20type Transport struct {
21	// Source supplies the token to add to outgoing requests'
22	// Authorization headers.
23	Source TokenSource
24
25	// Base is the base RoundTripper used to make HTTP requests.
26	// If nil, http.DefaultTransport is used.
27	Base http.RoundTripper
28}
29
30// RoundTrip authorizes and authenticates the request with an
31// access token from Transport's Source.
32func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
33	reqBodyClosed := false
34	if req.Body != nil {
35		defer func() {
36			if !reqBodyClosed {
37				req.Body.Close()
38			}
39		}()
40	}
41
42	if t.Source == nil {
43		return nil, errors.New("oauth2: Transport's Source is nil")
44	}
45	token, err := t.Source.Token()
46	if err != nil {
47		return nil, err
48	}
49
50	req2 := cloneRequest(req) // per RoundTripper contract
51	token.SetAuthHeader(req2)
52
53	// req.Body is assumed to be closed by the base RoundTripper.
54	reqBodyClosed = true
55	return t.base().RoundTrip(req2)
56}
57
58var cancelOnce sync.Once
59
60// CancelRequest does nothing. It used to be a legacy cancellation mechanism
61// but now only it only logs on first use to warn that it's deprecated.
62//
63// Deprecated: use contexts for cancellation instead.
64func (t *Transport) CancelRequest(req *http.Request) {
65	cancelOnce.Do(func() {
66		log.Printf("deprecated: golang.org/x/oauth2: Transport.CancelRequest no longer does anything; use contexts")
67	})
68}
69
70func (t *Transport) base() http.RoundTripper {
71	if t.Base != nil {
72		return t.Base
73	}
74	return http.DefaultTransport
75}
76
77// cloneRequest returns a clone of the provided *http.Request.
78// The clone is a shallow copy of the struct and its Header map.
79func cloneRequest(r *http.Request) *http.Request {
80	// shallow copy of the struct
81	r2 := new(http.Request)
82	*r2 = *r
83	// deep copy of the Header
84	r2.Header = make(http.Header, len(r.Header))
85	for k, s := range r.Header {
86		r2.Header[k] = append([]string(nil), s...)
87	}
88	return r2
89}
90