blob: 56004b29232350f6346db2f7be7a9e8124842256 [file] [log] [blame]
Jon Perritt816d2a02014-03-11 20:49:46 -05001package objects
2
3import (
4 "fmt"
Jon Perritt8c93a302014-09-28 22:35:57 -05005 "io"
6 "time"
Ash Wilson604320e2014-09-10 16:02:28 -04007
8 "github.com/racker/perigee"
9 "github.com/rackspace/gophercloud"
Ash Wilsonca6f7562014-09-16 15:43:54 -040010 "github.com/rackspace/gophercloud/pagination"
Jon Perritt816d2a02014-03-11 20:49:46 -050011)
12
Jon Perritte90aced2014-10-12 23:24:06 -050013// ListOptsBuilder allows extensions to add additional parameters to the List
14// request.
15type ListOptsBuilder interface {
16 ToObjectListParams() (bool, string, error)
17}
18
Jon Perritt8c93a302014-09-28 22:35:57 -050019// ListOpts is a structure that holds parameters for listing objects.
20type ListOpts struct {
21 Full bool
Jon Perritt04851d32014-10-14 02:07:13 -050022 Limit int `q:"limit"`
23 Marker string `q:"marker"`
24 EndMarker string `q:"end_marker"`
25 Format string `q:"format"`
26 Prefix string `q:"prefix"`
27 Delimiter string `q:"delimiter"`
28 Path string `q:"path"`
Ash Wilsonca6f7562014-09-16 15:43:54 -040029}
30
Jon Perritte90aced2014-10-12 23:24:06 -050031// ToObjectListParams formats a ListOpts into a query string and boolean
32// representing whether to list complete information for each object.
33func (opts ListOpts) ToObjectListParams() (bool, string, error) {
34 q, err := gophercloud.BuildQueryString(opts)
35 if err != nil {
36 return false, "", err
37 }
38 return opts.Full, q.String(), nil
39}
40
Jon Perritt816d2a02014-03-11 20:49:46 -050041// List is a function that retrieves all objects in a container. It also returns the details
42// for the container. To extract only the object information or names, pass the ListResult
Jon Perritteb575642014-04-24 15:16:31 -050043// response to the ExtractInfo or ExtractNames function, respectively.
Jon Perritte90aced2014-10-12 23:24:06 -050044func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager {
45 headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"}
Jon Perritt816d2a02014-03-11 20:49:46 -050046
Jon Perrittea4e3012014-10-09 22:03:19 -050047 url := listURL(c, containerName)
Jon Perrittde47eac2014-09-30 15:34:17 -050048 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -050049 full, query, err := opts.ToObjectListParams()
Jon Perrittde47eac2014-09-30 15:34:17 -050050 if err != nil {
Jon Perrittde47eac2014-09-30 15:34:17 -050051 return pagination.Pager{Err: err}
52 }
Jon Perritte90aced2014-10-12 23:24:06 -050053 url += query
Jon Perritt816d2a02014-03-11 20:49:46 -050054
Jon Perritte90aced2014-10-12 23:24:06 -050055 if full {
56 headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"}
Jon Perrittde47eac2014-09-30 15:34:17 -050057 }
Ash Wilsonca6f7562014-09-16 15:43:54 -040058 }
59
Ash Wilsonb8b16f82014-10-20 10:19:49 -040060 createPage := func(r pagination.PageResult) pagination.Page {
61 p := ObjectPage{pagination.MarkerPageBase{PageResult: r}}
Ash Wilsonca6f7562014-09-16 15:43:54 -040062 p.MarkerPageBase.Owner = p
63 return p
Jon Perritt816d2a02014-03-11 20:49:46 -050064 }
65
Ash Wilsonca6f7562014-09-16 15:43:54 -040066 pager := pagination.NewPager(c, url, createPage)
67 pager.Headers = headers
68 return pager
Jon Perritt816d2a02014-03-11 20:49:46 -050069}
70
Jon Perritte90aced2014-10-12 23:24:06 -050071// DownloadOptsBuilder allows extensions to add additional parameters to the
72// Download request.
73type DownloadOptsBuilder interface {
74 ToObjectDownloadParams() (map[string]string, string, error)
75}
76
Jon Perritt8c93a302014-09-28 22:35:57 -050077// DownloadOpts is a structure that holds parameters for downloading an object.
78type DownloadOpts struct {
79 IfMatch string `h:"If-Match"`
80 IfModifiedSince time.Time `h:"If-Modified-Since"`
81 IfNoneMatch string `h:"If-None-Match"`
82 IfUnmodifiedSince time.Time `h:"If-Unmodified-Since"`
83 Range string `h:"Range"`
84 Expires string `q:"expires"`
85 MultipartManifest string `q:"multipart-manifest"`
86 Signature string `q:"signature"`
87}
88
Jon Perritte90aced2014-10-12 23:24:06 -050089// ToObjectDownloadParams formats a DownloadOpts into a query string and map of
90// headers.
91func (opts ListOpts) ToObjectDownloadParams() (map[string]string, string, error) {
92 q, err := gophercloud.BuildQueryString(opts)
93 if err != nil {
94 return nil, "", err
95 }
96 h, err := gophercloud.BuildHeaders(opts)
97 if err != nil {
98 return nil, q.String(), err
99 }
100 return h, q.String(), nil
101}
102
Jon Perritt816d2a02014-03-11 20:49:46 -0500103// Download is a function that retrieves the content and metadata for an object.
Jon Perritte90aced2014-10-12 23:24:06 -0500104// To extract just the content, pass the DownloadResult response to the
105// ExtractContent function.
106func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) DownloadResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500107 var res DownloadResult
Jon Perritt8c93a302014-09-28 22:35:57 -0500108
Jon Perrittea4e3012014-10-09 22:03:19 -0500109 url := downloadURL(c, containerName, objectName)
Ash Wilson77857dc2014-10-22 09:09:02 -0400110 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500111
Jon Perrittde47eac2014-09-30 15:34:17 -0500112 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -0500113 headers, query, err := opts.ToObjectDownloadParams()
Jon Perrittde47eac2014-09-30 15:34:17 -0500114 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500115 res.Err = err
116 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500117 }
118
119 for k, v := range headers {
120 h[k] = v
121 }
122
Jon Perritte90aced2014-10-12 23:24:06 -0500123 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500124 }
125
Jon Perritt816d2a02014-03-11 20:49:46 -0500126 resp, err := perigee.Request("GET", url, perigee.Options{
Jon Perritt816d2a02014-03-11 20:49:46 -0500127 MoreHeaders: h,
Ash Wilsone47ea9e2014-09-10 16:03:44 -0400128 OkCodes: []int{200},
Jon Perritt816d2a02014-03-11 20:49:46 -0500129 })
Jamie Hannaford2e784862014-10-27 10:40:27 +0100130
131 res.Body = resp.HttpResponse.Body
Jon Perritt5db08922014-09-30 21:32:48 -0500132 res.Err = err
Ash Wilson72e4d2c2014-10-20 10:27:30 -0400133 res.Header = resp.HttpResponse.Header
Jamie Hannaford2e784862014-10-27 10:40:27 +0100134
Jon Perritt5db08922014-09-30 21:32:48 -0500135 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500136}
137
Jon Perritte90aced2014-10-12 23:24:06 -0500138// CreateOptsBuilder allows extensions to add additional parameters to the
139// Create request.
140type CreateOptsBuilder interface {
141 ToObjectCreateParams() (map[string]string, string, error)
142}
143
Jon Perritt8c93a302014-09-28 22:35:57 -0500144// CreateOpts is a structure that holds parameters for creating an object.
145type CreateOpts struct {
146 Metadata map[string]string
147 ContentDisposition string `h:"Content-Disposition"`
148 ContentEncoding string `h:"Content-Encoding"`
149 ContentLength int `h:"Content-Length"`
150 ContentType string `h:"Content-Type"`
151 CopyFrom string `h:"X-Copy-From"`
152 DeleteAfter int `h:"X-Delete-After"`
153 DeleteAt int `h:"X-Delete-At"`
154 DetectContentType string `h:"X-Detect-Content-Type"`
155 ETag string `h:"ETag"`
156 IfNoneMatch string `h:"If-None-Match"`
157 ObjectManifest string `h:"X-Object-Manifest"`
158 TransferEncoding string `h:"Transfer-Encoding"`
159 Expires string `q:"expires"`
160 MultipartManifest string `q:"multiple-manifest"`
161 Signature string `q:"signature"`
Jon Perritt816d2a02014-03-11 20:49:46 -0500162}
163
Jon Perritte90aced2014-10-12 23:24:06 -0500164// ToObjectCreateParams formats a CreateOpts into a query string and map of
165// headers.
166func (opts CreateOpts) ToObjectCreateParams() (map[string]string, string, error) {
167 q, err := gophercloud.BuildQueryString(opts)
168 if err != nil {
169 return nil, "", err
170 }
171 h, err := gophercloud.BuildHeaders(opts)
172 if err != nil {
173 return nil, q.String(), err
174 }
175
176 for k, v := range opts.Metadata {
177 h["X-Object-Meta-"+k] = v
178 }
179
180 return h, q.String(), nil
181}
182
Jon Perritt816d2a02014-03-11 20:49:46 -0500183// Create is a function that creates a new object or replaces an existing object.
Jon Perritte90aced2014-10-12 23:24:06 -0500184func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.Reader, opts CreateOptsBuilder) CreateResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500185 var res CreateResult
Jon Perritt816d2a02014-03-11 20:49:46 -0500186
Jon Perrittea4e3012014-10-09 22:03:19 -0500187 url := createURL(c, containerName, objectName)
Ash Wilson77857dc2014-10-22 09:09:02 -0400188 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500189
Jon Perrittde47eac2014-09-30 15:34:17 -0500190 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -0500191 headers, query, err := opts.ToObjectCreateParams()
Jon Perrittde47eac2014-09-30 15:34:17 -0500192 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500193 res.Err = err
194 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500195 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500196
Jon Perrittde47eac2014-09-30 15:34:17 -0500197 for k, v := range headers {
198 h[k] = v
199 }
Jon Perritt816d2a02014-03-11 20:49:46 -0500200
Jon Perritte90aced2014-10-12 23:24:06 -0500201 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500202 }
Jon Perritt816d2a02014-03-11 20:49:46 -0500203
Jon Perritta77ba0d2014-10-17 01:15:29 -0500204 contentType := h["Content-Type"]
Jon Perritt816d2a02014-03-11 20:49:46 -0500205
Jon Perritt5db08922014-09-30 21:32:48 -0500206 resp, err := perigee.Request("PUT", url, perigee.Options{
Jon Perritta77ba0d2014-10-17 01:15:29 -0500207 ContentType: contentType,
208 ReqBody: content,
Jon Perritt816d2a02014-03-11 20:49:46 -0500209 MoreHeaders: h,
Jon Perritta77ba0d2014-10-17 01:15:29 -0500210 OkCodes: []int{201, 202},
Jon Perritt816d2a02014-03-11 20:49:46 -0500211 })
Ash Wilson72e4d2c2014-10-20 10:27:30 -0400212 res.Header = resp.HttpResponse.Header
Jon Perritt5db08922014-09-30 21:32:48 -0500213 res.Err = err
214 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500215}
216
Jon Perritte90aced2014-10-12 23:24:06 -0500217// CopyOptsBuilder allows extensions to add additional parameters to the
218// Copy request.
219type CopyOptsBuilder interface {
Jon Perritt04851d32014-10-14 02:07:13 -0500220 ToObjectCopyMap() (map[string]string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500221}
222
223// CopyOpts is a structure that holds parameters for copying one object to
224// another.
Jon Perritt8c93a302014-09-28 22:35:57 -0500225type CopyOpts struct {
226 Metadata map[string]string
227 ContentDisposition string `h:"Content-Disposition"`
228 ContentEncoding string `h:"Content-Encoding"`
229 ContentType string `h:"Content-Type"`
230 Destination string `h:"Destination,required"`
231}
232
Jon Perritt04851d32014-10-14 02:07:13 -0500233// ToObjectCopyMap formats a CopyOpts into a map of headers.
234func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500235 if opts.Destination == "" {
236 return nil, fmt.Errorf("Required CopyOpts field 'Destination' not set.")
237 }
238 h, err := gophercloud.BuildHeaders(opts)
239 if err != nil {
240 return nil, err
241 }
242 for k, v := range opts.Metadata {
243 h["X-Object-Meta-"+k] = v
244 }
245 return h, nil
246}
247
Jon Perritt816d2a02014-03-11 20:49:46 -0500248// Copy is a function that copies one object to another.
Jon Perritte90aced2014-10-12 23:24:06 -0500249func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) CopyResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500250 var res CopyResult
Ash Wilson77857dc2014-10-22 09:09:02 -0400251 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500252
Jon Perritt04851d32014-10-14 02:07:13 -0500253 headers, err := opts.ToObjectCopyMap()
Jon Perritt8c93a302014-09-28 22:35:57 -0500254 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500255 res.Err = err
256 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500257 }
Jon Perritte90aced2014-10-12 23:24:06 -0500258
Jon Perritt8c93a302014-09-28 22:35:57 -0500259 for k, v := range headers {
Jon Perritt816d2a02014-03-11 20:49:46 -0500260 h[k] = v
261 }
262
Jon Perrittea4e3012014-10-09 22:03:19 -0500263 url := copyURL(c, containerName, objectName)
Jon Perritt5db08922014-09-30 21:32:48 -0500264 resp, err := perigee.Request("COPY", url, perigee.Options{
Jon Perritt8c93a302014-09-28 22:35:57 -0500265 MoreHeaders: h,
266 OkCodes: []int{201},
267 })
Ash Wilson72e4d2c2014-10-20 10:27:30 -0400268 res.Header = resp.HttpResponse.Header
Jon Perritt5db08922014-09-30 21:32:48 -0500269 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500270}
271
Jon Perritte90aced2014-10-12 23:24:06 -0500272// DeleteOptsBuilder allows extensions to add additional parameters to the
273// Delete request.
274type DeleteOptsBuilder interface {
Jon Perritt26780d52014-10-14 11:35:58 -0500275 ToObjectDeleteQuery() (string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500276}
277
Jon Perritt8c93a302014-09-28 22:35:57 -0500278// DeleteOpts is a structure that holds parameters for deleting an object.
279type DeleteOpts struct {
280 MultipartManifest string `q:"multipart-manifest"`
281}
282
Jon Perritt26780d52014-10-14 11:35:58 -0500283// ToObjectDeleteQuery formats a DeleteOpts into a query string.
284func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500285 q, err := gophercloud.BuildQueryString(opts)
286 if err != nil {
287 return "", err
288 }
289 return q.String(), nil
290}
291
Jon Perritt8c93a302014-09-28 22:35:57 -0500292// Delete is a function that deletes an object.
Jon Perritte90aced2014-10-12 23:24:06 -0500293func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) DeleteResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500294 var res DeleteResult
Jon Perrittea4e3012014-10-09 22:03:19 -0500295 url := deleteURL(c, containerName, objectName)
Jon Perritt8c93a302014-09-28 22:35:57 -0500296
Jon Perrittde47eac2014-09-30 15:34:17 -0500297 if opts != nil {
Jon Perritt26780d52014-10-14 11:35:58 -0500298 query, err := opts.ToObjectDeleteQuery()
Jon Perrittde47eac2014-09-30 15:34:17 -0500299 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500300 res.Err = err
301 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500302 }
Jon Perritte90aced2014-10-12 23:24:06 -0500303 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500304 }
305
Jon Perritt10a7ec12014-10-27 11:29:33 -0500306 resp, err := perigee.Request("DELETE", url, perigee.Options{
Ash Wilson77857dc2014-10-22 09:09:02 -0400307 MoreHeaders: c.AuthenticatedHeaders(),
Jon Perritt8c93a302014-09-28 22:35:57 -0500308 OkCodes: []int{204},
309 })
Jon Perritt10a7ec12014-10-27 11:29:33 -0500310 res.Header = resp.HttpResponse.Header
311 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500312 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500313}
314
Jon Perritte90aced2014-10-12 23:24:06 -0500315// GetOptsBuilder allows extensions to add additional parameters to the
316// Get request.
317type GetOptsBuilder interface {
Jon Perritt26780d52014-10-14 11:35:58 -0500318 ToObjectGetQuery() (string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500319}
320
Jon Perritt8c93a302014-09-28 22:35:57 -0500321// GetOpts is a structure that holds parameters for getting an object's metadata.
322type GetOpts struct {
323 Expires string `q:"expires"`
324 Signature string `q:"signature"`
325}
326
Jon Perritt26780d52014-10-14 11:35:58 -0500327// ToObjectGetQuery formats a GetOpts into a query string.
328func (opts GetOpts) ToObjectGetQuery() (string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500329 q, err := gophercloud.BuildQueryString(opts)
330 if err != nil {
331 return "", err
332 }
333 return q.String(), nil
334}
335
Jon Perritt8c93a302014-09-28 22:35:57 -0500336// Get is a function that retrieves the metadata of an object. To extract just the custom
337// metadata, pass the GetResult response to the ExtractMetadata function.
Jon Perritte90aced2014-10-12 23:24:06 -0500338func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) GetResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500339 var res GetResult
Jon Perrittea4e3012014-10-09 22:03:19 -0500340 url := getURL(c, containerName, objectName)
Jon Perrittde47eac2014-09-30 15:34:17 -0500341
342 if opts != nil {
Jon Perritt26780d52014-10-14 11:35:58 -0500343 query, err := opts.ToObjectGetQuery()
Jon Perrittde47eac2014-09-30 15:34:17 -0500344 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500345 res.Err = err
346 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500347 }
Jon Perritte90aced2014-10-12 23:24:06 -0500348 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500349 }
350
Jon Perritt8c93a302014-09-28 22:35:57 -0500351 resp, err := perigee.Request("HEAD", url, perigee.Options{
Ash Wilson77857dc2014-10-22 09:09:02 -0400352 MoreHeaders: c.AuthenticatedHeaders(),
Jon Perritt8c93a302014-09-28 22:35:57 -0500353 OkCodes: []int{200, 204},
354 })
Ash Wilson72e4d2c2014-10-20 10:27:30 -0400355 res.Header = resp.HttpResponse.Header
Jon Perritt5db08922014-09-30 21:32:48 -0500356 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500357 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500358}
359
Jon Perritte90aced2014-10-12 23:24:06 -0500360// UpdateOptsBuilder allows extensions to add additional parameters to the
361// Update request.
362type UpdateOptsBuilder interface {
Jon Perritt04851d32014-10-14 02:07:13 -0500363 ToObjectUpdateMap() (map[string]string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500364}
365
Jon Perritt8c93a302014-09-28 22:35:57 -0500366// UpdateOpts is a structure that holds parameters for updating, creating, or deleting an
367// object's metadata.
368type UpdateOpts struct {
369 Metadata map[string]string
370 ContentDisposition string `h:"Content-Disposition"`
371 ContentEncoding string `h:"Content-Encoding"`
372 ContentType string `h:"Content-Type"`
373 DeleteAfter int `h:"X-Delete-After"`
374 DeleteAt int `h:"X-Delete-At"`
375 DetectContentType bool `h:"X-Detect-Content-Type"`
376}
377
Jon Perritt04851d32014-10-14 02:07:13 -0500378// ToObjectUpdateMap formats a UpdateOpts into a map of headers.
379func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500380 h, err := gophercloud.BuildHeaders(opts)
381 if err != nil {
382 return nil, err
383 }
384 for k, v := range opts.Metadata {
385 h["X-Object-Meta-"+k] = v
386 }
387 return h, nil
388}
389
Jon Perritt8c93a302014-09-28 22:35:57 -0500390// Update is a function that creates, updates, or deletes an object's metadata.
Jon Perritte90aced2014-10-12 23:24:06 -0500391func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) UpdateResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500392 var res UpdateResult
Ash Wilson77857dc2014-10-22 09:09:02 -0400393 h := c.AuthenticatedHeaders()
Jon Perritt8c93a302014-09-28 22:35:57 -0500394
Jon Perrittde47eac2014-09-30 15:34:17 -0500395 if opts != nil {
Jon Perritt04851d32014-10-14 02:07:13 -0500396 headers, err := opts.ToObjectUpdateMap()
Jon Perrittde47eac2014-09-30 15:34:17 -0500397 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500398 res.Err = err
399 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500400 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500401
Jon Perrittde47eac2014-09-30 15:34:17 -0500402 for k, v := range headers {
403 h[k] = v
404 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500405 }
406
Jon Perrittea4e3012014-10-09 22:03:19 -0500407 url := updateURL(c, containerName, objectName)
Jon Perritt5db08922014-09-30 21:32:48 -0500408 resp, err := perigee.Request("POST", url, perigee.Options{
Jon Perritt816d2a02014-03-11 20:49:46 -0500409 MoreHeaders: h,
Ash Wilsone47ea9e2014-09-10 16:03:44 -0400410 OkCodes: []int{202},
Jon Perritt816d2a02014-03-11 20:49:46 -0500411 })
Ash Wilson72e4d2c2014-10-20 10:27:30 -0400412 res.Header = resp.HttpResponse.Header
Jon Perritt5db08922014-09-30 21:32:48 -0500413 res.Err = err
414 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500415}