Repository: fsouza/fake-gcs-server
Pull request #125 (2019-11-11 12:21:35)

Support predefined canned ACL when creating object
also support full ACL rule instead of just entity


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 30efd80..270904e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -73,23 +73,23 @@ jobs:
           args: -c "./fake-gcs-server -h"
 
       - name: test-python-example
-        uses: docker://python:3
+        uses: docker://python:3-alpine
         with:
-          entrypoint: bash
+          entrypoint: sh
           args: ci/run-python-example.sh
 
       - name: test-node-example
-        uses: docker://node:10
+        uses: docker://node:10-alpine
         with:
-          entrypoint: bash
+          entrypoint: sh
           args: ci/run-node-example.sh
 
       - name: test-go-example
-        uses: docker://golang:1.13
+        uses: docker://golang:1.13-alpine
         env:
           GOROOT: /usr/local/go
         with:
-          entrypoint: bash
+          entrypoint: sh
           args: ci/run-go-example.sh
 
       - name: docker-publish
diff --git a/README.md b/README.md
index 67d3277..2a27e52 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
 # fake-gcs-server
 
-[![Build Status](https://github.com/fsouza/fake-gcs-server/workflows/Build/badge.svg)](https://github.com/fsouza/fake-gcs-server/actions)
-[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://godoc.org/github.com/fsouza/fake-gcs-server/fakestorage)
+[![Build Status](https://github.com/fsouza/fake-gcs-server/workflows/Build/badge.svg)](https://github.com/fsouza/fake-gcs-server/actions?query=branch:master+workflow:Build)
+[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/fsouza/fake-gcs-server/fakestorage?tab=doc)
 
 fake-gcs-server provides an emulator for Google Cloud Storage API. It can be
 used as a library in Go projects and/or as a standalone binary/Docker image.
 
 The library is available inside the package
-[``github.com/fsouza/fake-gcs-server/fakestorage``](https://godoc.org/github.com/fsouza/fake-gcs-server/fakestorage)
+[``github.com/fsouza/fake-gcs-server/fakestorage``](https://pkg.go.dev/github.com/fsouza/fake-gcs-server/fakestorage?tab=doc)
 and can be used from within test suites in Go package. The emulator is
 available as a binary that can be built manually, downloaded from the [releases
 page](https://github.com/fsouza/fake-gcs-server/releases) or pulled from Docker
diff --git a/examples/go/go.mod b/examples/go/go.mod
index b61da35..3b458be 100644
--- a/examples/go/go.mod
+++ b/examples/go/go.mod
@@ -3,6 +3,6 @@ module github.com/zigius/fake-gcs-server/examples/go-example
 go 1.12
 
 require (
-	cloud.google.com/go/storage v1.2.1
-	google.golang.org/api v0.13.0
+	cloud.google.com/go/storage v1.4.0
+	google.golang.org/api v0.14.0
 )
diff --git a/examples/go/go.sum b/examples/go/go.sum
index 292b797..3b149d2 100644
--- a/examples/go/go.sum
+++ b/examples/go/go.sum
@@ -22,6 +22,10 @@ cloud.google.com/go/storage v1.2.0 h1:j4FZW6a1zW237It0ORRgQ5knC6H4AAI/OEADjmNPQ6
 cloud.google.com/go/storage v1.2.0/go.mod h1:9gYIx9Yslppm7JnJFIRyUhybmKXFCReO7Eq8bdUJZ4k=
 cloud.google.com/go/storage v1.2.1 h1:3G66kIUChoxgxJPzwpWroEnXjfbLuZgbCNAHIvN/sKY=
 cloud.google.com/go/storage v1.2.1/go.mod h1:kpwTAahUQmhyVVGgLWQh2GdyPDZSA3UJDjMm/fDV2oQ=
+cloud.google.com/go/storage v1.3.0 h1:2Ze/3nQD5F+HfL0xOPM2EeawDWs+NPRtzgcre+17iZU=
+cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA=
+cloud.google.com/go/storage v1.4.0 h1:KDdqY5VTXBTqpSbctVTt0mVvfanP6JZzNzLE0qNY100=
+cloud.google.com/go/storage v1.4.0/go.mod h1:ZusYJWlOshgSBGbt6K3GnB3MT3H1xs2id9+TCl4fDBA=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -151,6 +155,10 @@ golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0 h1:azkp5oIgy7LNGQ64URezZcc
 golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a h1:RzzIfXstYPS78k0QViPGpDcTlV+QuYrbxVmsxDHdxTs=
 golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc h1:V4uzEpfPev8S0NzQIqZE2HsWLn0BP+3KeizTW0xmsUg=
+golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2 h1:EtTFh6h4SAKemS+CURDMTDIANuduG5zKEXShyy18bGA=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -161,6 +169,8 @@ google.golang.org/api v0.11.0 h1:n/qM3q0/rV2F0pox7o0CvNhlPvZAo7pLbef122cbLJ0=
 google.golang.org/api v0.11.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
 google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -180,6 +190,10 @@ google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o
 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo=
 google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9 h1:6XzpBoANz1NqMNfDXzc2QmHmbb1vyMsvRfoP5rM+K1I=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
diff --git a/examples/node/.editorconfig b/examples/node/.editorconfig
new file mode 100644
index 0000000..9db28dd
--- /dev/null
+++ b/examples/node/.editorconfig
@@ -0,0 +1,3 @@
+[*.{js,json}]
+indent_style = space
+indent_size = 2
diff --git a/examples/node/index.js b/examples/node/index.js
index f87cd4d..70430e9 100644
--- a/examples/node/index.js
+++ b/examples/node/index.js
@@ -1,5 +1,5 @@
 process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
-process.on("unhandledRejection", err => { throw err });
+process.on("unhandledRejection", err => {throw err});
 
 async function listBuckets() {
   // [START storage_list_buckets]
diff --git a/examples/node/package-lock.json b/examples/node/package-lock.json
index 721e769..0f42646 100644
--- a/examples/node/package-lock.json
+++ b/examples/node/package-lock.json
@@ -21,28 +21,28 @@
       }
     },
     "@google-cloud/paginator": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.1.tgz",
-      "integrity": "sha512-HZ6UTGY/gHGNriD7OCikYWL/Eu0sTEur2qqse2w6OVsz+57se3nTkqH14JIPxtf0vlEJ8IJN5w3BdZ22pjCB8g==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.2.tgz",
+      "integrity": "sha512-PCddVtZWvw0iZ3BLIsCXMBQvxUcS9O5CgfHBu8Zd8T3DCiML+oQED1odsbl3CQ9d3RrvBaj+eIh7Dv12D15PbA==",
       "requires": {
         "arrify": "^2.0.0",
         "extend": "^3.0.2"
       }
     },
     "@google-cloud/projectify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.1.tgz",
-      "integrity": "sha512-xknDOmsMgOYHksKc1GPbwDLsdej8aRNIA17SlSZgQdyrcC0lx0OGo4VZgYfwoEU1YS8oUxF9Y+6EzDOb0eB7Xg=="
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.2.tgz",
+      "integrity": "sha512-WnkGxvk4U1kAJpoS/Ehk+3MZXVW+XHHhwc/QyD6G8Za4xml3Fv+NRn/bYffl1TxSg+gE0N0mj9Shgc7e8+fl8A=="
     },
     "@google-cloud/promisify": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.2.tgz",
-      "integrity": "sha512-7WfV4R/3YV5T30WRZW0lqmvZy9hE2/p9MvpI34WuKa2Wz62mLu5XplGTFEMK6uTbJCLWUxTcZ4J4IyClKucE5g=="
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.3.tgz",
+      "integrity": "sha512-Rufgfl3TnkIil3CjsH33Q6093zeoVqyqCdvtvgHuCqRJxCZYfaVPIyr8JViMeLTD4Ja630pRKKZVSjKggoVbNg=="
     },
     "@google-cloud/storage": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-4.1.1.tgz",
-      "integrity": "sha512-JcHMSUebPz2KopVThlc7uuCLIpoVSbAYWu2ITKqPlyRQ5aM2bfWJ1czNxSb2qZUtCGuyTDPRRqMBfOMaq+IAog==",
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-4.1.3.tgz",
+      "integrity": "sha512-79Ag+4eQq+KFJcKB85AimepoqTJOGuDLAmJd7JkLc8NM12a87JTCoGi65oi1eZ4H77AV0uUQxSS2Fo/hZL3+kQ==",
       "requires": {
         "@google-cloud/common": "^2.1.1",
         "@google-cloud/paginator": "^2.0.0",
@@ -50,7 +50,7 @@
         "arrify": "^2.0.0",
         "compressible": "^2.0.12",
         "concat-stream": "^2.0.0",
-        "date-and-time": "^0.10.0",
+        "date-and-time": "^0.11.0",
         "duplexify": "^3.5.0",
         "extend": "^3.0.2",
         "gaxios": "^2.0.1",
@@ -152,9 +152,9 @@
       "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="
     },
     "date-and-time": {
-      "version": "0.10.0",
-      "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.10.0.tgz",
-      "integrity": "sha512-IbIzxtvK80JZOVsWF6+NOjunTaoFVYxkAQoyzmflJyuRCJAJebehy48mPiCAedcGp4P7/UO3QYRWa0fe6INftg=="
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.11.0.tgz",
+      "integrity": "sha512-VyzhHurex4wlg9oMszn7O+kxHchphWjzDn7Mv0WfkFKI6hSNOQePpTBFGsnRakvLNzQKXqPBAVV8DOxUGtUxqA=="
     },
     "debug": {
       "version": "3.2.6",
@@ -261,18 +261,18 @@
       }
     },
     "gcp-metadata": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.0.tgz",
-      "integrity": "sha512-ympv+yQ6k5QuWCuwQqnGEvFGS7MBKdcQdj1i188v3bW9QLFIchTGaBCEZxSQapT0jffdn1vdt8oJhB5VBWQO1Q==",
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.2.tgz",
+      "integrity": "sha512-vR7kcJMCYJG/mYWp/a1OszdOqnLB/XW1GorWW1hc1lWVNL26L497zypWb9cG0CYDQ4Bl1Wk0+fSZFFjwJlTQgQ==",
       "requires": {
-        "gaxios": "^2.0.1",
+        "gaxios": "^2.1.0",
         "json-bigint": "^0.3.0"
       }
     },
     "gcs-resumable-upload": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.0.tgz",
-      "integrity": "sha512-PclXJiEngrVx0c4K0LfE1XOxhmOkBEy39Rrhspdn6jAbbwe4OQMZfjo7Z1LHBrh57+bNZeIN4M+BooYppCoHSg==",
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.1.tgz",
+      "integrity": "sha512-zEO7L+jz99VznQsbsF7vFTnIFbSu+CjdJqt5htnjIrfsp5j+QCVBvbbKdqpaTfCPzpUPYj1Q9O9DhIh/8newfA==",
       "requires": {
         "abort-controller": "^3.0.0",
         "configstore": "^5.0.0",
@@ -298,9 +298,9 @@
       }
     },
     "google-p12-pem": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.2.tgz",
-      "integrity": "sha512-UfnEARfJKI6pbmC1hfFFm+UAcZxeIwTiEcHfqKe/drMsXD/ilnVjF7zgOGpHXyhuvX6jNJK3S8A0hOQjwtFxEw==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.3.tgz",
+      "integrity": "sha512-Tq2kBCANxYYPxaBpTgCpRfdoPs9+/lNzc/Iaee4kuMVW5ascD+HwhpBsTLwH85C9Ev4qfB8KKHmpPQYyD2vg2w==",
       "requires": {
         "node-forge": "^0.9.0"
       }
@@ -311,9 +311,9 @@
       "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
     },
     "gtoken": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.1.tgz",
-      "integrity": "sha512-2FEmEDGi4NdM6u+mtaLjSDDtHiw5wT+nBsI+yrSeFO6fVqPEytYVF6uiIpRaOaZhRP+ozjYWuwwtMlrjAyTcYA==",
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.3.tgz",
+      "integrity": "sha512-ofW+FiXjswyKdkjMcDbe6E4K7cDDdE82dGDhZIc++kUECqaE7MSErf6arJPAjcnYn1qxE1/Ti06qQuqgVusovQ==",
       "requires": {
         "gaxios": "^2.1.0",
         "google-p12-pem": "^2.0.0",
@@ -471,18 +471,11 @@
       "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ=="
     },
     "mime-types": {
-      "version": "2.1.24",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
-      "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+      "version": "2.1.25",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
+      "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
       "requires": {
-        "mime-db": "1.40.0"
-      },
-      "dependencies": {
-        "mime-db": {
-          "version": "1.40.0",
-          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
-          "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
-        }
+        "mime-db": "1.42.0"
       }
     },
     "mimic-fn": {
diff --git a/examples/python/requirements.txt b/examples/python/requirements.txt
index 90ea41a..3c42ea7 100644
--- a/examples/python/requirements.txt
+++ b/examples/python/requirements.txt
@@ -1,3 +1,3 @@
-google-cloud-storage==1.22.0
+google-cloud-storage==1.23.0
 requests==2.22.0
-urllib3==1.25.6
+urllib3==1.25.7
diff --git a/fakestorage/object.go b/fakestorage/object.go
index e94e888..b64ea5a 100644
--- a/fakestorage/object.go
+++ b/fakestorage/object.go
@@ -19,14 +19,15 @@ import (
 
 // Object represents the object that is stored within the fake server.
 type Object struct {
-	BucketName  string `json:"-"`
-	Name        string `json:"name"`
-	ContentType string `json:"contentType"`
-	Content     []byte `json:"-"`
+	BucketName      string `json:"-"`
+	Name            string `json:"name"`
+	ContentType     string `json:"contentType"`
+	ContentEncoding string `json:"contentEncoding"`
+	Content         []byte `json:"-"`
 	// Crc32c checksum of Content. calculated by server when it's upload methods are used.
 	Crc32c  string            `json:"crc32c,omitempty"`
 	Md5Hash string            `json:"md5hash,omitempty"`
-	ACL     storage.ACLEntity `json:"acl,omitempty"`
+	ACL     []storage.ACLRule `json:"acl,omitempty"`
 }
 
 func (o *Object) id() string {
@@ -97,14 +98,16 @@ func (s *Server) ListObjects(bucketName, prefix, delimiter string) ([]Object, []
 func toBackendObjects(objects []Object) []backend.Object {
 	backendObjects := []backend.Object{}
 	for _, o := range objects {
+		acl, _ := json.Marshal(o.ACL)
 		backendObjects = append(backendObjects, backend.Object{
-			BucketName:  o.BucketName,
-			Name:        o.Name,
-			Content:     o.Content,
-			ContentType: o.ContentType,
-			Crc32c:      o.Crc32c,
-			Md5Hash:     o.Md5Hash,
-			ACL:         string(o.ACL),
+			BucketName:      o.BucketName,
+			Name:            o.Name,
+			Content:         o.Content,
+			ContentType:     o.ContentType,
+			ContentEncoding: o.ContentEncoding,
+			Crc32c:          o.Crc32c,
+			Md5Hash:         o.Md5Hash,
+			ACL:             acl,
 		})
 	}
 	return backendObjects
@@ -113,14 +116,17 @@ func toBackendObjects(objects []Object) []backend.Object {
 func fromBackendObjects(objects []backend.Object) []Object {
 	backendObjects := []Object{}
 	for _, o := range objects {
+		var acl []storage.ACLRule
+		_ = json.Unmarshal(o.ACL, &acl)
 		backendObjects = append(backendObjects, Object{
-			BucketName:  o.BucketName,
-			Name:        o.Name,
-			Content:     o.Content,
-			ContentType: o.ContentType,
-			Crc32c:      o.Crc32c,
-			Md5Hash:     o.Md5Hash,
-			ACL:         storage.ACLEntity(o.ACL),
+			BucketName:      o.BucketName,
+			Name:            o.Name,
+			Content:         o.Content,
+			ContentType:     o.ContentType,
+			ContentEncoding: o.ContentEncoding,
+			Crc32c:          o.Crc32c,
+			Md5Hash:         o.Md5Hash,
+			ACL:             acl,
 		})
 	}
 	return backendObjects
@@ -154,6 +160,12 @@ func (s *Server) listObjects(w http.ResponseWriter, r *http.Request) {
 
 func (s *Server) getObject(w http.ResponseWriter, r *http.Request) {
 	vars := mux.Vars(r)
+
+	if alt := r.URL.Query().Get("alt"); alt == "media" {
+		s.downloadObject(w, r)
+		return
+	}
+
 	encoder := json.NewEncoder(w)
 	obj, err := s.GetObject(vars["bucketName"], vars["objectName"])
 	if err != nil {
@@ -195,14 +207,29 @@ func (s *Server) setObjectACL(w http.ResponseWriter, r *http.Request) {
 	vars := mux.Vars(r)
 
 	obj, err := s.GetObject(vars["bucketName"], vars["objectName"])
-	entity := storage.ACLEntity(vars["entity"])
 
 	if err != nil {
 		http.Error(w, "not found", http.StatusNotFound)
 		return
 	}
 
-	obj.ACL = entity
+	var data struct {
+		Entity string
+		Role   string
+	}
+
+	decoder := json.NewDecoder(r.Body)
+	if err := decoder.Decode(&data); err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+
+	entity := storage.ACLEntity(data.Entity)
+	role := storage.ACLRole(data.Role)
+	obj.ACL = []storage.ACLRule{{
+		Entity: entity,
+		Role:   role,
+	}}
 
 	s.CreateObject(obj)
 
diff --git a/fakestorage/response.go b/fakestorage/response.go
index 13ba8a7..ce0cad0 100644
--- a/fakestorage/response.go
+++ b/fakestorage/response.go
@@ -8,7 +8,6 @@ import (
 	"net/http"
 	"sort"
 
-	cloudstorage "cloud.google.com/go/storage"
 	"google.golang.org/api/googleapi"
 	"google.golang.org/api/storage/v1"
 )
@@ -58,28 +57,32 @@ func newListObjectsResponse(objs []Object, prefixes []string) listResponse {
 }
 
 type objectResponse struct {
-	Kind        string `json:"kind"`
-	Name        string `json:"name"`
-	ID          string `json:"id"`
-	Bucket      string `json:"bucket"`
-	Size        int64  `json:"size,string"`
-	ContentType string `json:"contentType,omitempty"`
-	Crc32c      string `json:"crc32c,omitempty"`
-	ACL         string `json:"acl,omitempty"`
-	Md5Hash     string `json:"md5hash,omitempty"`
+	Kind            string                         `json:"kind"`
+	Name            string                         `json:"name"`
+	ID              string                         `json:"id"`
+	Bucket          string                         `json:"bucket"`
+	Size            int64                          `json:"size,string"`
+	ContentType     string                         `json:"contentType,omitempty"`
+	ContentEncoding string                         `json:"contentEncoding,omitempty"`
+	Crc32c          string                         `json:"crc32c,omitempty"`
+	ACL             []*storage.ObjectAccessControl `json:"acl,omitempty"`
+	Md5Hash         string                         `json:"md5hash,omitempty"`
 }
 
 func newObjectResponse(obj Object) objectResponse {
+	acl := getAccessControlsListFromObject(obj)
+
 	return objectResponse{
-		Kind:        "storage#object",
-		ID:          obj.id(),
-		Bucket:      obj.BucketName,
-		Name:        obj.Name,
-		Size:        int64(len(obj.Content)),
-		ContentType: obj.ContentType,
-		Crc32c:      obj.Crc32c,
-		ACL:         string(obj.ACL),
-		Md5Hash:     obj.Md5Hash,
+		Kind:            "storage#object",
+		ID:              obj.id(),
+		Bucket:          obj.BucketName,
+		Name:            obj.Name,
+		Size:            int64(len(obj.Content)),
+		ContentType:     obj.ContentType,
+		ContentEncoding: obj.ContentEncoding,
+		Crc32c:          obj.Crc32c,
+		Md5Hash:         obj.Md5Hash,
+		ACL:             acl,
 	}
 }
 
@@ -88,23 +91,35 @@ type aclListResponse struct {
 }
 
 func newACLListResponse(obj Object) aclListResponse {
+	if len(obj.ACL) == 0 {
+		return aclListResponse{}
+	}
+
+	aclItems := getAccessControlsListFromObject(obj)
+
 	return aclListResponse{
 		&storage.ObjectAccessControls{
 			ServerResponse: googleapi.ServerResponse{
 				HTTPStatusCode: http.StatusOK,
 			},
-			Items: []*storage.ObjectAccessControl{
-				{
-					Bucket: obj.BucketName,
-					Entity: string(obj.ACL),
-					Object: obj.Name,
-					Role:   string(cloudstorage.RoleReader),
-				},
-			},
+			Items: aclItems,
 		},
 	}
 }
 
+func getAccessControlsListFromObject(obj Object) []*storage.ObjectAccessControl {
+	aclItems := make([]*storage.ObjectAccessControl, len(obj.ACL))
+	for idx, aclRule := range obj.ACL {
+		aclItems[idx] = &storage.ObjectAccessControl{
+			Bucket: obj.BucketName,
+			Entity: string(aclRule.Entity),
+			Object: obj.Name,
+			Role:   string(aclRule.Role),
+		}
+	}
+	return aclItems
+}
+
 type rewriteResponse struct {
 	Kind                string         `json:"kind"`
 	TotalBytesRewritten int64          `json:"totalBytesRewritten,string"`
diff --git a/fakestorage/server.go b/fakestorage/server.go
index 9825af1..ae2d5c9 100644
--- a/fakestorage/server.go
+++ b/fakestorage/server.go
@@ -145,21 +145,30 @@ func (s *Server) setTransportToMux() {
 }
 
 func (s *Server) buildMuxer() {
+	const apiPrefix = "/storage/v1"
 	s.mux = mux.NewRouter()
+
+	routers := []*mux.Router{
+		s.mux.PathPrefix(apiPrefix).Subrouter(),
+		s.mux.Host(s.publicHost).PathPrefix(apiPrefix).Subrouter(),
+	}
+
+	for _, r := range routers {
+		r.Path("/b").Methods("GET").HandlerFunc(s.listBuckets)
+		r.Path("/b").Methods("POST").HandlerFunc(s.createBucketByPost)
+		r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(s.getBucket)
+		r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(s.listObjects)
+		r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
+		r.Path("/b/{bucketName}/o/{objectName:.+}/acl").Methods("GET").HandlerFunc(s.listObjectACL)
+		r.Path("/b/{bucketName}/o/{objectName:.+}/acl/{entity}").Methods("PUT").HandlerFunc(s.setObjectACL)
+		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.getObject)
+		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("DELETE").HandlerFunc(s.deleteObject)
+		r.Path("/b/{sourceBucket}/o/{sourceObject:.+}/rewriteTo/b/{destinationBucket}/o/{destinationObject:.+}").HandlerFunc(s.rewriteObject)
+	}
+
 	s.mux.Host(s.publicHost).Path("/{bucketName}/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
 	bucketHost := fmt.Sprintf("{bucketName}.%s", s.publicHost)
 	s.mux.Host(bucketHost).Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
-	r := s.mux.PathPrefix("/storage/v1").Subrouter()
-	r.Path("/b").Methods("GET").HandlerFunc(s.listBuckets)
-	r.Path("/b").Methods("POST").HandlerFunc(s.createBucketByPost)
-	r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(s.getBucket)
-	r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(s.listObjects)
-	r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
-	r.Path("/b/{bucketName}/o/{objectName:.+}/acl").Methods("GET").HandlerFunc(s.listObjectACL)
-	r.Path("/b/{bucketName}/o/{objectName:.+}/acl/{entity}").Methods("PUT").HandlerFunc(s.setObjectACL)
-	r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.getObject)
-	r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("DELETE").HandlerFunc(s.deleteObject)
-	r.Path("/b/{sourceBucket}/o/{sourceObject:.+}/rewriteTo/b/{destinationBucket}/o/{destinationObject:.+}").HandlerFunc(s.rewriteObject)
 	s.mux.Path("/download/storage/v1/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.downloadObject)
 	s.mux.Path("/upload/storage/v1/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
 	s.mux.Path("/upload/resumable/{uploadId}").Methods("PUT", "POST").HandlerFunc(s.uploadFileContent)
diff --git a/fakestorage/upload.go b/fakestorage/upload.go
index df1c7b4..e1e4b25 100644
--- a/fakestorage/upload.go
+++ b/fakestorage/upload.go
@@ -19,13 +19,16 @@ import (
 	"strconv"
 	"strings"
 
+	"cloud.google.com/go/storage"
 	"github.com/gorilla/mux"
 )
 
 const contentTypeHeader = "Content-Type"
 
 type multipartMetadata struct {
-	Name string `json:"name"`
+	ContentType     string `json:"contentType"`
+	ContentEncoding string `json:"contentEncoding"`
+	Name            string `json:"name"`
 }
 
 type contentRange struct {
@@ -60,6 +63,8 @@ func (s *Server) insertObject(w http.ResponseWriter, r *http.Request) {
 func (s *Server) simpleUpload(bucketName string, w http.ResponseWriter, r *http.Request) {
 	defer r.Body.Close()
 	name := r.URL.Query().Get("name")
+	predefinedACL := r.URL.Query().Get("predefinedAcl")
+	contentEncoding := r.URL.Query().Get("contentEncoding")
 	if name == "" {
 		http.Error(w, "name is required for simple uploads", http.StatusBadRequest)
 		return
@@ -70,12 +75,14 @@ func (s *Server) simpleUpload(bucketName string, w http.ResponseWriter, r *http.
 		return
 	}
 	obj := Object{
-		BucketName:  bucketName,
-		Name:        name,
-		Content:     data,
-		ContentType: r.Header.Get(contentTypeHeader),
-		Crc32c:      encodedCrc32cChecksum(data),
-		Md5Hash:     encodedMd5Hash(data),
+		BucketName:      bucketName,
+		Name:            name,
+		Content:         data,
+		ContentType:     r.Header.Get(contentTypeHeader),
+		ContentEncoding: contentEncoding,
+		Crc32c:          encodedCrc32cChecksum(data),
+		Md5Hash:         encodedMd5Hash(data),
+		ACL:             getObjectACL(predefinedACL),
 	}
 	err = s.createObject(obj)
 	if err != nil {
@@ -86,6 +93,24 @@ func (s *Server) simpleUpload(bucketName string, w http.ResponseWriter, r *http.
 	json.NewEncoder(w).Encode(obj)
 }
 
+func getObjectACL(predefinedACL string) []storage.ACLRule {
+	if predefinedACL == "publicRead" {
+		return []storage.ACLRule{
+			{
+				Entity: "allUsers",
+				Role:   "READER",
+			},
+		}
+	}
+
+	return []storage.ACLRule{
+		{
+			Entity: "projectOwner",
+			Role:   "OWNER",
+		},
+	}
+}
+
 var crc32cTable = crc32.MakeTable(crc32.Castagnoli)
 
 func crc32cChecksum(content []byte) []byte {
@@ -120,6 +145,7 @@ func encodedMd5Hash(content []byte) string {
 func (s *Server) multipartUpload(bucketName string, w http.ResponseWriter, r *http.Request) {
 	defer r.Body.Close()
 	_, params, err := mime.ParseMediaType(r.Header.Get(contentTypeHeader))
+
 	if err != nil {
 		http.Error(w, "invalid Content-Type header", http.StatusBadRequest)
 		return
@@ -134,6 +160,7 @@ func (s *Server) multipartUpload(bucketName string, w http.ResponseWriter, r *ht
 	for ; err == nil; part, err = reader.NextPart() {
 		if metadata == nil {
 			metadata, err = loadMetadata(part)
+			contentType = metadata.ContentType
 		} else {
 			contentType = part.Header.Get(contentTypeHeader)
 			content, err = loadContent(part)
@@ -148,17 +175,20 @@ func (s *Server) multipartUpload(bucketName string, w http.ResponseWriter, r *ht
 	}
 
 	objName := r.URL.Query().Get("name")
+	predefinedACL := r.URL.Query().Get("predefinedAcl")
 	if objName == "" {
 		objName = metadata.Name
 	}
 
 	obj := Object{
-		BucketName:  bucketName,
-		Name:        objName,
-		Content:     content,
-		ContentType: contentType,
-		Crc32c:      encodedCrc32cChecksum(content),
-		Md5Hash:     encodedMd5Hash(content),
+		BucketName:      bucketName,
+		Name:            objName,
+		Content:         content,
+		ContentType:     contentType,
+		ContentEncoding: metadata.ContentEncoding,
+		Crc32c:          encodedCrc32cChecksum(content),
+		Md5Hash:         encodedMd5Hash(content),
+		ACL:             getObjectACL(predefinedACL),
 	}
 	err = s.createObject(obj)
 	if err != nil {
@@ -171,6 +201,8 @@ func (s *Server) multipartUpload(bucketName string, w http.ResponseWriter, r *ht
 
 func (s *Server) resumableUpload(bucketName string, w http.ResponseWriter, r *http.Request) {
 	objName := r.URL.Query().Get("name")
+	predefinedACL := r.URL.Query().Get("predefinedAcl")
+	contentEncoding := r.URL.Query().Get("contentEncoding")
 	if objName == "" {
 		metadata, err := loadMetadata(r.Body)
 		if err != nil {
@@ -180,8 +212,10 @@ func (s *Server) resumableUpload(bucketName string, w http.ResponseWriter, r *ht
 		objName = metadata.Name
 	}
 	obj := Object{
-		BucketName: bucketName,
-		Name:       objName,
+		BucketName:      bucketName,
+		Name:            objName,
+		ContentEncoding: contentEncoding,
+		ACL:             getObjectACL(predefinedACL),
 	}
 	uploadID, err := generateUploadID()
 	if err != nil {
diff --git a/go.mod b/go.mod
index 45f0303..47f23c3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,11 @@
 module github.com/fsouza/fake-gcs-server
 
 require (
-	cloud.google.com/go/storage v1.2.1
+	cloud.google.com/go/storage v1.4.0
 	github.com/google/go-cmp v0.3.1
 	github.com/gorilla/mux v1.7.3
 	github.com/sirupsen/logrus v1.4.2
-	google.golang.org/api v0.13.0
+	google.golang.org/api v0.14.0
 )
 
 go 1.13
diff --git a/go.sum b/go.sum
index 3833616..ffb4e0e 100644
--- a/go.sum
+++ b/go.sum
@@ -23,6 +23,10 @@ cloud.google.com/go/storage v1.2.0 h1:j4FZW6a1zW237It0ORRgQ5knC6H4AAI/OEADjmNPQ6
 cloud.google.com/go/storage v1.2.0/go.mod h1:9gYIx9Yslppm7JnJFIRyUhybmKXFCReO7Eq8bdUJZ4k=
 cloud.google.com/go/storage v1.2.1 h1:3G66kIUChoxgxJPzwpWroEnXjfbLuZgbCNAHIvN/sKY=
 cloud.google.com/go/storage v1.2.1/go.mod h1:kpwTAahUQmhyVVGgLWQh2GdyPDZSA3UJDjMm/fDV2oQ=
+cloud.google.com/go/storage v1.3.0 h1:2Ze/3nQD5F+HfL0xOPM2EeawDWs+NPRtzgcre+17iZU=
+cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA=
+cloud.google.com/go/storage v1.4.0 h1:KDdqY5VTXBTqpSbctVTt0mVvfanP6JZzNzLE0qNY100=
+cloud.google.com/go/storage v1.4.0/go.mod h1:ZusYJWlOshgSBGbt6K3GnB3MT3H1xs2id9+TCl4fDBA=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -186,6 +190,10 @@ golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0 h1:azkp5oIgy7LNGQ64URezZcc
 golang.org/x/tools v0.0.0-20191104232314-dc038396d1f0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a h1:RzzIfXstYPS78k0QViPGpDcTlV+QuYrbxVmsxDHdxTs=
 golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc h1:V4uzEpfPev8S0NzQIqZE2HsWLn0BP+3KeizTW0xmsUg=
+golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2 h1:EtTFh6h4SAKemS+CURDMTDIANuduG5zKEXShyy18bGA=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -197,6 +205,8 @@ google.golang.org/api v0.11.0 h1:n/qM3q0/rV2F0pox7o0CvNhlPvZAo7pLbef122cbLJ0=
 google.golang.org/api v0.11.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
 google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -221,6 +231,10 @@ google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o
 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo=
 google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9 h1:6XzpBoANz1NqMNfDXzc2QmHmbb1vyMsvRfoP5rM+K1I=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
diff --git a/internal/backend/object.go b/internal/backend/object.go
index 8f7221e..3378d92 100644
--- a/internal/backend/object.go
+++ b/internal/backend/object.go
@@ -6,13 +6,14 @@ package backend
 
 // Object represents the object that is stored within the fake server.
 type Object struct {
-	BucketName  string `json:"-"`
-	Name        string `json:"-"`
-	ContentType string
-	Content     []byte
-	Crc32c      string
-	Md5Hash     string
-	ACL         string
+	BucketName      string `json:"-"`
+	Name            string `json:"-"`
+	ContentType     string
+	ContentEncoding string
+	Content         []byte
+	Crc32c          string
+	Md5Hash         string
+	ACL             []byte
 }
 
 // ID is useful for comparing objects


Test patch:
diff --git a/fakestorage/object_test.go b/fakestorage/object_test.go
index 65cde4c..bda49b5 100644
--- a/fakestorage/object_test.go
+++ b/fakestorage/object_test.go
@@ -31,21 +31,23 @@ func uint32Checksum(b []byte) uint32 {
 
 func TestServerClientObjectAttrs(t *testing.T) {
 	const (
-		bucketName  = "some-bucket"
-		objectName  = "img/hi-res/party-01.jpg"
-		content     = "some nice content"
-		contentType = "text/plain; charset=utf-8"
+		bucketName      = "some-bucket"
+		objectName      = "img/hi-res/party-01.jpg"
+		content         = "some nice content"
+		contentType     = "text/plain; charset=utf-8"
+		contentEncoding = "gzip"
 	)
 	checksum := uint32Checksum([]byte(content))
 	hash := md5Hash([]byte(content))
 	objs := []Object{
 		{
-			BucketName:  bucketName,
-			Name:        objectName,
-			Content:     []byte(content),
-			ContentType: contentType,
-			Crc32c:      encodedChecksum(uint32ToBytes(checksum)),
-			Md5Hash:     encodedHash(hash),
+			BucketName:      bucketName,
+			Name:            objectName,
+			Content:         []byte(content),
+			ContentType:     contentType,
+			ContentEncoding: contentEncoding,
+			Crc32c:          encodedChecksum(uint32ToBytes(checksum)),
+			Md5Hash:         encodedHash(hash),
 		},
 	}
 
@@ -68,6 +70,9 @@ func TestServerClientObjectAttrs(t *testing.T) {
 		if attrs.ContentType != contentType {
 			t.Errorf("wrong content type\nwant %q\ngot  %q", contentType, attrs.ContentType)
 		}
+		if attrs.ContentEncoding != contentEncoding {
+			t.Errorf("wrong content encoding\nwant %q\ngot  %q", contentEncoding, attrs.ContentEncoding)
+		}
 		if attrs.CRC32C != checksum {
 			t.Errorf("wrong checksum returned\nwant %d\ngot   %d", checksum, attrs.CRC32C)
 		}
diff --git a/fakestorage/server_test.go b/fakestorage/server_test.go
index 9a03f4d..e5c1ed8 100644
--- a/fakestorage/server_test.go
+++ b/fakestorage/server_test.go
@@ -83,6 +83,13 @@ func testDownloadObject(t *testing.T, server *Server) {
 			map[string]string{"accept-ranges": "bytes", "content-length": "21"},
 			"body {display: none;}",
 		},
+		{
+			"GET: using storage api",
+			http.MethodGet,
+			"https://storage.googleapis.com/storage/v1/b/some-bucket/o/files/txt/text-01.txt?alt=media",
+			map[string]string{"accept-ranges": "bytes", "content-length": "9"},
+			"something",
+		},
 		{
 			"HEAD: bucket in the path",
 			http.MethodHead,
diff --git a/fakestorage/upload_test.go b/fakestorage/upload_test.go
index bf46c23..4d893d1 100644
--- a/fakestorage/upload_test.go
+++ b/fakestorage/upload_test.go
@@ -12,6 +12,7 @@ import (
 	"strings"
 	"testing"
 
+	"cloud.google.com/go/storage"
 	"google.golang.org/api/googleapi"
 )
 
@@ -183,6 +184,66 @@ func TestServerClientSimpleUpload(t *testing.T) {
 	checkChecksum(t, []byte(data), obj)
 }
 
+func TestServerClientUploadWithPredefinedAclPublicRead(t *testing.T) {
+	server := NewServer(nil)
+	defer server.Stop()
+	server.CreateBucket("other-bucket")
+
+	const data = "some nice content"
+	const contentType = "text/plain"
+	const contentEncoding = "gzip"
+	req, err := http.NewRequest("POST", server.URL()+"/storage/v1/b/other-bucket/o?predefinedAcl=publicRead&uploadType=media&name=some/nice/object.txt&contentEncoding="+contentEncoding, strings.NewReader(data))
+	if err != nil {
+		t.Fatal(err)
+	}
+	req.Header.Set("Content-Type", contentType)
+	client := http.Client{
+		Transport: &http.Transport{
+			// #nosec
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		},
+	}
+	resp, err := client.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer resp.Body.Close()
+	expectedStatus := http.StatusOK
+	if resp.StatusCode != expectedStatus {
+		t.Errorf("wrong status code\nwant %d\ngot  %d", expectedStatus, resp.StatusCode)
+	}
+
+	obj, err := server.GetObject("other-bucket", "some/nice/object.txt")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	attrs, err := server.Client().Bucket("other-bucket").Object("some/nice/object.txt").Attrs(context.Background())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if attrs.ContentEncoding != contentEncoding {
+		t.Errorf("wrong contentEncoding\nwant %q\ngot %q", contentEncoding, attrs.ContentEncoding)
+	}
+
+	acl, err := server.Client().Bucket("other-bucket").Object("some/nice/object.txt").ACL().List(context.Background())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !isACLPublic(acl) {
+		t.Errorf("wrong acl\ngot %+v", acl)
+	}
+	if string(obj.Content) != data {
+		t.Errorf("wrong content\nwant %q\ngot  %q", string(obj.Content), data)
+	}
+	if obj.ContentType != contentType {
+		t.Errorf("wrong content type\nwant %q\ngot  %q", contentType, obj.ContentType)
+	}
+	checkChecksum(t, []byte(data), obj)
+}
+
 func TestServerClientSimpleUploadNoName(t *testing.T) {
 	server := NewServer(nil)
 	defer server.Stop()
@@ -290,3 +351,12 @@ func TestParseContentRange(t *testing.T) {
 		})
 	}
 }
+
+func isACLPublic(acl []storage.ACLRule) bool {
+	for _, entry := range acl {
+		if entry.Entity == storage.AllUsers && entry.Role == storage.RoleReader {
+			return true
+		}
+	}
+	return false
+}