haskell-servant / haskell-servant/servant

Trailing slashes in CaptureAll router behave unpredictably

Open
#1,908 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Haskell
Stars
2k
Forks
427
Avg merge
2d 23h
Merged PRs (30d)
5

Description

Consider the following program:

```haskell
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module Repo where

import Control.Monad.Except (throwError)
import Data.List (isSuffixOf)
import Data.Proxy (Proxy(..))
import Data.Text (Text)
import GHC.Generics (Generic)
import Network.HTTP.Types.Header (hLocation)
import Network.Wai.Handler.Warp (run)
import Servant.API
import Servant.Links
import Servant.Server

data API mode = API
{ myroute :: mode :- "test" :> CaptureAll "segs" Text :> Get '[JSON] [Text]
}
deriving Generic

server :: [Text] -> Handler [Text]
server ts =
case isSuffixOf [""] ts of
False ->
throwError err303
{ errHeaders = pure
( hLocation
, mappend "/" $ toHeader $ fieldLink myroute $ ts <> [""]
)
}
True -> pure ts

main :: IO ()
main = run 8081 $ serve (Proxy @(NamedRoutes API)) $ API server
```

which 303 redirects `test/:stuff` to `test/:stuff/`. This works when `:stuff` is nonempty, eg `test/abc` correctly gets redirected to `test/abc/` (and returns `["abc", ""]`).

However, when `:stuff` is empty, this server loops, redirecting the page back to itself:

```
Request

GET /test/ HTTP/1.1
Host: localhost:8081
Pragma: no-cache

---

Response

HTTP/1.1 303 See Other
Location: /test/
```

Contributor guide

Open the contributing guide

Research direction

Start by running the provided Haskell program and reproducing the redirect loop for GET /test/. Read the CaptureAll routing and link-generation paths involved in that request. Done means a trailing slash is handled without redirecting /test/ to itself, while the nonempty case still redirects test/abc to test/abc/ and returns the expected segments.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell
Domain
backend-api-design
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.