Skip to content

Commit

Permalink
add permissive options response from all endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
deej-io committed Oct 10, 2024
1 parent 0d623b8 commit 68d5323
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,6 @@ cython_debug/

.cloudstorage
.idea

.envrc
.direnv
44 changes: 32 additions & 12 deletions src/gcp_storage_emulator/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
PUT = "PUT"
DELETE = "DELETE"
PATCH = "PATCH"
OPTIONS = "OPTIONS"


def _wipe_data(req, res, storage):
Expand All @@ -38,22 +39,32 @@ def _health_check(req, res, storage):
res.write("OK")


def _options(request, response, storage, *args, **kwargs):
response["content-type"] = "text/html; charset=UTF-8"
response["allow"] = "OPTIONS,GET,POST,PUT,DELETE,PATCH"
response["access-control-allow-origin"] = "*"
response["access-control-allow-methods"] = "GET,POST,PUT,PATCH,DELETE,OPTIONS"


HANDLERS = (
(r"^{}/b$".format(settings.API_ENDPOINT), {GET: buckets.ls, POST: buckets.insert}),
(
r"^{}/b$".format(settings.API_ENDPOINT),
{GET: buckets.ls, POST: buckets.insert, OPTIONS: _options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)$".format(settings.API_ENDPOINT),
{GET: buckets.get, DELETE: buckets.delete},
{GET: buckets.get, DELETE: buckets.delete, OPTIONS: _options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o$".format(settings.API_ENDPOINT),
{GET: objects.ls},
{GET: objects.ls, OPTIONS: _options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)/copyTo/b/".format(
settings.API_ENDPOINT
)
+ r"(?P<dest_bucket_name>[-.\w]+)/o/(?P<dest_object_id>.*[^/]+)$",
{POST: objects.copy},
{POST: objects.copy, OPTIONS: _options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)/rewriteTo/b/".format(
Expand All @@ -66,36 +77,41 @@ def _health_check(req, res, storage):
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)/compose$".format(
settings.API_ENDPOINT
),
{POST: objects.compose},
{POST: objects.compose, OPTIONS: _options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)$".format(
settings.API_ENDPOINT
),
{GET: objects.get, DELETE: objects.delete, PATCH: objects.patch},
{
GET: objects.get,
DELETE: objects.delete,
PATCH: objects.patch,
OPTIONS: _options,
},
),
# Non-default API endpoints
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o$".format(settings.UPLOAD_API_ENDPOINT),
{POST: objects.insert, PUT: objects.upload_partial},
{POST: objects.insert, PUT: objects.upload_partial, OPTIONS: _options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)$".format(
settings.DOWNLOAD_API_ENDPOINT
),
{GET: objects.download},
{GET: objects.download, OPTIONS: _options},
),
(
r"^{}$".format(settings.BATCH_API_ENDPOINT),
{POST: objects.batch},
{POST: objects.batch, OPTIONS: _options},
),
# Internal API, not supported by the real GCS
(r"^/$", {GET: _health_check}), # Health check endpoint
(r"^/wipe$", {GET: _wipe_data}), # Wipe all data
(r"^/$", {GET: _health_check, OPTIONS: _options}), # Health check endpoint
(r"^/wipe$", {GET: _wipe_data, OPTIONS: _options}), # Wipe all data
# Public file serving, same as object.download and signed URLs
(
r"^/(?P<bucket_name>[-.\w]+)/(?P<object_id>.*[^/]+)$",
{GET: objects.download, PUT: objects.xml_upload},
{GET: objects.download, PUT: objects.xml_upload, OPTIONS: _options},
),
)

Expand Down Expand Up @@ -377,6 +393,10 @@ def do_PATCH(self):
router = Router(self)
router.handle(PATCH)

def do_OPTIONS(self):
router = Router(self)
router.handle(OPTIONS)

def log_message(self, format, *args):
logger.info(format % args)

Expand Down
10 changes: 10 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,13 @@ def test_path_does_not_exist(self):
response = requests.get(url)
self.assertEqual(response.status_code, 501)
self.assertEqual(response.content, "".encode("utf-8"))

def test_options(self):
url = self._url("/")
response = requests.options(url)
self.assertEqual(response.status_code, 200)
self.assertIn("access-control-allow-methods", response.headers)
self.assertTrue(
"GET,POST,PUT,PATCH,DELETE,OPTIONS"
in response.headers["access-control-allow-methods"]
)

0 comments on commit 68d5323

Please sign in to comment.