Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 1 | // Copyright 2015 The Go Authors. All rights reserved. |
| 2 | // |
| 3 | // Use of this source code is governed by a BSD-style |
| 4 | // license that can be found in the LICENSE file or at |
| 5 | // https://developers.google.com/open-source/licenses/bsd. |
| 6 | |
| 7 | // This file implements a http.RoundTripper that authenticates |
| 8 | // requests issued against api.github.com endpoint. |
| 9 | |
| 10 | package httputil |
| 11 | |
| 12 | import ( |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 13 | "net/http" |
| 14 | "net/url" |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 15 | ) |
| 16 | |
| 17 | // AuthTransport is an implementation of http.RoundTripper that authenticates |
| 18 | // with the GitHub API. |
| 19 | // |
| 20 | // When both a token and client credentials are set, the latter is preferred. |
| 21 | type AuthTransport struct { |
Ross Light | 2fa0678 | 2017-10-09 10:48:13 -0700 | [diff] [blame] | 22 | UserAgent string |
| 23 | GithubToken string |
| 24 | GithubClientID string |
| 25 | GithubClientSecret string |
| 26 | Base http.RoundTripper |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 27 | } |
| 28 | |
| 29 | // RoundTrip implements the http.RoundTripper interface. |
| 30 | func (t *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { |
| 31 | var reqCopy *http.Request |
| 32 | if t.UserAgent != "" { |
| 33 | reqCopy = copyRequest(req) |
| 34 | reqCopy.Header.Set("User-Agent", t.UserAgent) |
| 35 | } |
Ross Light | 376e728 | 2017-10-09 10:52:51 -0700 | [diff] [blame] | 36 | if req.URL.Host == "api.github.com" && req.URL.Scheme == "https" { |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 37 | switch { |
Ross Light | 2fa0678 | 2017-10-09 10:48:13 -0700 | [diff] [blame] | 38 | case t.GithubClientID != "" && t.GithubClientSecret != "": |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 39 | if reqCopy == nil { |
| 40 | reqCopy = copyRequest(req) |
| 41 | } |
| 42 | if reqCopy.URL.RawQuery == "" { |
Ross Light | 2fa0678 | 2017-10-09 10:48:13 -0700 | [diff] [blame] | 43 | reqCopy.URL.RawQuery = "client_id=" + t.GithubClientID + "&client_secret=" + t.GithubClientSecret |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 44 | } else { |
Ross Light | 2fa0678 | 2017-10-09 10:48:13 -0700 | [diff] [blame] | 45 | reqCopy.URL.RawQuery += "&client_id=" + t.GithubClientID + "&client_secret=" + t.GithubClientSecret |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 46 | } |
Ross Light | 2fa0678 | 2017-10-09 10:48:13 -0700 | [diff] [blame] | 47 | case t.GithubToken != "": |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 48 | if reqCopy == nil { |
| 49 | reqCopy = copyRequest(req) |
| 50 | } |
Ross Light | 2fa0678 | 2017-10-09 10:48:13 -0700 | [diff] [blame] | 51 | reqCopy.Header.Set("Authorization", "token "+t.GithubToken) |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 52 | } |
| 53 | } |
| 54 | if reqCopy != nil { |
| 55 | return t.base().RoundTrip(reqCopy) |
| 56 | } |
| 57 | return t.base().RoundTrip(req) |
| 58 | } |
| 59 | |
| 60 | // CancelRequest cancels an in-flight request by closing its connection. |
Andy Finkenstadt | b0d8f6a | 2015-07-13 18:37:03 -0700 | [diff] [blame] | 61 | func (t *AuthTransport) CancelRequest(req *http.Request) { |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 62 | type canceler interface { |
| 63 | CancelRequest(req *http.Request) |
| 64 | } |
Andy Finkenstadt | b0d8f6a | 2015-07-13 18:37:03 -0700 | [diff] [blame] | 65 | if cr, ok := t.base().(canceler); ok { |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 66 | cr.CancelRequest(req) |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | func (t *AuthTransport) base() http.RoundTripper { |
| 71 | if t.Base != nil { |
| 72 | return t.Base |
| 73 | } |
| 74 | return http.DefaultTransport |
| 75 | } |
| 76 | |
Rafal Jeczalik | 519e754 | 2015-03-02 16:26:43 +0100 | [diff] [blame] | 77 | func copyRequest(req *http.Request) *http.Request { |
| 78 | req2 := new(http.Request) |
| 79 | *req2 = *req |
| 80 | req2.URL = new(url.URL) |
| 81 | *req2.URL = *req.URL |
| 82 | req2.Header = make(http.Header, len(req.Header)) |
| 83 | for k, s := range req.Header { |
| 84 | req2.Header[k] = append([]string(nil), s...) |
| 85 | } |
| 86 | return req2 |
| 87 | } |