blob: fdad3b47809911024dd52510843e1d2b353145e9 [file] [log] [blame]
Rafal Jeczalik519e7542015-03-02 16:26:43 +01001// 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
10package httputil
11
12import (
Rafal Jeczalik519e7542015-03-02 16:26:43 +010013 "net/http"
14 "net/url"
Rafal Jeczalik519e7542015-03-02 16:26:43 +010015)
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.
21type AuthTransport struct {
Ross Light2fa06782017-10-09 10:48:13 -070022 UserAgent string
23 GithubToken string
24 GithubClientID string
25 GithubClientSecret string
26 Base http.RoundTripper
Rafal Jeczalik519e7542015-03-02 16:26:43 +010027}
28
29// RoundTrip implements the http.RoundTripper interface.
30func (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 Light376e7282017-10-09 10:52:51 -070036 if req.URL.Host == "api.github.com" && req.URL.Scheme == "https" {
Rafal Jeczalik519e7542015-03-02 16:26:43 +010037 switch {
Ross Light2fa06782017-10-09 10:48:13 -070038 case t.GithubClientID != "" && t.GithubClientSecret != "":
Rafal Jeczalik519e7542015-03-02 16:26:43 +010039 if reqCopy == nil {
40 reqCopy = copyRequest(req)
41 }
42 if reqCopy.URL.RawQuery == "" {
Ross Light2fa06782017-10-09 10:48:13 -070043 reqCopy.URL.RawQuery = "client_id=" + t.GithubClientID + "&client_secret=" + t.GithubClientSecret
Rafal Jeczalik519e7542015-03-02 16:26:43 +010044 } else {
Ross Light2fa06782017-10-09 10:48:13 -070045 reqCopy.URL.RawQuery += "&client_id=" + t.GithubClientID + "&client_secret=" + t.GithubClientSecret
Rafal Jeczalik519e7542015-03-02 16:26:43 +010046 }
Ross Light2fa06782017-10-09 10:48:13 -070047 case t.GithubToken != "":
Rafal Jeczalik519e7542015-03-02 16:26:43 +010048 if reqCopy == nil {
49 reqCopy = copyRequest(req)
50 }
Ross Light2fa06782017-10-09 10:48:13 -070051 reqCopy.Header.Set("Authorization", "token "+t.GithubToken)
Rafal Jeczalik519e7542015-03-02 16:26:43 +010052 }
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 Finkenstadtb0d8f6a2015-07-13 18:37:03 -070061func (t *AuthTransport) CancelRequest(req *http.Request) {
Rafal Jeczalik519e7542015-03-02 16:26:43 +010062 type canceler interface {
63 CancelRequest(req *http.Request)
64 }
Andy Finkenstadtb0d8f6a2015-07-13 18:37:03 -070065 if cr, ok := t.base().(canceler); ok {
Rafal Jeczalik519e7542015-03-02 16:26:43 +010066 cr.CancelRequest(req)
67 }
68}
69
70func (t *AuthTransport) base() http.RoundTripper {
71 if t.Base != nil {
72 return t.Base
73 }
74 return http.DefaultTransport
75}
76
Rafal Jeczalik519e7542015-03-02 16:26:43 +010077func 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}