googleapi.go raw

   1  // Copyright 2011 Google LLC. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  // Package googleapi contains the common code shared by all Google API
   6  // libraries.
   7  package googleapi // import "google.golang.org/api/googleapi"
   8  
   9  import (
  10  	"bytes"
  11  	"encoding/json"
  12  	"fmt"
  13  	"io"
  14  	"net/http"
  15  	"net/url"
  16  	"strings"
  17  	"time"
  18  
  19  	"google.golang.org/api/internal/third_party/uritemplates"
  20  )
  21  
  22  // ContentTyper is an interface for Readers which know (or would like
  23  // to override) their Content-Type. If a media body doesn't implement
  24  // ContentTyper, the type is sniffed from the content using
  25  // http.DetectContentType.
  26  type ContentTyper interface {
  27  	ContentType() string
  28  }
  29  
  30  // A SizeReaderAt is a ReaderAt with a Size method.
  31  // An io.SectionReader implements SizeReaderAt.
  32  type SizeReaderAt interface {
  33  	io.ReaderAt
  34  	Size() int64
  35  }
  36  
  37  // ServerResponse is embedded in each Do response and
  38  // provides the HTTP status code and header sent by the server.
  39  type ServerResponse struct {
  40  	// HTTPStatusCode is the server's response status code. When using a
  41  	// resource method's Do call, this will always be in the 2xx range.
  42  	HTTPStatusCode int
  43  	// Header contains the response header fields from the server.
  44  	Header http.Header
  45  }
  46  
  47  const (
  48  	// Version defines the gax version being used. This is typically sent
  49  	// in an HTTP header to services.
  50  	Version = "0.5"
  51  
  52  	// UserAgent is the header string used to identify this package.
  53  	UserAgent = "google-api-go-client/" + Version
  54  
  55  	// DefaultUploadChunkSize is the default chunk size to use for resumable
  56  	// uploads if not specified by the user.
  57  	DefaultUploadChunkSize = 16 * 1024 * 1024
  58  
  59  	// MinUploadChunkSize is the minimum chunk size that can be used for
  60  	// resumable uploads.  All user-specified chunk sizes must be multiple of
  61  	// this value.
  62  	MinUploadChunkSize = 256 * 1024
  63  )
  64  
  65  // Error contains an error response from the server.
  66  type Error struct {
  67  	// Code is the HTTP response status code and will always be populated.
  68  	Code int `json:"code"`
  69  	// Message is the server response message and is only populated when
  70  	// explicitly referenced by the JSON server response.
  71  	Message string `json:"message"`
  72  	// Details provide more context to an error.
  73  	Details []interface{} `json:"details"`
  74  	// Body is the raw response returned by the server.
  75  	// It is often but not always JSON, depending on how the request fails.
  76  	Body string
  77  	// Header contains the response header fields from the server.
  78  	Header http.Header
  79  
  80  	Errors []ErrorItem
  81  	// err is typically a wrapped apierror.APIError, see
  82  	// google-api-go-client/internal/gensupport/error.go.
  83  	err error
  84  }
  85  
  86  // ErrorItem is a detailed error code & message from the Google API frontend.
  87  type ErrorItem struct {
  88  	// Reason is the typed error code. For example: "some_example".
  89  	Reason string `json:"reason"`
  90  	// Message is the human-readable description of the error.
  91  	Message string `json:"message"`
  92  }
  93  
  94  func (e *Error) Error() string {
  95  	if len(e.Errors) == 0 && e.Message == "" {
  96  		return fmt.Sprintf("googleapi: got HTTP response code %d with body: %v", e.Code, e.Body)
  97  	}
  98  	var buf bytes.Buffer
  99  	fmt.Fprintf(&buf, "googleapi: Error %d: ", e.Code)
 100  	if e.Message != "" {
 101  		fmt.Fprintf(&buf, "%s", e.Message)
 102  	}
 103  	if len(e.Details) > 0 {
 104  		var detailBuf bytes.Buffer
 105  		enc := json.NewEncoder(&detailBuf)
 106  		enc.SetIndent("", "  ")
 107  		if err := enc.Encode(e.Details); err == nil {
 108  			fmt.Fprint(&buf, "\nDetails:")
 109  			fmt.Fprintf(&buf, "\n%s", detailBuf.String())
 110  
 111  		}
 112  	}
 113  	if len(e.Errors) == 0 {
 114  		return strings.TrimSpace(buf.String())
 115  	}
 116  	if len(e.Errors) == 1 && e.Errors[0].Message == e.Message {
 117  		fmt.Fprintf(&buf, ", %s", e.Errors[0].Reason)
 118  		return buf.String()
 119  	}
 120  	fmt.Fprintln(&buf, "\nMore details:")
 121  	for _, v := range e.Errors {
 122  		fmt.Fprintf(&buf, "Reason: %s, Message: %s\n", v.Reason, v.Message)
 123  	}
 124  	return buf.String()
 125  }
 126  
 127  // Wrap allows an existing Error to wrap another error. See also [Error.Unwrap].
 128  func (e *Error) Wrap(err error) {
 129  	e.err = err
 130  }
 131  
 132  func (e *Error) Unwrap() error {
 133  	return e.err
 134  }
 135  
 136  type errorReply struct {
 137  	Error *Error `json:"error"`
 138  }
 139  
 140  // CheckResponse returns an error (of type *Error) if the response
 141  // status code is not 2xx.
 142  func CheckResponse(res *http.Response) error {
 143  	if res.StatusCode >= 200 && res.StatusCode <= 299 {
 144  		return nil
 145  	}
 146  	slurp, err := io.ReadAll(res.Body)
 147  	if err == nil {
 148  		return CheckResponseWithBody(res, slurp)
 149  	}
 150  	return &Error{
 151  		Code:   res.StatusCode,
 152  		Body:   string(slurp),
 153  		Header: res.Header,
 154  	}
 155  
 156  }
 157  
 158  // CheckResponseWithBody returns an error (of type *Error) if the response
 159  // status code is not 2xx. Distinct from CheckResponse to allow for checking
 160  // a previously-read body to maintain error detail content.
 161  func CheckResponseWithBody(res *http.Response, body []byte) error {
 162  	if res.StatusCode >= 200 && res.StatusCode <= 299 {
 163  		return nil
 164  	}
 165  
 166  	jerr, err := errorReplyFromBody(body)
 167  	if err == nil && jerr.Error != nil {
 168  		if jerr.Error.Code == 0 {
 169  			jerr.Error.Code = res.StatusCode
 170  		}
 171  		jerr.Error.Body = string(body)
 172  		jerr.Error.Header = res.Header
 173  		return jerr.Error
 174  	}
 175  
 176  	return &Error{
 177  		Code:   res.StatusCode,
 178  		Body:   string(body),
 179  		Header: res.Header,
 180  	}
 181  }
 182  
 183  // errorReplyFromBody attempts to get the error from body. The body
 184  // may be a JSON object or JSON array, or may be something else.
 185  func errorReplyFromBody(body []byte) (*errorReply, error) {
 186  	jerr := new(errorReply)
 187  	if len(body) > 0 && body[0] == '[' {
 188  		// Attempt JSON array
 189  		jsonArr := []*errorReply{jerr}
 190  		err := json.Unmarshal(body, &jsonArr)
 191  		return jerr, err
 192  	}
 193  	// Attempt JSON object
 194  	err := json.Unmarshal(body, jerr)
 195  	return jerr, err
 196  }
 197  
 198  // IsNotModified reports whether err is the result of the
 199  // server replying with http.StatusNotModified.
 200  // Such error values are sometimes returned by "Do" methods
 201  // on calls when If-None-Match is used.
 202  func IsNotModified(err error) bool {
 203  	if err == nil {
 204  		return false
 205  	}
 206  	ae, ok := err.(*Error)
 207  	return ok && ae.Code == http.StatusNotModified
 208  }
 209  
 210  // CheckMediaResponse returns an error (of type *Error) if the response
 211  // status code is not 2xx. Unlike CheckResponse it does not assume the
 212  // body is a JSON error document.
 213  // It is the caller's responsibility to close res.Body.
 214  func CheckMediaResponse(res *http.Response) error {
 215  	if res.StatusCode >= 200 && res.StatusCode <= 299 {
 216  		return nil
 217  	}
 218  	slurp, _ := io.ReadAll(io.LimitReader(res.Body, 1<<20))
 219  	return &Error{
 220  		Code:   res.StatusCode,
 221  		Body:   string(slurp),
 222  		Header: res.Header,
 223  	}
 224  }
 225  
 226  // MarshalStyle defines whether to marshal JSON with a {"data": ...} wrapper.
 227  type MarshalStyle bool
 228  
 229  // WithDataWrapper marshals JSON with a {"data": ...} wrapper.
 230  var WithDataWrapper = MarshalStyle(true)
 231  
 232  // WithoutDataWrapper marshals JSON without a {"data": ...} wrapper.
 233  var WithoutDataWrapper = MarshalStyle(false)
 234  
 235  // JSONReader is like JSONBuffer, but returns an io.Reader instead.
 236  func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
 237  	buf, err := wrap.JSONBuffer(v)
 238  	if err != nil {
 239  		return nil, err
 240  	}
 241  	return buf, nil
 242  }
 243  
 244  // JSONBuffer encodes the body and wraps it if needed.
 245  func (wrap MarshalStyle) JSONBuffer(v interface{}) (*bytes.Buffer, error) {
 246  	buf := new(bytes.Buffer)
 247  	if wrap {
 248  		buf.Write([]byte(`{"data": `))
 249  	}
 250  	err := json.NewEncoder(buf).Encode(v)
 251  	if err != nil {
 252  		return nil, err
 253  	}
 254  	if wrap {
 255  		buf.Write([]byte(`}`))
 256  	}
 257  	return buf, nil
 258  }
 259  
 260  // ProgressUpdater is a function that is called upon every progress update of a resumable upload.
 261  // This is the only part of a resumable upload (from googleapi) that is usable by the developer.
 262  // The remaining usable pieces of resumable uploads is exposed in each auto-generated API.
 263  type ProgressUpdater func(current, total int64)
 264  
 265  // MediaOption defines the interface for setting media options.
 266  type MediaOption interface {
 267  	setOptions(o *MediaOptions)
 268  }
 269  
 270  type contentTypeOption string
 271  
 272  func (ct contentTypeOption) setOptions(o *MediaOptions) {
 273  	o.ContentType = string(ct)
 274  	if o.ContentType == "" {
 275  		o.ForceEmptyContentType = true
 276  	}
 277  }
 278  
 279  // ContentType returns a MediaOption which sets the Content-Type header for media uploads.
 280  // If ctype is empty, the Content-Type header will be omitted.
 281  func ContentType(ctype string) MediaOption {
 282  	return contentTypeOption(ctype)
 283  }
 284  
 285  type chunkSizeOption int
 286  
 287  func (cs chunkSizeOption) setOptions(o *MediaOptions) {
 288  	size := int(cs)
 289  	if size%MinUploadChunkSize != 0 {
 290  		size += MinUploadChunkSize - (size % MinUploadChunkSize)
 291  	}
 292  	o.ChunkSize = size
 293  }
 294  
 295  // ChunkSize returns a MediaOption which sets the chunk size for media uploads.
 296  // size will be rounded up to the nearest multiple of 256K.
 297  // Media which contains fewer than size bytes will be uploaded in a single request.
 298  // Media which contains size bytes or more will be uploaded in separate chunks.
 299  // If size is zero, media will be uploaded in a single request.
 300  func ChunkSize(size int) MediaOption {
 301  	return chunkSizeOption(size)
 302  }
 303  
 304  type chunkTransferTimeoutOption time.Duration
 305  
 306  func (cd chunkTransferTimeoutOption) setOptions(o *MediaOptions) {
 307  	o.ChunkTransferTimeout = time.Duration(cd)
 308  }
 309  
 310  // ChunkTransferTimeout returns a MediaOption which sets a per-chunk
 311  // transfer timeout for resumable uploads. If a single chunk has been
 312  // attempting to upload for longer than this time then the old req got canceled and retried.
 313  // The default is no timeout for the request.
 314  func ChunkTransferTimeout(timeout time.Duration) MediaOption {
 315  	return chunkTransferTimeoutOption(timeout)
 316  }
 317  
 318  type chunkRetryDeadlineOption time.Duration
 319  
 320  func (cd chunkRetryDeadlineOption) setOptions(o *MediaOptions) {
 321  	o.ChunkRetryDeadline = time.Duration(cd)
 322  }
 323  
 324  // ChunkRetryDeadline returns a MediaOption which sets a per-chunk retry
 325  // deadline. If a single chunk has been attempting to upload for longer than
 326  // this time and the request fails, it will no longer be retried, and the error
 327  // will be returned to the caller.
 328  // This is only applicable for files which are large enough to require
 329  // a multi-chunk resumable upload.
 330  // The default value is 32s.
 331  // To set a deadline on the entire upload, use context timeout or cancellation.
 332  func ChunkRetryDeadline(deadline time.Duration) MediaOption {
 333  	return chunkRetryDeadlineOption(deadline)
 334  }
 335  
 336  type enableAutoChecksumOption struct{}
 337  
 338  func (d enableAutoChecksumOption) setOptions(o *MediaOptions) {
 339  	o.EnableAutoChecksum = true
 340  }
 341  
 342  // EnableAutoChecksum returns a MediaOption that enables automatic checksum
 343  // calculation, which is only supported for resumable multi-chunk uploads.
 344  // The computed checksum is sent on the final upload request to the server.
 345  // Writes are rejected in the event of a checksum mismatch.
 346  func EnableAutoChecksum() MediaOption {
 347  	return enableAutoChecksumOption{}
 348  }
 349  
 350  // MediaOptions stores options for customizing media upload.  It is not used by developers directly.
 351  type MediaOptions struct {
 352  	ContentType           string
 353  	ForceEmptyContentType bool
 354  	ChunkSize             int
 355  	ChunkRetryDeadline    time.Duration
 356  	ChunkTransferTimeout  time.Duration
 357  	EnableAutoChecksum    bool
 358  }
 359  
 360  // ProcessMediaOptions stores options from opts in a MediaOptions.
 361  // It is not used by developers directly.
 362  func ProcessMediaOptions(opts []MediaOption) *MediaOptions {
 363  	mo := &MediaOptions{ChunkSize: DefaultUploadChunkSize}
 364  	for _, o := range opts {
 365  		o.setOptions(mo)
 366  	}
 367  	return mo
 368  }
 369  
 370  // ResolveRelative resolves relatives such as "http://www.golang.org/" and
 371  // "topics/myproject/mytopic" into a single string, such as
 372  // "http://www.golang.org/topics/myproject/mytopic". It strips all parent
 373  // references (e.g. ../..) as well as anything after the host
 374  // (e.g. /bar/gaz gets stripped out of foo.com/bar/gaz).
 375  //
 376  // ResolveRelative panics if either basestr or relstr is not able to be parsed.
 377  func ResolveRelative(basestr, relstr string) string {
 378  	u, err := url.Parse(basestr)
 379  	if err != nil {
 380  		panic(fmt.Sprintf("failed to parse %q", basestr))
 381  	}
 382  	afterColonPath := ""
 383  	if i := strings.IndexRune(relstr, ':'); i > 0 {
 384  		afterColonPath = relstr[i+1:]
 385  		relstr = relstr[:i]
 386  	}
 387  	rel, err := url.Parse(relstr)
 388  	if err != nil {
 389  		panic(fmt.Sprintf("failed to parse %q", relstr))
 390  	}
 391  	u = u.ResolveReference(rel)
 392  	us := u.String()
 393  	if afterColonPath != "" {
 394  		us = fmt.Sprintf("%s:%s", us, afterColonPath)
 395  	}
 396  	us = strings.Replace(us, "%7B", "{", -1)
 397  	us = strings.Replace(us, "%7D", "}", -1)
 398  	us = strings.Replace(us, "%2A", "*", -1)
 399  	return us
 400  }
 401  
 402  // Expand subsitutes any {encoded} strings in the URL passed in using
 403  // the map supplied.
 404  //
 405  // This calls SetOpaque to avoid encoding of the parameters in the URL path.
 406  func Expand(u *url.URL, expansions map[string]string) {
 407  	escaped, unescaped, err := uritemplates.Expand(u.Path, expansions)
 408  	if err == nil {
 409  		u.Path = unescaped
 410  		u.RawPath = escaped
 411  	}
 412  }
 413  
 414  // CloseBody is used to close res.Body.
 415  // Prior to calling Close, it also tries to Read a small amount to see an EOF.
 416  // Not seeing an EOF can prevent HTTP Transports from reusing connections.
 417  func CloseBody(res *http.Response) {
 418  	if res == nil || res.Body == nil {
 419  		return
 420  	}
 421  	// Justification for 3 byte reads: two for up to "\r\n" after
 422  	// a JSON/XML document, and then 1 to see EOF if we haven't yet.
 423  	// TODO(bradfitz): detect Go 1.3+ and skip these reads.
 424  	// See https://codereview.appspot.com/58240043
 425  	// and https://codereview.appspot.com/49570044
 426  	buf := make([]byte, 1)
 427  	for i := 0; i < 3; i++ {
 428  		_, err := res.Body.Read(buf)
 429  		if err != nil {
 430  			break
 431  		}
 432  	}
 433  	res.Body.Close()
 434  
 435  }
 436  
 437  // VariantType returns the type name of the given variant.
 438  // If the map doesn't contain the named key or the value is not a []interface{}, "" is returned.
 439  // This is used to support "variant" APIs that can return one of a number of different types.
 440  func VariantType(t map[string]interface{}) string {
 441  	s, _ := t["type"].(string)
 442  	return s
 443  }
 444  
 445  // ConvertVariant uses the JSON encoder/decoder to fill in the struct 'dst' with the fields found in variant 'v'.
 446  // This is used to support "variant" APIs that can return one of a number of different types.
 447  // It reports whether the conversion was successful.
 448  func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
 449  	var buf bytes.Buffer
 450  	err := json.NewEncoder(&buf).Encode(v)
 451  	if err != nil {
 452  		return false
 453  	}
 454  	return json.Unmarshal(buf.Bytes(), dst) == nil
 455  }
 456  
 457  // A Field names a field to be retrieved with a partial response.
 458  // https://cloud.google.com/storage/docs/json_api/v1/how-tos/performance
 459  //
 460  // Partial responses can dramatically reduce the amount of data that must be sent to your application.
 461  // In order to request partial responses, you can specify the full list of fields
 462  // that your application needs by adding the Fields option to your request.
 463  //
 464  // Field strings use camelCase with leading lower-case characters to identify fields within the response.
 465  //
 466  // For example, if your response has a "NextPageToken" and a slice of "Items" with "Id" fields,
 467  // you could request just those fields like this:
 468  //
 469  //	svc.Events.List().Fields("nextPageToken", "items/id").Do()
 470  //
 471  // or if you were also interested in each Item's "Updated" field, you can combine them like this:
 472  //
 473  //	svc.Events.List().Fields("nextPageToken", "items(id,updated)").Do()
 474  //
 475  // Another way to find field names is through the Google API explorer:
 476  // https://developers.google.com/apis-explorer/#p/
 477  type Field string
 478  
 479  // CombineFields combines fields into a single string.
 480  func CombineFields(s []Field) string {
 481  	r := make([]string, len(s))
 482  	for i, v := range s {
 483  		r[i] = string(v)
 484  	}
 485  	return strings.Join(r, ",")
 486  }
 487  
 488  // A CallOption is an optional argument to an API call.
 489  // It should be treated as an opaque value by users of Google APIs.
 490  //
 491  // A CallOption is something that configures an API call in a way that is
 492  // not specific to that API; for instance, controlling the quota user for
 493  // an API call is common across many APIs, and is thus a CallOption.
 494  type CallOption interface {
 495  	Get() (key, value string)
 496  }
 497  
 498  // A MultiCallOption is an option argument to an API call and can be passed
 499  // anywhere a CallOption is accepted. It additionally supports returning a slice
 500  // of values for a given key.
 501  type MultiCallOption interface {
 502  	CallOption
 503  	GetMulti() (key string, value []string)
 504  }
 505  
 506  // QuotaUser returns a CallOption that will set the quota user for a call.
 507  // The quota user can be used by server-side applications to control accounting.
 508  // It can be an arbitrary string up to 40 characters, and will override UserIP
 509  // if both are provided.
 510  func QuotaUser(u string) CallOption { return quotaUser(u) }
 511  
 512  type quotaUser string
 513  
 514  func (q quotaUser) Get() (string, string) { return "quotaUser", string(q) }
 515  
 516  // UserIP returns a CallOption that will set the "userIp" parameter of a call.
 517  // This should be the IP address of the originating request.
 518  func UserIP(ip string) CallOption { return userIP(ip) }
 519  
 520  type userIP string
 521  
 522  func (i userIP) Get() (string, string) { return "userIp", string(i) }
 523  
 524  // Trace returns a CallOption that enables diagnostic tracing for a call.
 525  // traceToken is an ID supplied by Google support.
 526  func Trace(traceToken string) CallOption { return traceTok(traceToken) }
 527  
 528  type traceTok string
 529  
 530  func (t traceTok) Get() (string, string) { return "trace", "token:" + string(t) }
 531  
 532  type queryParameter struct {
 533  	key    string
 534  	values []string
 535  }
 536  
 537  // QueryParameter allows setting the value(s) of an arbitrary key.
 538  func QueryParameter(key string, values ...string) CallOption {
 539  	return queryParameter{key: key, values: append([]string{}, values...)}
 540  }
 541  
 542  // Get will never actually be called -- GetMulti will.
 543  func (q queryParameter) Get() (string, string) {
 544  	return "", ""
 545  }
 546  
 547  // GetMulti returns the key and values values associated to that key.
 548  func (q queryParameter) GetMulti() (string, []string) {
 549  	return q.key, q.values
 550  }
 551  
 552  // TODO: Fields too
 553