1// Copyright 2015 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package transport/http supports network connections to HTTP servers. 16// This package is not intended for use by end developers. Use the 17// google.golang.org/api/option package to configure API clients. 18package http 19 20import ( 21 "errors" 22 "net/http" 23 24 "golang.org/x/net/context" 25 "golang.org/x/oauth2" 26 "google.golang.org/api/googleapi/transport" 27 "google.golang.org/api/internal" 28 "google.golang.org/api/option" 29) 30 31// NewClient returns an HTTP client for use communicating with a Google cloud 32// service, configured with the given ClientOptions. It also returns the endpoint 33// for the service as specified in the options. 34func NewClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) { 35 var o internal.DialSettings 36 for _, opt := range opts { 37 opt.Apply(&o) 38 } 39 if o.GRPCConn != nil { 40 return nil, "", errors.New("unsupported gRPC connection specified") 41 } 42 // TODO(cbro): consider injecting the User-Agent even if an explicit HTTP client is provided? 43 if o.HTTPClient != nil { 44 return o.HTTPClient, o.Endpoint, nil 45 } 46 if o.APIKey != "" { 47 hc := &http.Client{ 48 Transport: &transport.APIKey{ 49 Key: o.APIKey, 50 Transport: userAgentTransport{ 51 base: baseTransport(ctx), 52 userAgent: o.UserAgent, 53 }, 54 }, 55 } 56 return hc, o.Endpoint, nil 57 } 58 creds, err := internal.Creds(ctx, &o) 59 if err != nil { 60 return nil, "", err 61 } 62 hc := &http.Client{ 63 Transport: &oauth2.Transport{ 64 Source: creds.TokenSource, 65 Base: userAgentTransport{ 66 base: baseTransport(ctx), 67 userAgent: o.UserAgent, 68 }, 69 }, 70 } 71 return hc, o.Endpoint, nil 72} 73 74type userAgentTransport struct { 75 userAgent string 76 base http.RoundTripper 77} 78 79func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) { 80 rt := t.base 81 if rt == nil { 82 return nil, errors.New("transport: no Transport specified") 83 } 84 if t.userAgent == "" { 85 return rt.RoundTrip(req) 86 } 87 newReq := *req 88 newReq.Header = make(http.Header) 89 for k, vv := range req.Header { 90 newReq.Header[k] = vv 91 } 92 // TODO(cbro): append to existing User-Agent header? 93 newReq.Header["User-Agent"] = []string{t.userAgent} 94 return rt.RoundTrip(&newReq) 95} 96 97// Set at init time by dial_appengine.go. If nil, we're not on App Engine. 98var appengineUrlfetchHook func(context.Context) http.RoundTripper 99 100// baseTransport returns the base HTTP transport. 101// On App Engine, this is urlfetch.Transport, otherwise it's http.DefaultTransport. 102func baseTransport(ctx context.Context) http.RoundTripper { 103 if appengineUrlfetchHook != nil { 104 return appengineUrlfetchHook(ctx) 105 } 106 return http.DefaultTransport 107} 108