object-api/openstack-object-storage-dev/section_object-api-storage-object-svc.xml
Miranda Zhang b98907be31 Change CORS headers to be assigned to containers
Copy content from
http://docs.openstack.org/developer/swift/cors.html
to replace misleading content in
http://docs.openstack.org/api/openstack-object-storage/1.0/
content/assigning-cors-headers-to-requests.html

Change ">" to ">" in example

Use $publicURL to represent the endpoint which includes
the API version number and the account/tenantID.

Closes-Bug: #1260840

Change-Id: I9dfe46d610bfcf2f9646cb28a4246954c180252d
2014-02-06 01:03:18 +00:00

1060 lines
58 KiB
XML

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE section [
<!-- Some useful entities borrowed from HTML -->
<!ENTITY ndash "&#x2013;">
<!ENTITY mdash "&#x2014;">
<!ENTITY hellip "&#x2026;">
<!-- Useful for describing APIs -->
<!ENTITY COPY '<command xmlns="http://docbook.org/ns/docbook">COPY</command>'>
<!ENTITY GET '<command xmlns="http://docbook.org/ns/docbook">GET</command>'>
<!ENTITY HEAD '<command xmlns="http://docbook.org/ns/docbook">HEAD</command>'>
<!ENTITY PUT '<command xmlns="http://docbook.org/ns/docbook">PUT</command>'>
<!ENTITY POST '<command xmlns="http://docbook.org/ns/docbook">POST</command>'>
<!ENTITY DELETE '<command xmlns="http://docbook.org/ns/docbook">DELETE</command>'>
]>
<section xmlns="http://docbook.org/ns/docbook"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"
xml:id="storage-object-services">
<title>Storage Object Services</title>
<para>An object represents the data and metadata for the files
stored in the system. Through the ReST interface, you can
include metadata for an object by adding custom HTTP headers
to the request and the data payload as the request body.
Objects cannot exceed 5 GB and must have names that do not
exceed 1024 bytes after URL encoding. However, you can segment
a large object into 5 GB segments and upload the segments.
Then, you can download the segments as a single concatenated
object. You can use HTTP requests to work with the segments
and manifests directly.</para>
<informaltable rules="all">
<thead>
<tr>
<td colspan="1">Verb</td>
<td colspan="2">URI</td>
<td colspan="3">Description</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="1">&GET;</td>
<td colspan="2"
>/<parameter>account</parameter>/<parameter>container</parameter>/<parameter>object</parameter></td>
<td colspan="3">Gets object details.</td>
</tr>
<tr>
<td colspan="1">&PUT;</td>
<td colspan="2"
>/<parameter>account</parameter>/<parameter>container</parameter>/<parameter>object</parameter></td>
<td colspan="3">Creates or updates object.</td>
</tr>
<tr>
<td colspan="1">&PUT;</td>
<td colspan="2"
>/<parameter>account</parameter>/<parameter>container</parameter>/<parameter>object</parameter></td>
<td colspan="3">Chunked transfer encoding.</td>
</tr>
<tr>
<td colspan="1">&DELETE;</td>
<td colspan="2"
>/<parameter>account</parameter>/<parameter>container</parameter>/<parameter>object</parameter></td>
<td colspan="3">Deletes a specified object.</td>
</tr>
<tr>
<td colspan="1">&HEAD;</td>
<td colspan="2"
>/<parameter>account</parameter>/<parameter>container</parameter>/<parameter>object</parameter></td>
<td colspan="3">Gets object metadata.</td>
</tr>
<tr>
<td colspan="1">&POST;</td>
<td colspan="2"
>/<parameter>account</parameter>/<parameter>container</parameter>/<parameter>object</parameter></td>
<td colspan="3">Updates object metadata.</td>
</tr>
</tbody>
</informaltable>
<variablelist>
<title>Optional headers for HEAD and GET</title>
<varlistentry>
<term><code>X-Newest</code></term>
<listitem>
<para>Set the optional <code>X-newest</code> header to
<code>True</code> in HEAD and GET requests to
have Object Storage return the latest version of
the object. If set to <code>True</code>, Object
Storage queries all replicas to return the most
recent one. Without this header, Object Storage
responds faster after it finds one valid replica.
Because setting this header to <code>True</code>
is more expensive for the back end, use it only
when it is absolutely needed.</para>
</listitem>
</varlistentry>
</variablelist>
<section xml:id="retrieve-object">
<title>Get Object Details</title>
<para>Perform &GET; operations against an object to get object
data.</para>
<para>You can perform conditional &GET; requests by using the
following HTTP headers in the request:</para>
<itemizedlist>
<listitem>
<para><literal>If-Match</literal></para>
</listitem>
<listitem>
<para><literal>If-None-Match</literal></para>
</listitem>
<listitem>
<para><literal>If-Modified-Since</literal></para>
</listitem>
<listitem>
<para><literal>If-Unmodified-Since</literal></para>
</listitem>
</itemizedlist>
<para>These headers are documented in <link
xlink:href="http://www.ietf.org/rfc/rfc2616.txt"
>http://www.ietf.org/rfc/rfc2616.txt</link>.</para>
<para>You can use the HTTP <code>Range</code> header to fetch
portions of data by using one or more range
specifications. To specify many ranges, separate the range
specifications with a comma.</para>
<para>The types of range specifications are:</para>
<itemizedlist>
<listitem>
<para><emphasis role="bold">Byte range
specification</emphasis>. Use
FIRST_BYTE_OFFSET to specify the start of the data
range, and LAST_BYTE_OFFSET to specify the end.
You can omit the LAST_BYTE_OFFSET and if you do,
the value defaults to the offset of the last byte
of data.</para>
</listitem>
<listitem>
<para><emphasis role="bold">Suffix byte range
specification</emphasis>. Use LENGTH bytes to
specify the length of the data range.</para>
</listitem>
</itemizedlist>
<para>The following forms of the header specify the following
ranges of data:</para>
<informaltable rules="all">
<col width="40%"/>
<col width="60%"/>
<thead>
<tr>
<th>Header</th>
<th>Range of object data</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<para><code>Range: bytes=-5</code></para></td>
<td><para>The last five bytes.</para>
</td>
</tr>
<tr>
<td><para><code>Range:
bytes=10-14</code></para></td>
<td>The five bytes of data after a 10-byte
offset.</td>
</tr>
<tr>
<td><para><code>Range:
bytes=10-14,-5</code></para></td>
<td><para>A multi-part response that contains the five bytes of data after
a 10-byte offset and the last five
bytes.</para>
<para>The <code>Content-Type</code> of the
response is then
<code>multipart/byteranges</code>.</para>
</td>
</tr>
<tr>
<td>
<para><code>Range:
bytes=4-6</code></para></td>
<td><para>Bytes 4 to 6 inclusive.</para></td>
</tr>
<tr>
<td>
<para><code>Range:
bytes=2-2</code></para></td>
<td><para>Byte 2, which is the third byte of the data.</para>
</td>
</tr>
<tr>
<td>
<para><code>Range: bytes=6-</code></para></td>
<td><para>Byte 6 and after.</para>
</td>
</tr>
<tr>
<td>
<para><code>Range:
bytes=1-3,2-5</code></para></td>
<td><para>A multi-part response that contains
bytes 1 to 3 inclusive, and bytes 2 to 5
inclusive.</para>
<para>The <code>Content-Type</code> of the
response is then
<code>multipart/byteranges</code>.</para>
</td>
</tr>
</tbody>
</informaltable>
<example>
<title>Get Object Details HTTP Request</title>
<literallayout class="monospaced"><xi:include href="samples/object-get-req.txt" parse="text"/></literallayout>
</example>
<para>The object data is returned in the response body. Object
metadata is returned as HTTP headers. A status of
2<replaceable>xx</replaceable> (between 200 and 299,
inclusive) indicates success; status 404 (Not Found) is
returned if no such object exists.</para>
<example>
<title>Get Object Details HTTP Response</title>
<literallayout class="monospaced"><xi:include href="samples/object-get-resp.txt" parse="text"/></literallayout>
</example>
<para>These examples include an object that contains 10 bytes
of data: <literal>0123456789</literal>.</para>
<example>
<title>Get Object Details HTTP Request Using Range</title>
<literallayout class="monospaced"><xi:include href="samples/object-get-range-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Get Object Details HTTP Response When Using
Range</title>
<literallayout class="monospaced"><xi:include href="samples/object-get-range-resp.txt" parse="text"/></literallayout>
</example>
<example>
<title>Get Object Details HTTP Request Using Multiple
Ranges</title>
<literallayout class="monospaced"><xi:include href="samples/object-get-ranges-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Get Object Details HTTP Response When Using
Multiple Ranges</title>
<literallayout class="monospaced"><xi:include href="samples/object-get-ranges-resp.txt" parse="text"/></literallayout>
</example>
</section>
<section xml:id="create-update-object">
<title>Create or Update Object</title>
<para>&PUT; operations are used to write, or overwrite, an
object's content and metadata.</para>
<para>You can ensure end-to-end data integrity by including an
MD5 checksum of your object's data in the ETag header. You
are not required to include the ETag header, but it is
recommended to ensure that the storage system successfully
stored your object's content.</para>
<para>You can cause an object to expire after a certain date
by using the <code>X-Delete-At</code> or
<code>X-Delete-After</code> headers during an object
&PUT; operation. When Object Storage detects one of these
headers, the system automatically stops serving that
object at the specified time and shortly after the
expiration date, it removes the object from the storage
system.</para>
<para>The HTTP response will include the MD5 checksum of the
data written to the storage system. If you do not send the
ETag in the request, you should compare the value returned
with your content's MD5 locally to perform the end-to-end
data validation on the client side. For manifest objects,
the ETag is the MD5 checksum of the concatenated string of
ETags for each of the segments in the manifest. Refer to
<link linkend="large-object-creation">Create Large
Objects</link> for more information.</para>
<para>Objects can be assigned custom metadata by including
additional HTTP headers on the &PUT; request.</para>
<para>The object can be created with custom metadata via HTTP
headers identified with the <code>X-Object-Meta-</code>
prefix, or arbitrary headers set with the
<literal>allowed_headers</literal> option in the
object-server configuration.</para>
<example>
<title>Create or Update Object HTTP Request</title>
<literallayout class="monospaced"><xi:include href="samples/object-create-req.txt" parse="text"/></literallayout>
<literallayout class="monospaced">[ ... ]</literallayout>
</example>
<para>No response body is returned. A status code of 201
(Created) indicates a successful write; status 411 (Length
Required) denotes a missing <code>Content-Length</code> or
<code>Content-Type</code> header in the request. If
the MD5 checksum of the data written to the storage system
does NOT match the (optionally) supplied ETag value, a 422
(Unprocessable Entity) response is returned.</para>
<example>
<title>Create or Update Object HTTP Response</title>
<literallayout class="monospaced"><xi:include href="samples/object-create-resp.txt" parse="text"/></literallayout>
</example>
<section xml:id="chunked-transfer-encoding">
<title>Chunked Transfer Encoding</title>
<para>Users can upload data without needing to know in
advance the amount of data to be uploaded. Users can
do this by specifying an HTTP header of
<code>Transfer-Encoding: chunked</code> and not
using a <code>Content-Length</code> header. A good use
of this feature would be doing a DB dump, piping the
output through gzip, then piping the data directly
into OpenStack Object Storage without having to buffer
the data to disk to compute the file size. If users
attempt to upload more than 5GB with this method, the
server will close the TCP/IP connection after 5GB and
purge the customer data from the system. Users must
take responsibility for ensuring the data they
transfer will be less than 5GB or for splitting it
into 5GB chunks, each in its own storage object. If
you have files that are larger than 5GB see <xref
linkend="large-object-creation"/>.</para>
<example>
<title>Upload Unspecified Quantity of Content HTTP
Request</title>
<literallayout class="monospaced"><xi:include href="samples/chunked-transfer-encoding-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Upload Unspecified Quantity of Content HTTP
Response</title>
<literallayout class="monospaced"><xi:include href="samples/chunked-transfer-encoding-resp.txt" parse="text"/></literallayout>
</example>
</section>
</section>
<section xml:id="large-object-creation">
<title>Create Large Objects</title>
<para>The content of an object cannot be larger than 5GB (by
default). However, you can store larger content using two
types of objects:</para>
<itemizedlist>
<listitem>
<para>Divide your content into pieces and upload each
piece into its own object. These objects are known
as segment objects.</para>
</listitem>
<listitem>
<para>Create a manifest object. A manifest object
"points to" the segment objects.</para>
</listitem>
</itemizedlist>
<para>Segment objects do not have any special features and can
be created, updated, downloaded and deleted as described
elsewhere in this document. However, a manifest object is
special -- when you download, the system concatenates the
contents of the segment objects and returns this in the
response body of the request. This behavior extends to the
response headers returned by &GET; and &HEAD; operations.
The Content-Length is the total size of all segment
objects and the ETag is calculated by taking the ETag
value of each segment, concatenating them together and
then returning the MD5 checksum of the result.</para>
<note>
<para>If you use the &COPY; operation using a manifest
object as the source, the new object is a "normal"
object (not segmented). If the total size of the
source segment objects exceeds 5 GB, the &COPY;
operation fails. However, as explained later, you can
make a duplicate of the manifest object. This new
object can be larger than 5 GB.</para>
</note>
<para>The manifest object type are:</para>
<itemizedlist>
<listitem>
<para>Static large objects. The manifest object
content is an ordered list of the names of the
segment objects in json format.</para>
</listitem>
<listitem>
<para>Dynamic large objects. The manifest object has
no content. However, it has
<code>X-Object-Manifest</code> metadata. The
value of this is
<code>&lt;container>/&lt;prefix></code>, where
<code>&lt;container></code> is the name of the
container where the segment objects are stored and
<code>&lt;prefix></code> is a string that all
the segment objects have in common.</para>
</listitem>
</itemizedlist>
<para>While both types of manifest objects have similar
behavior, there are differences as explained in the
following table.</para>
<table rules="all">
<caption>Comparison of Static and Dynamic Large
Objects</caption>
<thead>
<tr>
<th>Feature</th>
<th>Static Large Object</th>
<th>Dynamic Large Object</th>
</tr>
</thead>
<tbody>
<tr>
<td>End-to-end integrity</td>
<td>Assured. The list of segments includes the MD5
checksum (ETag) of each segment. You cannot
upload the manifest object if the ETag in the
list differs from the segment object already
uploaded. If a segment is somehow lost, an
attempt to download the manifest object will
result in an error.</td>
<td>Not guaranteed. The eventual consistency model
means that although you may have uploaded a
segment object, it may not appear in the
container listing until later. If you download
the manifest before it appears in the
container, it will not form part of the
content returned in response to a &GET;
request.</td>
</tr>
<tr>
<td>Upload order</td>
<td>The segment objects must be uploaded before
the manifest object.</td>
<td>You can upload manifest and segment objects in
any order. You are recommended to upload the
manifest object after the segments in case a
premature download of the manifest occurs.
However, this is not enforced.</td>
</tr>
<tr>
<td>Removal or addition of segment objects</td>
<td>You cannot add or remove segment objects from
the manifest. However, you can create a
completely new manifest object of the same
name with a different manifest list.</td>
<td>You can upload new segment objects or remove
existing segments --- the names must simply
match the <code>&lt;prefix></code> supplied in
<code>X-Object-Manifest</code>.</td>
</tr>
<tr>
<td>Segment object size and number</td>
<td>Segment objects must be at least 1MB in size
(by default). The final segment object can be
any size. At most 1000 segments are supported
(by default).</td>
<td>Segment objects can be of any size.</td>
</tr>
<tr>
<td>Segment object container name</td>
<td>The manifest list includes the container name
of each object, i.e., segment objects may be
in different containers.</td>
<td>All segment objects must be in the same
container</td>
</tr>
<tr>
<td>Manifest Object Metadata</td>
<td>The object has
<code>X-Static-Large-Object</code> set to
<code>true</code>. You do not set this
metadata directly. Instead the system sets it
when you &PUT; a static manifest object.</td>
<td>The <code>X-Object-Manifest</code> value is
the <code>&lt;container>/&lt;prefix></code>
indicating where the segment objects are
located. You supply this request header in the
&PUT; operation</td>
</tr>
<tr>
<td>Making a copy of the manifest object</td>
<td>To make a copy of the manifest object, include
the <code>?multipart-manifest=get</code> query
string with the &COPY; operation. The new
object contains the same manifest as the
original. The segment objects are not copied.
Instead, both the original and new manifest
objects share the same set of segment
objects.</td>
<td>The &COPY; operation does not create a
manifest object. To duplicate a manifest
object, use the &GET; operation to read the
value of <code>X-Object-Manifest</code> and
use this value in the
<code>X-Object-Manifest</code> request
header in a &PUT; operation. This creates a
new manifest object that shares the same set
of segment objects as the original manifest
object.</td>
</tr>
</tbody>
</table>
<section xml:id="dynamic-large-object-creation">
<title>Dynamic Large Objects</title>
<para>Objects that are larger than 5GB must be segmented,
prior to upload. You then upload the segments like you
would any other object and create a dynamic large
manifest object telling OpenStack Object Storage how
to find the segments of the large object. The segments
remain individually addressable, but retrieving the
manifest object streams all the segments concatenated.
There is no limit to the number of segments that can
be a part of a single large object.</para>
<para>To ensure the download works correctly, you must
upload all the object segments to the same container
and ensure that each object name is prefixed in such a
way that it sorts in the order in which it should be
concatenated. You also create and upload a manifest
file. The manifest file is a zero-byte file with the
extra X-Object-Manifest:
&lt;container&gt;/&lt;prefix&gt; header, where
&lt;container&gt; is the container the object segments
are in and &lt;prefix&gt; is the common prefix for all
the segments. The container and common prefix must be
UTF-8 encoded and URL-encoded in the X-Object-Manifest
header.</para>
<para>It is best to upload all the segments first and then
create or update the manifest. With this method, the
full object will not be available for downloading
until the upload is complete. Also, you can upload a
new set of segments to a second location and then
update the manifest to point to this new location.
During the upload of the new segments, the original
manifest will still be available to download the first
set of segments.</para>
<example>
<title>Upload Segment of Large Object HTTP
Request</title>
<literallayout class="monospaced"><xi:include href="samples/large-object-upload-segment-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Upload Segment of Large Object HTTP
Response</title>
<literallayout class="monospaced">s</literallayout>
</example>
<para>No response body is returned. A status code of 2xx
(between 200 and 299, inclusive) indicates a
successful write; status 411 (Length Required) denotes
a missing <code>Content-Length</code> or
<code>Content-Type</code> header in the request.
If the MD5 checksum of the data written to the storage
system does NOT match the (optionally) supplied ETag
value, a 422 (Unprocessable Entity) response is
returned.</para>
<para>You can continue uploading segments like this
example shows, prior to uploading the manifest.</para>
<example>
<title>Upload Next Segment of Large Object HTTP
Request</title>
<literallayout class="monospaced"><xi:include href="samples/large-object-upload-next-segment-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Upload Next Segment of Large Object HTTP
Response</title>
<literallayout class="monospaced">w</literallayout>
</example>
<para>Next, upload the manifest you created that indicates
the container the object segments reside within. Note
that uploading additional segments after the manifest
is created will cause the concatenated object to be
that much larger but you do not need to recreate the
manifest file for subsequent additional
segments.</para>
<example>
<title>Upload Manifest HTTP Request</title>
<literallayout class="monospaced"><xi:include href="samples/upload-manifest-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Upload Manifest HTTP Response</title>
<literallayout class="monospaced"><xi:include href="samples/upload-manifest-resp.txt" parse="text"/></literallayout>
</example>
<para>The response's Content-Type for a &GET; or &HEAD; on
the manifest will be the same as the Content-Type set
during the PUT request that created the manifest. You
can easily change the Content-Type by reissuing the
&PUT; request.</para>
</section>
<section xml:id="static-large-objects">
<title>Static Large Objects</title>
<para>To create a static large object:</para>
<orderedlist>
<listitem>
<para>Divide your content into pieces and create
(upload) a segment object to contain each
piece. You must record the <code>ETag</code>
response header returned by the&PUT;
operation. Alternatively, you can calculate
the MD5 checksum of the segment prior to
uploading and include this in the
<code>ETag</code> request header. This
ensures that the upload cannot corrupt your
data.</para>
</listitem>
<listitem>
<para>List the name of each segment object along
with its size and MD5 checksum in order.
Create a manifest object. You indicate that
this is a manifest object by including the
<code>?multipart-manifest=put</code> query
string at the end of the manifest object
name.</para>
</listitem>
</orderedlist>
<para>The body of the &PUT; request on the manifest object
comprises a json list, where each element contains the
following:</para>
<itemizedlist>
<listitem>
<para><code>path</code> - this is the container
and object name in the following format:
<code>&lt;container-name>/&lt;object-name></code></para>
</listitem>
<listitem>
<para><code>etag</code> - this is the MD5 checksum
of the content of the segment object. This
must match the <code>ETag</code> of that
object.</para>
</listitem>
<listitem>
<para><code>size_bytes</code> - this is the size
of the segment object. This must match the
<code>Content-Length</code> of that
object</para>
</listitem>
</itemizedlist>
<example>
<title>Static Large Object Manifest List</title>
<para>This is an example containing three segment
objects. In this example, you can use several
containers and the object names do not have to
conform to a specific pattern, in contrast to
dynamic large objects.</para>
<literallayout class="monospaced"><xi:include href="samples/slo-manifest-example.txt" parse="text"/></literallayout>
</example>
<para>The <code>Content-Length</code> request header must
contain the length of the json content -- not the
length of the segment objects. However, after the
&PUT; operation completes, the
<code>Content-Length</code> metadata is set to the
total length of all the object segments. A similar
situation applies to the <code>ETag</code>. If used in
the &PUT; operation, it must contain the MD5 checksum
of the json content. The <code>ETag</code> metadata
value is then set to be the MD5 checksum of the
concatenated <code>ETag</code> values of the object
segments. You may also set the
<code>Content-Type</code> request header and
custom object metadata.</para>
<para>When the &PUT; operation sees the
<code>?multipart-manifest=put</code> query string,
it reads the request body and verifies that each
segment object exists and that the sizes and ETags
match. If there is a mismatch, the &PUT;operation will
fail.</para>
<para>If everything matches, the manifest object is
created. The <code>X-Static-Large-Object</code>
metadata is set to <code>true</code> indicating that
this is a static object manifest.</para>
<para>Normally when you perform a &GET; operation on the
manifest object, the response body contains the
concatenated content of the segment objects. To
download the manifest list, use the query string
<code>?multipart-manifest=get</code>. The
resulting list will not be identically formatted as
the manifest you originally used in the &PUT;
operation.</para>
<para>If you use the &DELETE; operation on a manifest
object, the manifest object is deleted -- the segment
objects are not affected. However, if you add the
query parameter
<code>?multipart-manifest=delete</code>, the
segment objects are deleted and if all are
successfully deleted, the manifest object is also
deleted.</para>
<para>To change the manifest, use a &PUT; operation with
the <code>?multipart-manifest=put</code> query string.
This will create a <emphasis>new</emphasis> manifest
object. You may also update the object metadata in the
usual way.</para>
</section>
</section>
<section xml:id="assigning-cors-headers-to-requests">
<title>Assign CORS headers to requests</title>
<para>CORS is a specification that stands for Cross-Origin
Resource Sharing. It defines how browsers and servers
communicate across origins using HTTP headers, such as
those assigned by Object Storage API requests. These
headers are supported with the Object Storage API. You can
read more about the definition of the Access-Control-
response headers and Origin response header at <link
xlink:href="http://www.w3.org/TR/access-control/"
>www.w3.org/TR/access-control/</link>.
<table rules="all">
<caption>Supported Headers</caption>
<col width="50%"/>
<col width="50%"/>
<thead>
<tr>
<td><emphasis>Metadata</emphasis></td>
<td><emphasis>Use</emphasis></td>
</tr>
</thead>
<tbody>
<tr>
<td>X-Container-Meta-Access-Control-Allow-Origin</td>
<td>Origins to be allowed to make Cross Origin Requests, space separated.</td>
</tr>
<tr>
<td>X-Container-Meta-Access-Control-Max-Age</td>
<td>Max age for the Origin to hold the preflight results.</td>
</tr>
<tr>
<td>X-Container-Meta-Access-Control-Expose-Headers</td>
<td>Headers exposed to the user agent (e.g. browser) in the the actual request response. Space separated.</td>
</tr>
</tbody>
</table>
</para>
<para>CORS metadata is held on the container only. The values given apply to the container itself and all objects within it.</para>
<example>
<title>Assign CORS header request: HTTP</title>
<para>In the example, the origin header is assigned that
indicates where the file came from. This allows you to
provide security that requests to your Object Storage
repository are indeed from the correct
origination:</para>
<literallayout class="monospaced"><xi:include href="samples/object-assign-cors-header-req.txt" parse="text"/></literallayout>
</example>
<para>You can find more details in the <link
xlink:href="http://docs.openstack.org/developer/swift/cors.html"
>Swift Documentation</link>.</para>
</section>
<section
xml:id="enabling-file-compression-with-content-encoding-header">
<title>Enabling File Compression with the Content-Encoding
Header</title>
<para>The Content-Encoding header allows a file to be
compressed without losing the identity of the underlying
media type of the file, for example, a video.</para>
<example>
<title>Content-Encoding Header HTTP Request</title>
<para>In the example, the content-encoding header is
assigned with an attachment type that indicates how
the file should be downloaded:</para>
<literallayout class="monospaced"><xi:include href="samples/content-encoding-header-req.txt" parse="text"/></literallayout>
</example>
</section>
<section
xml:id="enabling-browser-bypass-with-content-disposition-header">
<title>Enabling Browser Bypass with the Content-Disposition
Header</title>
<para>When an object is assigned the Content-Disposition
header you can override a browser's default behavior for a
file so that the download program saves the file rather
than displaying it using default browser settings.</para>
<example>
<title>Content-Disposition Header HTTP Request</title>
<para>In the example, the content-encoding header is
assigned with an attachment type that indicates how
the file should be downloaded.</para>
<literallayout class="monospaced"><xi:include href="samples/content-disposition-header-req.txt" parse="text"/></literallayout>
</example>
</section>
<section xml:id="Expiring_Objects-e1e3228">
<title>Expiring Objects with the X-Delete-After and
X-Delete-At Headers</title>
<para>When an object is assigned either an
<code>X-Delete-After</code> or
<code>X-Delete-At</code> header when doing a &PUT; or
&POST; on the object, it is scheduled for deletion. This
feature is helpful for objects you do not want to
permanently store, such as log files, recurring full
backups of a dataset, or documents or images you know will
be outdated at a future time.</para>
<para>The <code>X-Delete-At</code> header requires a Unix
Epoch timestamp, in integer form; for example: 1348691905
represents Wed, 26 Sep 2012 20:38:25 GMT.
<!-- Exchanged POSIX in / favor of Epoch for the / sake of clarity dsh 02-06-12 -->By
setting the header to a specific Epoch time, you indicate
when you want the object to expire, not be served, and be
deleted completely from the storage system.</para>
<para>The <code>X-Delete-After</code> header takes an integer
number of seconds and calculates the amount of time from
now that you want the object to be deleted. The proxy
server that receives the request converts this header into
an <code>X-Delete-At</code> header and calculates the
deletion time using its current time plus the value given
in seconds.</para>
<para>For existing objects that you want to assign expiration
headers to, use the &POST; operation.</para>
<example>
<title>Delete Object at HTTP Request</title>
<para>In the example, the <code>X-Delete-At</code> header
is assigned with a Unix Epoch timestamp in integer
form for Mon, 11 Jun 2012 15:38:25 GMT. Use <link
xlink:href="http://www.epochconverter.com/"
>http://www.epochconverter.com/</link> for example
timestamps and a batch converter.</para>
<literallayout class="monospaced"><xi:include href="samples/object-delete-at-req.txt" parse="text"/></literallayout>
</example>
<example>
<title>Delete Object after HTTP Request</title>
<para><!-- Reworded this here / paragraph to make more sense / and match what I see dsh 02-06-12 -->In
this example, the <code>X-Delete-After</code> header
is assigned a value in seconds, equivalent to 10 days.
After this time, the object expires.</para>
<!-- Removed, "The system then converts the time necessary for an <code>X-Delete-At</code> header operation." Doesn't seem to match dsh 02-06-12-->
<literallayout class="monospaced"><xi:include href="samples/object-delete-after-req.txt" parse="text"/></literallayout>
</example>
</section>
<section xml:id="Object_Versioning-e1e3230">
<!-- begin 4.3.2.7 -->
<title>Object Versioning</title>
<para>Object versioning allows you to store multiple versions
of your content to recover from unintended overwrites. It
provides an easy method to implement version control which
can be used on any type of content. It is strongly
recommended that you put non-current objects in a
container apart from where the current versions exist.
Once you enable Object Versioning on a container (such as
a "current version" container), &PUT;s to existing objects
in that container copy the prior object to a separate
"non-current version" container. Each of the non-current
versions of an object has a time stamp appended to it, so
you know when it was created.</para>
<para>To enable object versioning, your cloud provider has to
set <code>allow_versions</code> to <code>TRUE</code> in
their container config. Then, create a container where
your non-current versions will be written. Next, set the
metadata <code>X-Versions-Location</code> header on the
container that holds the current versions of your objects.
Set the metadata header to point to the new non-current
version container you created. The name of the container
must be UTF-8 encoded and then URL-encoded before putting
into the <code>X-Versions-Location</code> header. This is
where your non-current versions will be stored. Once this
is done, each object in your current-version container
will have Object Versioning enabled; changes to the
objects automatically create non-current versions in the
separate container.</para>
<para>Nothing is written to the non-current version container
when you initially &PUT; an object into the
current-version container. Only when you make edits to the
objects via &PUT; will you create non-current versions.
These non-current versions are labeled according to the
schema below.</para>
<para>
<emphasis>Naming Schema:</emphasis> Non-current versions
are assigned the name
&lt;length&gt;&lt;object_name&gt;/&lt;timestamp&gt;, where
length is the 3-character zero-padded hexadecimal
character length of the &lt;object_name&gt; and
&lt;timestamp&gt; is when the it was initially created as
a current version.</para>
<para>Any return status in the 2xx range, such as 202
(Accepted), denotes success. Status codes in the 4xx or
5xx range denote failure. You should retry your request if
you receive an error. Please note, however, that if you
have specified a container that does not exist as your
non-current version container, a status of 412
(Precondition Failed) returns when you edit the versioned
object. If you receive this error, check that the
container exists.</para>
<para>A &GET; to a versioned object returns the current
version of the object without having to do any request
redirects or metadata lookups.</para>
<para>A &POST; to a versioned object only updates the object's
metadata; it does not create a new version of the object.
In other words, new versions are only created when the
content of the object changes.</para>
<para>A &DELETE; to a versioned object removes the current
version of the object and replaces it with the next-most
current version, moving it from the non-current container
to the current. This next-most current version carries
with it any metadata last set on it. If want to completely
remove an object and you have five total versions of it,
you must &DELETE; it five times.</para>
<note>
<para>A large-object manifest file cannot be versioned,
but it may point to versioned segments.</para>
</note>
<para>To turn off Object Versioning on your current version
container, remove its <code>X-Versions-Location</code>
metadata by sending an empty key value.</para>
<example>
<title>Object Versioning with cURL</title>
<para>Make sure a version-storing container exists,
creating it if necessary (this example names it
"versions"). Then create a container with the
<code>X-Versions-Location</code> header. In this
example, this container is named "current". You can
also add the <code>X-Versions-Location</code> header
to an existing container. In this example, the name of
the container is “versions”; the location for the
current version is the container "current".</para>
<para>Create a container named versions.</para>
<screen><prompt>$</prompt> <userinput>curl -i -XPUT -H "X-Auth-Token: &lt;token&gt;" http://&lt;storage_url&gt;/versions</userinput></screen>
<para>Create a container named current with the
<code>X-Versions-Location</code> header that
references "versions".</para>
<screen><prompt>$</prompt> <userinput>curl -i -XPUT -H "X-Auth-Token: &lt;token&gt;" \
-H "X-Versions-Location: versions" http://&lt;storage_url&gt;/current</userinput></screen>
<para>Create an object (the first version):</para>
<screen><prompt>$</prompt> <userinput>curl -i -XPUT --data-binary 1 -H "X-Auth-Token: &lt;token&gt;" \
http://&lt;storage_url&gt;/current/myobject</userinput></screen>
<para>Now create a new version of that object:</para>
<screen><prompt>$</prompt> <userinput>curl -i -XPUT --data-binary 2 -H "X-Auth-Token: &lt;token&gt;" \
http://&lt;storage_url&gt;/current/myobject</userinput></screen>
<para>See a listing of the older versions of the
object:</para>
<screen><prompt>$</prompt> <userinput>curl -i -H "X-Auth-Token: &lt;token&gt;" \
http://&lt;storage_url&gt;/versions?prefix=008myobject/</userinput></screen>
<para>Now delete the current version of the object and see
that the older version is gone:</para>
<screen><prompt>$</prompt> <userinput>curl -i -XDELETE -H "X-Auth-Token: &lt;token&gt;" \
http://&lt;storage_url&gt;/current/myobject</userinput></screen>
<screen><userinput><prompt>$</prompt> curl -i -H "X-Auth-Token: &lt;token&gt;" \
http://&lt;storage_url&gt;/versions?prefix=008myobject/</userinput></screen>
</example>
</section>
<section xml:id="copy-object">
<title>Copy Object</title>
<para>Suppose you upload a file with the wrong object name or
content type, or you needed to move some objects to
another container. Without a server-side copy feature, you
would need to repeat uploading the same content and then
delete the existing object. With server-side object copy,
you can save the step of re-uploading the content and thus
also save the associated bandwidth charges, if any were to
apply.</para>
<para>There are two ways to copy an existing object to another
object in OpenStack Object Storage. One way is to do a
&PUT; to the new object (the target) location, but add the
<code>“X-Copy-From”</code> header to designate the
source of the data. The header value should be the
container and object name of the source object in the form
of “/container/object”. The container and object name must
be UTF-8 encoded and then URL-encoded. Also, the
<code>X-Copy-From</code> &PUT; requests require a
Content-Length header, even if it is zero (0).</para>
<example>
<title>Object Copy Method 1</title>
<literallayout class="monospaced"><xi:include href="samples/object-copy-1-req.txt" parse="text"/></literallayout>
</example>
<para>The second way to do an object copy is similar. Do a
&COPY; to the existing object, and include the
“Destination” header to specify the target of the copy.
The header value is the container and new object name in
the form of “/container/object”.</para>
<example>
<title>Object Copy Method 2</title>
<literallayout class="monospaced"><xi:include href="samples/object-copy-2-req.txt" parse="text"/></literallayout>
</example>
<para>With both of these methods, the destination container
must exist before attempting the copy.</para>
<para>If the source object is a manifest object, i.e., an
object with <code>X-Object-Manifest</code> or
<code>X-Static-Large-Object:true</code> metadata, the
copy operation concatenates the segment objects when
making the content for the destination object. You cannot
copy an object larger than 5GB (by default). Refer to
<link linkend="large-object-creation">Large Object
Creation</link> for more information about large
objects.</para>
<para>However, for a <link linkend="static-large-objects"
>Static Large Object</link> manifest, it is possible
to copy the manifest so that the destination becomes a
manifest object. Use the
<code>?multipart-manifest=get</code> query string to
indicate that you want a copy of the manifest object. The
new object will contain the same manifest as the original.
The segment objects are not copied. Instead, both the
original and new manifest objects share the same set of
segment objects.</para>
<para>If you wanted to move the object rather than copy it,
you need to send a &DELETE; request to the old object. A
move is simply a &COPY; + &DELETE;. All metadata is
preserved during the object copy. Note that you can set
metadata on the request to copy the object (either the
&PUT; or the &COPY;) and the metadata will overwrite any
conflicting keys on the target (new) object. One
interesting use case is to copy an object to itself and
set the content type to a new value. This is the only way
to change the content type of an existing object.</para>
</section>
<section xml:id="delete-object">
<title>Delete Object</title>
<para>&DELETE; operations on an object are used to permanently
remove an object from the storage system (metadata and
data).</para>
<para>Deleting an object is processed immediately at the time
of the request. Any subsequent &GET;, &HEAD;, &POST;, or
&DELETE; operations will return a 404 (Not Found)
error.</para>
<para>For <link linkend="static-large-objects">static large
object manifests</link>, you can add the query
parameter <code>?multipart-manifest=delete</code>. The
segment objects are deleted and if all are successfully
deleted, the manifest object is also deleted.</para>
<para>Objects with the <code>X-Delete-At</code> or
<code>X-Delete-After</code> header assigned are
deleted within one day of the expiration time and the
object is not served immediately after the expiration
time. Refer to <link linkend="Expiring_Objects-e1e3228"
>Expiring Objects</link> for more details.</para>
<example>
<title>Object Delete HTTP Request</title>
<literallayout class="monospaced"><xi:include href="samples/object-delete-req.txt" parse="text"/></literallayout>
</example>
<para>No response body is returned. A status code of 2xx (
between 200 and 299, inclusive) indicates success; status
code 404 (Not Found) is returned when the object does not
exist.</para>
<example>
<title>Object Delete HTTP Response</title>
<literallayout class="monospaced"><xi:include href="samples/object-delete-resp.txt" parse="text"/></literallayout>
</example>
</section>
<section xml:id="retrieve-object-metadata">
<title>Get Object Metadata</title>
<para>&HEAD; operations on an object are used to retrieve
object metadata and other standard HTTP headers.</para>
<para>The only required header to be sent in the request is
the authorization token.</para>
<example>
<title>Get Object Metadata HTTP Request</title>
<literallayout class="monospaced"><xi:include href="samples/object-metadata-req.txt" parse="text"/></literallayout>
</example>
<para>No response body is returned. Metadata is returned as
HTTP headers. A status code of 2xx (between 200 and 299,
inclusive) indicates success; status 404 (Not Found) is
returned when the object does not exist.</para>
<example>
<title>Get Object Metadata HTTP Response</title>
<literallayout class="monospaced"><xi:include href="samples/object-metadata-resp.txt" parse="text"/></literallayout>
</example>
</section>
<section xml:id="update-object-metadata">
<title>Update Object Metadata</title>
<para>&POST; operations against an object name are used to set
and overwrite arbitrary key/value metadata or to assign
headers not already assigned such as
<code>X-Delete-At</code> or
<code>X-Delete-After</code> for expiring objects. You
cannot use the &POST; operation to change any of the
object's other headers such as <code>Content-Type</code>,
<code>ETag</code>, etc. It is not used to upload
storage objects (see &PUT;). Also refer to <link
linkend="copy-object">copying an object</link> when
you need to update metadata or other headers such as
Content-Type or CORS headers.</para>
<para>Key names must be prefixed with
<code>X-Object-Meta-</code>. A &POST; request will
delete all existing metadata added with a previous
&PUT;/&POST;.</para>
<example>
<title>Update Object Metadata HTTP Request</title>
<literallayout class="monospaced"><xi:include href="samples/object-update-metadata-req.txt" parse="text"/></literallayout>
</example>
<para>No response body is returned. A status code of 2xx
(between 200 and 299, inclusive) indicates success; status
404 (Not Found) is returned if the requested object does
not exist.</para>
<example>
<title>Update Object Metadata HTTP Response</title>
<literallayout class="monospaced"><xi:include href="samples/object-update-metadata-resp.txt" parse="text"/></literallayout>
</example>
</section>
</section>