github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/google-api-go-client/googleapi/googleapi.go (about)

     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.
     4  
     5  // Package googleapi contains the common code shared by all Google API
     6  // libraries.
     7  package googleapi
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"mime/multipart"
    16  	"net/http"
    17  	"net/textproto"
    18  	"net/url"
    19  	"os"
    20  	"strings"
    21  )
    22  
    23  // ContentTyper is an interface for Readers which know (or would like
    24  // to override) their Content-Type. If a media body doesn't implement
    25  // ContentTyper, the type is sniffed from the content using
    26  // http.DetectContentType.
    27  type ContentTyper interface {
    28  	ContentType() string
    29  }
    30  
    31  const Version = "0.5"
    32  
    33  type Error struct {
    34  	Code    int    `json:"code"`
    35  	Message string `json:"message"`
    36  }
    37  
    38  func (e *Error) Error() string {
    39  	return fmt.Sprintf("googleapi: Error %d: %s", e.Code, e.Message)
    40  }
    41  
    42  type errorReply struct {
    43  	Error *Error `json:"error"`
    44  }
    45  
    46  func CheckResponse(res *http.Response) error {
    47  	if res.StatusCode >= 200 && res.StatusCode <= 299 {
    48  		return nil
    49  	}
    50  	slurp, err := ioutil.ReadAll(res.Body)
    51  	if err == nil {
    52  		jerr := new(errorReply)
    53  		err = json.Unmarshal(slurp, jerr)
    54  		if err == nil && jerr.Error != nil {
    55  			return jerr.Error
    56  		}
    57  	}
    58  	return fmt.Errorf("googleapi: got HTTP response code %d and error reading body: %v",
    59  		res.StatusCode, err)
    60  }
    61  
    62  type MarshalStyle bool
    63  
    64  var WithDataWrapper = MarshalStyle(true)
    65  var WithoutDataWrapper = MarshalStyle(false)
    66  
    67  func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
    68  	buf := new(bytes.Buffer)
    69  	if wrap {
    70  		buf.Write([]byte(`{"data": `))
    71  	}
    72  	err := json.NewEncoder(buf).Encode(v)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	if wrap {
    77  		buf.Write([]byte(`}`))
    78  	}
    79  	return buf, nil
    80  }
    81  
    82  func getMediaType(media io.Reader) (io.Reader, string) {
    83  	if typer, ok := media.(ContentTyper); ok {
    84  		return media, typer.ContentType()
    85  	}
    86  
    87  	typ := "application/octet-stream"
    88  	buf := make([]byte, 1024)
    89  	n, err := media.Read(buf)
    90  	buf = buf[:n]
    91  	if err == nil {
    92  		typ = http.DetectContentType(buf)
    93  	}
    94  	return io.MultiReader(bytes.NewBuffer(buf), media), typ
    95  }
    96  
    97  type Lengther interface {
    98  	Len() int
    99  }
   100  
   101  // endingWithErrorReader from r until it returns an error.  If the
   102  // final error from r is os.EOF and e is non-nil, e is used instead.
   103  type endingWithErrorReader struct {
   104  	r io.Reader
   105  	e error
   106  }
   107  
   108  func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
   109  	n, err = er.r.Read(p)
   110  	if err == io.EOF && er.e != nil {
   111  		err = er.e
   112  	}
   113  	return
   114  }
   115  
   116  func getReaderSize(r io.Reader) (io.Reader, int64) {
   117  	// Ideal case, the reader knows its own size.
   118  	if lr, ok := r.(Lengther); ok {
   119  		return r, int64(lr.Len())
   120  	}
   121  
   122  	// But maybe it's a seeker and we can seek to the end to find its size.
   123  	if s, ok := r.(io.Seeker); ok {
   124  		pos0, err := s.Seek(0, os.SEEK_CUR)
   125  		if err == nil {
   126  			posend, err := s.Seek(0, os.SEEK_END)
   127  			if err == nil {
   128  				_, err = s.Seek(pos0, os.SEEK_SET)
   129  				if err == nil {
   130  					return r, posend - pos0
   131  				} else {
   132  					// We moved it forward but can't restore it.
   133  					// Seems unlikely, but can't really restore now.
   134  					return endingWithErrorReader{strings.NewReader(""), err}, posend - pos0
   135  				}
   136  			}
   137  		}
   138  	}
   139  
   140  	// Otherwise we have to make a copy to calculate how big the reader is.
   141  	buf := new(bytes.Buffer)
   142  	// TODO(bradfitz): put a cap on this copy? spill to disk after
   143  	// a certain point?
   144  	_, err := io.Copy(buf, r)
   145  	return endingWithErrorReader{buf, err}, int64(buf.Len())
   146  }
   147  
   148  func typeHeader(contentType string) textproto.MIMEHeader {
   149  	h := make(textproto.MIMEHeader)
   150  	h.Set("Content-Type", contentType)
   151  	return h
   152  }
   153  
   154  // countingWriter counts the number of bytes it receives to write, but
   155  // discards them.
   156  type countingWriter struct {
   157  	n *int64
   158  }
   159  
   160  func (w countingWriter) Write(p []byte) (int, error) {
   161  	*w.n += int64(len(p))
   162  	return len(p), nil
   163  }
   164  
   165  // ConditionallyIncludeMedia does nothing if media is nil.
   166  //
   167  // bodyp is an in/out parameter.  It should initially point to the
   168  // reader of the application/json (or whatever) payload to send in the
   169  // API request.  It's updated to point to the multipart body reader.
   170  //
   171  // ctypep is an in/out parameter.  It should initially point to the
   172  // content type of the bodyp, usually "application/json".  It's updated
   173  // to the "multipart/related" content type, with random boundary.
   174  //
   175  // The return value is the content-length of the entire multpart body.
   176  func ConditionallyIncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) (totalContentLength int64, ok bool) {
   177  	if media == nil {
   178  		return
   179  	}
   180  	// Get the media type and size. The type check might return a
   181  	// different reader instance, so do the size check first,
   182  	// which looks at the specific type of the io.Reader.
   183  	var mediaType string
   184  	if typer, ok := media.(ContentTyper); ok {
   185  		mediaType = typer.ContentType()
   186  	}
   187  	media, mediaSize := getReaderSize(media)
   188  	if mediaType == "" {
   189  		media, mediaType = getMediaType(media)
   190  	}
   191  	body, bodyType := *bodyp, *ctypep
   192  	body, bodySize := getReaderSize(body)
   193  
   194  	// Calculate how big the the multipart will be.
   195  	{
   196  		totalContentLength = bodySize + mediaSize
   197  		mpw := multipart.NewWriter(countingWriter{&totalContentLength})
   198  		mpw.CreatePart(typeHeader(bodyType))
   199  		mpw.CreatePart(typeHeader(mediaType))
   200  		mpw.Close()
   201  	}
   202  
   203  	pr, pw := io.Pipe()
   204  	mpw := multipart.NewWriter(pw)
   205  	*bodyp = pr
   206  	*ctypep = "multipart/related; boundary=" + mpw.Boundary()
   207  	go func() {
   208  		defer pw.Close()
   209  		defer mpw.Close()
   210  
   211  		w, err := mpw.CreatePart(typeHeader(bodyType))
   212  		if err != nil {
   213  			return
   214  		}
   215  		_, err = io.Copy(w, body)
   216  		if err != nil {
   217  			return
   218  		}
   219  
   220  		w, err = mpw.CreatePart(typeHeader(mediaType))
   221  		if err != nil {
   222  			return
   223  		}
   224  		_, err = io.Copy(w, media)
   225  		if err != nil {
   226  			return
   227  		}
   228  	}()
   229  	return totalContentLength, true
   230  }
   231  
   232  func ResolveRelative(basestr, relstr string) string {
   233  	u, _ := url.Parse(basestr)
   234  	rel, _ := url.Parse(relstr)
   235  	u = u.ResolveReference(rel)
   236  	us := u.String()
   237  	us = strings.Replace(us, "%7B", "{", -1)
   238  	us = strings.Replace(us, "%7D", "}", -1)
   239  	return us
   240  }
   241  
   242  // has4860Fix is whether this Go environment contains the fix for
   243  // http://golang.org/issue/4860
   244  var has4860Fix bool
   245  
   246  // init initializes has4860Fix by checking the behavior of the net/http package.
   247  func init() {
   248  	r := http.Request{
   249  		URL: &url.URL{
   250  			Scheme: "http",
   251  			Opaque: "//opaque",
   252  		},
   253  	}
   254  	b := &bytes.Buffer{}
   255  	r.Write(b)
   256  	has4860Fix = bytes.HasPrefix(b.Bytes(), []byte("GET http"))
   257  }
   258  
   259  // SetOpaque sets u.Opaque from u.Path such that HTTP requests to it
   260  // don't alter any hex-escaped characters in u.Path.
   261  func SetOpaque(u *url.URL) {
   262  	u.Opaque = "//" + u.Host + u.Path
   263  	if !has4860Fix {
   264  		u.Opaque = u.Scheme + ":" + u.Opaque
   265  	}
   266  }