Skip to content

Trailing slashes in CaptureAll router behave unpredictably #1908

Description

@isovector

Consider the following program:

{-# 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/

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions