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 }