1
// Copyright 2011 Google Inc. 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.
5
// Package googleapi contains the common code shared by all Google API
22
"google.golang.org/api/googleapi/internal/uritemplates"
25
// ContentTyper is an interface for Readers which know (or would like
26
// to override) their Content-Type. If a media body doesn't implement
27
// ContentTyper, the type is sniffed from the content using
28
// http.DetectContentType.
29
type ContentTyper interface {
35
// Error contains an error response from the server.
37
// Code is the HTTP response status code and will always be populated.
38
Code int `json:"code"`
39
// Message is the server response message and is only populated when
40
// explicitly referenced by the JSON server response.
41
Message string `json:"message"`
42
// Body is the raw response returned by the server.
43
// It is often but not always JSON, depending on how the request fails.
49
// ErrorItem is a detailed error code & message from the Google API frontend.
50
type ErrorItem struct {
51
// Reason is the typed error code. For example: "some_example".
52
Reason string `json:"reason"`
53
// Message is the human-readable description of the error.
54
Message string `json:"message"`
57
func (e *Error) Error() string {
58
if len(e.Errors) == 0 && e.Message == "" {
59
return fmt.Sprintf("googleapi: got HTTP response code %d with body: %v", e.Code, e.Body)
62
fmt.Fprintf(&buf, "googleapi: Error %d: ", e.Code)
64
fmt.Fprintf(&buf, "%s", e.Message)
66
if len(e.Errors) == 0 {
67
return strings.TrimSpace(buf.String())
69
if len(e.Errors) == 1 && e.Errors[0].Message == e.Message {
70
fmt.Fprintf(&buf, ", %s", e.Errors[0].Reason)
73
fmt.Fprintln(&buf, "\nMore details:")
74
for _, v := range e.Errors {
75
fmt.Fprintf(&buf, "Reason: %s, Message: %s\n", v.Reason, v.Message)
80
type errorReply struct {
81
Error *Error `json:"error"`
84
// CheckResponse returns an error (of type *Error) if the response
85
// status code is not 2xx.
86
func CheckResponse(res *http.Response) error {
87
if res.StatusCode >= 200 && res.StatusCode <= 299 {
90
slurp, err := ioutil.ReadAll(res.Body)
92
jerr := new(errorReply)
93
err = json.Unmarshal(slurp, jerr)
94
if err == nil && jerr.Error != nil {
95
if jerr.Error.Code == 0 {
96
jerr.Error.Code = res.StatusCode
98
jerr.Error.Body = string(slurp)
103
Code: res.StatusCode,
108
type MarshalStyle bool
110
var WithDataWrapper = MarshalStyle(true)
111
var WithoutDataWrapper = MarshalStyle(false)
113
func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
114
buf := new(bytes.Buffer)
116
buf.Write([]byte(`{"data": `))
118
err := json.NewEncoder(buf).Encode(v)
123
buf.Write([]byte(`}`))
128
func getMediaType(media io.Reader) (io.Reader, string) {
129
if typer, ok := media.(ContentTyper); ok {
130
return media, typer.ContentType()
133
typ := "application/octet-stream"
134
buf := make([]byte, 1024)
135
n, err := media.Read(buf)
138
typ = http.DetectContentType(buf)
140
return io.MultiReader(bytes.NewBuffer(buf), media), typ
143
type Lengther interface {
147
// endingWithErrorReader from r until it returns an error. If the
148
// final error from r is os.EOF and e is non-nil, e is used instead.
149
type endingWithErrorReader struct {
154
func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
155
n, err = er.r.Read(p)
156
if err == io.EOF && er.e != nil {
162
func getReaderSize(r io.Reader) (io.Reader, int64) {
163
// Ideal case, the reader knows its own size.
164
if lr, ok := r.(Lengther); ok {
165
return r, int64(lr.Len())
168
// But maybe it's a seeker and we can seek to the end to find its size.
169
if s, ok := r.(io.Seeker); ok {
170
pos0, err := s.Seek(0, os.SEEK_CUR)
172
posend, err := s.Seek(0, os.SEEK_END)
174
_, err = s.Seek(pos0, os.SEEK_SET)
176
return r, posend - pos0
178
// We moved it forward but can't restore it.
179
// Seems unlikely, but can't really restore now.
180
return endingWithErrorReader{strings.NewReader(""), err}, posend - pos0
186
// Otherwise we have to make a copy to calculate how big the reader is.
187
buf := new(bytes.Buffer)
188
// TODO(bradfitz): put a cap on this copy? spill to disk after
190
_, err := io.Copy(buf, r)
191
return endingWithErrorReader{buf, err}, int64(buf.Len())
194
func typeHeader(contentType string) textproto.MIMEHeader {
195
h := make(textproto.MIMEHeader)
196
h.Set("Content-Type", contentType)
200
// countingWriter counts the number of bytes it receives to write, but
202
type countingWriter struct {
206
func (w countingWriter) Write(p []byte) (int, error) {
207
*w.n += int64(len(p))
211
// ConditionallyIncludeMedia does nothing if media is nil.
213
// bodyp is an in/out parameter. It should initially point to the
214
// reader of the application/json (or whatever) payload to send in the
215
// API request. It's updated to point to the multipart body reader.
217
// ctypep is an in/out parameter. It should initially point to the
218
// content type of the bodyp, usually "application/json". It's updated
219
// to the "multipart/related" content type, with random boundary.
221
// The return value is the content-length of the entire multpart body.
222
func ConditionallyIncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) (totalContentLength int64, ok bool) {
226
// Get the media type and size. The type check might return a
227
// different reader instance, so do the size check first,
228
// which looks at the specific type of the io.Reader.
230
if typer, ok := media.(ContentTyper); ok {
231
mediaType = typer.ContentType()
233
media, mediaSize := getReaderSize(media)
235
media, mediaType = getMediaType(media)
237
body, bodyType := *bodyp, *ctypep
238
body, bodySize := getReaderSize(body)
240
// Calculate how big the the multipart will be.
242
totalContentLength = bodySize + mediaSize
243
mpw := multipart.NewWriter(countingWriter{&totalContentLength})
244
mpw.CreatePart(typeHeader(bodyType))
245
mpw.CreatePart(typeHeader(mediaType))
250
mpw := multipart.NewWriter(pw)
252
*ctypep = "multipart/related; boundary=" + mpw.Boundary()
257
w, err := mpw.CreatePart(typeHeader(bodyType))
261
_, err = io.Copy(w, body)
266
w, err = mpw.CreatePart(typeHeader(mediaType))
270
_, err = io.Copy(w, media)
275
return totalContentLength, true
278
func ResolveRelative(basestr, relstr string) string {
279
u, _ := url.Parse(basestr)
280
rel, _ := url.Parse(relstr)
281
u = u.ResolveReference(rel)
283
us = strings.Replace(us, "%7B", "{", -1)
284
us = strings.Replace(us, "%7D", "}", -1)
288
// has4860Fix is whether this Go environment contains the fix for
289
// http://golang.org/issue/4860
292
// init initializes has4860Fix by checking the behavior of the net/http package.
302
has4860Fix = bytes.HasPrefix(b.Bytes(), []byte("GET http"))
305
// SetOpaque sets u.Opaque from u.Path such that HTTP requests to it
306
// don't alter any hex-escaped characters in u.Path.
307
func SetOpaque(u *url.URL) {
308
u.Opaque = "//" + u.Host + u.Path
310
u.Opaque = u.Scheme + ":" + u.Opaque
314
// Expand subsitutes any {encoded} strings in the URL passed in using
317
// This calls SetOpaque to avoid encoding of the parameters in the URL path.
318
func Expand(u *url.URL, expansions map[string]string) {
319
expanded, err := uritemplates.Expand(u.Path, expansions)
326
// CloseBody is used to close res.Body.
327
// Prior to calling Close, it also tries to Read a small amount to see an EOF.
328
// Not seeing an EOF can prevent HTTP Transports from reusing connections.
329
func CloseBody(res *http.Response) {
330
if res == nil || res.Body == nil {
333
// Justification for 3 byte reads: two for up to "\r\n" after
334
// a JSON/XML document, and then 1 to see EOF if we haven't yet.
335
// TODO(bradfitz): detect Go 1.3+ and skip these reads.
336
// See https://codereview.appspot.com/58240043
337
// and https://codereview.appspot.com/49570044
338
buf := make([]byte, 1)
339
for i := 0; i < 3; i++ {
340
_, err := res.Body.Read(buf)
349
// VariantType returns the type name of the given variant.
350
// If the map doesn't contain the named key or the value is not a []interface{}, "" is returned.
351
// This is used to support "variant" APIs that can return one of a number of different types.
352
func VariantType(t map[string]interface{}) string {
353
s, _ := t["type"].(string)
357
// ConvertVariant uses the JSON encoder/decoder to fill in the struct 'dst' with the fields found in variant 'v'.
358
// This is used to support "variant" APIs that can return one of a number of different types.
359
// It reports whether the conversion was successful.
360
func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
362
err := json.NewEncoder(&buf).Encode(v)
366
return json.Unmarshal(buf.Bytes(), dst) == nil
369
// A Field names a field to be retrieved with a partial response.
370
// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
372
// Partial responses can dramatically reduce the amount of data that must be sent to your application.
373
// In order to request partial responses, you can specify the full list of fields
374
// that your application needs by adding the Fields option to your request.
376
// Field strings use camelCase with leading lower-case characters to identify fields within the response.
378
// For example, if your response has a "NextPageToken" and a slice of "Items" with "Id" fields,
379
// you could request just those fields like this:
381
// svc.Events.List().Fields("nextPageToken", "items/id").Do()
383
// or if you were also interested in each Item's "Updated" field, you can combine them like this:
385
// svc.Events.List().Fields("nextPageToken", "items(id,updated)").Do()
387
// More information about field formatting can be found here:
388
// https://developers.google.com/+/api/#fields-syntax
390
// Another way to find field names is through the Google API explorer:
391
// https://developers.google.com/apis-explorer/#p/
394
// CombineFields combines fields into a single string.
395
func CombineFields(s []Field) string {
396
r := make([]string, len(s))
397
for i, v := range s {
400
return strings.Join(r, ",")