/usr/share/gocode/src/github.com/google/cups-connector/gcp/http.go is in google-cloud-print-connector 0.0~git20151105.24.1902938-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /*
Copyright 2015 Google Inc. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
package gcp
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/google/cups-connector/lib"
"golang.org/x/oauth2"
)
/*
glibc < 2.20 and OSX 10.10 have problems when C.getaddrinfo is called many
times concurrently. When the connector shares more than about 230 printers, and
GCP is called once per printer in concurrent goroutines, http.Client.Do starts
to fail with a lookup error.
This solution, a semaphore, limits the quantity of concurrent HTTP requests,
which also limits the quantity of concurrent calls to net.LookupHost (which
calls C.getaddrinfo()).
I would rather wait for the Go compiler to solve this problem than make this a
configurable option, hence this long-winded comment.
https://github.com/golang/go/issues/3575
https://github.com/golang/go/issues/6336
*/
var lock *lib.Semaphore = lib.NewSemaphore(100)
// newClient creates an instance of http.Client, wrapped with OAuth credentials.
func newClient(oauthClientID, oauthClientSecret, oauthAuthURL, oauthTokenURL, refreshToken string, scopes ...string) (*http.Client, error) {
config := oauth2.Config{
ClientID: oauthClientID,
ClientSecret: oauthClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: oauthAuthURL,
TokenURL: oauthTokenURL,
},
RedirectURL: RedirectURL,
Scopes: scopes,
}
token := oauth2.Token{RefreshToken: refreshToken}
client := config.Client(oauth2.NoContext, &token)
return client, nil
}
// getWithRetry calls get() and retries once on HTTP failure
// (response code != 200).
func getWithRetry(hc *http.Client, url string) (*http.Response, error) {
response, err := get(hc, url)
if response != nil && response.StatusCode == http.StatusOK {
return response, err
}
return get(hc, url)
}
// get GETs a URL. Returns the response object (not body), in case the body
// is very large.
//
// The caller must close the returned Response.Body object if err == nil.
func get(hc *http.Client, url string) (*http.Response, error) {
request, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
request.Header.Set("X-CloudPrint-Proxy", lib.ShortName)
lock.Acquire()
response, err := hc.Do(request)
lock.Release()
if err != nil {
return nil, fmt.Errorf("GET failure: %s", err)
}
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("GET HTTP-level failure: %s %s", url, response.Status)
}
return response, nil
}
// postWithRetry calls post() and retries once on HTTP failure
// (response code != 200).
func postWithRetry(hc *http.Client, url string, form url.Values) ([]byte, uint, int, error) {
responseBody, gcpErrorCode, httpStatusCode, err := post(hc, url, form)
if responseBody != nil && httpStatusCode == http.StatusOK {
return responseBody, gcpErrorCode, httpStatusCode, err
}
return post(hc, url, form)
}
// post POSTs to a URL. Returns the body of the response.
//
// Returns the response body, GCP error code, HTTP status, and error.
// None of the returned fields is guaranteed to be non-zero.
func post(hc *http.Client, url string, form url.Values) ([]byte, uint, int, error) {
requestBody := strings.NewReader(form.Encode())
request, err := http.NewRequest("POST", url, requestBody)
if err != nil {
return nil, 0, 0, err
}
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("X-CloudPrint-Proxy", lib.ShortName)
lock.Acquire()
response, err := hc.Do(request)
lock.Release()
if err != nil {
return nil, 0, 0, fmt.Errorf("POST failure: %s", err)
}
defer response.Body.Close()
responseBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, 0, response.StatusCode, err
}
if response.StatusCode != http.StatusOK {
return responseBody, 0, response.StatusCode, fmt.Errorf("/%s POST HTTP-level failure: %s", url, response.Status)
}
var responseStatus struct {
Success bool
Message string
ErrorCode uint
}
if err = json.Unmarshal(responseBody, &responseStatus); err != nil {
return responseBody, 0, response.StatusCode, err
}
if !responseStatus.Success {
return responseBody, responseStatus.ErrorCode, response.StatusCode, fmt.Errorf(
"%s call failed: %s", url, responseStatus.Message)
}
return responseBody, responseStatus.ErrorCode, response.StatusCode, nil
}
|