From 2798ced9b9107db0611896acffc97ebf8b8ec6cd Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Tue, 11 Aug 2020 20:22:12 -0500 Subject: [PATCH] Fix charset=utf-8 appending to binary output --- CHANGELOG.md | 1 + src/PostgREST/ApiRequest.hs | 2 +- src/PostgREST/Types.hs | 11 +++++++++-- test/Feature/HtmlRawOutputSpec.hs | 2 +- test/Feature/QuerySpec.hs | 4 ++-- test/Feature/RpcSpec.hs | 4 ++-- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d0aea43f..1a9e2b9db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #1094, Fix expired JWTs starting an empty transaction on the db - @steve-chavez - #1162, Fix location header for POST request with select= without PK - @wolfgangwalther - #1585, Fix error messages on connection failure for localized postgres on Windows - @wolfgangwalther + - #1636, Fix `application/octet-stream` appending `charset=utf-8` - @steve-chavez ### Changed diff --git a/src/PostgREST/ApiRequest.hs b/src/PostgREST/ApiRequest.hs index 386b2c666..f154f05cd 100644 --- a/src/PostgREST/ApiRequest.hs +++ b/src/PostgREST/ApiRequest.hs @@ -201,7 +201,7 @@ userApiRequest confSchemas rootSpec dbStructure req reqBody (CTTextCSV, _) -> do json <- csvToJson <$> CSV.decodeByName reqBody note "All lines must have same number of fields" $ payloadAttributes (JSON.encode json) json - (CTOther "application/x-www-form-urlencoded", _) -> + (CTUrlEncoded, _) -> let json = paramsFromList . map (toS *** toS) . parseSimpleQuery $ toS reqBody keys = S.fromList $ M.keys json in Right $ ProcessedJSON (JSON.encode json) keys diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index a3ee004ea..377a8f431 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -31,12 +31,17 @@ import Protolude.Conv (toS) -- | Enumeration of currently supported response content types data ContentType = CTApplicationJSON | CTSingularJSON | CTTextCSV | CTTextPlain - | CTOpenAPI | CTOctetStream + | CTOpenAPI | CTUrlEncoded | CTOctetStream | CTAny | CTOther ByteString deriving (Show, Eq) -- | Convert from ContentType to a full HTTP Header toHeader :: ContentType -> Header -toHeader ct = (hContentType, toMime ct <> "; charset=utf-8") +toHeader ct = (hContentType, toMime ct <> charset) + where + charset = case ct of + CTOctetStream -> mempty + CTOther _ -> mempty + _ -> "; charset=utf-8" -- | Convert from ContentType to a ByteString representing the mime type toMime :: ContentType -> ByteString @@ -45,6 +50,7 @@ toMime CTTextCSV = "text/csv" toMime CTTextPlain = "text/plain" toMime CTOpenAPI = "application/openapi+json" toMime CTSingularJSON = "application/vnd.pgrst.object+json" +toMime CTUrlEncoded = "application/x-www-form-urlencoded" toMime CTOctetStream = "application/octet-stream" toMime CTAny = "*/*" toMime (CTOther ct) = ct @@ -58,6 +64,7 @@ decodeContentType ct = case BS.takeWhile (/= BS.c2w ';') ct of "application/openapi+json" -> CTOpenAPI "application/vnd.pgrst.object+json" -> CTSingularJSON "application/vnd.pgrst.object" -> CTSingularJSON + "application/x-www-form-urlencoded" -> CTUrlEncoded "application/octet-stream" -> CTOctetStream "*/*" -> CTAny ct' -> CTOther ct' diff --git a/test/Feature/HtmlRawOutputSpec.hs b/test/Feature/HtmlRawOutputSpec.hs index ace649e14..5d65292aa 100644 --- a/test/Feature/HtmlRawOutputSpec.hs +++ b/test/Feature/HtmlRawOutputSpec.hs @@ -26,5 +26,5 @@ spec = describe "When raw-media-types is set to \"text/html\"" $ | |] { matchStatus = 200 - , matchHeaders = ["Content-Type" <:> "text/html; charset=utf-8"] + , matchHeaders = ["Content-Type" <:> "text/html"] } diff --git a/test/Feature/QuerySpec.hs b/test/Feature/QuerySpec.hs index 12f0d7a24..b27c21385 100644 --- a/test/Feature/QuerySpec.hs +++ b/test/Feature/QuerySpec.hs @@ -791,7 +791,7 @@ spec actualPgVersion = do request methodGet "/images_base64?select=img&name=eq.A.png" (acceptHdrs "application/octet-stream") "" `shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCC" { matchStatus = 200 - , matchHeaders = ["Content-Type" <:> "application/octet-stream; charset=utf-8"] + , matchHeaders = ["Content-Type" <:> "application/octet-stream"] } it "can get raw output with Accept: text/plain" $ @@ -817,7 +817,7 @@ spec actualPgVersion = do request methodGet "/images_base64?select=img&name=in.(A.png,B.png)" (acceptHdrs "application/octet-stream") "" `shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEX///8AAP94wDzzAAAAL0lEQVQIW2NgwAb+HwARH0DEDyDxwAZEyGAhLODqHmBRzAcn5GAS///A1IF14AAA5/Adbiiz/0gAAAAASUVORK5CYII=" { matchStatus = 200 - , matchHeaders = ["Content-Type" <:> "application/octet-stream; charset=utf-8"] + , matchHeaders = ["Content-Type" <:> "application/octet-stream"] } describe "values with quotes in IN and NOT IN" $ do diff --git a/test/Feature/RpcSpec.hs b/test/Feature/RpcSpec.hs index d04134de3..ca2716e6a 100644 --- a/test/Feature/RpcSpec.hs +++ b/test/Feature/RpcSpec.hs @@ -729,7 +729,7 @@ spec actualPgVersion = request methodPost "/rpc/ret_base64_bin" (acceptHdrs "application/octet-stream") "" `shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCC" { matchStatus = 200 - , matchHeaders = ["Content-Type" <:> "application/octet-stream; charset=utf-8"] + , matchHeaders = ["Content-Type" <:> "application/octet-stream"] } it "can get raw output with Accept: text/plain" $ @@ -744,7 +744,7 @@ spec actualPgVersion = request methodPost "/rpc/ret_rows_with_base64_bin?select=img" (acceptHdrs "application/octet-stream") "" `shouldRespondWith` "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEUAAAD/AAAb/40iAAAAP0lEQVQI12NgwAbYG2AE/wEYwQMiZB4ACQkQYZEAIgqAhAGIKLCAEQ8kgMT/P1CCEUwc4IMSzA3sUIIdCHECAGSQEkeOTUyCAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABlBMVEX///8AAP94wDzzAAAAL0lEQVQIW2NgwAb+HwARH0DEDyDxwAZEyGAhLODqHmBRzAcn5GAS///A1IF14AAA5/Adbiiz/0gAAAAASUVORK5CYII=" { matchStatus = 200 - , matchHeaders = ["Content-Type" <:> "application/octet-stream; charset=utf-8"] + , matchHeaders = ["Content-Type" <:> "application/octet-stream"] } it "fails if a single column is not selected" $