use generic parameter building functions; pagination in unit tests
diff --git a/openstack/storage/v1/containers/containers.go b/openstack/storage/v1/containers/containers.go
deleted file mode 100644
index 9ae344b..0000000
--- a/openstack/storage/v1/containers/containers.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package containers
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Container is a structure that holds information related to a storage container.
-type Container map[string]interface{}
-
-// ListOpts is a structure that holds parameters for listing containers.
-type ListOpts struct {
-	Full   bool
-	Params map[string]string
-}
-
-// CreateOpts is a structure that holds parameters for creating a container.
-type CreateOpts struct {
-	Name     string
-	Metadata map[string]string
-	Headers  map[string]string
-}
-
-// DeleteOpts is a structure that holds parameters for deleting a container.
-type DeleteOpts struct {
-	Name   string
-	Params map[string]string
-}
-
-// UpdateOpts is a structure that holds parameters for updating, creating, or deleting a
-// container's metadata.
-type UpdateOpts struct {
-	Name     string
-	Metadata map[string]string
-	Headers  map[string]string
-}
-
-// GetOpts is a structure that holds parameters for getting a container's metadata.
-type GetOpts struct {
-	Name string
-}
-
-// ExtractInfo is a function that takes a ListResult and returns the containers' information.
-func ExtractInfo(page pagination.Page) ([]Container, error) {
-	untyped := page.(ListResult).Body.([]interface{})
-	results := make([]Container, len(untyped))
-	for index, each := range untyped {
-		results[index] = Container(each.(map[string]interface{}))
-	}
-	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.(ListResult)
-	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"].(string))
-		}
-		return names, nil
-	case strings.HasPrefix(ct, "text/plain"):
-		names := make([]string, 0, 50)
-
-		body := string(page.(ListResult).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)
-	}
-}
-
-// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
-// and returns the custom metadata associated with the container.
-func ExtractMetadata(gr GetResult) map[string]string {
-	metadata := make(map[string]string)
-	for k, v := range gr.Header {
-		if strings.HasPrefix(k, "X-Container-Meta-") {
-			key := strings.TrimPrefix(k, "X-Container-Meta-")
-			metadata[key] = v[0]
-		}
-	}
-	return metadata
-}
diff --git a/openstack/storage/v1/containers/containers_test.go b/openstack/storage/v1/containers/containers_test.go
deleted file mode 100644
index 3296bb1..0000000
--- a/openstack/storage/v1/containers/containers_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package containers
-
-import (
-	"bytes"
-	"encoding/json"
-	"io/ioutil"
-	"net/http"
-	"reflect"
-	"testing"
-)
-
-
-func TestExtractContainerMetadata(t *testing.T) {
-	getResult := &http.Response{}
-
-	expected := map[string]string{}
-
-	actual := ExtractMetadata(getResult)
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected: %+v\nActual:%+v", expected, actual)
-	}
-}
-
-func TestExtractContainerInfo(t *testing.T) {
-	responseBody := `
-		[
-			{
-				"count": 3,
-				"bytes": 2000,
-				"name": "artemis"
-			},
-			{
-				"count": 1,
-				"bytes": 450,
-				"name": "diana"
-			}
-		]
-	`
-
-	listResult := &http.Response{
-		Body: ioutil.NopCloser(bytes.NewBufferString(responseBody)),
-	}
-
-	var expected []Container
-	err := json.Unmarshal([]byte(responseBody), &expected)
-	if err != nil {
-		t.Errorf("Error unmarshaling JSON: %s", err)
-	}
-
-	actual, err := ExtractInfo(listResult)
-	if err != nil {
-		t.Errorf("Error extracting containers info: %s", err)
-	}
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("\nExpected: %+v\nActual:   %+v", expected, actual)
-	}
-}
-
-func TestExtractConatinerNames(t *testing.T) {
-	responseBody := "artemis\ndiana\n"
-
-	listResult := &http.Response{
-		Body: ioutil.NopCloser(bytes.NewBufferString(responseBody)),
-	}
-
-	expected := []string{"artemis", "diana"}
-
-	actual, err := ExtractNames(listResult)
-	if err != nil {
-		t.Errorf("Error extracting container names: %s", err)
-	}
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected: %+v\nActual:%+v", expected, actual)
-	}
-}
diff --git a/openstack/storage/v1/containers/requests.go b/openstack/storage/v1/containers/requests.go
index a5435a2..3a6a265 100644
--- a/openstack/storage/v1/containers/requests.go
+++ b/openstack/storage/v1/containers/requests.go
@@ -1,57 +1,39 @@
 package containers
 
 import (
-	"net/http"
-
 	"github.com/racker/perigee"
 	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/utils"
 	"github.com/rackspace/gophercloud/pagination"
 )
 
-// ListResult is a *http.Response that is returned from a call to the List function.
-type ListResult struct {
-	pagination.MarkerPageBase
+// 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 []byte `q:"delimiter"`
 }
 
-// IsEmpty returns true if a ListResult contains no container names.
-func (r ListResult) 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 ListResult) LastMarker() (string, error) {
-	names, err := ExtractNames(r)
-	if err != nil {
-		return "", err
-	}
-	if len(names) == 0 {
-		return "", nil
-	}
-	return names[len(names)-1], nil
-}
-
-// GetResult is a *http.Response that is returned from a call to the Get function.
-type GetResult *http.Response
-
 // List is a function that retrieves all objects in a container. It also returns the details
 // for the account. To extract just the container information or names, pass the ListResult
 // response to the ExtractInfo or ExtractNames function, respectively.
 func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
 	var headers map[string]string
 
-	query := utils.BuildQuery(opts.Params)
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return pagination.Pager{Err: err}
+	}
 
 	if !opts.Full {
-		headers = map[string]string{"Content-Type": "text/plain"}
+		headers = map[string]string{"Accept": "text/plain"}
 	}
 
 	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
-		p := ListResult{pagination.MarkerPageBase{LastHTTPResponse: r}}
+		p := ContainerPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
 		p.MarkerPageBase.Owner = p
 		return p
 	}
@@ -62,13 +44,30 @@
 	return pager
 }
 
-// Create is a function that creates a new container.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) (Container, error) {
-	var ci Container
+// 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"`
+}
 
+// Create is a function that creates a new container.
+func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOpts) (Container, error) {
+	var container Container
 	h := c.Provider.AuthenticatedHeaders()
 
-	for k, v := range opts.Headers {
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return container, err
+	}
+
+	for k, v := range headers {
 		h[k] = v
 	}
 
@@ -76,38 +75,49 @@
 		h["X-Container-Meta-"+k] = v
 	}
 
-	url := containerURL(c, opts.Name)
-	_, err := perigee.Request("PUT", url, perigee.Options{
+	_, err = perigee.Request("PUT", containerURL(c, containerName), perigee.Options{
 		MoreHeaders: h,
 		OkCodes:     []int{201, 204},
 	})
 	if err == nil {
-		ci = Container{
-			"name": opts.Name,
-		}
+		container = Container{"name": containerName}
 	}
-	return ci, err
+	return container, err
 }
 
 // Delete is a function that deletes a container.
-func Delete(c *gophercloud.ServiceClient, opts DeleteOpts) error {
-	h := c.Provider.AuthenticatedHeaders()
-
-	query := utils.BuildQuery(opts.Params)
-
-	url := containerURL(c, opts.Name) + query
-	_, err := perigee.Request("DELETE", url, perigee.Options{
-		MoreHeaders: h,
+func Delete(c *gophercloud.ServiceClient, containerName string) error {
+	_, err := perigee.Request("DELETE", containerURL(c, containerName), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
 	return err
 }
 
+// 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"`
+}
+
 // Update is a function that creates, updates, or deletes a container's metadata.
-func Update(c *gophercloud.ServiceClient, opts UpdateOpts) error {
+func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOpts) error {
 	h := c.Provider.AuthenticatedHeaders()
 
-	for k, v := range opts.Headers {
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return err
+	}
+
+	for k, v := range headers {
 		h[k] = v
 	}
 
@@ -115,8 +125,8 @@
 		h["X-Container-Meta-"+k] = v
 	}
 
-	url := containerURL(c, opts.Name)
-	_, err := perigee.Request("POST", url, perigee.Options{
+	url := containerURL(c, containerName)
+	_, err = perigee.Request("POST", url, perigee.Options{
 		MoreHeaders: h,
 		OkCodes:     []int{204},
 	})
@@ -125,13 +135,13 @@
 
 // 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, opts GetOpts) (GetResult, error) {
-	h := c.Provider.AuthenticatedHeaders()
-
-	url := containerURL(c, opts.Name)
-	resp, err := perigee.Request("HEAD", url, perigee.Options{
-		MoreHeaders: h,
+func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
+	var gr GetResult
+	resp, err := perigee.Request("HEAD", containerURL(c, containerName), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
 		OkCodes:     []int{204},
 	})
-	return &resp.HttpResponse, err
+	gr.Err = err
+	gr.Resp = &resp.HttpResponse
+	return gr
 }
diff --git a/openstack/storage/v1/containers/requests_test.go b/openstack/storage/v1/containers/requests_test.go
index 871cb21..3b59e00 100644
--- a/openstack/storage/v1/containers/requests_test.go
+++ b/openstack/storage/v1/containers/requests_test.go
@@ -1,14 +1,16 @@
 package containers
 
 import (
+	"fmt"
 	"net/http"
 	"testing"
 
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
 	"github.com/rackspace/gophercloud/testhelper"
 )
 
-const ( 
+const (
 	tokenId = "abcabcabcabc"
 )
 
@@ -29,12 +31,42 @@
 		testhelper.TestMethod(t, r, "GET")
 		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
 		testhelper.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Add("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `[{'count': 0,'bytes': 0,'name': 'janeausten'},{'count': 1,'bytes': 14,'name': 'marktwain'}]`)
 	})
 
 	client := serviceClient()
-	_, err := List(client, ListOpts{Full: true})
-	if err != nil {
-		t.Fatalf("Unexpected error listing containers info: %v", err)
+	count := 0
+	List(client, 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",
+			},
+		}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
 	}
 }
 
@@ -46,12 +78,31 @@
 		testhelper.TestMethod(t, r, "GET")
 		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
 		testhelper.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Set("Content-Type", "text/plain")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, "")
 	})
 
 	client := serviceClient()
-	_, err := List(client, ListOpts{})
-	if err != nil {
-		t.Fatalf("Unexpected error listing containers info: %v", err)
+	count := 0
+	List(client, 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"}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+
+	if count != 0 {
+		t.Fatalf("Expected 0 pages, got %d", count)
 	}
 }
 
@@ -67,9 +118,7 @@
 	})
 
 	client := serviceClient()
-	_, err := Create(client, CreateOpts{
-		Name: "testContainer",
-	})
+	_, err := Create(client, "testContainer", CreateOpts{})
 	if err != nil {
 		t.Fatalf("Unexpected error creating container: %v", err)
 	}
@@ -87,9 +136,7 @@
 	})
 
 	client := serviceClient()
-	err := Delete(client, DeleteOpts{
-		Name: "testContainer",
-	})
+	err := Delete(client, "testContainer")
 	if err != nil {
 		t.Fatalf("Unexpected error deleting container: %v", err)
 	}
@@ -107,9 +154,7 @@
 	})
 
 	client := serviceClient()
-	err := Update(client, UpdateOpts{
-		Name: "testContainer",
-	})
+	err := Update(client, "testContainer", UpdateOpts{})
 	if err != nil {
 		t.Fatalf("Unexpected error updating container metadata: %v", err)
 	}
@@ -124,12 +169,13 @@
 		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
 		testhelper.TestHeader(t, r, "Accept", "application/json")
 		w.WriteHeader(http.StatusNoContent)
+		fmt.Fprintf(w, `
+		
+		`)
 	})
 
 	client := serviceClient()
-	_, err := Get(client, GetOpts{
-			Name: "testContainer",
-	})
+	_, err := Get(client, "testContainer").ExtractMetadata()
 	if err != nil {
 		t.Fatalf("Unexpected error getting container metadata: %v", err)
 	}
diff --git a/openstack/storage/v1/containers/results.go b/openstack/storage/v1/containers/results.go
new file mode 100644
index 0000000..6a93b4d
--- /dev/null
+++ b/openstack/storage/v1/containers/results.go
@@ -0,0 +1,134 @@
+package containers
+
+import (
+	"fmt"
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+	"net/http"
+	"strings"
+)
+
+type Container map[string]interface{}
+
+type commonResult struct {
+	gophercloud.CommonResult
+}
+
+func (r GetResult) Extract() (*Container, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
+		Container *Container
+	}
+
+	err := mapstructure.Decode(r.Resp, &res)
+	if err != nil {
+		return nil, fmt.Errorf("Error decoding Object Storage Container: %v", err)
+	}
+
+	return res.Container, nil
+}
+
+type CreateResult struct {
+	commonResult
+}
+
+// GetResult represents the result of a get operation.
+type GetResult struct {
+	Resp *http.Response
+	Err  error
+}
+
+// UpdateResult represents the result of an update operation.
+type UpdateResult commonResult
+
+// DeleteResult represents the result of a delete operation.
+type DeleteResult commonResult
+
+// ListResult is a *http.Response that is returned from a call to the List function.
+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 {
+		results[index] = Container(each.(map[string]interface{}))
+	}
+	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"].(string))
+		}
+		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)
+	}
+}
+
+// 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
+}
diff --git a/openstack/storage/v1/objects/objects.go b/openstack/storage/v1/objects/objects.go
deleted file mode 100644
index cd248c8..0000000
--- a/openstack/storage/v1/objects/objects.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package objects
-
-import (
-	"fmt"
-	"io"
-	"io/ioutil"
-	"strings"
-
-	"github.com/rackspace/gophercloud/pagination"
-)
-
-// Object is a structure that holds information related to a storage object.
-type Object map[string]interface{}
-
-// ListOpts is a structure that holds parameters for listing objects.
-type ListOpts struct {
-	Container string
-	Full      bool
-	Params    map[string]string
-}
-
-// DownloadOpts is a structure that holds parameters for downloading an object.
-type DownloadOpts struct {
-	Container string
-	Name      string
-	Headers   map[string]string
-	Params    map[string]string
-}
-
-// CreateOpts is a structure that holds parameters for creating an object.
-type CreateOpts struct {
-	Container string
-	Name      string
-	Content   io.Reader
-	Metadata  map[string]string
-	Headers   map[string]string
-	Params    map[string]string
-}
-
-// CopyOpts is a structure that holds parameters for copying one object to another.
-type CopyOpts struct {
-	Container    string
-	Name         string
-	NewContainer string
-	NewName      string
-	Metadata     map[string]string
-	Headers      map[string]string
-}
-
-// DeleteOpts is a structure that holds parameters for deleting an object.
-type DeleteOpts struct {
-	Container string
-	Name      string
-	Params    map[string]string
-}
-
-// GetOpts is a structure that holds parameters for getting an object's metadata.
-type GetOpts struct {
-	Container string
-	Name      string
-	Params    map[string]string
-}
-
-// UpdateOpts is a structure that holds parameters for updating, creating, or deleting an
-// object's metadata.
-type UpdateOpts struct {
-	Container string
-	Name      string
-	Metadata  map[string]string
-	Headers   map[string]string
-}
-
-// ExtractInfo is a function that takes a page of objects and returns their full information.
-func ExtractInfo(page pagination.Page) ([]Object, error) {
-	untyped := page.(ListResult).Body.([]interface{})
-	results := make([]Object, len(untyped))
-	for index, each := range untyped {
-		results[index] = Object(each.(map[string]interface{}))
-	}
-	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.(ListResult)
-	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"].(string))
-		}
-		return names, nil
-	case strings.HasPrefix(ct, "text/plain"):
-		names := make([]string, 0, 50)
-
-		body := string(page.(ListResult).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)
-	}
-}
-
-// ExtractContent is a function that takes a DownloadResult (of type *http.Response)
-// and returns the object's content.
-func ExtractContent(dr DownloadResult) ([]byte, error) {
-	var body []byte
-	defer dr.Body.Close()
-	body, err := ioutil.ReadAll(dr.Body)
-	if err != nil {
-		return body, fmt.Errorf("Error trying to read DownloadResult body: %v", err)
-	}
-	return body, nil
-}
-
-// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
-// and returns the custom metadata associated with the object.
-func ExtractMetadata(gr GetResult) map[string]string {
-	metadata := make(map[string]string)
-	for k, v := range gr.Header {
-		if strings.HasPrefix(k, "X-Object-Meta-") {
-			key := strings.TrimPrefix(k, "X-Object-Meta-")
-			metadata[key] = v[0]
-		}
-	}
-	return metadata
-}
diff --git a/openstack/storage/v1/objects/objects_test.go b/openstack/storage/v1/objects/objects_test.go
deleted file mode 100644
index 31fa837..0000000
--- a/openstack/storage/v1/objects/objects_test.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package objects
-
-import (
-	"bytes"
-	"encoding/json"
-	"io/ioutil"
-	"net/http"
-	"reflect"
-	"testing"
-)
-
-func TestExtractObjectMetadata(t *testing.T) {
-	getResult := &http.Response{}
-
-	expected := map[string]string{}
-
-	actual := ExtractMetadata(getResult)
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected: %+v\nActual:%+v", expected, actual)
-	}
-}
-
-func TestExtractContent(t *testing.T) {
-	responseBody := "'Twas brillig, and the slithy toves"
-	downloadResult := &http.Response{
-		Body: ioutil.NopCloser(bytes.NewBufferString(responseBody)),
-	}
-	expected := []byte("'Twas brillig, and the slithy toves")
-	actual, err := ExtractContent(downloadResult)
-	if err != nil {
-		t.Errorf("Error extracting object content: %s", err)
-	}
-	if !reflect.DeepEqual(actual, expected) {
-		t.Errorf("Expected: %+v\nActual:%+v", expected, actual)
-	}
-}
-
-func TestExtractObjectInfo(t *testing.T) {
-	responseBody := `
-		[
-		    {
-				"hash": "451e372e48e0f6b1114fa0724aa79fa1",
-		        "last_modified": "2014-01-15T16:41:49.390270",
-				"bytes": 14,
-				"name": "goodbye",
-				"content_type": "application/octet-stream"
-			},
-			{
-		        "hash": "ed076287532e86365e841e92bfc50d8c",
-			    "last_modified": "2014-01-15T16:37:43.427570",
-				"bytes": 12,
-				"name": "helloworld",
-				"content_type": "application/octet-stream"
-			}
-		]	
-	`
-
-	listResult := &http.Response{
-		Body: ioutil.NopCloser(bytes.NewBufferString(responseBody)),
-	}
-
-	var expected []Object
-	err := json.Unmarshal([]byte(responseBody), &expected)
-	if err != nil {
-		t.Errorf("Error unmarshaling JSON: %s", err)
-	}
-
-	actual, err := ExtractInfo(listResult)
-	if err != nil {
-		t.Errorf("Error extracting objects info: %s", err)
-	}
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected: %+v\nActual: %+v", expected, actual)
-	}
-}
-
-func TestExtractObjectNames(t *testing.T) {
-	responseBody := "goodbye\nhelloworld\n"
-
-	listResult := &http.Response{
-		Body: ioutil.NopCloser(bytes.NewBufferString(responseBody)),
-	}
-
-	expected := []string{"goodbye", "helloworld"}
-
-	actual, err := ExtractNames(listResult)
-	if err != nil {
-		t.Errorf("Error extracting object names: %s", err)
-	}
-
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Expected: %+v\nActual:%+v", expected, actual)
-	}
-}
diff --git a/openstack/storage/v1/objects/requests.go b/openstack/storage/v1/objects/requests.go
index 7b00127..363843c 100644
--- a/openstack/storage/v1/objects/requests.go
+++ b/openstack/storage/v1/objects/requests.go
@@ -2,97 +2,132 @@
 
 import (
 	"fmt"
-	"net/http"
+	"io"
+	"time"
 
 	"github.com/racker/perigee"
 	"github.com/rackspace/gophercloud"
-	"github.com/rackspace/gophercloud/openstack/utils"
 	"github.com/rackspace/gophercloud/pagination"
 )
 
-// ListResult is a single page of objects that is returned from a call to the List function.
-type ListResult struct {
-	pagination.MarkerPageBase
+// 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 [1]byte `q:"delimiter"`
+	Path      string  `q:"path"`
 }
 
-// IsEmpty returns true if a ListResult contains no object names.
-func (r ListResult) 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 ListResult) LastMarker() (string, error) {
-	names, err := ExtractNames(r)
-	if err != nil {
-		return "", err
-	}
-	if len(names) == 0 {
-		return "", nil
-	}
-	return names[len(names)-1], nil
-}
-
-// DownloadResult is a *http.Response that is returned from a call to the Download function.
-type DownloadResult *http.Response
-
-// GetResult is a *http.Response that is returned from a call to the Get function.
-type GetResult *http.Response
-
 // 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, opts ListOpts) pagination.Pager {
+func List(c *gophercloud.ServiceClient, containerName string, opts ListOpts) pagination.Pager {
 	var headers map[string]string
 
-	query := utils.BuildQuery(opts.Params)
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		fmt.Printf("Error building query string: %v", err)
+		return pagination.Pager{Err: err}
+	}
 
 	if !opts.Full {
-		headers = map[string]string{"Content-Type": "text/plain"}
+		headers = map[string]string{"Accept": "text/plain"}
 	}
 
 	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
-		p := ListResult{pagination.MarkerPageBase{LastHTTPResponse: r}}
+		p := ObjectPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
 		p.MarkerPageBase.Owner = p
 		return p
 	}
 
-	url := containerURL(c, opts.Container) + query
+	url := containerURL(c, containerName) + query
 	pager := pagination.NewPager(c, url, createPage)
 	pager.Headers = headers
 	return pager
 }
 
+// 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"`
+}
+
 // 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, opts DownloadOpts) (DownloadResult, error) {
+func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOpts) DownloadResult {
+	var dr DownloadResult
+
 	h := c.Provider.AuthenticatedHeaders()
 
-	for k, v := range opts.Headers {
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		dr.Err = err
+		return dr
+	}
+
+	for k, v := range headers {
 		h[k] = v
 	}
 
-	query := utils.BuildQuery(opts.Params)
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		dr.Err = err
+		return dr
+	}
 
-	url := objectURL(c, opts.Container, opts.Name) + query
+	url := objectURL(c, containerName, objectName) + query
 	resp, err := perigee.Request("GET", url, perigee.Options{
 		MoreHeaders: h,
 		OkCodes:     []int{200},
 	})
-	return &resp.HttpResponse, err
+	dr.Err = err
+	dr.Resp = &resp.HttpResponse
+	return dr
+}
+
+// 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"`
 }
 
 // Create is a function that creates a new object or replaces an existing object.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) error {
+func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.Reader, opts CreateOpts) error {
 	var reqBody []byte
 
 	h := c.Provider.AuthenticatedHeaders()
 
-	for k, v := range opts.Headers {
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil
+	}
+
+	for k, v := range headers {
 		h[k] = v
 	}
 
@@ -100,9 +135,11 @@
 		h["X-Object-Meta-"+k] = v
 	}
 
-	query := utils.BuildQuery(opts.Params)
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return err
+	}
 
-	content := opts.Content
 	if content != nil {
 		reqBody = make([]byte, 0)
 		_, err := content.Read(reqBody)
@@ -111,8 +148,8 @@
 		}
 	}
 
-	url := objectURL(c, opts.Container, opts.Name) + query
-	_, err := perigee.Request("PUT", url, perigee.Options{
+	url := objectURL(c, containerName, objectName) + query
+	_, err = perigee.Request("PUT", url, perigee.Options{
 		ReqBody:     reqBody,
 		MoreHeaders: h,
 		OkCodes:     []int{201},
@@ -120,56 +157,24 @@
 	return err
 }
 
+// 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"`
+}
+
 // Copy is a function that copies one object to another.
-func Copy(c *gophercloud.ServiceClient, opts CopyOpts) error {
+func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOpts) error {
 	h := c.Provider.AuthenticatedHeaders()
 
-	for k, v := range opts.Metadata {
-		h["X-Object-Meta-"+k] = v
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return err
 	}
-
-	h["Destination"] = fmt.Sprintf("/%s/%s", opts.NewContainer, opts.NewName)
-
-	url := objectURL(c, opts.Container, opts.Name)
-	_, err := perigee.Request("COPY", url, perigee.Options{
-		MoreHeaders: h,
-		OkCodes:     []int{201},
-	})
-	return err
-}
-
-// Delete is a function that deletes an object.
-func Delete(c *gophercloud.ServiceClient, opts DeleteOpts) error {
-	h := c.Provider.AuthenticatedHeaders()
-
-	query := utils.BuildQuery(opts.Params)
-
-	url := objectURL(c, opts.Container, opts.Name) + query
-	_, err := perigee.Request("DELETE", url, perigee.Options{
-		MoreHeaders: h,
-		OkCodes:     []int{204},
-	})
-	return err
-}
-
-// 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, opts GetOpts) (GetResult, error) {
-	h := c.Provider.AuthenticatedHeaders()
-
-	url := objectURL(c, opts.Container, opts.Name)
-	resp, err := perigee.Request("HEAD", url, perigee.Options{
-		MoreHeaders: h,
-		OkCodes:     []int{204},
-	})
-	return &resp.HttpResponse, err
-}
-
-// Update is a function that creates, updates, or deletes an object's metadata.
-func Update(c *gophercloud.ServiceClient, opts UpdateOpts) error {
-	h := c.Provider.AuthenticatedHeaders()
-
-	for k, v := range opts.Headers {
+	for k, v := range headers {
 		h[k] = v
 	}
 
@@ -177,8 +182,93 @@
 		h["X-Object-Meta-"+k] = v
 	}
 
-	url := objectURL(c, opts.Container, opts.Name)
-	_, err := perigee.Request("POST", url, perigee.Options{
+	url := objectURL(c, containerName, objectName)
+	_, err = perigee.Request("COPY", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{201},
+	})
+	return err
+}
+
+// DeleteOpts is a structure that holds parameters for deleting an object.
+type DeleteOpts struct {
+	MultipartManifest string `q:"multipart-manifest"`
+}
+
+// Delete is a function that deletes an object.
+func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOpts) error {
+	h := c.Provider.AuthenticatedHeaders()
+
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return err
+	}
+
+	url := objectURL(c, containerName, objectName) + query
+	_, err = perigee.Request("DELETE", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return err
+}
+
+// GetOpts is a structure that holds parameters for getting an object's metadata.
+type GetOpts struct {
+	Expires   string `q:"expires"`
+	Signature string `q:"signature"`
+}
+
+// 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 GetOpts) GetResult {
+	var gr GetResult
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		gr.Err = err
+		return gr
+	}
+
+	url := objectURL(c, containerName, objectName) + query
+	resp, err := perigee.Request("HEAD", url, perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{200, 204},
+	})
+	gr.Err = err
+	gr.Resp = &resp.HttpResponse
+	return gr
+}
+
+// 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"`
+}
+
+// Update is a function that creates, updates, or deletes an object's metadata.
+func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOpts) error {
+	h := c.Provider.AuthenticatedHeaders()
+
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return nil
+	}
+
+	for k, v := range headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+
+	url := objectURL(c, containerName, objectName)
+	_, err = perigee.Request("POST", url, perigee.Options{
 		MoreHeaders: h,
 		OkCodes:     []int{202},
 	})
diff --git a/openstack/storage/v1/objects/requests_test.go b/openstack/storage/v1/objects/requests_test.go
index ca829ae..c3047c6 100644
--- a/openstack/storage/v1/objects/requests_test.go
+++ b/openstack/storage/v1/objects/requests_test.go
@@ -2,14 +2,16 @@
 
 import (
 	"bytes"
+	"fmt"
 	"net/http"
 	"testing"
 
 	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
 	"github.com/rackspace/gophercloud/testhelper"
 )
 
-const ( 
+const (
 	tokenId = "abcabcabcabc"
 )
 
@@ -22,24 +24,26 @@
 	}
 }
 
-func TestDownloadObject(t * testing.T) {
+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", tokenId)
 		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, "Successful download with Gophercloud")
 	})
 
 	client := serviceClient()
-	_, err := Download(client, DownloadOpts{
-		Container: "testContainer",
-		Name: "testObject",
-	})
+	content, err := Download(client, "testContainer", "testObject", DownloadOpts{}).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) {
@@ -49,16 +53,39 @@
 	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
 		testhelper.TestMethod(t, r, "GET")
 		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
-		testhelper.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Set("Content-Type", "application/json")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, `[{'hash': '451e372e48e0f6b1114fa0724aa79fa1','last_modified': '2014-01-15T16:41:49.390270','bytes': 14,'name': 'goodbye','content_type': 'application/octet-stream'}]`)
 	})
 
 	client := serviceClient()
-	_, err := List(client, ListOpts{
-		Full: true,
-		Container: "testContainer",
+	count := 0
+	List(client, "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",
+				"last_modified": "2014-01-15T16:41:49.390270",
+				"bytes":         14,
+				"name":          "goodbye",
+				"content_type":  "application/octet-stream",
+			},
+		}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
 	})
-	if err != nil {
-		t.Fatalf("Unexpected error listing objects info: %v", err)
+
+	if count != 1 {
+		t.Errorf("Expected 1 page, got %d", count)
 	}
 }
 
@@ -70,17 +97,33 @@
 		testhelper.TestMethod(t, r, "GET")
 		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
 		testhelper.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Add("Content-Type", "text/plain")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprintf(w, "")
 	})
 
 	client := serviceClient()
-	_, err := List(client, ListOpts{
-		Container: "testContainer",
+	count := 0
+	List(client, "testContainer", ListOpts{}).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{"helloworld", "goodbye"}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
 	})
-	if err != nil {
-		t.Fatalf("Unexpected error listing object names: %v", err)
+
+	if count != 0 {
+		t.Fatalf("Expected 0 pages, got %d", count)
 	}
 }
-
 func TestCreateObject(t *testing.T) {
 	testhelper.SetupHTTP()
 	defer testhelper.TeardownHTTP()
@@ -93,11 +136,8 @@
 	})
 
 	client := serviceClient()
-	err := Create(client, CreateOpts{
-		Content: bytes.NewBufferString("Did gyre and gimble in the wabe:"),
-		Container: "testContainer",
-		Name: "testObject",
-	})
+	content := bytes.NewBufferString("Did gyre and gimble in the wabe")
+	err := Create(client, "testContainer", "testObject", content, CreateOpts{})
 	if err != nil {
 		t.Fatalf("Unexpected error creating object: %v", err)
 	}
@@ -116,12 +156,7 @@
 	})
 
 	client := serviceClient()
-	err := Copy(client, CopyOpts{
-		NewContainer: "newTestContainer",
-		NewName: "newTestObject",
-		Container: "testContainer",
-		Name: "testObject",
-	})
+	err := Copy(client, "testContainer", "testObject", CopyOpts{Destination: "/newTestContainer/newTestObject"})
 	if err != nil {
 		t.Fatalf("Unexpected error copying object: %v", err)
 	}
@@ -139,10 +174,7 @@
 	})
 
 	client := serviceClient()
-	err := Delete(client, DeleteOpts{
-		Container: "testContainer",
-		Name: "testObject",
-	})
+	err := Delete(client, "testContainer", "testObject", DeleteOpts{})
 	if err != nil {
 		t.Fatalf("Unexpected error deleting object: %v", err)
 	}
@@ -161,17 +193,13 @@
 	})
 
 	client := serviceClient()
-	err := Update(client, UpdateOpts{
-		Container: "testContainer",
-		Name: "testObject",
-		Metadata: metadata,
-	})
+	err := Update(client, "testContainer", "testObject", UpdateOpts{Metadata: metadata})
 	if err != nil {
 		t.Fatalf("Unexpected error updating object metadata: %v", err)
 	}
 }
 
-func TestGetContainer(t *testing.T) {
+func TestGetObject(t *testing.T) {
 	testhelper.SetupHTTP()
 	defer testhelper.TeardownHTTP()
 
@@ -183,11 +211,10 @@
 	})
 
 	client := serviceClient()
-	_, err := Get(client, GetOpts{
-			Container: "testContainer",
-			Name: "testObject",
-	})
+	expected := metadata
+	actual, err := Get(client, "testContainer", "testObject", GetOpts{}).ExtractMetadata()
 	if err != nil {
 		t.Fatalf("Unexpected error getting object metadata: %v", err)
 	}
+	testhelper.CheckDeepEquals(t, expected, actual)
 }
diff --git a/openstack/storage/v1/objects/results.go b/openstack/storage/v1/objects/results.go
new file mode 100644
index 0000000..8808d03
--- /dev/null
+++ b/openstack/storage/v1/objects/results.go
@@ -0,0 +1,125 @@
+package objects
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// Object is a structure that holds information related to a storage object.
+type Object map[string]interface{}
+
+// ListResult 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
+}
+
+// DownloadResult is a *http.Response that is returned from a call to the Download function.
+type DownloadResult struct {
+	Resp *http.Response
+	Err  error
+}
+
+// GetResult is a *http.Response that is returned from a call to the Get function.
+type GetResult struct {
+	Resp *http.Response
+	Err  error
+}
+
+// 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 {
+		results[index] = Object(each.(map[string]interface{}))
+	}
+	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"].(string))
+		}
+		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
+	default:
+		return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
+	}
+}
+
+// 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
+}
+
+// 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
+}