Skip to content

Commit

Permalink
Merge pull request #2 from AsimovBio/cors-options
Browse files Browse the repository at this point in the history
Enables OPTIONS HTTP method for preflight requests in the FE
  • Loading branch information
radzki authored Aug 29, 2023
2 parents e07fca1 + 0915eae commit 039f5c8
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 13 deletions.
10 changes: 10 additions & 0 deletions src/gcp_storage_emulator/handlers/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,13 @@ def batch(request, response, storage, *args, **kwargs):
response.write("\r\n\r\n")

response.write("--{}--".format(boundary))


def options(request, response, storage, *args, **kwargs):
response.write("HTTP/1.1 200 OK\r\n")
response.write("Content-Type: text/html; charset=UTF-8\r\n")
response.write("allow: OPTIONS,GET,POST,PUT,DELETE,PATCH\r\n")
response.write("access-control-allow-origin: *\r\n")
response.write(
"access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS\r\n"
)
42 changes: 29 additions & 13 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 @@ -39,56 +40,64 @@ def _health_check(req, res, storage):


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: objects.options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)$".format(settings.API_ENDPOINT),
{GET: buckets.get, DELETE: buckets.delete},
{GET: buckets.get, DELETE: buckets.delete, OPTIONS: objects.options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o$".format(settings.API_ENDPOINT),
{GET: objects.ls},
{GET: objects.ls, OPTIONS: objects.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: objects.options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)/compose$".format(
settings.API_ENDPOINT
),
{POST: objects.compose},
{POST: objects.compose, OPTIONS: objects.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: objects.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: objects.options},
),
(
r"^{}/b/(?P<bucket_name>[-.\w]+)/o/(?P<object_id>.*[^/]+)$".format(
settings.DOWNLOAD_API_ENDPOINT
),
{GET: objects.download},
{GET: objects.download, OPTIONS: objects.options},
),
(
r"^{}$".format(settings.BATCH_API_ENDPOINT),
{POST: objects.batch},
{POST: objects.batch, OPTIONS: objects.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: objects.options}), # Health check endpoint
(r"^/wipe$", {GET: _wipe_data, OPTIONS: objects.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: objects.options},
),
)

Expand Down Expand Up @@ -318,9 +327,12 @@ def handle(self, method):
request = Request(self._request_handler, method)
response = Response(self._request_handler)

response["Access-Control-Allow-Origin"] = "*"

for regex, handlers in HANDLERS:
pattern = re.compile(regex)
match = pattern.fullmatch(request.path)

if match:
request.set_match(match)
handler = handlers.get(method)
Expand All @@ -341,7 +353,7 @@ def handle(self, method):
"Method not implemented: {} - {}".format(request.method, request.path)
)
response.status = HTTPStatus.NOT_IMPLEMENTED

response.close()


Expand Down Expand Up @@ -370,6 +382,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
9 changes: 9 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,12 @@ 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.assertTrue(
"GET,POST,PUT,PATCH,DELETE,OPTIONS"
in response.headers["access-control-allow-methods"]
)

0 comments on commit 039f5c8

Please sign in to comment.