diff --git a/CHANGELOG.md b/CHANGELOG.md index 58961ab34..6607fcba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #2341, The search path now correctly identifies schemas with uppercase and special characters in their names (regression) - @laurenceisla - #2364, "404 Not Found" on nested routes and "405 Method Not Allowed" errors no longer start an empty database transaction - @steve-chavez - #2342, Fix inaccurate result count when an inner embed was selected after a normal embed in the query string - @laurenceisla + - #2376, OPTIONS requests no longer start an empty database transaction - @steve-chavez ### Changed diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 7cb46fe64..1c04c092e 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -206,11 +206,14 @@ postgrestResponse conf@AppConfig{..} maybeDbStructure jsonDbS pgVer pool AuthRes liftEither . mapLeft Error.ApiRequestError $ ApiRequest.userApiRequest conf dbStructure req body - let handleReq apiReq = handleRequest $ RequestContext conf dbStructure apiReq pgVer + let ctx apiReq = RequestContext conf dbStructure apiReq pgVer - runDbHandler pool (txMode apiRequest) (Just authRole /= configDbAnonRole) configDbPreparedStatements . - Middleware.optionalRollback conf apiRequest $ - Middleware.runPgLocals conf authClaims authRole handleReq apiRequest jsonDbS pgVer + if iAction apiRequest == ActionInfo then + handleInfo (iTarget apiRequest) (ctx apiRequest) + else + runDbHandler pool (txMode apiRequest) (Just authRole /= configDbAnonRole) configDbPreparedStatements . + Middleware.optionalRollback conf apiRequest $ + Middleware.runPgLocals conf authClaims authRole (handleRequest . ctx) apiRequest jsonDbS pgVer runDbHandler :: SQL.Pool -> SQL.Mode -> Bool -> Bool -> DbHandler a -> Handler IO a runDbHandler pool mode authenticated prepared handler = do @@ -237,8 +240,6 @@ handleRequest context@(RequestContext _ _ ApiRequest{..} _) = handleSingleUpsert identifier context (ActionMutate MutationDelete, TargetIdent identifier) -> handleDelete identifier context - (ActionInfo, TargetIdent identifier) -> - handleInfo identifier context (ActionInvoke invMethod, TargetProc proc _) -> handleInvoke invMethod proc context (ActionInspect headersOnly, TargetDefaultSpec tSchema) -> @@ -414,8 +415,8 @@ handleDelete identifier context@(RequestContext _ _ ApiRequest{..} _) = do else response HTTP.status204 [contentRangeHeader] mempty) -handleInfo :: Monad m => QualifiedIdentifier -> RequestContext -> Handler m Wai.Response -handleInfo identifier RequestContext{..} = +handleInfo :: Monad m => Target -> RequestContext -> Handler m Wai.Response +handleInfo target RequestContext{..} = case tbl of Just table -> return $ Wai.responseLBS HTTP.status200 [allOrigins, allowH table] mempty @@ -423,7 +424,9 @@ handleInfo identifier RequestContext{..} = -- TODO is this right? When no tbl is found on the schema cache we disallow OPTIONS? throwError $ Error.ApiRequestError ApiRequestTypes.NotFound where - tbl = HM.lookup identifier (dbTables ctxDbStructure) + tbl = case target of + TargetIdent identifier -> HM.lookup identifier (dbTables ctxDbStructure) + _ -> Nothing allOrigins = ("Access-Control-Allow-Origin", "*") allowH table = ( HTTP.hAllow diff --git a/test/io/test_io.py b/test/io/test_io.py index 3fb29df33..a1e215811 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -972,6 +972,33 @@ def test_no_pool_connection_required_on_bad_http_logic(defaultenv): assert response.status_code == 405 +def test_no_pool_connection_required_on_options(defaultenv): + "no pool connection should be consumed for OPTIONS requests" + + env = { + **defaultenv, + "PGRST_DB_POOL": "1", + } + + with run(env=env) as postgrest: + # First we retain the only pool connection available + # The try/except is a hack for not waiting for the response, taken from https://stackoverflow.com/a/45601591/4692662 + try: + postgrest.session.get("/rpc/sleep?seconds=50", timeout=0.1) + except requests.exceptions.ReadTimeout: + pass + + # Then the following OPTIONS requests should succeed rapidly + + # OPTIONS on a table shouldn't require opening a connection + response = postgrest.session.options("/projects") + assert response.status_code == 200 + + # OPTIONS on RPC is not implemented yet, still it shouldn't require opening a connection + response = postgrest.session.options("/rpc/hello") + assert response.status_code == 405 + + def test_no_pool_connection_required_on_bad_jwt_claim(defaultenv): "no pool connection should be consumed for failing on invalid jwt"