Moving calls to client helper while I'm at it
diff --git a/_site/openstack/objectstorage/v1/accounts/doc.go b/_site/openstack/objectstorage/v1/accounts/doc.go
new file mode 100644
index 0000000..f5f894a
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/accounts/doc.go
@@ -0,0 +1,8 @@
+// Package accounts contains functionality for working with Object Storage
+// account resources. An account is the top-level resource the object storage
+// hierarchy: containers belong to accounts, objects belong to containers.
+//
+// Another way of thinking of an account is like a namespace for all your
+// resources. It is synonymous with a project or tenant in other OpenStack
+// services.
+package accounts
diff --git a/_site/openstack/objectstorage/v1/accounts/requests.go b/_site/openstack/objectstorage/v1/accounts/requests.go
new file mode 100644
index 0000000..55fcb05
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/accounts/requests.go
@@ -0,0 +1,107 @@
+package accounts
+
+import (
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+)
+
+// GetOptsBuilder allows extensions to add additional headers to the Get
+// request.
+type GetOptsBuilder interface {
+	ToAccountGetMap() (map[string]string, error)
+}
+
+// GetOpts is a structure that contains parameters for getting an account's
+// metadata.
+type GetOpts struct {
+	Newest bool `h:"X-Newest"`
+}
+
+// ToAccountGetMap formats a GetOpts into a map[string]string of headers.
+func (opts GetOpts) ToAccountGetMap() (map[string]string, error) {
+	return gophercloud.BuildHeaders(opts)
+}
+
+// Get is a function that retrieves an account's metadata. To extract just the
+// custom metadata, call the ExtractMetadata method on the GetResult. To extract
+// all the headers that are returned (including the metadata), call the
+// ExtractHeaders method on the GetResult.
+func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) GetResult {
+	var res GetResult
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, err := opts.ToAccountGetMap()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+	}
+
+	resp, err := perigee.Request("HEAD", getURL(c), perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
+
+// UpdateOptsBuilder allows extensions to add additional headers to the Update
+// request.
+type UpdateOptsBuilder interface {
+	ToAccountUpdateMap() (map[string]string, error)
+}
+
+// UpdateOpts is a structure that contains parameters for updating, creating, or
+// deleting an account's metadata.
+type UpdateOpts struct {
+	Metadata          map[string]string
+	ContentType       string `h:"Content-Type"`
+	DetectContentType bool   `h:"X-Detect-Content-Type"`
+	TempURLKey        string `h:"X-Account-Meta-Temp-URL-Key"`
+	TempURLKey2       string `h:"X-Account-Meta-Temp-URL-Key-2"`
+}
+
+// ToAccountUpdateMap formats an UpdateOpts into a map[string]string of headers.
+func (opts UpdateOpts) ToAccountUpdateMap() (map[string]string, error) {
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, err
+	}
+	for k, v := range opts.Metadata {
+		headers["X-Account-Meta-"+k] = v
+	}
+	return headers, err
+}
+
+// Update is a function that creates, updates, or deletes an account's metadata.
+// To extract the headers returned, call the ExtractHeaders method on the
+// UpdateResult.
+func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) UpdateResult {
+	var res UpdateResult
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, err := opts.ToAccountUpdateMap()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+		for k, v := range headers {
+			h[k] = v
+		}
+	}
+
+	resp, err := perigee.Request("POST", updateURL(c), perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
diff --git a/_site/openstack/objectstorage/v1/accounts/requests_test.go b/_site/openstack/objectstorage/v1/accounts/requests_test.go
new file mode 100644
index 0000000..0090eea
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/accounts/requests_test.go
@@ -0,0 +1,53 @@
+package accounts
+
+import (
+	"net/http"
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	fake "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+var metadata = map[string]string{"gophercloud-test": "accounts"}
+
+func TestUpdateAccount(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts")
+
+		w.Header().Set("X-Account-Container-Count", "2")
+		w.Header().Set("X-Account-Bytes-Used", "14")
+		w.Header().Set("X-Account-Meta-Subject", "books")
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	options := &UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}}
+	_, err := Update(fake.ServiceClient(), options).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unable to update account: %v", err)
+	}
+}
+
+func TestGetAccount(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "HEAD")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		w.Header().Set("X-Account-Meta-Foo", "bar")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	expected := map[string]string{"Foo": "bar"}
+	actual, err := Get(fake.ServiceClient(), &GetOpts{}).ExtractMetadata()
+	if err != nil {
+		t.Fatalf("Unable to get account metadata: %v", err)
+	}
+	th.CheckDeepEquals(t, expected, actual)
+}
diff --git a/_site/openstack/objectstorage/v1/accounts/results.go b/_site/openstack/objectstorage/v1/accounts/results.go
new file mode 100644
index 0000000..8ff8183
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/accounts/results.go
@@ -0,0 +1,34 @@
+package accounts
+
+import (
+	"strings"
+
+	objectstorage "github.com/rackspace/gophercloud/openstack/objectstorage/v1"
+)
+
+// GetResult is returned from a call to the Get function. See v1.CommonResult.
+type GetResult struct {
+	objectstorage.CommonResult
+}
+
+// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
+// and returns the custom metatdata associated with the account.
+func (gr GetResult) ExtractMetadata() (map[string]string, error) {
+	if gr.Err != nil {
+		return nil, gr.Err
+	}
+
+	metadata := make(map[string]string)
+	for k, v := range gr.Resp.Header {
+		if strings.HasPrefix(k, "X-Account-Meta-") {
+			key := strings.TrimPrefix(k, "X-Account-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata, nil
+}
+
+// UpdateResult is returned from a call to the Update function. See v1.CommonResult.
+type UpdateResult struct {
+	objectstorage.CommonResult
+}
diff --git a/_site/openstack/objectstorage/v1/accounts/urls.go b/_site/openstack/objectstorage/v1/accounts/urls.go
new file mode 100644
index 0000000..9952fe4
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/accounts/urls.go
@@ -0,0 +1,11 @@
+package accounts
+
+import "github.com/rackspace/gophercloud"
+
+func getURL(c *gophercloud.ServiceClient) string {
+	return c.Endpoint
+}
+
+func updateURL(c *gophercloud.ServiceClient) string {
+	return getURL(c)
+}
diff --git a/_site/openstack/objectstorage/v1/accounts/urls_test.go b/_site/openstack/objectstorage/v1/accounts/urls_test.go
new file mode 100644
index 0000000..074d52d
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/accounts/urls_test.go
@@ -0,0 +1,26 @@
+package accounts
+
+import (
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const endpoint = "http://localhost:57909/"
+
+func endpointClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{Endpoint: endpoint}
+}
+
+func TestGetURL(t *testing.T) {
+	actual := getURL(endpointClient())
+	expected := endpoint
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestUpdateURL(t *testing.T) {
+	actual := updateURL(endpointClient())
+	expected := endpoint
+	th.CheckEquals(t, expected, actual)
+}
diff --git a/_site/openstack/objectstorage/v1/common.go b/_site/openstack/objectstorage/v1/common.go
new file mode 100644
index 0000000..1a6c44a
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/common.go
@@ -0,0 +1,25 @@
+package v1
+
+import (
+	"net/http"
+)
+
+// CommonResult is a structure that contains the response and error of a call to an
+// object storage endpoint.
+type CommonResult struct {
+	Resp *http.Response
+	Err  error
+}
+
+// ExtractHeaders will extract and return the headers from a *http.Response.
+func (cr CommonResult) ExtractHeaders() (http.Header, error) {
+	if cr.Err != nil {
+		return nil, cr.Err
+	}
+
+	var headers http.Header
+	if cr.Err != nil {
+		return headers, cr.Err
+	}
+	return cr.Resp.Header, nil
+}
diff --git a/_site/openstack/objectstorage/v1/containers/doc.go b/_site/openstack/objectstorage/v1/containers/doc.go
new file mode 100644
index 0000000..5fed553
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/containers/doc.go
@@ -0,0 +1,8 @@
+// Package containers contains functionality for working with Object Storage
+// container resources. A container serves as a logical namespace for objects
+// that are placed inside it - an object with the same name in two different
+// containers represents two different objects.
+//
+// In addition to containing objects, you can also use the container to control
+// access to objects by using an access control list (ACL).
+package containers
diff --git a/_site/openstack/objectstorage/v1/containers/requests.go b/_site/openstack/objectstorage/v1/containers/requests.go
new file mode 100644
index 0000000..ce3f540
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/containers/requests.go
@@ -0,0 +1,206 @@
+package containers
+
+import (
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// ListOptsBuilder allows extensions to add additional parameters to the List
+// request.
+type ListOptsBuilder interface {
+	ToContainerListParams() (bool, string, error)
+}
+
+// ListOpts is a structure that holds options for listing containers.
+type ListOpts struct {
+	Full      bool
+	Limit     int    `q:"limit"`
+	Marker    string `q:"marker"`
+	EndMarker string `q:"end_marker"`
+	Format    string `q:"format"`
+	Prefix    string `q:"prefix"`
+	Delimiter string `q:"delimiter"`
+}
+
+// ToContainerListParams formats a ListOpts into a query string and boolean
+// representing whether to list complete information for each container.
+func (opts ListOpts) ToContainerListParams() (bool, string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return false, "", err
+	}
+	return opts.Full, q.String(), nil
+}
+
+// List is a function that retrieves containers associated with the account as
+// well as account metadata. It returns a pager which can be iterated with the
+// EachPage function.
+func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
+	headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"}
+
+	url := listURL(c)
+	if opts != nil {
+		full, query, err := opts.ToContainerListParams()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+
+		if full {
+			headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"}
+		}
+	}
+
+	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
+		p := ContainerPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
+		p.MarkerPageBase.Owner = p
+		return p
+	}
+
+	pager := pagination.NewPager(c, url, createPage)
+	pager.Headers = headers
+	return pager
+}
+
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+	ToContainerCreateMap() (map[string]string, error)
+}
+
+// CreateOpts is a structure that holds parameters for creating a container.
+type CreateOpts struct {
+	Metadata          map[string]string
+	ContainerRead     string `h:"X-Container-Read"`
+	ContainerSyncTo   string `h:"X-Container-Sync-To"`
+	ContainerSyncKey  string `h:"X-Container-Sync-Key"`
+	ContainerWrite    string `h:"X-Container-Write"`
+	ContentType       string `h:"Content-Type"`
+	DetectContentType bool   `h:"X-Detect-Content-Type"`
+	IfNoneMatch       string `h:"If-None-Match"`
+	VersionsLocation  string `h:"X-Versions-Location"`
+}
+
+// ToContainerCreateMap formats a CreateOpts into a map of headers.
+func (opts CreateOpts) ToContainerCreateMap() (map[string]string, error) {
+	h, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, err
+	}
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+	return h, nil
+}
+
+// Create is a function that creates a new container.
+func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsBuilder) CreateResult {
+	var res CreateResult
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, err := opts.ToContainerCreateMap()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+	}
+
+	resp, err := perigee.Request("PUT", createURL(c, containerName), perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{201, 204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
+
+// Delete is a function that deletes a container.
+func Delete(c *gophercloud.ServiceClient, containerName string) DeleteResult {
+	var res DeleteResult
+	resp, err := perigee.Request("DELETE", deleteURL(c, containerName), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
+
+// UpdateOptsBuilder allows extensions to add additional parameters to the
+// Update request.
+type UpdateOptsBuilder interface {
+	ToContainerUpdateMap() (map[string]string, error)
+}
+
+// UpdateOpts is a structure that holds parameters for updating, creating, or
+// deleting a container's metadata.
+type UpdateOpts struct {
+	Metadata               map[string]string
+	ContainerRead          string `h:"X-Container-Read"`
+	ContainerSyncTo        string `h:"X-Container-Sync-To"`
+	ContainerSyncKey       string `h:"X-Container-Sync-Key"`
+	ContainerWrite         string `h:"X-Container-Write"`
+	ContentType            string `h:"Content-Type"`
+	DetectContentType      bool   `h:"X-Detect-Content-Type"`
+	RemoveVersionsLocation string `h:"X-Remove-Versions-Location"`
+	VersionsLocation       string `h:"X-Versions-Location"`
+}
+
+// ToContainerUpdateMap formats a CreateOpts into a map of headers.
+func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) {
+	h, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, err
+	}
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+	return h, nil
+}
+
+// Update is a function that creates, updates, or deletes a container's
+// metadata.
+func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) UpdateResult {
+	var res UpdateResult
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, err := opts.ToContainerUpdateMap()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+	}
+
+	resp, err := perigee.Request("POST", updateURL(c, containerName), perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
+
+// Get is a function that retrieves the metadata of a container. To extract just
+// the custom metadata, pass the GetResult response to the ExtractMetadata
+// function.
+func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
+	var res GetResult
+	resp, err := perigee.Request("HEAD", getURL(c, containerName), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
diff --git a/_site/openstack/objectstorage/v1/containers/requests_test.go b/_site/openstack/objectstorage/v1/containers/requests_test.go
new file mode 100644
index 0000000..9562676
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/containers/requests_test.go
@@ -0,0 +1,196 @@
+package containers
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud/pagination"
+	th "github.com/rackspace/gophercloud/testhelper"
+	fake "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+var metadata = map[string]string{"gophercloud-test": "containers"}
+
+func TestListContainerInfo(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `[
+				{
+					"count": 0,
+					"bytes": 0,
+					"name": "janeausten"
+				},
+				{
+					"count": 1,
+					"bytes": 14,
+					"name": "marktwain"
+				}
+			]`)
+		case "marktwain":
+			fmt.Fprintf(w, `[]`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	count := 0
+
+	List(fake.ServiceClient(), &ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractInfo(page)
+		if err != nil {
+			t.Errorf("Failed to extract container info: %v", err)
+			return false, err
+		}
+
+		expected := []Container{
+			Container{
+				Count: 0,
+				Bytes: 0,
+				Name:  "janeausten",
+			},
+			Container{
+				Count: 1,
+				Bytes: 14,
+				Name:  "marktwain",
+			},
+		}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestListContainerNames(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Set("Content-Type", "text/plain")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, "janeausten\nmarktwain\n")
+		case "marktwain":
+			fmt.Fprintf(w, ``)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	count := 0
+
+	List(fake.ServiceClient(), &ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractNames(page)
+		if err != nil {
+			t.Errorf("Failed to extract container names: %v", err)
+			return false, err
+		}
+
+		expected := []string{"janeausten", "marktwain"}
+
+		th.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreateContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "PUT")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Add("X-Container-Meta-Foo", "bar")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	options := CreateOpts{ContentType: "application/json", Metadata: map[string]string{"foo": "bar"}}
+	headers, err := Create(fake.ServiceClient(), "testContainer", options).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error creating container: %v", err)
+	}
+	th.CheckEquals(t, "bar", headers["X-Container-Meta-Foo"][0])
+}
+
+func TestDeleteContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "DELETE")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	_, err := Delete(fake.ServiceClient(), "testContainer").ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error deleting container: %v", err)
+	}
+}
+
+func TestUpateContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	options := &UpdateOpts{Metadata: map[string]string{"foo": "bar"}}
+	_, err := Update(fake.ServiceClient(), "testContainer", options).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error updating container metadata: %v", err)
+	}
+}
+
+func TestGetContainer(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+
+	th.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "HEAD")
+		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	_, err := Get(fake.ServiceClient(), "testContainer").ExtractMetadata()
+	if err != nil {
+		t.Fatalf("Unexpected error getting container metadata: %v", err)
+	}
+}
diff --git a/_site/openstack/objectstorage/v1/containers/results.go b/_site/openstack/objectstorage/v1/containers/results.go
new file mode 100644
index 0000000..227a9dc
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/containers/results.go
@@ -0,0 +1,139 @@
+package containers
+
+import (
+	"fmt"
+	"strings"
+
+	objectstorage "github.com/rackspace/gophercloud/openstack/objectstorage/v1"
+	"github.com/rackspace/gophercloud/pagination"
+
+	"github.com/mitchellh/mapstructure"
+)
+
+// Container represents a container resource.
+type Container struct {
+	// The total number of bytes stored in the container.
+	Bytes int `json:"bytes" mapstructure:"bytes"`
+
+	// The total number of objects stored in the container.
+	Count int `json:"count" mapstructure:"count"`
+
+	// The name of the container.
+	Name string `json:"name" mapstructure:"name"`
+}
+
+// ContainerPage is the page returned by a pager when traversing over a
+// collection of containers.
+type ContainerPage struct {
+	pagination.MarkerPageBase
+}
+
+// IsEmpty returns true if a ListResult contains no container names.
+func (r ContainerPage) IsEmpty() (bool, error) {
+	names, err := ExtractNames(r)
+	if err != nil {
+		return true, err
+	}
+	return len(names) == 0, nil
+}
+
+// LastMarker returns the last container name in a ListResult.
+func (r ContainerPage) LastMarker() (string, error) {
+	names, err := ExtractNames(r)
+	if err != nil {
+		return "", err
+	}
+	if len(names) == 0 {
+		return "", nil
+	}
+	return names[len(names)-1], nil
+}
+
+// ExtractInfo is a function that takes a ListResult and returns the containers' information.
+func ExtractInfo(page pagination.Page) ([]Container, error) {
+	untyped := page.(ContainerPage).Body.([]interface{})
+	results := make([]Container, len(untyped))
+	for index, each := range untyped {
+		container := each.(map[string]interface{})
+		err := mapstructure.Decode(container, &results[index])
+		if err != nil {
+			return results, err
+		}
+	}
+	return results, nil
+}
+
+// ExtractNames is a function that takes a ListResult and returns the containers' names.
+func ExtractNames(page pagination.Page) ([]string, error) {
+	casted := page.(ContainerPage)
+	ct := casted.Header.Get("Content-Type")
+
+	switch {
+	case strings.HasPrefix(ct, "application/json"):
+		parsed, err := ExtractInfo(page)
+		if err != nil {
+			return nil, err
+		}
+
+		names := make([]string, 0, len(parsed))
+		for _, container := range parsed {
+			names = append(names, container.Name)
+		}
+		return names, nil
+	case strings.HasPrefix(ct, "text/plain"):
+		names := make([]string, 0, 50)
+
+		body := string(page.(ContainerPage).Body.([]uint8))
+		for _, name := range strings.Split(body, "\n") {
+			if len(name) > 0 {
+				names = append(names, name)
+			}
+		}
+
+		return names, nil
+	default:
+		return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
+	}
+}
+
+// GetResult represents the result of a get operation.
+type GetResult struct {
+	objectstorage.CommonResult
+}
+
+// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
+// and returns the custom metadata associated with the container.
+func (gr GetResult) ExtractMetadata() (map[string]string, error) {
+	if gr.Err != nil {
+		return nil, gr.Err
+	}
+	metadata := make(map[string]string)
+	for k, v := range gr.Resp.Header {
+		if strings.HasPrefix(k, "X-Container-Meta-") {
+			key := strings.TrimPrefix(k, "X-Container-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata, nil
+}
+
+// CreateResult represents the result of a create operation. To extract the
+// the headers from the HTTP response, you can invoke the 'ExtractHeaders'
+// method on the result struct.
+type CreateResult struct {
+	objectstorage.CommonResult
+}
+
+// UpdateResult represents the result of an update operation. To extract the
+// the headers from the HTTP response, you can invoke the 'ExtractHeaders'
+// method on the result struct.
+type UpdateResult struct {
+	objectstorage.CommonResult
+}
+
+// DeleteResult represents the result of a delete operation. To extract the
+// the headers from the HTTP response, you can invoke the 'ExtractHeaders'
+// method on the result struct.
+type DeleteResult struct {
+	objectstorage.CommonResult
+}
diff --git a/_site/openstack/objectstorage/v1/containers/urls.go b/_site/openstack/objectstorage/v1/containers/urls.go
new file mode 100644
index 0000000..f864f84
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/containers/urls.go
@@ -0,0 +1,23 @@
+package containers
+
+import "github.com/rackspace/gophercloud"
+
+func listURL(c *gophercloud.ServiceClient) string {
+	return c.Endpoint
+}
+
+func createURL(c *gophercloud.ServiceClient, container string) string {
+	return c.ServiceURL(container)
+}
+
+func getURL(c *gophercloud.ServiceClient, container string) string {
+	return createURL(c, container)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, container string) string {
+	return createURL(c, container)
+}
+
+func updateURL(c *gophercloud.ServiceClient, container string) string {
+	return createURL(c, container)
+}
diff --git a/_site/openstack/objectstorage/v1/containers/urls_test.go b/_site/openstack/objectstorage/v1/containers/urls_test.go
new file mode 100644
index 0000000..d043a2a
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/containers/urls_test.go
@@ -0,0 +1,43 @@
+package containers
+
+import (
+	"github.com/rackspace/gophercloud"
+	th "github.com/rackspace/gophercloud/testhelper"
+	"testing"
+)
+
+const endpoint = "http://localhost:57909/"
+
+func endpointClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{Endpoint: endpoint}
+}
+
+func TestListURL(t *testing.T) {
+	actual := listURL(endpointClient())
+	expected := endpoint
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestCreateURL(t *testing.T) {
+	actual := createURL(endpointClient(), "foo")
+	expected := endpoint + "foo"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestGetURL(t *testing.T) {
+	actual := getURL(endpointClient(), "foo")
+	expected := endpoint + "foo"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestDeleteURL(t *testing.T) {
+	actual := deleteURL(endpointClient(), "foo")
+	expected := endpoint + "foo"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestUpdateURL(t *testing.T) {
+	actual := updateURL(endpointClient(), "foo")
+	expected := endpoint + "foo"
+	th.CheckEquals(t, expected, actual)
+}
diff --git a/_site/openstack/objectstorage/v1/objects/doc.go b/_site/openstack/objectstorage/v1/objects/doc.go
new file mode 100644
index 0000000..30a9add
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/objects/doc.go
@@ -0,0 +1,5 @@
+// Package objects contains functionality for working with Object Storage
+// object resources. An object is a resource that represents and contains data
+// - such as documents, images, and so on. You can also store custom metadata
+// with an object.
+package objects
diff --git a/_site/openstack/objectstorage/v1/objects/requests.go b/_site/openstack/objectstorage/v1/objects/requests.go
new file mode 100644
index 0000000..f6e355d
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/objects/requests.go
@@ -0,0 +1,420 @@
+package objects
+
+import (
+	"fmt"
+	"io"
+	"time"
+
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// ListOptsBuilder allows extensions to add additional parameters to the List
+// request.
+type ListOptsBuilder interface {
+	ToObjectListParams() (bool, string, error)
+}
+
+// ListOpts is a structure that holds parameters for listing objects.
+type ListOpts struct {
+	Full      bool
+	Limit     int    `q:"limit"`
+	Marker    string `q:"marker"`
+	EndMarker string `q:"end_marker"`
+	Format    string `q:"format"`
+	Prefix    string `q:"prefix"`
+	Delimiter string `q:"delimiter"`
+	Path      string `q:"path"`
+}
+
+// ToObjectListParams formats a ListOpts into a query string and boolean
+// representing whether to list complete information for each object.
+func (opts ListOpts) ToObjectListParams() (bool, string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return false, "", err
+	}
+	return opts.Full, q.String(), nil
+}
+
+// List is a function that retrieves all objects in a container. It also returns the details
+// for the container. To extract only the object information or names, pass the ListResult
+// response to the ExtractInfo or ExtractNames function, respectively.
+func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager {
+	headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"}
+
+	url := listURL(c, containerName)
+	if opts != nil {
+		full, query, err := opts.ToObjectListParams()
+		if err != nil {
+			fmt.Printf("Error building query string: %v", err)
+			return pagination.Pager{Err: err}
+		}
+		url += query
+
+		if full {
+			headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"}
+		}
+	}
+
+	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
+		p := ObjectPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
+		p.MarkerPageBase.Owner = p
+		return p
+	}
+
+	pager := pagination.NewPager(c, url, createPage)
+	pager.Headers = headers
+	return pager
+}
+
+// DownloadOptsBuilder allows extensions to add additional parameters to the
+// Download request.
+type DownloadOptsBuilder interface {
+	ToObjectDownloadParams() (map[string]string, string, error)
+}
+
+// DownloadOpts is a structure that holds parameters for downloading an object.
+type DownloadOpts struct {
+	IfMatch           string    `h:"If-Match"`
+	IfModifiedSince   time.Time `h:"If-Modified-Since"`
+	IfNoneMatch       string    `h:"If-None-Match"`
+	IfUnmodifiedSince time.Time `h:"If-Unmodified-Since"`
+	Range             string    `h:"Range"`
+	Expires           string    `q:"expires"`
+	MultipartManifest string    `q:"multipart-manifest"`
+	Signature         string    `q:"signature"`
+}
+
+// ToObjectDownloadParams formats a DownloadOpts into a query string and map of
+// headers.
+func (opts ListOpts) ToObjectDownloadParams() (map[string]string, string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return nil, "", err
+	}
+	h, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, q.String(), err
+	}
+	return h, q.String(), nil
+}
+
+// Download is a function that retrieves the content and metadata for an object.
+// To extract just the content, pass the DownloadResult response to the
+// ExtractContent function.
+func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) DownloadResult {
+	var res DownloadResult
+
+	url := downloadURL(c, containerName, objectName)
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, query, err := opts.ToObjectDownloadParams()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+
+		url += query
+	}
+
+	resp, err := perigee.Request("GET", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{200},
+	})
+	res.Err = err
+	res.Resp = &resp.HttpResponse
+	return res
+}
+
+// CreateOptsBuilder allows extensions to add additional parameters to the
+// Create request.
+type CreateOptsBuilder interface {
+	ToObjectCreateParams() (map[string]string, string, error)
+}
+
+// CreateOpts is a structure that holds parameters for creating an object.
+type CreateOpts struct {
+	Metadata           map[string]string
+	ContentDisposition string `h:"Content-Disposition"`
+	ContentEncoding    string `h:"Content-Encoding"`
+	ContentLength      int    `h:"Content-Length"`
+	ContentType        string `h:"Content-Type"`
+	CopyFrom           string `h:"X-Copy-From"`
+	DeleteAfter        int    `h:"X-Delete-After"`
+	DeleteAt           int    `h:"X-Delete-At"`
+	DetectContentType  string `h:"X-Detect-Content-Type"`
+	ETag               string `h:"ETag"`
+	IfNoneMatch        string `h:"If-None-Match"`
+	ObjectManifest     string `h:"X-Object-Manifest"`
+	TransferEncoding   string `h:"Transfer-Encoding"`
+	Expires            string `q:"expires"`
+	MultipartManifest  string `q:"multiple-manifest"`
+	Signature          string `q:"signature"`
+}
+
+// ToObjectCreateParams formats a CreateOpts into a query string and map of
+// headers.
+func (opts CreateOpts) ToObjectCreateParams() (map[string]string, string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return nil, "", err
+	}
+	h, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, q.String(), err
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+
+	return h, q.String(), nil
+}
+
+// Create is a function that creates a new object or replaces an existing object.
+func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.Reader, opts CreateOptsBuilder) CreateResult {
+	var res CreateResult
+	var reqBody []byte
+
+	url := createURL(c, containerName, objectName)
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, query, err := opts.ToObjectCreateParams()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+
+		url += query
+	}
+
+	if content != nil {
+		reqBody = make([]byte, 0)
+		_, err := content.Read(reqBody)
+		if err != nil {
+			res.Err = err
+			return res
+		}
+	}
+
+	resp, err := perigee.Request("PUT", url, perigee.Options{
+		ReqBody:     reqBody,
+		MoreHeaders: h,
+		OkCodes:     []int{201},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
+
+// CopyOptsBuilder allows extensions to add additional parameters to the
+// Copy request.
+type CopyOptsBuilder interface {
+	ToObjectCopyMap() (map[string]string, error)
+}
+
+// CopyOpts is a structure that holds parameters for copying one object to
+// another.
+type CopyOpts struct {
+	Metadata           map[string]string
+	ContentDisposition string `h:"Content-Disposition"`
+	ContentEncoding    string `h:"Content-Encoding"`
+	ContentType        string `h:"Content-Type"`
+	Destination        string `h:"Destination,required"`
+}
+
+// ToObjectCopyMap formats a CopyOpts into a map of headers.
+func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) {
+	if opts.Destination == "" {
+		return nil, fmt.Errorf("Required CopyOpts field 'Destination' not set.")
+	}
+	h, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, err
+	}
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+	return h, nil
+}
+
+// Copy is a function that copies one object to another.
+func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) CopyResult {
+	var res CopyResult
+	h := c.Provider.AuthenticatedHeaders()
+
+	headers, err := opts.ToObjectCopyMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	for k, v := range headers {
+		h[k] = v
+	}
+
+	url := copyURL(c, containerName, objectName)
+	resp, err := perigee.Request("COPY", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{201},
+	})
+	res.Resp = &resp.HttpResponse
+	return res
+}
+
+// DeleteOptsBuilder allows extensions to add additional parameters to the
+// Delete request.
+type DeleteOptsBuilder interface {
+	ToObjectDeleteString() (string, error)
+}
+
+// DeleteOpts is a structure that holds parameters for deleting an object.
+type DeleteOpts struct {
+	MultipartManifest string `q:"multipart-manifest"`
+}
+
+// ToObjectDeleteString formats a DeleteOpts into a query string.
+func (opts DeleteOpts) ToObjectDeleteString() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// Delete is a function that deletes an object.
+func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) DeleteResult {
+	var res DeleteResult
+	url := deleteURL(c, containerName, objectName)
+
+	if opts != nil {
+		query, err := opts.ToObjectDeleteString()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+		url += query
+	}
+
+	resp, err := perigee.Request("DELETE", url, perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
+
+// GetOptsBuilder allows extensions to add additional parameters to the
+// Get request.
+type GetOptsBuilder interface {
+	ToObjectGetString() (string, error)
+}
+
+// GetOpts is a structure that holds parameters for getting an object's metadata.
+type GetOpts struct {
+	Expires   string `q:"expires"`
+	Signature string `q:"signature"`
+}
+
+// ToObjectGetString formats a GetOpts into a query string.
+func (opts GetOpts) ToObjectGetString() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return "", err
+	}
+	return q.String(), nil
+}
+
+// Get is a function that retrieves the metadata of an object. To extract just the custom
+// metadata, pass the GetResult response to the ExtractMetadata function.
+func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) GetResult {
+	var res GetResult
+	url := getURL(c, containerName, objectName)
+
+	if opts != nil {
+		query, err := opts.ToObjectGetString()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+		url += query
+	}
+
+	resp, err := perigee.Request("HEAD", url, perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{200, 204},
+	})
+	res.Err = err
+	res.Resp = &resp.HttpResponse
+	return res
+}
+
+// UpdateOptsBuilder allows extensions to add additional parameters to the
+// Update request.
+type UpdateOptsBuilder interface {
+	ToObjectUpdateMap() (map[string]string, error)
+}
+
+// UpdateOpts is a structure that holds parameters for updating, creating, or deleting an
+// object's metadata.
+type UpdateOpts struct {
+	Metadata           map[string]string
+	ContentDisposition string `h:"Content-Disposition"`
+	ContentEncoding    string `h:"Content-Encoding"`
+	ContentType        string `h:"Content-Type"`
+	DeleteAfter        int    `h:"X-Delete-After"`
+	DeleteAt           int    `h:"X-Delete-At"`
+	DetectContentType  bool   `h:"X-Detect-Content-Type"`
+}
+
+// ToObjectUpdateMap formats a UpdateOpts into a map of headers.
+func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) {
+	h, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil, err
+	}
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+	return h, nil
+}
+
+// Update is a function that creates, updates, or deletes an object's metadata.
+func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) UpdateResult {
+	var res UpdateResult
+	h := c.Provider.AuthenticatedHeaders()
+
+	if opts != nil {
+		headers, err := opts.ToObjectUpdateMap()
+		if err != nil {
+			res.Err = err
+			return res
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+	}
+
+	url := updateURL(c, containerName, objectName)
+	resp, err := perigee.Request("POST", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{202},
+	})
+	res.Resp = &resp.HttpResponse
+	res.Err = err
+	return res
+}
diff --git a/_site/openstack/objectstorage/v1/objects/requests_test.go b/_site/openstack/objectstorage/v1/objects/requests_test.go
new file mode 100644
index 0000000..11d7c44
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/objects/requests_test.go
@@ -0,0 +1,248 @@
+package objects
+
+import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud/pagination"
+	"github.com/rackspace/gophercloud/testhelper"
+	fake "github.com/rackspace/gophercloud/testhelper/client"
+)
+
+var metadata = map[string]string{"Gophercloud-Test": "objects"}
+
+func TestDownloadObject(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, "Successful download with Gophercloud")
+	})
+
+	content, err := Download(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractContent()
+	if err != nil {
+		t.Fatalf("Unexpected error downloading object: %v", err)
+	}
+	if string(content) != "Successful download with Gophercloud" {
+		t.Errorf("Expected %s, got %s", "Successful download with Gophercloud", content)
+	}
+}
+
+func TestListObjectInfo(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `[
+				{
+					"hash": "451e372e48e0f6b1114fa0724aa79fa1",
+					"last_modified": "2009-11-10 23:00:00 +0000 UTC",
+					"bytes": 14,
+					"name": "goodbye",
+					"content_type": "application/octet-stream"
+				},
+				{
+					"hash": "451e372e48e0f6b1114fa0724aa79fa1",
+					"last_modified": "2009-11-10 23:00:00 +0000 UTC",
+					"bytes": 14,
+					"name": "hello",
+					"content_type": "application/octet-stream"
+				}
+			]`)
+		case "hello":
+			fmt.Fprintf(w, `[]`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	count := 0
+
+	err := List(fake.ServiceClient(), "testContainer", &ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractInfo(page)
+		if err != nil {
+			t.Errorf("Failed to extract object info: %v", err)
+			return false, err
+		}
+
+		expected := []Object{
+			Object{
+				Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
+				LastModified: "2009-11-10 23:00:00 +0000 UTC",
+				Bytes:        14,
+				Name:         "goodbye",
+				ContentType:  "application/octet-stream",
+			},
+			Object{
+				Hash:         "451e372e48e0f6b1114fa0724aa79fa1",
+				LastModified: "2009-11-10 23:00:00 +0000 UTC",
+				Bytes:        14,
+				Name:         "hello",
+				ContentType:  "application/octet-stream",
+			},
+		}
+
+		testhelper.CheckDeepEquals(t, actual, expected)
+
+		return true, nil
+	})
+	if err != nil {
+		t.Error(err)
+	}
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestListObjectNames(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Set("Content-Type", "text/plain")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, "hello\ngoodbye\n")
+		case "goodbye":
+			fmt.Fprintf(w, "")
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	count := 0
+	List(fake.ServiceClient(), "testContainer", &ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
+		count++
+		actual, err := ExtractNames(page)
+		if err != nil {
+			t.Errorf("Failed to extract object names: %v", err)
+			return false, err
+		}
+
+		expected := []string{"hello", "goodbye"}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
+	}
+}
+
+func TestCreateObject(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "PUT")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	content := bytes.NewBufferString("Did gyre and gimble in the wabe")
+	options := &CreateOpts{ContentType: "application/json"}
+	_, err := Create(fake.ServiceClient(), "testContainer", "testObject", content, options).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error creating object: %v", err)
+	}
+}
+
+func TestCopyObject(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "COPY")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		testhelper.TestHeader(t, r, "Destination", "/newTestContainer/newTestObject")
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	options := &CopyOpts{Destination: "/newTestContainer/newTestObject"}
+	_, err := Copy(fake.ServiceClient(), "testContainer", "testObject", options).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error copying object: %v", err)
+	}
+}
+
+func TestDeleteObject(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "DELETE")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	_, err := Delete(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error deleting object: %v", err)
+	}
+}
+
+func TestUpateObjectMetadata(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "POST")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		testhelper.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects")
+		w.WriteHeader(http.StatusAccepted)
+	})
+
+	_, err := Update(fake.ServiceClient(), "testContainer", "testObject", &UpdateOpts{Metadata: metadata}).ExtractHeaders()
+	if err != nil {
+		t.Fatalf("Unexpected error updating object metadata: %v", err)
+	}
+}
+
+func TestGetObject(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "HEAD")
+		testhelper.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.Header().Add("X-Object-Meta-Gophercloud-Test", "objects")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	expected := metadata
+	actual, err := Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata()
+	if err != nil {
+		t.Fatalf("Unexpected error getting object metadata: %v", err)
+	}
+	testhelper.CheckDeepEquals(t, expected, actual)
+}
diff --git a/_site/openstack/objectstorage/v1/objects/results.go b/_site/openstack/objectstorage/v1/objects/results.go
new file mode 100644
index 0000000..1dda7a3
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/objects/results.go
@@ -0,0 +1,158 @@
+package objects
+
+import (
+	"fmt"
+	"io/ioutil"
+	"strings"
+
+	objectstorage "github.com/rackspace/gophercloud/openstack/objectstorage/v1"
+	"github.com/rackspace/gophercloud/pagination"
+
+	"github.com/mitchellh/mapstructure"
+)
+
+// Object is a structure that holds information related to a storage object.
+type Object struct {
+	Bytes        int    `json:"bytes" mapstructure:"bytes"`
+	ContentType  string `json:"content_type" mapstructure:"content_type"`
+	Hash         string `json:"hash" mapstructure:"hash"`
+	LastModified string `json:"last_modified" mapstructure:"last_modified"`
+	Name         string `json:"name" mapstructure:"name"`
+}
+
+// ObjectPage is a single page of objects that is returned from a call to the
+// List function.
+type ObjectPage struct {
+	pagination.MarkerPageBase
+}
+
+// IsEmpty returns true if a ListResult contains no object names.
+func (r ObjectPage) IsEmpty() (bool, error) {
+	names, err := ExtractNames(r)
+	if err != nil {
+		return true, err
+	}
+	return len(names) == 0, nil
+}
+
+// LastMarker returns the last object name in a ListResult.
+func (r ObjectPage) LastMarker() (string, error) {
+	names, err := ExtractNames(r)
+	if err != nil {
+		return "", err
+	}
+	if len(names) == 0 {
+		return "", nil
+	}
+	return names[len(names)-1], nil
+}
+
+// ExtractInfo is a function that takes a page of objects and returns their full information.
+func ExtractInfo(page pagination.Page) ([]Object, error) {
+	untyped := page.(ObjectPage).Body.([]interface{})
+	results := make([]Object, len(untyped))
+	for index, each := range untyped {
+		object := each.(map[string]interface{})
+		err := mapstructure.Decode(object, &results[index])
+		if err != nil {
+			return results, err
+		}
+	}
+	return results, nil
+}
+
+// ExtractNames is a function that takes a page of objects and returns only their names.
+func ExtractNames(page pagination.Page) ([]string, error) {
+	casted := page.(ObjectPage)
+	ct := casted.Header.Get("Content-Type")
+	switch {
+	case strings.HasPrefix(ct, "application/json"):
+		parsed, err := ExtractInfo(page)
+		if err != nil {
+			return nil, err
+		}
+
+		names := make([]string, 0, len(parsed))
+		for _, object := range parsed {
+			names = append(names, object.Name)
+		}
+
+		return names, nil
+	case strings.HasPrefix(ct, "text/plain"):
+		names := make([]string, 0, 50)
+
+		body := string(page.(ObjectPage).Body.([]uint8))
+		for _, name := range strings.Split(body, "\n") {
+			if len(name) > 0 {
+				names = append(names, name)
+			}
+		}
+
+		return names, nil
+	case strings.HasPrefix(ct, "text/html"):
+		return []string{}, nil
+	default:
+		return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
+	}
+}
+
+// DownloadResult is a *http.Response that is returned from a call to the Download function.
+type DownloadResult struct {
+	objectstorage.CommonResult
+}
+
+// ExtractContent is a function that takes a DownloadResult (of type *http.Response)
+// and returns the object's content.
+func (dr DownloadResult) ExtractContent() ([]byte, error) {
+	if dr.Err != nil {
+		return nil, nil
+	}
+	var body []byte
+	defer dr.Resp.Body.Close()
+	body, err := ioutil.ReadAll(dr.Resp.Body)
+	if err != nil {
+		return body, fmt.Errorf("Error trying to read DownloadResult body: %v", err)
+	}
+	return body, nil
+}
+
+// GetResult is a *http.Response that is returned from a call to the Get function.
+type GetResult struct {
+	objectstorage.CommonResult
+}
+
+// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
+// and returns the custom metadata associated with the object.
+func (gr GetResult) ExtractMetadata() (map[string]string, error) {
+	if gr.Err != nil {
+		return nil, gr.Err
+	}
+	metadata := make(map[string]string)
+	for k, v := range gr.Resp.Header {
+		if strings.HasPrefix(k, "X-Object-Meta-") {
+			key := strings.TrimPrefix(k, "X-Object-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata, nil
+}
+
+// CreateResult represents the result of a create operation.
+type CreateResult struct {
+	objectstorage.CommonResult
+}
+
+// UpdateResult represents the result of an update operation.
+type UpdateResult struct {
+	objectstorage.CommonResult
+}
+
+// DeleteResult represents the result of a delete operation.
+type DeleteResult struct {
+	objectstorage.CommonResult
+}
+
+// CopyResult represents the result of a copy operation.
+type CopyResult struct {
+	objectstorage.CommonResult
+}
diff --git a/_site/openstack/objectstorage/v1/objects/urls.go b/_site/openstack/objectstorage/v1/objects/urls.go
new file mode 100644
index 0000000..d2ec62c
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/objects/urls.go
@@ -0,0 +1,33 @@
+package objects
+
+import (
+	"github.com/rackspace/gophercloud"
+)
+
+func listURL(c *gophercloud.ServiceClient, container string) string {
+	return c.ServiceURL(container)
+}
+
+func copyURL(c *gophercloud.ServiceClient, container, object string) string {
+	return c.ServiceURL(container, object)
+}
+
+func createURL(c *gophercloud.ServiceClient, container, object string) string {
+	return copyURL(c, container, object)
+}
+
+func getURL(c *gophercloud.ServiceClient, container, object string) string {
+	return copyURL(c, container, object)
+}
+
+func deleteURL(c *gophercloud.ServiceClient, container, object string) string {
+	return copyURL(c, container, object)
+}
+
+func downloadURL(c *gophercloud.ServiceClient, container, object string) string {
+	return copyURL(c, container, object)
+}
+
+func updateURL(c *gophercloud.ServiceClient, container, object string) string {
+	return copyURL(c, container, object)
+}
diff --git a/_site/openstack/objectstorage/v1/objects/urls_test.go b/_site/openstack/objectstorage/v1/objects/urls_test.go
new file mode 100644
index 0000000..1dcfe35
--- /dev/null
+++ b/_site/openstack/objectstorage/v1/objects/urls_test.go
@@ -0,0 +1,56 @@
+package objects
+
+import (
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+const endpoint = "http://localhost:57909/"
+
+func endpointClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{Endpoint: endpoint}
+}
+
+func TestListURL(t *testing.T) {
+	actual := listURL(endpointClient(), "foo")
+	expected := endpoint + "foo"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestCopyURL(t *testing.T) {
+	actual := copyURL(endpointClient(), "foo", "bar")
+	expected := endpoint + "foo/bar"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestCreateURL(t *testing.T) {
+	actual := createURL(endpointClient(), "foo", "bar")
+	expected := endpoint + "foo/bar"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestGetURL(t *testing.T) {
+	actual := getURL(endpointClient(), "foo", "bar")
+	expected := endpoint + "foo/bar"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestDeleteURL(t *testing.T) {
+	actual := deleteURL(endpointClient(), "foo", "bar")
+	expected := endpoint + "foo/bar"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestDownloadURL(t *testing.T) {
+	actual := downloadURL(endpointClient(), "foo", "bar")
+	expected := endpoint + "foo/bar"
+	th.CheckEquals(t, expected, actual)
+}
+
+func TestUpdateURL(t *testing.T) {
+	actual := updateURL(endpointClient(), "foo", "bar")
+	expected := endpoint + "foo/bar"
+	th.CheckEquals(t, expected, actual)
+}