diff --git a/src/Dbapi.hs b/src/Dbapi.hs index 74d65cacd..38c21e22c 100644 --- a/src/Dbapi.hs +++ b/src/Dbapi.hs @@ -99,11 +99,11 @@ respondWithRangedResult rr = responseLBS status [ jsonContentType, ("Content-Range", - if rrTotal rr == 0 - then "*/0" - else (BS.pack $ show from) <> "-" - <> (BS.pack $ show to) <> "/" - <> (BS.pack $ show total) + if total == 0 || from > total + then "*/" <> BS.pack (show total) + else BS.pack (show from) <> "-" + <> BS.pack (show to) <> "/" + <> BS.pack (show total) ) ] (rrBody rr) @@ -111,9 +111,11 @@ respondWithRangedResult rr = from = rrFrom rr to = rrTo rr total = rrTotal rr - status = if total == 0 then status204 - else if (1 + to - from) < total then status206 - else status200 + status + | from > total = status416 + | total == 0 = status204 + | (1 + to - from) < total = status206 + | otherwise = status200 requestedVersion :: RequestHeaders -> Maybe Int requestedVersion hdrs = diff --git a/src/PgQuery.hs b/src/PgQuery.hs index 67007d314..daa6a34f0 100644 --- a/src/PgQuery.hs +++ b/src/PgQuery.hs @@ -29,7 +29,7 @@ data RangedResult = RangedResult { , rrTo :: Int , rrTotal :: Int , rrBody :: BL.ByteString -} +} deriving (Show) type QuotedSql = (String, [SqlValue]) @@ -44,7 +44,7 @@ getRows schema table qq range conn = do r <- quickQuery conn query [] return $ case r of - [[_, _, SqlNull]] -> RangedResult 0 0 0 "" + [[total, _, SqlNull]] -> RangedResult offset 0 (fromSql total) "" [[total, limited_total, json]] -> RangedResult offset (offset + fromSql limited_total - 1) (fromSql total) (fromSql json) diff --git a/src/RangeQuery.hs b/src/RangeQuery.hs index 00d6d78a7..6312d7c4e 100644 --- a/src/RangeQuery.hs +++ b/src/RangeQuery.hs @@ -49,4 +49,4 @@ offset :: NonnegRange -> Int offset range = case rangeLower range of BoundaryBelow from -> from - _ -> 0 -- should never happen + _ -> error "range without lower bound" -- should never happen diff --git a/test/Feature/RangeSpec.hs b/test/Feature/RangeSpec.hs index 790bacb05..cf4667325 100644 --- a/test/Feature/RangeSpec.hs +++ b/test/Feature/RangeSpec.hs @@ -39,11 +39,29 @@ spec = around appWithFixture $ `shouldRespondWith` ResponseMatcher { matchBody = Nothing , matchStatus = 204 - , matchHeaders = [] + , matchHeaders = [("Content-Range", "*/0")] } - context "of invalid range" $ + context "of invalid range" $ do it "fails with 416 for offside range" $ request methodGet "/items" (rangeHdrs $ ByteRangeFromTo 1 0) "" `shouldRespondWith` 416 + + it "refuses a range with nonzero start when there are no items" $ + request methodGet "/menagerie" + (rangeHdrs $ ByteRangeFromTo 1 2) "" + `shouldRespondWith` ResponseMatcher { + matchBody = Nothing + , matchStatus = 416 + , matchHeaders = [("Content-Range", "*/0")] + } + + it "refuses a range requesting start past last item" $ + request methodGet "/items" + (rangeHdrs $ ByteRangeFromTo 100 199) "" + `shouldRespondWith` ResponseMatcher { + matchBody = Nothing + , matchStatus = 416 + , matchHeaders = [("Content-Range", "*/15")] + }