From 03fb3ab430b9fd69f8c0b553695483c55513ecb3 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Mon, 3 Jun 2013 05:27:25 -0700 Subject: [PATCH] Add UnauthenticatedRateLimitedTransport This allows for making unauthenticated requests subject to your application's rate limit (as opposed to the lower rate limit for anonymous requests) --- github/github.go | 80 +++++++++++++++++++++++++++++++++++++++++++ github/github_test.go | 25 ++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/github/github.go b/github/github.go index d7b0cc0..1bcc4b5 100644 --- a/github/github.go +++ b/github/github.go @@ -48,6 +48,7 @@ package github import ( "bytes" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -262,3 +263,82 @@ func (c *Client) RateLimit() (*Rate, error) { _, err = c.Do(req, response) return response.Rate, err } + +/* +UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls +that need to use a higher rate limit associated with your OAuth application. + + t := &github.UnauthenticatedRateLimitedTransport{ + ClientID: "your app's client ID", + ClientSecret: "your app's client secret", + } + client := github.NewClient(t.Client()) + +This will append the querystring params client_id=xxx&client_secret=yyy to all +requests. + +See http://developer.github.com/v3/#unauthenticated-rate-limited-requests for +more information. +*/ +type UnauthenticatedRateLimitedTransport struct { + // ClientID is the GitHub OAuth client ID of the current application, which + // can be found by selecting its entry in the list at + // https://github.com/settings/applications. + ClientID string + + // ClientSecret is the GitHub OAuth client secret of the current + // application. + ClientSecret string + + // Transport is the underlying HTTP transport to use when making requests. + // It will default to http.DefaultTransport if nil. + Transport http.RoundTripper +} + +func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if t.ClientID == "" { + return nil, errors.New("ClientID is empty") + } + if t.ClientSecret == "" { + return nil, errors.New("ClientSecret is empty") + } + + // To set extra querystring params, we must make a copy of the Request so + // that we don't modify the Request we were given. This is required by the + // specification of http.RoundTripper. + req = cloneRequest(req) + q := req.URL.Query() + q.Set("client_id", t.ClientID) + q.Set("client_secret", t.ClientSecret) + req.URL.RawQuery = q.Encode() + + // Make the HTTP request. + return t.transport().RoundTrip(req) +} + +// Client returns an *http.Client that makes requests which are subject to the +// rate limit of your OAuth application. +func (t *UnauthenticatedRateLimitedTransport) Client() *http.Client { + return &http.Client{Transport: t} +} + +func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport +} + +// cloneRequest returns a clone of the provided *http.Request. The clone is a +// shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { + // shallow copy of the struct + r2 := new(http.Request) + *r2 = *r + // deep copy of the Header + r2.Header = make(http.Header) + for k, s := range r.Header { + r2.Header[k] = s + } + return r2 +} diff --git a/github/github_test.go b/github/github_test.go index d47d053..930c178 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -292,3 +292,28 @@ func TestRateLimit(t *testing.T) { t.Errorf("RateLimit returned %+v, want %+v", rate, want) } } + +func TestUnauthenticatedRateLimitedTransport(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + var v, want string + q := r.URL.Query() + if v, want = q.Get("client_id"), "id"; v != want { + t.Errorf("OAuth Client ID = %v, want %v", v, want) + } + if v, want = q.Get("client_secret"), "secret"; v != want { + t.Errorf("OAuth Client Secret = %v, want %v", v, want) + } + }) + + tp := &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + ClientSecret: "secret", + } + unauthedClient := NewClient(tp.Client()) + unauthedClient.BaseURL = client.BaseURL + req, _ := unauthedClient.NewRequest("GET", "/", nil) + unauthedClient.Do(req, nil) +}