diff --git a/.spec-results b/.spec-results index dcdbaa4..ff48968 100644 --- a/.spec-results +++ b/.spec-results @@ -1,71 +1,2864 @@ [ + [ + "Axon Header Typed Accept Aac does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Aac parses \"audio/aac\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Abw does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Abw parses \"application/x-abiword\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Arc does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Arc parses \"application/x-freearc\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Archive7z does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Archive7z parses \"application/x-7z-compressed\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Avi does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Avi parses \"video/x-msvideo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Avif does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Avif parses \"image/avif\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Azw does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Azw parses \"application/vnd.amazon.ebook\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bin does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bin parses \"application/octet-stream\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bmp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bmp parses \"OS/2 Bitmap Graphics \\timage/bmp\"", + { + "timestamp": "1733343000643.0", + "success": false + } + ], + [ + "Axon Header Typed Accept Bmp parses \"image/bmp\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bz does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bz parses \"application/x-bzip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bz2 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Bz2 parses \"application/x-bzip2\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Cda does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Cda parses \"application/x-cdf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Csh does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Csh parses \"application/x-csh\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Css does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Css parses \"text/css\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Csv does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Csv parses \"text/csv\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Doc does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Doc parses \"application/msword\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Docx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Docx parses \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Eot does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Eot parses \"application/vnd.ms-fontobject\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Epub does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Epub parses \"application/epub+zip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Gif does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Gif parses \"image/gif\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Gz does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Gz parses \"application/gzip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Html does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Html parses \"text/html\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ico does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ico parses \"image/vnd.microsoft.icon\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ics does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ics parses \"text/calendar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Jar does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Jar parses \"application/java-archive\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Jpeg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Jpeg parses \"image/jpeg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Js does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Js parses \"text/javascript (Specifications: HTML and RFC 9239)\"", + { + "timestamp": "1733343000643.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Js parses \"text/javascript\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Json does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Json parses \"application/json\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Jsonld does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Jsonld parses \"application/ld+json\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept MIME.MIME parses \"application/json\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept MIME.MIME parses \"foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept MIME.MIME parses \"text/plain\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept MIME.MIME parses \"text/plain;charset=utf8\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Midi does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Midi parses \"audio/midi\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mjs does not parse \"unknown\"", + { + "timestamp": "1733343000643.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mjs parses \"text/javascript\"", + { + "timestamp": "1733343000643.0", + "success": false + } + ], + [ + "Axon Header Typed Accept Mp3 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mp3 parses \"audio/mpeg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mp4 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mp4 parses \"video/mp4\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mpeg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mpeg parses \"video/mpeg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mpkg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Mpkg parses \"application/vnd.apple.installer+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Odp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Odp parses \"application/vnd.oasis.opendocument.presentation\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ods does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ods parses \"application/vnd.oasis.opendocument.spreadsheet\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Odt does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Odt parses \"application/vnd.oasis.opendocument.text\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Oga does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Oga parses \"audio/ogg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ogv does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ogv parses \"video/ogg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ogx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ogx parses \"application/ogg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Opus does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Opus parses \"audio/opus\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Otf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Otf parses \"font/otf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Pdf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Pdf parses \"application/pdf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Php does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Php parses \"application/x-httpd-php\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Png does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Png parses \"image/png\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ppt does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ppt parses \"application/vnd.ms-powerpoint\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Pptx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Pptx parses \"application/vnd.openxmlformats-officedocument.presentationml.presentation\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Rar does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Rar parses \"application/vnd.rar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Rtf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Rtf parses \"application/rtf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Sh does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Sh parses \"application/x-sh\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept String parses \"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept String parses \"foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept String parses foo", + { + "timestamp": "1733342553785.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Svg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Svg parses \"image/svg+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Tar does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Tar parses \"application/x-tar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Tif does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Tif parses \"image/tiff\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ts does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ts parses \"video/mp2t\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ttf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Ttf parses \"font/ttf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Txt does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Txt parses \"text/plain\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Video3g2 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Video3g2 parses \"video/3gpp2\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Video3gp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Video3gp parses \"video/3gpp\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Vsd does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Vsd parses \"application/vnd.visio\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Wav does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Wav parses \"audio/wav\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Weba does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Weba parses \"audio/webm\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Webm does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Webm parses \"video/webm\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Webp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Webp parses \"image/webp\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Woff does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Woff parses \"font/woff\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Woff2 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Woff2 parses \"font/woff2\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xhtml does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xhtml parses \"application/xhtml+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xls does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xls parses \"application/vnd.ms-excel\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xlsx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xlsx parses \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xml does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xml parses \"application/xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xul does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Xul parses \"application/vnd.mozilla.xul+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Zip does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Accept Zip parses \"application/zip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowCredentials does not parse \"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowCredentials does not parse \"false\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowCredentials parses \"true\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \" * \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \" Vary \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \" Vary\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \"* \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \"*\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \"Accept, Vary, Content-Type\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowHeaders parses \"Vary\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \" GET , PATCH \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \" * \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \"* \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \"*\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \"GET\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \"GET, PATCH\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowMethods parses \"get\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowOrigin parses \" foo \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowOrigin parses \" * \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowOrigin parses \"* \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowOrigin parses \"*\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowOrigin parses \"foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlAllowOrigin parses \"https://example.com\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \" * \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \" Vary \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \" Vary\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \"* \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \"*\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \"Accept, Vary, Content-Type\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlExposeHeaders parses \"Vary\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlMaxAge parses \" 0\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlMaxAge parses \" 123 \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlMaxAge parses \"23190\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestHeaders parses \" Vary \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestHeaders parses \" Vary\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestHeaders parses \"Accept, Vary, Content-Type\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestHeaders parses \"Vary\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestMethod parses \" patCh \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestMethod parses \"GET\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed AccessControlRequestMethod parses \"get\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Age parses \" 0\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Age parses \" 123 \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Age parses \"23190\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Allow parses \" GET , PATCH \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Allow parses \"GET\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Allow parses \"GET, PATCH\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Allow parses \"get\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Allows parses \" GET , PATCH \"", + { + "timestamp": "1733344903861.0", + "success": true + } + ], + [ + "Axon Header Typed Allows parses \"GET\"", + { + "timestamp": "1733344903861.0", + "success": true + } + ], + [ + "Axon Header Typed Allows parses \"GET, PATCH\"", + { + "timestamp": "1733344903861.0", + "success": true + } + ], + [ + "Axon Header Typed Allows parses \"get\"", + { + "timestamp": "1733344903861.0", + "success": true + } + ], + [ + "Axon Header Typed Authorization does not parse \"bar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Authorization parses \" Bing bar \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Authorization parses \"Bing bar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Authorization parses \"Foo bar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed BasicAuth does not parse \"Basic foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed BasicAuth does not parse \"Bearer ZGVtbzpwQDU1dzByZA==\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed BasicAuth parses \"Basic ZGVtbzpwQDU1dzByZA==\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed BearerAuth does not parse \"Basic foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed BearerAuth parses \"Bearer foo \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed BearerAuth parses \"Bearer foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl does not parse \" \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl does not parse \" foo \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl does not parse \" foo=bar \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl does not parse \"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl does not parse \"foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl does not parse \"foo=bar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl parses \" \"", + { + "timestamp": "1733346704017.0", + "success": false + } + ], + [ + "Axon Header Typed CacheControl parses \" max-age=604800 \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl parses \"\"", + { + "timestamp": "1733346704017.0", + "success": false + } + ], + [ + "Axon Header Typed CacheControl parses \"max-age=20, s-maxage=10, no-cache, must-revalidate, proxy-revalidate, no-store, private, public, must-understand, no-transform, immutable, stale-while-revalidate, stale-if-error\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl parses \"max-age=604800\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed CacheControl parses \"max-age=604800, must-revalidate\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection does not parse \" \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection does not parse \"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection parses \" \"", + { + "timestamp": "1733369826770.0", + "success": false + } + ], + [ + "Axon Header Typed Connection parses \" a , b , c,d\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection parses \" cLoSe \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection parses \" close \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection parses \"\"", + { + "timestamp": "1733369826770.0", + "success": false + } + ], + [ + "Axon Header Typed Connection parses \"close\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed Connection parses \"fuaiowf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \" attachment; filename=\\\"foo.txt\\\" \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \" form-data; filename=\\\"foo.txt\\\" ; name=\\\"foo\\\" \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \" inline \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"attachment\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"attachment; filename=\\\"foo.txt\\\"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"form-data\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"form-data; filename=\\\"foo.txt\\\"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"form-data; name=\\\"foo\\\"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"inline \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentDisposition parses \"inline\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentEncoding parses \" gzip \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentEncoding parses \" gzip , deflate \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentEncoding parses \"gzip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentLength parses \" 0 \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentLength parses \" 1 \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentLength parses \" 1212943817 \"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Aac does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Aac parses \"audio/aac\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Abw does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Abw parses \"application/x-abiword\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Arc does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Arc parses \"application/x-freearc\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Archive7z does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Archive7z parses \"application/x-7z-compressed\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Avi does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Avi parses \"video/x-msvideo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Avif does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Avif parses \"image/avif\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Azw does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Azw parses \"application/vnd.amazon.ebook\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bin does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bin parses \"application/octet-stream\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bmp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bmp parses \"image/bmp\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bz does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bz parses \"application/x-bzip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bz2 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Bz2 parses \"application/x-bzip2\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Cda does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Cda parses \"application/x-cdf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Csh does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Csh parses \"application/x-csh\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Css does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Css parses \"text/css\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Csv does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Csv parses \"text/csv\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Doc does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Doc parses \"application/msword\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Docx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Docx parses \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Eot does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Eot parses \"application/vnd.ms-fontobject\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Epub does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Epub parses \"application/epub+zip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Gif does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Gif parses \"image/gif\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Gz does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Gz parses \"application/gzip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Html does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Html parses \"text/html\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ico does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ico parses \"image/vnd.microsoft.icon\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ics does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ics parses \"text/calendar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Jar does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Jar parses \"application/java-archive\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Jpeg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Jpeg parses \"image/jpeg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Js does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Js parses \"text/javascript\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Json does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Json parses \"application/json\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Jsonld does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Jsonld parses \"application/ld+json\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType MIME.MIME parses \"application/json\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType MIME.MIME parses \"foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType MIME.MIME parses \"text/plain\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType MIME.MIME parses \"text/plain;charset=utf8\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Midi does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Midi parses \"audio/midi\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mp3 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mp3 parses \"audio/mpeg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mp4 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mp4 parses \"video/mp4\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mpeg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mpeg parses \"video/mpeg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mpkg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Mpkg parses \"application/vnd.apple.installer+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Odp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Odp parses \"application/vnd.oasis.opendocument.presentation\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ods does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ods parses \"application/vnd.oasis.opendocument.spreadsheet\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Odt does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Odt parses \"application/vnd.oasis.opendocument.text\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Oga does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Oga parses \"audio/ogg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ogv does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ogv parses \"video/ogg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ogx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ogx parses \"application/ogg\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Opus does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Opus parses \"audio/opus\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Otf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Otf parses \"font/otf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Pdf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Pdf parses \"application/pdf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Php does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Php parses \"application/x-httpd-php\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Png does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Png parses \"image/png\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ppt does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ppt parses \"application/vnd.ms-powerpoint\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Pptx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Pptx parses \"application/vnd.openxmlformats-officedocument.presentationml.presentation\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Rar does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Rar parses \"application/vnd.rar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Rtf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Rtf parses \"application/rtf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Sh does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Sh parses \"application/x-sh\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType String parses \"\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType String parses \"foo\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Svg does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Svg parses \"image/svg+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Tar does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Tar parses \"application/x-tar\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Tif does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Tif parses \"image/tiff\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ts does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ts parses \"video/mp2t\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ttf does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Ttf parses \"font/ttf\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Txt does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Txt parses \"text/plain\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Video3g2 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Video3g2 parses \"video/3gpp2\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Video3gp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Video3gp parses \"video/3gpp\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Vsd does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Vsd parses \"application/vnd.visio\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Wav does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Wav parses \"audio/wav\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Weba does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Weba parses \"audio/webm\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Webm does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Webm parses \"video/webm\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Webp does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Webp parses \"image/webp\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Woff does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Woff parses \"font/woff\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Woff2 does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Woff2 parses \"font/woff2\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xhtml does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xhtml parses \"application/xhtml+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xls does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xls parses \"application/vnd.ms-excel\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xlsx does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xlsx parses \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xml does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xml parses \"application/xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xul does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Xul parses \"application/vnd.mozilla.xul+xml\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Zip does not parse \"unknown\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], + [ + "Axon Header Typed ContentType Zip parses \"application/zip\"", + { + "timestamp": "1733370664343.0", + "success": true + } + ], [ "Axon Request Parts Body extracts a JSON body", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Body extracts a string body from a buffer", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Body extracts a string body from a cached string", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Body extracts a string body from a readable stream", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Path ... but does if ends in IgnoreRest", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Path does not partially match a route ...", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Path extracts an int", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Path extracts an int and a string", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Path matches a route matching literal", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], [ "Axon Request Parts Path matches a route matching multiple literals", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], @@ -100,7 +2893,7 @@ [ "Axon Request Parts extracts header, method, path, JSON body", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], @@ -114,7 +2907,7 @@ [ "Axon Request Parts extracts the whole request", { - "timestamp": "1733176427760.0", + "timestamp": "1733370664343.0", "success": true } ], diff --git a/spago.lock b/spago.lock index 409fc13..d934490 100644 --- a/spago.lock +++ b/spago.lock @@ -20,6 +20,7 @@ { "arrays": ">=7.3.0 <8.0.0" }, + "b64", { "bifunctors": ">=6.0.0 <7.0.0" }, @@ -38,9 +39,6 @@ { "exceptions": ">=6.1.0 <7.0.0" }, - { - "ezfetch": ">=1.1.0 <2.0.0" - }, { "foldable-traversable": ">=6.0.0 <7.0.0" }, @@ -96,7 +94,6 @@ ], "build_plan": [ "aff", - "aff-promise", "argonaut-codecs", "argonaut-core", "arraybuffer-types", @@ -115,7 +112,6 @@ "enums", "exceptions", "exists", - "ezfetch", "filterable", "foldable-traversable", "foreign", @@ -131,7 +127,6 @@ "lazy", "lists", "maybe", - "media-types", "newtype", "node-buffer", "node-event-emitter", @@ -168,9 +163,6 @@ "unsafe-coerce", "url-immutable", "variant", - "web-dom", - "web-events", - "web-file", "web-streams" ] }, @@ -800,15 +792,6 @@ "unsafe-coerce" ] }, - "aff-promise": { - "type": "registry", - "version": "4.0.0", - "integrity": "sha256-Kq5EupbUpXeUXx4JqGQE7/RTTz/H6idzWhsocwlEFhM=", - "dependencies": [ - "aff", - "foreign" - ] - }, "ansi": { "type": "registry", "version": "7.0.0", @@ -1088,42 +1071,6 @@ "enums" ] }, - "ezfetch": { - "type": "registry", - "version": "1.1.0", - "integrity": "sha256-EA8KHFS6PMuOdZiFt7h0E5D5yCf7/sWAfsRJoCE/xP8=", - "dependencies": [ - "aff", - "aff-promise", - "arraybuffer-types", - "b64", - "bifunctors", - "effect", - "either", - "exceptions", - "foldable-traversable", - "foreign", - "foreign-object", - "integers", - "maybe", - "newtype", - "node-buffer", - "node-streams", - "nullable", - "numbers", - "ordered-collections", - "prelude", - "record", - "simple-json", - "transformers", - "tuples", - "typelevel-prelude", - "unsafe-coerce", - "url-immutable", - "web-file", - "web-streams" - ] - }, "filterable": { "type": "registry", "version": "5.0.0", @@ -1361,15 +1308,6 @@ "prelude" ] }, - "media-types": { - "type": "registry", - "version": "6.0.0", - "integrity": "sha256-n/4FoGBasbVSYscGVRSyBunQ6CZbL3jsYL+Lp01mc9k=", - "dependencies": [ - "newtype", - "prelude" - ] - }, "mmorph": { "type": "registry", "version": "7.0.0", @@ -2017,35 +1955,6 @@ "unsafe-coerce" ] }, - "web-dom": { - "type": "registry", - "version": "6.0.0", - "integrity": "sha256-1kSKWFDI4LupdmpjK01b1MMxDFW7jvatEgPgVmCmSBQ=", - "dependencies": [ - "web-events" - ] - }, - "web-events": { - "type": "registry", - "version": "4.0.0", - "integrity": "sha256-YDt8b6u1tzGtnWyNRodne57iO8FNSGPaTCVzBUyUn4k=", - "dependencies": [ - "datetime", - "enums", - "foreign", - "nullable" - ] - }, - "web-file": { - "type": "registry", - "version": "4.0.0", - "integrity": "sha256-1h5jPBkvjY71jLEdwVadXCx86/2inNoMBO//Rd3eCSU=", - "dependencies": [ - "foreign", - "media-types", - "web-dom" - ] - }, "web-streams": { "type": "registry", "version": "4.0.0", diff --git a/spago.yaml b/spago.yaml index b04defa..8b2a702 100644 --- a/spago.yaml +++ b/spago.yaml @@ -1,6 +1,7 @@ package: name: axon dependencies: + - b64 - parsing - aff: ">=8.0.0 <9.0.0" - argonaut-codecs: ">=9.1.0 <10.0.0" @@ -13,7 +14,6 @@ package: - effect: ">=4.0.0 <5.0.0" - either: ">=6.1.0 <7.0.0" - exceptions: ">=6.1.0 <7.0.0" - - ezfetch: ">=1.1.0 <2.0.0" - foldable-traversable: ">=6.0.0 <7.0.0" - integers: ">=6.0.0 <7.0.0" - maybe: ">=6.0.0 <7.0.0" diff --git a/src/Axon.Header.Typed.purs b/src/Axon.Header.Typed.purs index 1ddb197..d71c509 100644 --- a/src/Axon.Header.Typed.purs +++ b/src/Axon.Header.Typed.purs @@ -17,62 +17,74 @@ import Data.Either (Either(..)) import Data.Either.Nested (type (\/)) import Data.Either.Nested as Either.Nested import Data.Enum (fromEnum, toEnum) +import Data.Filterable (filter) +import Data.Foldable (any, elem) import Data.Generic.Rep (class Generic) import Data.Int as Int import Data.MIME as MIME import Data.Map as Map -import Data.Maybe (Maybe(..), isJust) +import Data.Maybe (Maybe(..), isJust, maybe) import Data.Newtype (class Newtype, unwrap, wrap) +import Data.Set as Set import Data.Show.Generic (genericShow) import Data.String as String import Data.String.Base64 as String.Base64 import Data.String.CodeUnits as String.CodeUnit import Data.String.Lower (StringLower) import Data.String.Lower as String.Lower +import Data.String.Regex (Regex) +import Data.String.Regex as Regex import Data.String.Regex.Flags as Regex.Flags import Data.Time as Time import Data.Tuple (fst) import Data.Tuple.Nested (type (/\), (/\)) +import Effect.Console (log) import Effect.Exception as Error +import Effect.Unsafe (unsafePerformEffect) import Parsing (Parser) -import Parsing as Parse -import Parsing.Combinators as Parse.Combine -import Parsing.String as Parse.String -import Parsing.String.Basic as Parse.String.Basic +import Parsing (liftMaybe, fail, liftEither) as Parse +import Parsing.Combinators (between, choice, lookAhead, many, optional, sepBy, sepBy1, try) as Parse +import Parsing.String (anyTill, regex, string, eof, rest) as Parse +import Parsing.String.Basic (whiteSpace, space, intDecimal, alphaNum) as Parse import Partial.Unsafe (unsafePartial) import Prim.Row (class Nub, class Union) import Record as Record import Type.MIME as Type.MIME data Wildcard = Wildcard +derive instance Generic Wildcard _ derive instance Eq Wildcard +instance Show Wildcard where show = genericShow + +quotesRe :: Regex +quotesRe = unsafePartial $ (\(Right a) -> a) $ Regex.regex "(^\\s*\")|(\"\\s*$)" Regex.Flags.global datetimeParser :: Parser String DateTime datetimeParser = let as :: forall a. String -> a -> Parser String a - as s a = Parse.String.string s $> a + as s a = Parse.string s $> a - weekday = Parse.Combine.choice [as "Mon" Monday, as "Tue" Tuesday, as "Wed" Wednesday, as "Thu" Thursday, as "Fri" Friday, as "Sat" Saturday, as "Sun" Sunday] - month = Parse.Combine.choice [as "Jan" January, as "Feb" February, as "Mar" March, as "Apr" April, as "May" May, as "Jun" June, as "Jul" July, as "Aug" August, as "Sep" September, as "Oct" October, as "Nov" November, as "Dec" December] - day = Parse.String.Basic.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid day") - year = Parse.String.Basic.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid year") + weekday = Parse.choice [as "Mon" Monday, as "Tue" Tuesday, as "Wed" Wednesday, as "Thu" Thursday, as "Fri" Friday, as "Sat" Saturday, as "Sun" Sunday] + month = Parse.choice [as "Jan" January, as "Feb" February, as "Mar" March, as "Apr" April, as "May" May, as "Jun" June, as "Jul" July, as "Aug" August, as "Sep" September, as "Oct" October, as "Nov" November, as "Dec" December] + day = Parse.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid day") + year = Parse.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid year") date = ( pure (\d m y -> Date.exactDate y m d) - <*> (weekday *> Parse.String.Basic.whiteSpace *> day) - <*> (Parse.String.Basic.whiteSpace *> month) <*> (Parse.String.Basic.whiteSpace *> year) + <*> (weekday *> Parse.whiteSpace *> day) + <*> (Parse.whiteSpace *> month) <*> (Parse.whiteSpace *> year) ) >>= Parse.liftMaybe (const "invalid date") time = ( pure (\h m s ms -> Time.Time h m s ms) - <*> ((Parse.String.Basic.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid hour"))) - <*> (Parse.String.string ":" *> (Parse.String.Basic.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid minutes"))) - <*> (Parse.String.string ":" *> (Parse.String.Basic.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid seconds"))) + <*> ((Parse.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid hour"))) + <*> (Parse.string ":" *> (Parse.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid minutes"))) + <*> (Parse.string ":" *> (Parse.intDecimal <#> toEnum >>= Parse.liftMaybe (const "invalid seconds"))) <*> (toEnum 0 # Parse.liftMaybe (const "invalid milliseconds")) ) in - pure DateTime <*> (date <* Parse.String.Basic.whiteSpace) <*> time + pure DateTime <*> (date <* Parse.whiteSpace) <*> time printDateTime :: DateTime -> String @@ -117,41 +129,53 @@ printDateTime dt = # Array.intercalate " " commas :: forall a. Parser String a -> Parser String (Array a) -commas p = Parse.Combine.sepBy p (Parse.String.Basic.whiteSpace <* Parse.String.string "," <* Parse.String.Basic.whiteSpace) <#> Array.fromFoldable +commas p = Parse.sepBy p (Parse.whiteSpace <* Parse.string "," <* Parse.whiteSpace) <#> Array.fromFoldable commas1 :: forall a. Parser String a -> Parser String (NonEmptyArray a) -commas1 p = Parse.Combine.sepBy1 p (Parse.String.Basic.whiteSpace <* Parse.String.string "," <* Parse.String.Basic.whiteSpace) <#> Array.NonEmpty.fromFoldable1 +commas1 p = Parse.sepBy1 p (Parse.whiteSpace <* Parse.string "," <* Parse.whiteSpace) <#> Array.NonEmpty.fromFoldable1 + +semis :: forall a. Parser String a -> Parser String (Array a) +semis p = Parse.sepBy p (Parse.whiteSpace <* Parse.string ";" <* Parse.whiteSpace) <#> Array.fromFoldable wildcardParser :: Parser String Wildcard -wildcardParser = Parse.String.string "*" $> Wildcard +wildcardParser = Parse.whiteSpace *> (Parse.string "*" $> Wildcard) <* Parse.whiteSpace mimeParser :: Parser String MIME.MIME -mimeParser = Parse.String.anyTill (void Parse.String.Basic.space <|> Parse.String.eof) <#> fst <#> MIME.fromString +mimeParser = Parse.anyTill (void Parse.space <|> Parse.eof) <#> fst <#> MIME.fromString headerNameRegexParser :: Parser String String -headerNameRegexParser = unsafePartial $ (\(Right a) -> a) $ Parse.String.regex "[\\w-]+" Regex.Flags.noFlags +headerNameRegexParser = unsafePartial $ (\(Right a) -> a) $ Parse.regex "[\\w-]+" Regex.Flags.noFlags closeRegexParser :: Parser String String -closeRegexParser = unsafePartial $ (\(Right a) -> a) $ Parse.String.regex "close" Regex.Flags.ignoreCase +closeRegexParser = unsafePartial $ (\(Right a) -> a) $ Parse.regex "\\s*close\\s*" Regex.Flags.ignoreCase headerNameParser :: Parser String StringLower -headerNameParser = headerNameRegexParser <#> String.Lower.fromString +headerNameParser = Parse.between Parse.whiteSpace Parse.whiteSpace (headerNameRegexParser <#> String.Lower.fromString) methodParser :: Parser String Method -methodParser = Parse.Combine.many Parse.String.Basic.alphaNum <#> Array.fromFoldable <#> String.CodeUnit.fromCharArray >>= (\a -> Parse.liftMaybe (const $ "invalid method " <> a) $ Method.fromString a) +methodParser = Parse.between Parse.whiteSpace Parse.whiteSpace $ Parse.many Parse.alphaNum <#> Array.fromFoldable <#> String.CodeUnit.fromCharArray >>= (\a -> Parse.liftMaybe (const $ "invalid method " <> a) $ Method.fromString a) directiveParser :: Parser String (StringLower /\ Maybe String) directiveParser = let - boundary = Parse.String.string ";" <|> Parse.String.string "," <|> (Parse.String.eof *> pure "") - kvParser = do - k /\ stop <- Parse.String.anyTill (Parse.String.string "=" <|> boundary) - when (stop /= "=") $ Parse.fail "" - v <- Parse.String.anyTill boundary <#> fst - pure $ String.Lower.fromString (String.trim k) /\ Just (String.trim v) - kParser = Parse.String.anyTill boundary <#> fst <#> String.trim <#> String.Lower.fromString <#> (\k -> k /\ Nothing) + boundary = Parse.lookAhead $ Parse.try (Parse.string ",") <|> Parse.try (Parse.string ";") <|> Parse.try (Parse.eof *> pure "") + kvSep = Parse.string "=" + kvParser = + pure (\k v -> k /\ Just v) + <*> (Parse.whiteSpace *> Parse.anyTill kvSep <#> fst <#> String.trim <#> String.Lower.fromString) + <*> (Parse.whiteSpace *> Parse.anyTill boundary <#> fst <#> String.trim) + kParser = + Parse.whiteSpace + *> Parse.anyTill boundary + <#> fst + <#> String.trim + <#> String.Lower.fromString + <#> (\k -> k /\ Nothing) in - kvParser <|> kParser + (Parse.try kvParser <|> kParser) + <#> Just + <#> filter (\(k /\ v) -> not (String.null $ String.Lower.toString k) && maybe true (not <<< String.null) v) + >>= (Parse.liftMaybe $ const "empty directive") class TypedHeader a where headerName :: String @@ -165,68 +189,104 @@ derive instance Eq a => Eq (Accept a) instance Show a => Show (Accept a) where show = genericShow data AccessControlAllowCredentials = AccessControlAllowCredentials +derive instance Generic AccessControlAllowCredentials _ +derive instance Eq AccessControlAllowCredentials +instance Show AccessControlAllowCredentials where show = genericShow newtype AccessControlAllowHeaders = AccessControlAllowHeaders (Wildcard \/ NonEmptyArray StringLower) +derive instance Generic (AccessControlAllowHeaders) _ derive instance Newtype (AccessControlAllowHeaders) _ derive instance Eq (AccessControlAllowHeaders) +instance Show AccessControlAllowHeaders where show = genericShow newtype AccessControlAllowMethods = AccessControlAllowMethods (Wildcard \/ NonEmptyArray Method) derive instance Newtype (AccessControlAllowMethods) _ derive instance Eq (AccessControlAllowMethods) +derive instance Generic (AccessControlAllowMethods) _ +instance Show (AccessControlAllowMethods) where show = genericShow newtype AccessControlAllowOrigin = AccessControlAllowOrigin (Wildcard \/ String) derive instance Newtype (AccessControlAllowOrigin) _ derive instance Eq (AccessControlAllowOrigin) +derive instance Generic (AccessControlAllowOrigin) _ +instance Show (AccessControlAllowOrigin) where show = genericShow newtype AccessControlExposeHeaders = AccessControlExposeHeaders (Wildcard \/ Array StringLower) derive instance Newtype (AccessControlExposeHeaders) _ derive instance Eq (AccessControlExposeHeaders) +derive instance Generic (AccessControlExposeHeaders) _ +instance Show (AccessControlExposeHeaders) where show = genericShow newtype AccessControlMaxAge = AccessControlMaxAge Int derive instance Newtype (AccessControlMaxAge) _ derive instance Eq (AccessControlMaxAge) +derive instance Generic (AccessControlMaxAge) _ +instance Show (AccessControlMaxAge) where show = genericShow newtype AccessControlRequestHeaders = AccessControlRequestHeaders (NonEmptyArray StringLower) derive instance Newtype (AccessControlRequestHeaders) _ derive instance Eq (AccessControlRequestHeaders) +derive instance Generic (AccessControlRequestHeaders) _ +instance Show (AccessControlRequestHeaders) where show = genericShow newtype AccessControlRequestMethod = AccessControlRequestMethod Method derive instance Newtype (AccessControlRequestMethod) _ derive instance Eq (AccessControlRequestMethod) +derive instance Generic (AccessControlRequestMethod) _ +instance Show (AccessControlRequestMethod) where show = genericShow newtype Age = Age Int derive instance Newtype (Age) _ +derive instance Generic (Age) _ derive instance Eq (Age) +instance Show (Age) where show = genericShow newtype Allow = Allow (NonEmptyArray Method) derive instance Newtype (Allow) _ +derive instance Generic (Allow) _ derive instance Eq (Allow) +instance Show (Allow) where show = genericShow newtype AuthScheme = AuthScheme String derive instance Newtype (AuthScheme) _ +derive instance Generic (AuthScheme) _ derive instance Eq (AuthScheme) +instance Show (AuthScheme) where show = genericShow data Authorization = Authorization AuthScheme String +derive instance Generic (Authorization) _ +derive instance Eq (Authorization) +instance Show (Authorization) where show = genericShow newtype BearerAuth = BearerAuth String derive instance Newtype (BearerAuth) _ +derive instance Generic (BearerAuth) _ derive instance Eq (BearerAuth) +instance Show (BearerAuth) where show = genericShow newtype BasicAuth = BasicAuth {username :: String, password :: String} derive instance Newtype (BasicAuth) _ +derive instance Generic (BasicAuth) _ derive instance Eq (BasicAuth) +instance Show (BasicAuth) where show = genericShow newtype ByteRangeStart = ByteRangeStart Int derive instance Newtype (ByteRangeStart) _ +derive instance Generic (ByteRangeStart) _ derive instance Eq (ByteRangeStart) +instance Show (ByteRangeStart) where show = genericShow newtype ByteRangeEnd = ByteRangeEnd Int derive instance Newtype (ByteRangeEnd) _ +derive instance Generic (ByteRangeEnd) _ derive instance Eq (ByteRangeEnd) +instance Show (ByteRangeEnd) where show = genericShow newtype ByteRangeLength = ByteRangeLength Int derive instance Newtype (ByteRangeLength) _ +derive instance Generic (ByteRangeLength) _ derive instance Eq (ByteRangeLength) +instance Show (ByteRangeLength) where show = genericShow type CacheControl' = ( maxAge :: Maybe Int @@ -249,45 +309,67 @@ type CacheControl' = newtype CacheControl = CacheControl (Record CacheControl') derive instance Newtype (CacheControl) _ +derive instance Generic (CacheControl) _ derive instance Eq (CacheControl) +instance Show (CacheControl) where show = genericShow data ConnectionClose = ConnectionClose derive instance Eq (ConnectionClose) +instance Show (ConnectionClose) where show = genericShow +derive instance Generic (ConnectionClose) _ newtype Connection = Connection (ConnectionClose \/ NonEmptyArray StringLower) derive instance Newtype (Connection) _ +derive instance Generic (Connection) _ derive instance Eq (Connection) +instance Show (Connection) where show = genericShow newtype ContentDisposition = ContentDisposition (ContentDispositionInline \/ ContentDispositionAttachment \/ ContentDispositionFormData \/ Void) derive instance Newtype (ContentDisposition) _ +derive instance Generic (ContentDisposition) _ derive instance Eq (ContentDisposition) +instance Show (ContentDisposition) where show = genericShow data ContentDispositionInline = ContentDispositionInline +derive instance Generic (ContentDispositionInline) _ derive instance Eq (ContentDispositionInline) +instance Show (ContentDispositionInline) where show = genericShow newtype ContentDispositionAttachment = ContentDispositionAttachment {filename :: Maybe String} derive instance Newtype (ContentDispositionAttachment) _ +derive instance Generic (ContentDispositionAttachment) _ derive instance Eq (ContentDispositionAttachment) +instance Show (ContentDispositionAttachment) where show = genericShow newtype ContentDispositionFormData = ContentDispositionFormData {filename :: Maybe String, name :: Maybe String} derive instance Newtype (ContentDispositionFormData) _ +derive instance Generic (ContentDispositionFormData) _ derive instance Eq (ContentDispositionFormData) +instance Show (ContentDispositionFormData) where show = genericShow newtype ContentEncoding = ContentEncoding (NonEmptyArray String) derive instance Newtype (ContentEncoding) _ +derive instance Generic (ContentEncoding) _ derive instance Eq (ContentEncoding) +instance Show (ContentEncoding) where show = genericShow newtype ContentLength = ContentLength Int derive instance Newtype (ContentLength) _ +derive instance Generic (ContentLength) _ derive instance Eq (ContentLength) +instance Show (ContentLength) where show = genericShow newtype ContentLocation = ContentLocation String derive instance Newtype (ContentLocation) _ +derive instance Generic (ContentLocation) _ derive instance Eq (ContentLocation) +instance Show (ContentLocation) where show = genericShow newtype ContentRange = ContentRange ((ByteRangeStart /\ ByteRangeEnd /\ ByteRangeLength) \/ (ByteRangeStart /\ ByteRangeEnd) \/ ByteRangeLength \/ Void) derive instance Newtype (ContentRange) _ +derive instance Generic (ContentRange) _ derive instance Eq (ContentRange) +instance Show (ContentRange) where show = genericShow newtype ContentType a = ContentType a derive instance Generic (ContentType a) _ @@ -297,125 +379,186 @@ instance Show a => Show (ContentType a) where show = genericShow newtype Cookie = Cookie String derive instance Newtype (Cookie) _ +derive instance Generic (Cookie) _ +instance Show (Cookie) where show = genericShow derive instance Eq (Cookie) newtype Date = Date DateTime derive instance Newtype (Date) _ +derive instance Generic (Date) _ +instance Show (Date) where show = genericShow derive instance Eq (Date) newtype ETag = ETag String derive instance Newtype (ETag) _ +derive instance Generic (ETag) _ +instance Show (ETag) where show = genericShow derive instance Eq (ETag) data ExpectContinue = ExpectContinue newtype Expires = Expires DateTime derive instance Newtype (Expires) _ +derive instance Generic (Expires) _ +instance Show (Expires) where show = genericShow derive instance Eq (Expires) newtype Host = Host String derive instance Newtype (Host) _ +derive instance Generic (Host) _ +instance Show (Host) where show = genericShow derive instance Eq (Host) newtype IfMatch = IfMatch (Wildcard \/ NonEmptyArray String) derive instance Newtype (IfMatch) _ +derive instance Generic (IfMatch) _ +instance Show (IfMatch) where show = genericShow derive instance Eq (IfMatch) newtype IfNoneMatch = IfNoneMatch (Wildcard \/ NonEmptyArray MatchETag) derive instance Newtype (IfNoneMatch) _ +derive instance Generic (IfNoneMatch) _ +instance Show (IfNoneMatch) where show = genericShow derive instance Eq (IfNoneMatch) newtype IfModifiedSince = IfModifiedSince DateTime derive instance Newtype (IfModifiedSince) _ +derive instance Generic (IfModifiedSince) _ +instance Show (IfModifiedSince) where show = genericShow derive instance Eq (IfModifiedSince) newtype IfRange = IfRange (DateTime \/ String) derive instance Newtype (IfRange) _ +derive instance Generic (IfRange) _ +instance Show (IfRange) where show = genericShow derive instance Eq (IfRange) newtype IfUnmodifiedSince = IfUnmodifiedSince DateTime derive instance Newtype (IfUnmodifiedSince) _ +derive instance Generic (IfUnmodifiedSince) _ +instance Show (IfUnmodifiedSince) where show = genericShow derive instance Eq (IfUnmodifiedSince) newtype LastModified = LastModified DateTime derive instance Newtype (LastModified) _ +derive instance Generic (LastModified) _ +instance Show (LastModified) where show = genericShow derive instance Eq (LastModified) data MatchETag = MatchETag String | MatchETagWeak String derive instance Eq MatchETag +instance Show MatchETag where show = genericShow +derive instance Generic MatchETag _ newtype Origin = Origin String derive instance Newtype (Origin) _ +derive instance Generic (Origin) _ +instance Show (Origin) where show = genericShow derive instance Eq (Origin) data ProxyAuthorization = ProxyAuthorization AuthScheme String +derive instance Generic (ProxyAuthorization) _ +derive instance Eq (ProxyAuthorization) +instance Show (ProxyAuthorization) where show = genericShow -type RangeSpecifier = ByteRangeStart \/ (ByteRangeStart /\ ByteRangeEnd) \/ ByteRangeLength +type RangeSpecifier = ByteRangeStart \/ (ByteRangeStart /\ ByteRangeEnd) \/ ByteRangeLength \/ Void -newtype Range = Range (RangeSpecifier \/ Array RangeSpecifier) +newtype Range = Range (NonEmptyArray RangeSpecifier) derive instance Newtype (Range) _ +derive instance Generic (Range) _ +instance Show (Range) where show = genericShow derive instance Eq (Range) newtype Referer = Referer String derive instance Newtype (Referer) _ +derive instance Generic (Referer) _ +instance Show (Referer) where show = genericShow derive instance Eq (Referer) data ReferrerPolicy = ReferrerPolicyNoReferrer | ReferrerPolicyNoReferrerWhenDowngrade - | ReferrerPolicySameOrigin | ReferrerPolicyOrigin | ReferrerPolicyOriginWhenCrossOrigin - | ReferrerPolicyUnsafeURL + | ReferrerPolicySameOrigin | ReferrerPolicyStrictOrigin | ReferrerPolicyStrictOriginWhenCrossOrigin + | ReferrerPolicyUnsafeURL + +derive instance Generic (ReferrerPolicy) _ +derive instance Eq (ReferrerPolicy) +instance Show (ReferrerPolicy) where show = genericShow newtype RetryAfter = RetryAfter (DateTime \/ Int) derive instance Newtype (RetryAfter) _ +derive instance Generic (RetryAfter) _ +instance Show (RetryAfter) where show = genericShow derive instance Eq (RetryAfter) newtype SecWebsocketKey = SecWebsocketKey String derive instance Newtype (SecWebsocketKey) _ +derive instance Generic (SecWebsocketKey) _ +instance Show (SecWebsocketKey) where show = genericShow derive instance Eq (SecWebsocketKey) newtype SecWebsocketAccept = SecWebsocketAccept SecWebsocketKey derive instance Newtype (SecWebsocketAccept) _ +derive instance Generic (SecWebsocketAccept) _ +instance Show (SecWebsocketAccept) where show = genericShow derive instance Eq (SecWebsocketAccept) -newtype SecWebsocketVersion = SecWebsocketVersion (String \/ NonEmptyArray String) +newtype SecWebsocketVersion = SecWebsocketVersion (NonEmptyArray Int) derive instance Newtype (SecWebsocketVersion) _ +derive instance Generic (SecWebsocketVersion) _ +instance Show (SecWebsocketVersion) where show = genericShow derive instance Eq (SecWebsocketVersion) newtype Server = Server String derive instance Newtype (Server) _ +derive instance Generic (Server) _ +instance Show (Server) where show = genericShow derive instance Eq (Server) newtype SetCookie = SetCookie String derive instance Newtype (SetCookie) _ +derive instance Generic (SetCookie) _ +instance Show (SetCookie) where show = genericShow derive instance Eq (SetCookie) -newtype StrictTransportSecurity = StrictTransportSecurity {maxAge :: Int, includeSubdomains :: Boolean, preload :: Boolean} +newtype StrictTransportSecurity = StrictTransportSecurity {maxAge :: Maybe Int, includeSubdomains :: Boolean} derive instance Newtype (StrictTransportSecurity) _ +derive instance Generic (StrictTransportSecurity) _ +instance Show (StrictTransportSecurity) where show = genericShow derive instance Eq (StrictTransportSecurity) newtype TE = TE String derive instance Newtype (TE) _ +derive instance Generic (TE) _ +instance Show (TE) where show = genericShow derive instance Eq (TE) newtype TransferEncoding = TransferEncoding String derive instance Newtype (TransferEncoding) _ +derive instance Generic (TransferEncoding) _ +instance Show (TransferEncoding) where show = genericShow derive instance Eq (TransferEncoding) newtype Upgrade = Upgrade (NonEmptyArray String) derive instance Newtype (Upgrade) _ +derive instance Generic (Upgrade) _ +instance Show (Upgrade) where show = genericShow derive instance Eq (Upgrade) newtype UserAgent = UserAgent String derive instance Newtype (UserAgent) _ +derive instance Generic (UserAgent) _ +instance Show (UserAgent) where show = genericShow derive instance Eq (UserAgent) newtype Vary = Vary (Wildcard \/ NonEmptyArray StringLower) derive instance Newtype (Vary) _ +derive instance Generic (Vary) _ +instance Show (Vary) where show = genericShow derive instance Eq (Vary) cacheControlDefaults :: Record CacheControl' @@ -443,7 +586,7 @@ cacheControl a = CacheControl $ Record.merge a cacheControlDefaults instance TypedHeader (Accept String) where headerName = "Accept" - headerValueParser = Parse.String.rest <#> Accept + headerValueParser = Parse.rest <#> Accept headerValueEncode = unwrap instance TypedHeader (Accept MIME.MIME) where headerName = "Accept" @@ -565,10 +708,6 @@ instance TypedHeader (Accept Type.MIME.Midi) where headerName = "Accept" headerValueParser = mimeParser >>= (Parse.liftMaybe (const $ "expected " <> MIME.toString (Type.MIME.value @Type.MIME.Midi)) <<< Type.MIME.fromValue) <#> Accept headerValueEncode _ = MIME.toString (Type.MIME.value @Type.MIME.Midi) -instance TypedHeader (Accept Type.MIME.Mjs) where - headerName = "Accept" - headerValueParser = mimeParser >>= (Parse.liftMaybe (const $ "expected " <> MIME.toString (Type.MIME.value @Type.MIME.Mjs)) <<< Type.MIME.fromValue) <#> Accept - headerValueEncode _ = MIME.toString (Type.MIME.value @Type.MIME.Mjs) instance TypedHeader (Accept Type.MIME.Mp3) where headerName = "Accept" headerValueParser = mimeParser >>= (Parse.liftMaybe (const $ "expected " <> MIME.toString (Type.MIME.value @Type.MIME.Mp3)) <<< Type.MIME.fromValue) <#> Accept @@ -740,7 +879,7 @@ instance TypedHeader (Accept Type.MIME.Archive7z) where instance TypedHeader (ContentType String) where headerName = "Content-Type" - headerValueParser = Parse.String.rest <#> ContentType + headerValueParser = Parse.rest <#> ContentType headerValueEncode = unwrap instance TypedHeader (ContentType MIME.MIME) where headerName = "Content-Type" @@ -862,10 +1001,6 @@ instance TypedHeader (ContentType Type.MIME.Midi) where headerName = "Content-Type" headerValueParser = mimeParser >>= (Parse.liftMaybe (const $ "expected " <> MIME.toString (Type.MIME.value @Type.MIME.Midi)) <<< Type.MIME.fromValue) <#> ContentType headerValueEncode _ = MIME.toString (Type.MIME.value @Type.MIME.Midi) -instance TypedHeader (ContentType Type.MIME.Mjs) where - headerName = "Content-Type" - headerValueParser = mimeParser >>= (Parse.liftMaybe (const $ "expected " <> MIME.toString (Type.MIME.value @Type.MIME.Mjs)) <<< Type.MIME.fromValue) <#> ContentType - headerValueEncode _ = MIME.toString (Type.MIME.value @Type.MIME.Mjs) instance TypedHeader (ContentType Type.MIME.Mp3) where headerName = "Content-Type" headerValueParser = mimeParser >>= (Parse.liftMaybe (const $ "expected " <> MIME.toString (Type.MIME.value @Type.MIME.Mp3)) <<< Type.MIME.fromValue) <#> ContentType @@ -1037,7 +1172,7 @@ instance TypedHeader (ContentType Type.MIME.Archive7z) where instance TypedHeader AccessControlAllowCredentials where headerName = "Access-Control-Allow-Credentials" - headerValueParser = Parse.String.string "true" $> AccessControlAllowCredentials + headerValueParser = Parse.string "true" $> AccessControlAllowCredentials headerValueEncode _ = "true" instance TypedHeader AccessControlAllowHeaders where @@ -1046,7 +1181,7 @@ instance TypedHeader AccessControlAllowHeaders where let headers = commas1 headerNameParser <#> Right <#> AccessControlAllowHeaders in - (wildcardParser $> AccessControlAllowHeaders (Left Wildcard)) <|> headers + Parse.try (wildcardParser $> AccessControlAllowHeaders (Left Wildcard)) <|> Parse.try headers headerValueEncode (AccessControlAllowHeaders (Left Wildcard)) = "*" headerValueEncode (AccessControlAllowHeaders (Right hs)) = hs <#> String.Lower.toString # Array.NonEmpty.intercalate ", " @@ -1056,7 +1191,7 @@ instance TypedHeader AccessControlAllowMethods where let methods = commas1 methodParser <#> Right <#> AccessControlAllowMethods in - (wildcardParser $> AccessControlAllowMethods (Left Wildcard)) <|> methods + Parse.try (wildcardParser $> AccessControlAllowMethods (Left Wildcard)) <|> Parse.try methods headerValueEncode (AccessControlAllowMethods (Left Wildcard)) = "*" headerValueEncode (AccessControlAllowMethods (Right ms)) = ms <#> Method.toString # Array.NonEmpty.intercalate ", " @@ -1064,9 +1199,9 @@ instance TypedHeader AccessControlAllowOrigin where headerName = "Access-Control-Allow-Origin" headerValueParser = let - str = Parse.String.rest <#> Right <#> AccessControlAllowOrigin + str = Parse.rest <#> String.trim <#> Right <#> AccessControlAllowOrigin in - (wildcardParser $> AccessControlAllowOrigin (Left Wildcard)) <|> str + Parse.try (wildcardParser $> AccessControlAllowOrigin (Left Wildcard)) <|> Parse.try str headerValueEncode (AccessControlAllowOrigin (Left Wildcard)) = "*" headerValueEncode (AccessControlAllowOrigin (Right a)) = a @@ -1076,13 +1211,13 @@ instance TypedHeader AccessControlExposeHeaders where let str = commas headerNameParser <#> Right <#> AccessControlExposeHeaders in - (wildcardParser $> AccessControlExposeHeaders (Left Wildcard)) <|> str + Parse.try (wildcardParser $> AccessControlExposeHeaders (Left Wildcard)) <|> Parse.try str headerValueEncode (AccessControlExposeHeaders (Left Wildcard)) = "*" headerValueEncode (AccessControlExposeHeaders (Right hs)) = hs <#> String.Lower.toString # Array.intercalate ", " instance TypedHeader AccessControlMaxAge where headerName = "Access-Control-Max-Age" - headerValueParser = Parse.String.Basic.intDecimal <#> AccessControlMaxAge + headerValueParser = Parse.between Parse.whiteSpace Parse.whiteSpace Parse.intDecimal <#> AccessControlMaxAge headerValueEncode (AccessControlMaxAge i) = Int.toStringAs Int.decimal i instance TypedHeader AccessControlRequestHeaders where @@ -1097,7 +1232,7 @@ instance TypedHeader AccessControlRequestMethod where instance TypedHeader Age where headerName = "Age" - headerValueParser = Parse.String.Basic.intDecimal <#> Age + headerValueParser = Parse.between Parse.whiteSpace Parse.whiteSpace Parse.intDecimal <#> Age headerValueEncode (Age i) = Int.toStringAs Int.decimal i instance TypedHeader Allow where @@ -1109,9 +1244,9 @@ instance TypedHeader Authorization where headerName = "Authorization" headerValueParser = let - scheme = (Parse.String.anyTill (void Parse.String.Basic.space <|> Parse.String.eof) <#> fst <#> AuthScheme) + scheme = Parse.whiteSpace *> (Parse.anyTill (void (Parse.try Parse.space) <|> Parse.eof) <#> fst <#> String.trim <#> AuthScheme) in - pure Authorization <*> scheme <*> (Parse.String.rest <#> String.trim) + pure Authorization <*> scheme <*> (Parse.rest <#> String.trim) headerValueEncode (Authorization (AuthScheme s) v) = s <> " " <> v instance TypedHeader BasicAuth where @@ -1130,7 +1265,7 @@ instance TypedHeader BearerAuth where headerName = "Authorization" headerValueParser = do Authorization (AuthScheme s) v <- headerValueParser @Authorization - when (String.toLower s /= "basic") $ Parse.fail $ "expected Authorization scheme to be Bearer, found " <> s + when (String.toLower s /= "bearer") $ Parse.fail $ "expected Authorization scheme to be Bearer, found " <> s pure $ BearerAuth v headerValueEncode (BearerAuth a) = headerValueEncode $ Authorization (AuthScheme "Bearer") a @@ -1138,7 +1273,29 @@ instance TypedHeader BearerAuth where instance TypedHeader CacheControl where headerName = "Cache-Control" headerValueParser = do - directives <- commas1 directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable + directives <- commas directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable + let + keys = + [ "max-age" + , "max-stale" + , "min-fresh" + , "s-maxage" + , "no-cache" + , "no-store" + , "no-transform" + , "only-if-cached" + , "must-revalidate" + , "must-understand" + , "proxy-revalidate" + , "private" + , "public" + , "immutable" + , "stale-while-revalidate" + , "stale-if-error" + ] + when + (Map.keys directives # (Set.toUnfoldable :: _ (Array _)) # any (flip elem keys) # not) + (Parse.fail "no directives") pure $ CacheControl { maxAge: Map.lookup "max-age" directives # join >>= Int.fromString , maxStale: Map.lookup "max-stale" directives # join >>= Int.fromString @@ -1190,7 +1347,7 @@ instance TypedHeader Connection where let close = closeRegexParser $> Connection (Left ConnectionClose) in - close <|> (commas1 headerNameParser <#> Right <#> Connection) + Parse.try close <|> (commas1 headerNameParser <#> Right <#> Connection) headerValueEncode (Connection (Left ConnectionClose)) = "close" headerValueEncode (Connection (Right as)) = as <#> String.Lower.toString # Array.NonEmpty.intercalate ", " @@ -1198,24 +1355,26 @@ instance TypedHeader ContentDisposition where headerName = "Content-Disposition" headerValueParser = let - boundary = Parse.String.string ";" <|> (Parse.String.eof *> pure "") - inline = Parse.String.string "inline" *> boundary $> ContentDisposition (Either.Nested.in1 ContentDispositionInline) + boundary = Parse.whiteSpace *> (Parse.try (Parse.string ";") <|> Parse.try (Parse.eof *> pure "")) + inline = Parse.whiteSpace *> Parse.string "inline" *> boundary $> ContentDisposition (Either.Nested.in1 ContentDispositionInline) attachment = do - void $ Parse.String.string "attachment" *> boundary *> Parse.String.Basic.whiteSpace - directives <- commas1 directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable + void $ Parse.whiteSpace *> Parse.string "attachment" *> boundary *> Parse.whiteSpace + directives <- semis directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable let filename = - join (Map.lookup "filename" directives <|> Map.lookup "filename*" directives) + (Map.lookup "filename" directives <|> Map.lookup "filename*" directives) + # join + <#> Regex.replace quotesRe "" pure $ ContentDisposition $ Either.Nested.in2 $ ContentDispositionAttachment {filename} formData = do - void $ Parse.String.string "form-data" *> boundary *> Parse.String.Basic.whiteSpace - directives <- commas1 directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable + void $ Parse.whiteSpace *> Parse.string "form-data" *> boundary *> Parse.whiteSpace + directives <- semis directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable let - filename = join (Map.lookup "filename" directives) - name = join (Map.lookup "name" directives) + filename = Map.lookup "filename" directives # join <#> Regex.replace quotesRe "" + name = Map.lookup "name" directives # join <#> Regex.replace quotesRe "" pure $ ContentDisposition $ Either.Nested.in3 $ ContentDispositionFormData {filename, name} in - inline <|> attachment <|> formData + Parse.try inline <|> Parse.try attachment <|> Parse.try formData headerValueEncode (ContentDisposition a) = Either.Nested.either3 (const $ [Just "inline"]) @@ -1228,18 +1387,18 @@ instance TypedHeader ContentDisposition where instance TypedHeader ContentEncoding where headerName = "Content-Encoding" headerValueParser = - commas1 (Parse.Combine.many Parse.String.Basic.alphaNum <#> Array.fromFoldable <#> String.CodeUnit.fromCharArray) + commas1 (Parse.whiteSpace *> (Parse.many Parse.alphaNum <#> Array.fromFoldable <#> String.CodeUnit.fromCharArray) <* Parse.whiteSpace) <#> ContentEncoding headerValueEncode (ContentEncoding as) = Array.NonEmpty.intercalate ", " as instance TypedHeader ContentLength where headerName = "Content-Length" - headerValueParser = Parse.String.Basic.intDecimal <#> ContentLength + headerValueParser = Parse.whiteSpace *> (Parse.intDecimal <#> ContentLength) <* Parse.whiteSpace headerValueEncode (ContentLength a) = Int.toStringAs Int.decimal a instance TypedHeader ContentLocation where headerName = "Content-Location" - headerValueParser = Parse.String.rest <#> ContentLocation + headerValueParser = Parse.rest <#> ContentLocation headerValueEncode (ContentLocation a) = a instance TypedHeader ContentRange where @@ -1248,21 +1407,21 @@ instance TypedHeader ContentRange where let startEndSize = pure (\a b c -> a /\ b /\ c) - <*> ((Parse.String.Basic.intDecimal <#> ByteRangeStart) <* Parse.String.string "-") - <*> ((Parse.String.Basic.intDecimal <#> ByteRangeEnd) <* Parse.String.string "/") - <*> (Parse.String.Basic.intDecimal <#> ByteRangeLength) + <*> ((Parse.intDecimal <#> ByteRangeStart) <* Parse.string "-") + <*> ((Parse.intDecimal <#> ByteRangeEnd) <* Parse.string "/") + <*> (Parse.intDecimal <#> ByteRangeLength) startEnd = pure (\a b -> a /\ b) - <*> ((Parse.String.Basic.intDecimal <#> ByteRangeStart) <* Parse.String.string "-") - <*> ((Parse.String.Basic.intDecimal <#> ByteRangeEnd) <* Parse.String.string "/" <* wildcardParser) + <*> ((Parse.intDecimal <#> ByteRangeStart) <* Parse.string "-") + <*> ((Parse.intDecimal <#> ByteRangeEnd) <* Parse.string "/" <* wildcardParser) size = wildcardParser - *> Parse.String.string "/" - *> Parse.String.Basic.intDecimal <#> ByteRangeLength + *> Parse.string "/" + *> Parse.intDecimal <#> ByteRangeLength in - Parse.String.string "bytes" - *> Parse.String.Basic.whiteSpace - *> (startEndSize <#> Either.Nested.in1) <|> (startEnd <#> Either.Nested.in2) <|> (size <#> Either.Nested.in3) + Parse.string "bytes" + *> Parse.whiteSpace + *> Parse.try (startEndSize <#> Either.Nested.in1) <|> Parse.try (startEnd <#> Either.Nested.in2) <|> Parse.try (size <#> Either.Nested.in3) <#> ContentRange headerValueEncode (ContentRange a) = Either.Nested.either3 @@ -1274,7 +1433,7 @@ instance TypedHeader ContentRange where instance TypedHeader Cookie where headerName = "Cookie" - headerValueParser = Parse.String.rest <#> Cookie + headerValueParser = Parse.rest <#> Cookie headerValueEncode (Cookie a) = a instance TypedHeader Date where @@ -1284,12 +1443,12 @@ instance TypedHeader Date where instance TypedHeader ETag where headerName = "ETag" - headerValueParser = Parse.String.rest <#> ETag + headerValueParser = Parse.rest <#> ETag headerValueEncode (ETag a) = a instance TypedHeader ExpectContinue where headerName = "Expect" - headerValueParser = Parse.String.string "100-continue" $> ExpectContinue + headerValueParser = Parse.string "100-continue" $> ExpectContinue headerValueEncode ExpectContinue = "100-continue" instance TypedHeader Expires where @@ -1299,10 +1458,237 @@ instance TypedHeader Expires where instance TypedHeader Host where headerName = "Host" - headerValueParser = Parse.String.rest <#> Host + headerValueParser = Parse.rest <#> Host headerValueEncode (Host a) = a +instance TypedHeader IfMatch where + headerName = "If-Match" + headerValueParser = + let + wild = wildcardParser <#> Left <#> IfMatch + etag = + Parse.whiteSpace + *> Parse.optional (Parse.string "W/") + *> Parse.string "\"" + *> (Parse.anyTill (void $ Parse.string "\"") <#> fst) + <* Parse.whiteSpace + etags = commas1 etag <#> Right <#> IfMatch + in + Parse.try wild <|> Parse.try etags + headerValueEncode (IfMatch (Left Wildcard)) = "*" + headerValueEncode (IfMatch (Right as)) = as <#> (\a -> "\"" <> a <> "\"") # Array.NonEmpty.intercalate ", " + +instance TypedHeader IfNoneMatch where + headerName = "If-None-Match" + headerValueParser = + let + wild = wildcardParser <#> Left <#> IfNoneMatch + etag a = + Parse.string "\"" + *> (Parse.anyTill (void $ Parse.string "\"") <#> fst <#> a) + weakEtag = + Parse.whiteSpace + *> Parse.string "W/" + *> etag MatchETagWeak + <* Parse.whiteSpace + strongEtag = + Parse.whiteSpace + *> Parse.string "W/" + *> etag MatchETag + <* Parse.whiteSpace + etags = commas1 (Parse.try weakEtag <|> strongEtag) <#> Right <#> IfNoneMatch + in + Parse.try wild <|> Parse.try etags + headerValueEncode (IfNoneMatch (Left Wildcard)) = "*" + headerValueEncode (IfNoneMatch (Right as)) = + as + <#> ( case _ of + MatchETag a -> "\"" <> a <> "\"" + MatchETagWeak a -> "W/\"" <> a <> "\"" + ) + # Array.NonEmpty.intercalate ", " + +instance TypedHeader IfModifiedSince where + headerName = "If-Modified-Since" + headerValueParser = datetimeParser <#> IfModifiedSince + headerValueEncode (IfModifiedSince a) = printDateTime a + +instance TypedHeader IfRange where + headerName = "If-Modified-Since" + headerValueParser = + let + dt = datetimeParser <#> Left <#> IfRange + etag = + Parse.whiteSpace + *> Parse.string "\"" + *> Parse.anyTill (Parse.string "\"") + <* Parse.whiteSpace + <#> fst + <#> Right + <#> IfRange + in + Parse.try dt <|> Parse.try etag + headerValueEncode (IfRange (Left a)) = printDateTime a + headerValueEncode (IfRange (Right a)) = "\"" <> a <> "\"" + +instance TypedHeader IfUnmodifiedSince where + headerName = "If-Unmodified-Since" + headerValueParser = datetimeParser <#> IfUnmodifiedSince + headerValueEncode (IfUnmodifiedSince a) = printDateTime a + +instance TypedHeader LastModified where + headerName = "Last-Modified" + headerValueParser = datetimeParser <#> LastModified + headerValueEncode (LastModified a) = printDateTime a + instance TypedHeader Origin where headerName = "Origin" - headerValueParser = Parse.String.rest <#> Origin + headerValueParser = Parse.rest <#> Origin headerValueEncode (Origin a) = a + +instance TypedHeader ProxyAuthorization where + headerName = "Proxy-Authorization" + headerValueParser = + let + scheme = (Parse.anyTill (void (Parse.try Parse.space) <|> Parse.eof) <#> fst <#> AuthScheme) + in + pure ProxyAuthorization <*> scheme <*> (Parse.rest <#> String.trim) + headerValueEncode (ProxyAuthorization (AuthScheme s) v) = s <> " " <> v + +instance TypedHeader Range where + headerName = "Range" + headerValueParser = + let + int = Parse.intDecimal + dash = Parse.string "-" + rangeSpecStart = int <* dash <#> ByteRangeStart + rangeSpecStartEnd = pure (\a b -> a /\ b) <*> (int <* dash <#> ByteRangeStart) <*> (int <#> ByteRangeEnd) + rangeSpecSuffix = dash *> int <#> ByteRangeLength + rangeSpec = + Parse.try (rangeSpecStartEnd <#> Either.Nested.in2) + <|> Parse.try (rangeSpecSuffix <#> Either.Nested.in3) + <|> (rangeSpecStart <#> Either.Nested.in1) + in + commas1 rangeSpec <#> Range + headerValueEncode (Range as) = + as + <#> ( + Either.Nested.either3 + (\(ByteRangeStart a) -> Int.toStringAs Int.decimal a <> "-") + (\(ByteRangeStart a /\ ByteRangeEnd b) -> Int.toStringAs Int.decimal a <> "-" <> Int.toStringAs Int.decimal b) + (\(ByteRangeLength b) -> "-" <> Int.toStringAs Int.decimal b) + ) + # Array.NonEmpty.intercalate ", " + +instance TypedHeader Referer where + headerName = "Referer" + headerValueParser = Parse.rest <#> Referer + headerValueEncode (Referer a) = a + +instance TypedHeader ReferrerPolicy where + headerName = "Referrer-Policy" + headerValueParser = + let + noReferrer = Parse.string "no-referrer" $> ReferrerPolicyNoReferrer + noReferrerWhenDowngrade = Parse.string "no-referrer-when-downgrade" $> ReferrerPolicyNoReferrerWhenDowngrade + origin = Parse.string "origin" $> ReferrerPolicyOrigin + originWhenCrossOrigin = Parse.string "origin-when-cross-origin" $> ReferrerPolicyOriginWhenCrossOrigin + sameOrigin = Parse.string "same-origin" $> ReferrerPolicySameOrigin + strictOrigin = Parse.string "strict-origin" $> ReferrerPolicyStrictOrigin + strictOriginWhenCrossOrigin = Parse.string "strict-origin-when-cross-origin" $> ReferrerPolicyStrictOriginWhenCrossOrigin + unsafeURL = Parse.string "unsafe-url" $> ReferrerPolicyUnsafeURL + in + Parse.try noReferrer + <|> Parse.try noReferrerWhenDowngrade + <|> Parse.try origin + <|> Parse.try originWhenCrossOrigin + <|> Parse.try sameOrigin + <|> Parse.try strictOrigin + <|> Parse.try strictOriginWhenCrossOrigin + <|> unsafeURL + headerValueEncode ReferrerPolicyNoReferrer = "no-referrer" + headerValueEncode ReferrerPolicyNoReferrerWhenDowngrade = "no-referrer-when-downgrade" + headerValueEncode ReferrerPolicyOrigin = "origin" + headerValueEncode ReferrerPolicyOriginWhenCrossOrigin = "origin-when-cross-origin" + headerValueEncode ReferrerPolicySameOrigin = "same-origin" + headerValueEncode ReferrerPolicyStrictOrigin = "strict-origin" + headerValueEncode ReferrerPolicyStrictOriginWhenCrossOrigin = "strict-origin-when-cross-origin" + headerValueEncode ReferrerPolicyUnsafeURL = "unsafe-url" + +instance TypedHeader RetryAfter where + headerName = "Retry-After" + headerValueParser = Parse.try (datetimeParser <#> Left <#> RetryAfter) <|> Parse.try (Parse.intDecimal <#> Right <#> RetryAfter) + headerValueEncode (RetryAfter (Left a)) = printDateTime a + headerValueEncode (RetryAfter (Right a)) = Int.toStringAs Int.decimal a + +instance TypedHeader SecWebsocketKey where + headerName = "Sec-WebSocket-Key" + headerValueParser = Parse.rest <#> SecWebsocketKey + headerValueEncode (SecWebsocketKey a) = a + +instance TypedHeader SecWebsocketAccept where + headerName = "Sec-WebSocket-Accept" + headerValueParser = headerValueParser <#> SecWebsocketAccept + headerValueEncode (SecWebsocketAccept a) = headerValueEncode a + +instance TypedHeader SecWebsocketVersion where + headerName = "Sec-WebSocket-Version" + headerValueParser = commas1 (Parse.intDecimal) <#> SecWebsocketVersion + headerValueEncode (SecWebsocketVersion as) = as <#> Int.toStringAs Int.decimal # Array.NonEmpty.intercalate ", " + +instance TypedHeader Server where + headerName = "Server" + headerValueParser = Parse.rest <#> Server + headerValueEncode (Server a) = a + +instance TypedHeader SetCookie where + headerName = "Set-Cookie" + headerValueParser = Parse.rest <#> SetCookie + headerValueEncode (SetCookie a) = a + +instance TypedHeader StrictTransportSecurity where + headerName = "Strict-Transport-Security" + headerValueParser = do + directives <- commas1 directiveParser <#> map (\(k /\ v) -> String.Lower.toString k /\ v) <#> Map.fromFoldable + pure $ StrictTransportSecurity + { maxAge: Map.lookup "max-age" directives # join >>= Int.fromString + , includeSubdomains: Map.lookup "includesubdomains" directives # isJust + } + headerValueEncode (StrictTransportSecurity a) = + let + flag v k + | v = Just k + | otherwise = Nothing + int (Just i) k = Just (k <> "=" <> Int.toStringAs Int.decimal i) + int Nothing _ = Nothing + in + Array.intercalate ", " $ Array.catMaybes + [ int a.maxAge "max-age" + , flag a.includeSubdomains "includeSubDomains" + ] + +instance TypedHeader TE where + headerName = "TE" + headerValueParser = Parse.rest <#> TE + headerValueEncode (TE a) = a + +instance TypedHeader TransferEncoding where + headerName = "Transfer-Encoding" + headerValueParser = Parse.rest <#> TransferEncoding + headerValueEncode (TransferEncoding a) = a + +instance TypedHeader Upgrade where + headerName = "Upgrade" + headerValueParser = commas1 (Parse.anyTill Parse.space <#> fst) <#> Upgrade + headerValueEncode (Upgrade as) = as # Array.NonEmpty.intercalate ", " + +instance TypedHeader UserAgent where + headerName = "User-Agent" + headerValueParser = Parse.rest <#> UserAgent + headerValueEncode (UserAgent a) = a + +instance TypedHeader Vary where + headerName = "Vary" + headerValueParser = Parse.try (wildcardParser <#> Left <#> Vary) <|> Parse.try (commas1 headerNameParser <#> Right <#> Vary) + headerValueEncode (Vary (Left _)) = "*" + headerValueEncode (Vary (Right a)) = a <#> String.Lower.toString # Array.NonEmpty.intercalate ", " diff --git a/src/Axon.Request.Parts.Class.purs b/src/Axon.Request.Parts.Class.purs index 653de24..58d7c88 100644 --- a/src/Axon.Request.Parts.Class.purs +++ b/src/Axon.Request.Parts.Class.purs @@ -1,7 +1,10 @@ module Axon.Request.Parts.Class ( class RequestParts + , class RequestHandler + , invokeHandler , extractRequestParts - , Header(..) + , ExtractError(..) + , module Parts.Header , module Parts.Method , module Parts.Body , module Path.Parts @@ -16,79 +19,90 @@ import Axon.Request.Method (Method) import Axon.Request.Method as Method import Axon.Request.Parts.Body (Json(..), Stream(..)) import Axon.Request.Parts.Body (Json(..), Stream(..)) as Parts.Body +import Axon.Request.Parts.Header (Header(..), HeaderMap(..)) +import Axon.Request.Parts.Header (Header(..), HeaderMap(..)) as Parts.Header import Axon.Request.Parts.Method (Connect(..), Delete(..), Get(..), Options(..), Patch(..), Post(..), Put(..), Trace(..)) import Axon.Request.Parts.Method (Get(..), Post(..), Put(..), Patch(..), Delete(..), Trace(..), Options(..), Connect(..)) as Parts.Method import Axon.Request.Parts.Path (Path(..)) as Path.Parts import Axon.Request.Parts.Path (class DiscardTupledUnits, class PathParts, Path(..), discardTupledUnits, extractPathParts) -import Axon.Response (Response) -import Axon.Response as Response -import Control.Alternative (guard) +import Control.Monad.Error.Class (liftEither, liftMaybe, throwError) import Control.Monad.Except (ExceptT(..), runExceptT) -import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT) -import Control.Monad.Trans.Class (lift) import Data.Argonaut.Decode (class DecodeJson, decodeJson) +import Data.Argonaut.Decode.Error (printJsonDecodeError) import Data.Array as Array -import Data.Bifunctor (lmap) -import Data.Either (Either(..), hush) +import Data.Bifunctor (bimap, lmap) +import Data.Either (Either(..), note) import Data.Generic.Rep (class Generic) import Data.Map as Map -import Data.Maybe (Maybe(..)) -import Data.Newtype (class Newtype, wrap) +import Data.Show.Generic (genericShow) import Data.String.Lower as String.Lower import Data.Tuple.Nested (type (/\), (/\)) import Data.URL as URL import Effect.Aff (Aff) +import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Class (liftEffect) import Node.Buffer (Buffer) import Parsing (runParser) +import Parsing.String (parseErrorHuman) -newtype Header a = Header a -derive instance Generic (Header a) _ -derive instance Newtype (Header a) _ -derive newtype instance (Eq a) => Eq (Header a) -derive newtype instance (Ord a) => Ord (Header a) -derive newtype instance (Show a) => Show (Header a) +data ExtractError + = ExtractError String + | ExtractNext + | ExtractBadRequest String + +derive instance Generic ExtractError _ +derive instance Eq ExtractError +instance Show ExtractError where show = genericShow extractMethod :: forall a. a -> Method -> Request -> - Aff (Either Response (Maybe a)) -extractMethod a method r = - if Request.method r == method then - pure $ Right $ Just a - else - pure $ Right Nothing + Aff (Either ExtractError a) +extractMethod a method r = runExceptT do + when (Request.method r /= method) $ throwError ExtractNext + pure a + +class MonadAff m <= RequestHandler a m b | a -> b m where + invokeHandler :: Request -> a -> m (Either ExtractError b) + +instance (MonadAff m, RequestHandler f m b, RequestParts a) => RequestHandler (a -> f) m b where + invokeHandler req f = runExceptT do + a <- ExceptT $ liftAff $ extractRequestParts @a req + ExceptT $ invokeHandler req (f a) +else instance (MonadAff m) => RequestHandler (m a) m a where + invokeHandler _ m = m <#> Right class RequestParts a where - extractRequestParts :: Request -> Aff (Either Response (Maybe a)) + extractRequestParts :: Request -> Aff (Either ExtractError a) instance RequestParts Unit where - extractRequestParts _ = pure unit # runMaybeT # runExceptT + extractRequestParts _ = pure unit # runExceptT instance RequestParts Request where - extractRequestParts r = pure r # runMaybeT # runExceptT + extractRequestParts r = pure r # runExceptT instance RequestParts String where extractRequestParts r = Request.bodyString r - <#> lmap (const $ Response.fromStatus 500) - # ExceptT - # lift - # runMaybeT - # runExceptT + <#> lmap (const $ ExtractBadRequest "Expected body to be valid UTF-8") instance RequestParts (Either Request.BodyStringError String) where - extractRequestParts r = - Request.bodyString r - <#> Just - <#> Right + extractRequestParts r = Request.bodyString r <#> Right instance TypedHeader a => RequestParts (Header a) where - extractRequestParts r = runExceptT $ runMaybeT do - value <- Request.headers r # Map.lookup (String.Lower.fromString $ headerName @a) # pure # MaybeT - runParser value (headerValueParser @a) # hush # pure # MaybeT <#> Header + extractRequestParts r = runExceptT do + value <- + Request.headers r + # Map.lookup (String.Lower.fromString $ headerName @a) + # liftMaybe ExtractNext + runParser value (headerValueParser @a) + # bimap (ExtractBadRequest <<< Array.intercalate "\n" <<< parseErrorHuman value 5) Header + # liftEither + +instance RequestParts HeaderMap where + extractRequestParts r = runExceptT $ pure $ HeaderMap $ Request.headers r instance (PathParts a b, DiscardTupledUnits b c) => RequestParts (Path a c) where extractRequestParts r = @@ -98,16 +112,14 @@ instance (PathParts a b, DiscardTupledUnits b c) => RequestParts (Path a c) wher URL.PathRelative a -> a _ -> [] extract = extractPathParts @a @b (Request.url r) - ensureConsumed (leftover /\ x) = guard (Array.null leftover) $> x + ensureConsumed (leftover /\ x) = when (not $ Array.null leftover) (throwError ExtractNext) $> x in segments # extract - # Right - # MaybeT + # note ExtractNext >>= ensureConsumed <#> discardTupledUnits <#> Path - # runMaybeT # pure instance (DecodeJson a) => RequestParts (Json a) where @@ -115,39 +127,26 @@ instance (DecodeJson a) => RequestParts (Json a) where let jsonBody = Request.bodyJSON r - <#> lmap (const $ Response.fromStatus 500) + <#> lmap (ExtractBadRequest <<< show) # ExceptT - # lift decode j = decodeJson j - # lmap (const $ Response.fromStatus 400) + # lmap (ExtractBadRequest <<< printJsonDecodeError) # pure # ExceptT - # lift in - jsonBody >>= decode <#> Json # runMaybeT # runExceptT + jsonBody >>= decode <#> Json # runExceptT instance RequestParts Buffer where extractRequestParts r = - let - bufBody = - Request.bodyBuffer r - <#> lmap (const $ Response.fromStatus 500) - # ExceptT - # lift - in - bufBody # runMaybeT # runExceptT + Request.bodyBuffer r + <#> lmap (const $ ExtractBadRequest "Expected body") instance RequestParts Stream where extractRequestParts r = - let - streamBody = - Request.bodyReadable r - <#> lmap (const $ Response.fromStatus 500) - # ExceptT - # lift - in - streamBody <#> Stream # runMaybeT # runExceptT # liftEffect + Request.bodyReadable r + <#> bimap (const $ ExtractBadRequest "Expected body") Stream + # liftEffect instance RequestParts Get where extractRequestParts = extractMethod Get Method.GET @@ -174,7 +173,7 @@ instance RequestParts Trace where extractRequestParts = extractMethod Trace Method.TRACE instance (RequestParts a, RequestParts b) => RequestParts (a /\ b) where - extractRequestParts r = runExceptT $ runMaybeT do - a <- extractRequestParts @a r # ExceptT # MaybeT - b <- extractRequestParts @b r # ExceptT # MaybeT + extractRequestParts r = runExceptT do + a <- extractRequestParts @a r # ExceptT + b <- extractRequestParts @b r # ExceptT pure $ a /\ b diff --git a/src/Axon.Request.Parts.Header.purs b/src/Axon.Request.Parts.Header.purs new file mode 100644 index 0000000..e102be9 --- /dev/null +++ b/src/Axon.Request.Parts.Header.purs @@ -0,0 +1,22 @@ +module Axon.Request.Parts.Header where + +import Prelude + +import Data.Generic.Rep (class Generic) +import Data.Map (Map) +import Data.Newtype (class Newtype) +import Data.String.Lower (StringLower) + +newtype Header a = Header a +derive instance Generic (Header a) _ +derive instance Newtype (Header a) _ +derive newtype instance (Eq a) => Eq (Header a) +derive newtype instance (Ord a) => Ord (Header a) +derive newtype instance (Show a) => Show (Header a) + +newtype HeaderMap = HeaderMap (Map StringLower String) +derive instance Generic HeaderMap _ +derive instance Newtype HeaderMap _ +derive newtype instance Eq HeaderMap +derive newtype instance Ord HeaderMap +derive newtype instance Show HeaderMap diff --git a/src/Axon.Response.Body.purs b/src/Axon.Response.Body.purs index f6e36c4..6815671 100644 --- a/src/Axon.Response.Body.purs +++ b/src/Axon.Response.Body.purs @@ -4,8 +4,6 @@ import Prelude import Data.Argonaut.Core (Json, stringify) import Effect (Effect) -import Effect.Aff.HTTP.Form (Form, RawFormData) as HTTP -import Effect.Aff.HTTP.Form as HTTP.Form import Node.Buffer (Buffer) import Node.Stream as Stream @@ -13,19 +11,14 @@ data Body = BodyEmpty | BodyString String | BodyBuffer Buffer - | BodyFormData HTTP.RawFormData | BodyReadable (Stream.Readable ()) instance Show Body where show BodyEmpty = "BodyEmpty" show (BodyString s) = "BodyString " <> show s show (BodyBuffer _) = "BodyBuffer _" - show (BodyFormData _) = "BodyFormData _" show (BodyReadable _) = "BodyReadable _" -formBody :: HTTP.Form -> Effect Body -formBody f = HTTP.Form.toRawFormData f <#> BodyFormData - stringBody :: String -> Body stringBody = BodyString diff --git a/src/Axon.Response.purs b/src/Axon.Response.purs index 014462d..bde2986 100644 --- a/src/Axon.Response.purs +++ b/src/Axon.Response.purs @@ -15,7 +15,7 @@ module Axon.Response import Prelude import Axon.Response.Body (Body(..)) -import Axon.Response.Body (Body(..), formBody) as Body +import Axon.Response.Body (Body(..)) as Body import Data.FoldableWithIndex (foldlWithIndex) import Data.Generic.Rep (class Generic) import Data.Map (Map) diff --git a/src/Data.MIME.purs b/src/Data.MIME.purs new file mode 100644 index 0000000..2701533 --- /dev/null +++ b/src/Data.MIME.purs @@ -0,0 +1,165 @@ +module Data.MIME where + +import Prelude + +import Data.Eq.Generic (genericEq) +import Data.Generic.Rep (class Generic) +import Data.Show.Generic (genericShow) + +data MIME = Other String | Aac | Abw | Arc | Avif | Avi | Azw | Bin | Bmp | Bz | Bz2 | Cda | Csh | Css | Csv | Doc | Docx | Eot | Epub | Gz | Gif | Html | Ico | Ics | Jar | Jpeg | Js | Json | Jsonld | Midi | Mp3 | Mp4 | Mpeg | Mpkg | Odp | Ods | Odt | Oga | Ogv | Ogx | Opus | Otf | Png | Pdf | Php | Ppt | Pptx | Rar | Rtf | Sh | Svg | Tar | Tif | Ts | Ttf | Txt | Vsd | Wav | Weba | Webm | Webp | Woff | Woff2 | Xhtml | Xls | Xlsx | Xml | Xul | Zip | Video3gp | Video3g2 | Archive7z + +derive instance Generic MIME _ +instance Show MIME where + show = genericShow + +instance Eq MIME where + eq = genericEq + +toString :: MIME -> String +toString (Other s) = s +toString Aac = "audio/aac" +toString Abw = "application/x-abiword" +toString Arc = "application/x-freearc" +toString Avif = "image/avif" +toString Avi = "video/x-msvideo" +toString Azw = "application/vnd.amazon.ebook" +toString Bin = "application/octet-stream" +toString Bmp = "image/bmp" +toString Bz = "application/x-bzip" +toString Bz2 = "application/x-bzip2" +toString Cda = "application/x-cdf" +toString Csh = "application/x-csh" +toString Css = "text/css" +toString Csv = "text/csv" +toString Doc = "application/msword" +toString Docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" +toString Eot = "application/vnd.ms-fontobject" +toString Epub = "application/epub+zip" +toString Gz = "application/gzip" +toString Gif = "image/gif" +toString Html = "text/html" +toString Ico = "image/vnd.microsoft.icon" +toString Ics = "text/calendar" +toString Jar = "application/java-archive" +toString Jpeg = "image/jpeg" +toString Js = "text/javascript" +toString Json = "application/json" +toString Jsonld = "application/ld+json" +toString Midi = "audio/midi" +toString Mp3 = "audio/mpeg" +toString Mp4 = "video/mp4" +toString Mpeg = "video/mpeg" +toString Mpkg = "application/vnd.apple.installer+xml" +toString Odp = "application/vnd.oasis.opendocument.presentation" +toString Ods = "application/vnd.oasis.opendocument.spreadsheet" +toString Odt = "application/vnd.oasis.opendocument.text" +toString Oga = "audio/ogg" +toString Ogv = "video/ogg" +toString Ogx = "application/ogg" +toString Opus = "audio/opus" +toString Otf = "font/otf" +toString Png = "image/png" +toString Pdf = "application/pdf" +toString Php = "application/x-httpd-php" +toString Ppt = "application/vnd.ms-powerpoint" +toString Pptx = "application/vnd.openxmlformats-officedocument.presentationml.presentation" +toString Rar = "application/vnd.rar" +toString Rtf = "application/rtf" +toString Sh = "application/x-sh" +toString Svg = "image/svg+xml" +toString Tar = "application/x-tar" +toString Tif = "image/tiff" +toString Ts = "video/mp2t" +toString Ttf = "font/ttf" +toString Txt = "text/plain" +toString Vsd = "application/vnd.visio" +toString Wav = "audio/wav" +toString Weba = "audio/webm" +toString Webm = "video/webm" +toString Webp = "image/webp" +toString Woff = "font/woff" +toString Woff2 = "font/woff2" +toString Xhtml = "application/xhtml+xml" +toString Xls = "application/vnd.ms-excel" +toString Xlsx = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" +toString Xml = "application/xml" +toString Xul = "application/vnd.mozilla.xul+xml" +toString Zip = "application/zip" +toString Video3gp = "video/3gpp" +toString Video3g2 = "video/3gpp2" +toString Archive7z = "application/x-7z-compressed" + +fromString :: String -> MIME +fromString "audio/aac" = Aac +fromString "application/x-abiword" = Abw +fromString "application/x-freearc" = Arc +fromString "image/avif" = Avif +fromString "video/x-msvideo" = Avi +fromString "application/vnd.amazon.ebook" = Azw +fromString "application/octet-stream" = Bin +fromString "image/bmp" = Bmp +fromString "application/x-bzip" = Bz +fromString "application/x-bzip2" = Bz2 +fromString "application/x-cdf" = Cda +fromString "application/x-csh" = Csh +fromString "text/css" = Css +fromString "text/csv" = Csv +fromString "application/msword" = Doc +fromString "application/vnd.openxmlformats-officedocument.wordprocessingml.document" = Docx +fromString "application/vnd.ms-fontobject" = Eot +fromString "application/epub+zip" = Epub +fromString "application/gzip" = Gz +fromString "image/gif" = Gif +fromString "text/html" = Html +fromString "image/vnd.microsoft.icon" = Ico +fromString "text/calendar" = Ics +fromString "application/java-archive" = Jar +fromString "image/jpeg" = Jpeg +fromString "text/javascript" = Js +fromString "application/json" = Json +fromString "application/ld+json" = Jsonld +fromString "audio/midi" = Midi +fromString "audio/mpeg" = Mp3 +fromString "video/mp4" = Mp4 +fromString "video/mpeg" = Mpeg +fromString "application/vnd.apple.installer+xml" = Mpkg +fromString "application/vnd.oasis.opendocument.presentation" = Odp +fromString "application/vnd.oasis.opendocument.spreadsheet" = Ods +fromString "application/vnd.oasis.opendocument.text" = Odt +fromString "audio/ogg" = Oga +fromString "video/ogg" = Ogv +fromString "application/ogg" = Ogx +fromString "audio/opus" = Opus +fromString "font/otf" = Otf +fromString "image/png" = Png +fromString "application/pdf" = Pdf +fromString "application/x-httpd-php" = Php +fromString "application/vnd.ms-powerpoint" = Ppt +fromString "application/vnd.openxmlformats-officedocument.presentationml.presentation" = Pptx +fromString "application/vnd.rar" = Rar +fromString "application/rtf" = Rtf +fromString "application/x-sh" = Sh +fromString "image/svg+xml" = Svg +fromString "application/x-tar" = Tar +fromString "image/tiff" = Tif +fromString "video/mp2t" = Ts +fromString "font/ttf" = Ttf +fromString "text/plain" = Txt +fromString "application/vnd.visio" = Vsd +fromString "audio/wav" = Wav +fromString "audio/webm" = Weba +fromString "video/webm" = Webm +fromString "image/webp" = Webp +fromString "font/woff" = Woff +fromString "font/woff2" = Woff2 +fromString "application/xhtml+xml" = Xhtml +fromString "application/vnd.ms-excel" = Xls +fromString "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" = Xlsx +fromString "application/xml" = Xml +fromString "application/vnd.mozilla.xul+xml" = Xul +fromString "application/zip" = Zip +fromString "video/3gpp" = Video3gp +fromString "video/3gpp2" = Video3g2 +fromString "application/x-7z-compressed" = Archive7z +fromString s = Other s + diff --git a/src/Type.MIME.purs b/src/Type.MIME.purs index b4cba8f..369484b 100644 --- a/src/Type.MIME.purs +++ b/src/Type.MIME.purs @@ -301,16 +301,6 @@ instance TypelevelMIME Midi where fromValue _ = Nothing value = MIME.Midi -data Mjs = Mjs -derive instance Generic Mjs _ -derive instance Eq Mjs -instance Show Mjs where - show = genericShow -instance TypelevelMIME Mjs where - fromValue MIME.Mjs = Just Mjs - fromValue _ = Nothing - value = MIME.Mjs - data Mp3 = Mp3 derive instance Generic Mp3 _ derive instance Eq Mp3 diff --git a/test/Test/Axon.Header.Typed.purs b/test/Test/Axon.Header.Typed.purs new file mode 100644 index 0000000..ec8aa9c --- /dev/null +++ b/test/Test/Axon.Header.Typed.purs @@ -0,0 +1,618 @@ +module Test.Axon.Header.Typed where + +import Prelude + +import Axon.Header.Typed (class TypedHeader, Accept(..), AccessControlAllowCredentials(..), AccessControlAllowHeaders(..), AccessControlAllowMethods(..), AccessControlAllowOrigin(..), AccessControlExposeHeaders(..), AccessControlMaxAge(..), AccessControlRequestHeaders(..), AccessControlRequestMethod(..), Age(..), Allow(..), AuthScheme(..), Authorization(..), BasicAuth(..), BearerAuth(..), CacheControl(..), Connection(..), ConnectionClose(..), ContentDisposition(..), ContentDispositionAttachment(..), ContentDispositionFormData(..), ContentDispositionInline(..), ContentEncoding(..), ContentLength(..), ContentType(..), Wildcard(..), cacheControlDefaults, headerValueParser) +import Axon.Request.Method (Method(..)) +import Control.Monad.Error.Class (liftEither) +import Data.Bifunctor (lmap) +import Data.Either (Either(..), isLeft) +import Data.Either.Nested as Either.Nested +import Data.MIME as MIME +import Data.Maybe (Maybe(..)) +import Data.String.Lower as String.Lower +import Effect.Exception (error) +import Parsing (parseErrorMessage, runParser) +import Test.Spec (Spec, describe, it) +import Test.Spec.Assertions (shouldEqual, shouldNotEqual) +import Type.MIME as Type.MIME + +is :: forall h. Eq h => Show h => TypedHeader h => String -> h -> Spec Unit +is i exp = it ("parses " <> show i) do + act <- runParser i (headerValueParser @h) # lmap (error <<< parseErrorMessage) # liftEither + act `shouldEqual` exp + +isnt :: forall h. Eq h => Show h => TypedHeader h => String -> h -> Spec Unit +isnt i exp = it ("does not parse " <> show i) $ + case runParser i (headerValueParser @h) of + Left _ -> pure unit + Right act -> exp `shouldNotEqual` act + +spec :: Spec Unit +spec = + describe "Typed" do + describe "Accept String" do + "foo" `is` (Accept "foo") + "" `is` (Accept "") + describe "Accept MIME.MIME" do + "application/json" `is` (Accept MIME.Json) + "text/plain" `is` (Accept MIME.Txt) + "text/plain;charset=utf8" `is` (Accept $ MIME.Other "text/plain;charset=utf8") + "foo" `is` (Accept $ MIME.Other "foo") + describe "Accept Aac" do + "unknown" `isnt` Accept Type.MIME.Aac + "audio/aac" `is` Accept Type.MIME.Aac + describe "Accept Abw" do + "unknown" `isnt` Accept Type.MIME.Abw + "application/x-abiword" `is` Accept Type.MIME.Abw + describe "Accept Arc" do + "unknown" `isnt` Accept Type.MIME.Arc + "application/x-freearc" `is` Accept Type.MIME.Arc + describe "Accept Avif" do + "unknown" `isnt` Accept Type.MIME.Avif + "image/avif" `is` Accept Type.MIME.Avif + describe "Accept Avi" do + "unknown" `isnt` Accept Type.MIME.Avi + "video/x-msvideo" `is` Accept Type.MIME.Avi + describe "Accept Azw" do + "unknown" `isnt` Accept Type.MIME.Azw + "application/vnd.amazon.ebook" `is` Accept Type.MIME.Azw + describe "Accept Bin" do + "unknown" `isnt` Accept Type.MIME.Bin + "application/octet-stream" `is` Accept Type.MIME.Bin + describe "Accept Bmp" do + "unknown" `isnt` Accept Type.MIME.Bmp + "image/bmp" `is` Accept Type.MIME.Bmp + describe "Accept Bz" do + "unknown" `isnt` Accept Type.MIME.Bz + "application/x-bzip" `is` Accept Type.MIME.Bz + describe "Accept Bz2" do + "unknown" `isnt` Accept Type.MIME.Bz2 + "application/x-bzip2" `is` Accept Type.MIME.Bz2 + describe "Accept Cda" do + "unknown" `isnt` Accept Type.MIME.Cda + "application/x-cdf" `is` Accept Type.MIME.Cda + describe "Accept Csh" do + "unknown" `isnt` Accept Type.MIME.Csh + "application/x-csh" `is` Accept Type.MIME.Csh + describe "Accept Css" do + "unknown" `isnt` Accept Type.MIME.Css + "text/css" `is` Accept Type.MIME.Css + describe "Accept Csv" do + "unknown" `isnt` Accept Type.MIME.Csv + "text/csv" `is` Accept Type.MIME.Csv + describe "Accept Doc" do + "unknown" `isnt` Accept Type.MIME.Doc + "application/msword" `is` Accept Type.MIME.Doc + describe "Accept Docx" do + "unknown" `isnt` Accept Type.MIME.Docx + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" `is` Accept Type.MIME.Docx + describe "Accept Eot" do + "unknown" `isnt` Accept Type.MIME.Eot + "application/vnd.ms-fontobject" `is` Accept Type.MIME.Eot + describe "Accept Epub" do + "unknown" `isnt` Accept Type.MIME.Epub + "application/epub+zip" `is` Accept Type.MIME.Epub + describe "Accept Gz" do + "unknown" `isnt` Accept Type.MIME.Gz + "application/gzip" `is` Accept Type.MIME.Gz + describe "Accept Gif" do + "unknown" `isnt` Accept Type.MIME.Gif + "image/gif" `is` Accept Type.MIME.Gif + describe "Accept Html" do + "unknown" `isnt` Accept Type.MIME.Html + "text/html" `is` Accept Type.MIME.Html + describe "Accept Ico" do + "unknown" `isnt` Accept Type.MIME.Ico + "image/vnd.microsoft.icon" `is` Accept Type.MIME.Ico + describe "Accept Ics" do + "unknown" `isnt` Accept Type.MIME.Ics + "text/calendar" `is` Accept Type.MIME.Ics + describe "Accept Jar" do + "unknown" `isnt` Accept Type.MIME.Jar + "application/java-archive" `is` Accept Type.MIME.Jar + describe "Accept Jpeg" do + "unknown" `isnt` Accept Type.MIME.Jpeg + "image/jpeg" `is` Accept Type.MIME.Jpeg + describe "Accept Js" do + "unknown" `isnt` Accept Type.MIME.Js + "text/javascript" `is` Accept Type.MIME.Js + describe "Accept Json" do + "unknown" `isnt` Accept Type.MIME.Json + "application/json" `is` Accept Type.MIME.Json + describe "Accept Jsonld" do + "unknown" `isnt` Accept Type.MIME.Jsonld + "application/ld+json" `is` Accept Type.MIME.Jsonld + describe "Accept Midi" do + "unknown" `isnt` Accept Type.MIME.Midi + "audio/midi" `is` Accept Type.MIME.Midi + describe "Accept Mp3" do + "unknown" `isnt` Accept Type.MIME.Mp3 + "audio/mpeg" `is` Accept Type.MIME.Mp3 + describe "Accept Mp4" do + "unknown" `isnt` Accept Type.MIME.Mp4 + "video/mp4" `is` Accept Type.MIME.Mp4 + describe "Accept Mpeg" do + "unknown" `isnt` Accept Type.MIME.Mpeg + "video/mpeg" `is` Accept Type.MIME.Mpeg + describe "Accept Mpkg" do + "unknown" `isnt` Accept Type.MIME.Mpkg + "application/vnd.apple.installer+xml" `is` Accept Type.MIME.Mpkg + describe "Accept Odp" do + "unknown" `isnt` Accept Type.MIME.Odp + "application/vnd.oasis.opendocument.presentation" `is` Accept Type.MIME.Odp + describe "Accept Ods" do + "unknown" `isnt` Accept Type.MIME.Ods + "application/vnd.oasis.opendocument.spreadsheet" `is` Accept Type.MIME.Ods + describe "Accept Odt" do + "unknown" `isnt` Accept Type.MIME.Odt + "application/vnd.oasis.opendocument.text" `is` Accept Type.MIME.Odt + describe "Accept Oga" do + "unknown" `isnt` Accept Type.MIME.Oga + "audio/ogg" `is` Accept Type.MIME.Oga + describe "Accept Ogv" do + "unknown" `isnt` Accept Type.MIME.Ogv + "video/ogg" `is` Accept Type.MIME.Ogv + describe "Accept Ogx" do + "unknown" `isnt` Accept Type.MIME.Ogx + "application/ogg" `is` Accept Type.MIME.Ogx + describe "Accept Opus" do + "unknown" `isnt` Accept Type.MIME.Opus + "audio/opus" `is` Accept Type.MIME.Opus + describe "Accept Otf" do + "unknown" `isnt` Accept Type.MIME.Otf + "font/otf" `is` Accept Type.MIME.Otf + describe "Accept Png" do + "unknown" `isnt` Accept Type.MIME.Png + "image/png" `is` Accept Type.MIME.Png + describe "Accept Pdf" do + "unknown" `isnt` Accept Type.MIME.Pdf + "application/pdf" `is` Accept Type.MIME.Pdf + describe "Accept Php" do + "unknown" `isnt` Accept Type.MIME.Php + "application/x-httpd-php" `is` Accept Type.MIME.Php + describe "Accept Ppt" do + "unknown" `isnt` Accept Type.MIME.Ppt + "application/vnd.ms-powerpoint" `is` Accept Type.MIME.Ppt + describe "Accept Pptx" do + "unknown" `isnt` Accept Type.MIME.Pptx + "application/vnd.openxmlformats-officedocument.presentationml.presentation" `is` Accept Type.MIME.Pptx + describe "Accept Rar" do + "unknown" `isnt` Accept Type.MIME.Rar + "application/vnd.rar" `is` Accept Type.MIME.Rar + describe "Accept Rtf" do + "unknown" `isnt` Accept Type.MIME.Rtf + "application/rtf" `is` Accept Type.MIME.Rtf + describe "Accept Sh" do + "unknown" `isnt` Accept Type.MIME.Sh + "application/x-sh" `is` Accept Type.MIME.Sh + describe "Accept Svg" do + "unknown" `isnt` Accept Type.MIME.Svg + "image/svg+xml" `is` Accept Type.MIME.Svg + describe "Accept Tar" do + "unknown" `isnt` Accept Type.MIME.Tar + "application/x-tar" `is` Accept Type.MIME.Tar + describe "Accept Tif" do + "unknown" `isnt` Accept Type.MIME.Tif + "image/tiff" `is` Accept Type.MIME.Tif + describe "Accept Ts" do + "unknown" `isnt` Accept Type.MIME.Ts + "video/mp2t" `is` Accept Type.MIME.Ts + describe "Accept Ttf" do + "unknown" `isnt` Accept Type.MIME.Ttf + "font/ttf" `is` Accept Type.MIME.Ttf + describe "Accept Txt" do + "unknown" `isnt` Accept Type.MIME.Txt + "text/plain" `is` Accept Type.MIME.Txt + describe "Accept Vsd" do + "unknown" `isnt` Accept Type.MIME.Vsd + "application/vnd.visio" `is` Accept Type.MIME.Vsd + describe "Accept Wav" do + "unknown" `isnt` Accept Type.MIME.Wav + "audio/wav" `is` Accept Type.MIME.Wav + describe "Accept Weba" do + "unknown" `isnt` Accept Type.MIME.Weba + "audio/webm" `is` Accept Type.MIME.Weba + describe "Accept Webm" do + "unknown" `isnt` Accept Type.MIME.Webm + "video/webm" `is` Accept Type.MIME.Webm + describe "Accept Webp" do + "unknown" `isnt` Accept Type.MIME.Webp + "image/webp" `is` Accept Type.MIME.Webp + describe "Accept Woff" do + "unknown" `isnt` Accept Type.MIME.Woff + "font/woff" `is` Accept Type.MIME.Woff + describe "Accept Woff2" do + "unknown" `isnt` Accept Type.MIME.Woff2 + "font/woff2" `is` Accept Type.MIME.Woff2 + describe "Accept Xhtml" do + "unknown" `isnt` Accept Type.MIME.Xhtml + "application/xhtml+xml" `is` Accept Type.MIME.Xhtml + describe "Accept Xls" do + "unknown" `isnt` Accept Type.MIME.Xls + "application/vnd.ms-excel" `is` Accept Type.MIME.Xls + describe "Accept Xlsx" do + "unknown" `isnt` Accept Type.MIME.Xlsx + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" `is` Accept Type.MIME.Xlsx + describe "Accept Xml" do + "unknown" `isnt` Accept Type.MIME.Xml + "application/xml" `is` Accept Type.MIME.Xml + describe "Accept Xul" do + "unknown" `isnt` Accept Type.MIME.Xul + "application/vnd.mozilla.xul+xml" `is` Accept Type.MIME.Xul + describe "Accept Zip" do + "unknown" `isnt` Accept Type.MIME.Zip + "application/zip" `is` Accept Type.MIME.Zip + describe "Accept Video3gp" do + "unknown" `isnt` Accept Type.MIME.Video3gp + "video/3gpp" `is` Accept Type.MIME.Video3gp + describe "Accept Video3g2" do + "unknown" `isnt` Accept Type.MIME.Video3g2 + "video/3gpp2" `is` Accept Type.MIME.Video3g2 + describe "Accept Archive7z" do + "unknown" `isnt` Accept Type.MIME.Archive7z + "application/x-7z-compressed" `is` Accept Type.MIME.Archive7z + describe "ContentType String" do + "foo" `is` (ContentType "foo") + "" `is` (ContentType "") + describe "ContentType MIME.MIME" do + "application/json" `is` (ContentType MIME.Json) + "text/plain" `is` (ContentType MIME.Txt) + "text/plain;charset=utf8" `is` (ContentType $ MIME.Other "text/plain;charset=utf8") + "foo" `is` (ContentType $ MIME.Other "foo") + describe "ContentType Aac" do + "unknown" `isnt` ContentType Type.MIME.Aac + "audio/aac" `is` ContentType Type.MIME.Aac + describe "ContentType Abw" do + "unknown" `isnt` ContentType Type.MIME.Abw + "application/x-abiword" `is` ContentType Type.MIME.Abw + describe "ContentType Arc" do + "unknown" `isnt` ContentType Type.MIME.Arc + "application/x-freearc" `is` ContentType Type.MIME.Arc + describe "ContentType Avif" do + "unknown" `isnt` ContentType Type.MIME.Avif + "image/avif" `is` ContentType Type.MIME.Avif + describe "ContentType Avi" do + "unknown" `isnt` ContentType Type.MIME.Avi + "video/x-msvideo" `is` ContentType Type.MIME.Avi + describe "ContentType Azw" do + "unknown" `isnt` ContentType Type.MIME.Azw + "application/vnd.amazon.ebook" `is` ContentType Type.MIME.Azw + describe "ContentType Bin" do + "unknown" `isnt` ContentType Type.MIME.Bin + "application/octet-stream" `is` ContentType Type.MIME.Bin + describe "ContentType Bmp" do + "unknown" `isnt` ContentType Type.MIME.Bmp + "image/bmp" `is` ContentType Type.MIME.Bmp + describe "ContentType Bz" do + "unknown" `isnt` ContentType Type.MIME.Bz + "application/x-bzip" `is` ContentType Type.MIME.Bz + describe "ContentType Bz2" do + "unknown" `isnt` ContentType Type.MIME.Bz2 + "application/x-bzip2" `is` ContentType Type.MIME.Bz2 + describe "ContentType Cda" do + "unknown" `isnt` ContentType Type.MIME.Cda + "application/x-cdf" `is` ContentType Type.MIME.Cda + describe "ContentType Csh" do + "unknown" `isnt` ContentType Type.MIME.Csh + "application/x-csh" `is` ContentType Type.MIME.Csh + describe "ContentType Css" do + "unknown" `isnt` ContentType Type.MIME.Css + "text/css" `is` ContentType Type.MIME.Css + describe "ContentType Csv" do + "unknown" `isnt` ContentType Type.MIME.Csv + "text/csv" `is` ContentType Type.MIME.Csv + describe "ContentType Doc" do + "unknown" `isnt` ContentType Type.MIME.Doc + "application/msword" `is` ContentType Type.MIME.Doc + describe "ContentType Docx" do + "unknown" `isnt` ContentType Type.MIME.Docx + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" `is` ContentType Type.MIME.Docx + describe "ContentType Eot" do + "unknown" `isnt` ContentType Type.MIME.Eot + "application/vnd.ms-fontobject" `is` ContentType Type.MIME.Eot + describe "ContentType Epub" do + "unknown" `isnt` ContentType Type.MIME.Epub + "application/epub+zip" `is` ContentType Type.MIME.Epub + describe "ContentType Gz" do + "unknown" `isnt` ContentType Type.MIME.Gz + "application/gzip" `is` ContentType Type.MIME.Gz + describe "ContentType Gif" do + "unknown" `isnt` ContentType Type.MIME.Gif + "image/gif" `is` ContentType Type.MIME.Gif + describe "ContentType Html" do + "unknown" `isnt` ContentType Type.MIME.Html + "text/html" `is` ContentType Type.MIME.Html + describe "ContentType Ico" do + "unknown" `isnt` ContentType Type.MIME.Ico + "image/vnd.microsoft.icon" `is` ContentType Type.MIME.Ico + describe "ContentType Ics" do + "unknown" `isnt` ContentType Type.MIME.Ics + "text/calendar" `is` ContentType Type.MIME.Ics + describe "ContentType Jar" do + "unknown" `isnt` ContentType Type.MIME.Jar + "application/java-archive" `is` ContentType Type.MIME.Jar + describe "ContentType Jpeg" do + "unknown" `isnt` ContentType Type.MIME.Jpeg + "image/jpeg" `is` ContentType Type.MIME.Jpeg + describe "ContentType Js" do + "unknown" `isnt` ContentType Type.MIME.Js + "text/javascript" `is` ContentType Type.MIME.Js + describe "ContentType Json" do + "unknown" `isnt` ContentType Type.MIME.Json + "application/json" `is` ContentType Type.MIME.Json + describe "ContentType Jsonld" do + "unknown" `isnt` ContentType Type.MIME.Jsonld + "application/ld+json" `is` ContentType Type.MIME.Jsonld + describe "ContentType Midi" do + "unknown" `isnt` ContentType Type.MIME.Midi + "audio/midi" `is` ContentType Type.MIME.Midi + describe "ContentType Mp3" do + "unknown" `isnt` ContentType Type.MIME.Mp3 + "audio/mpeg" `is` ContentType Type.MIME.Mp3 + describe "ContentType Mp4" do + "unknown" `isnt` ContentType Type.MIME.Mp4 + "video/mp4" `is` ContentType Type.MIME.Mp4 + describe "ContentType Mpeg" do + "unknown" `isnt` ContentType Type.MIME.Mpeg + "video/mpeg" `is` ContentType Type.MIME.Mpeg + describe "ContentType Mpkg" do + "unknown" `isnt` ContentType Type.MIME.Mpkg + "application/vnd.apple.installer+xml" `is` ContentType Type.MIME.Mpkg + describe "ContentType Odp" do + "unknown" `isnt` ContentType Type.MIME.Odp + "application/vnd.oasis.opendocument.presentation" `is` ContentType Type.MIME.Odp + describe "ContentType Ods" do + "unknown" `isnt` ContentType Type.MIME.Ods + "application/vnd.oasis.opendocument.spreadsheet" `is` ContentType Type.MIME.Ods + describe "ContentType Odt" do + "unknown" `isnt` ContentType Type.MIME.Odt + "application/vnd.oasis.opendocument.text" `is` ContentType Type.MIME.Odt + describe "ContentType Oga" do + "unknown" `isnt` ContentType Type.MIME.Oga + "audio/ogg" `is` ContentType Type.MIME.Oga + describe "ContentType Ogv" do + "unknown" `isnt` ContentType Type.MIME.Ogv + "video/ogg" `is` ContentType Type.MIME.Ogv + describe "ContentType Ogx" do + "unknown" `isnt` ContentType Type.MIME.Ogx + "application/ogg" `is` ContentType Type.MIME.Ogx + describe "ContentType Opus" do + "unknown" `isnt` ContentType Type.MIME.Opus + "audio/opus" `is` ContentType Type.MIME.Opus + describe "ContentType Otf" do + "unknown" `isnt` ContentType Type.MIME.Otf + "font/otf" `is` ContentType Type.MIME.Otf + describe "ContentType Png" do + "unknown" `isnt` ContentType Type.MIME.Png + "image/png" `is` ContentType Type.MIME.Png + describe "ContentType Pdf" do + "unknown" `isnt` ContentType Type.MIME.Pdf + "application/pdf" `is` ContentType Type.MIME.Pdf + describe "ContentType Php" do + "unknown" `isnt` ContentType Type.MIME.Php + "application/x-httpd-php" `is` ContentType Type.MIME.Php + describe "ContentType Ppt" do + "unknown" `isnt` ContentType Type.MIME.Ppt + "application/vnd.ms-powerpoint" `is` ContentType Type.MIME.Ppt + describe "ContentType Pptx" do + "unknown" `isnt` ContentType Type.MIME.Pptx + "application/vnd.openxmlformats-officedocument.presentationml.presentation" `is` ContentType Type.MIME.Pptx + describe "ContentType Rar" do + "unknown" `isnt` ContentType Type.MIME.Rar + "application/vnd.rar" `is` ContentType Type.MIME.Rar + describe "ContentType Rtf" do + "unknown" `isnt` ContentType Type.MIME.Rtf + "application/rtf" `is` ContentType Type.MIME.Rtf + describe "ContentType Sh" do + "unknown" `isnt` ContentType Type.MIME.Sh + "application/x-sh" `is` ContentType Type.MIME.Sh + describe "ContentType Svg" do + "unknown" `isnt` ContentType Type.MIME.Svg + "image/svg+xml" `is` ContentType Type.MIME.Svg + describe "ContentType Tar" do + "unknown" `isnt` ContentType Type.MIME.Tar + "application/x-tar" `is` ContentType Type.MIME.Tar + describe "ContentType Tif" do + "unknown" `isnt` ContentType Type.MIME.Tif + "image/tiff" `is` ContentType Type.MIME.Tif + describe "ContentType Ts" do + "unknown" `isnt` ContentType Type.MIME.Ts + "video/mp2t" `is` ContentType Type.MIME.Ts + describe "ContentType Ttf" do + "unknown" `isnt` ContentType Type.MIME.Ttf + "font/ttf" `is` ContentType Type.MIME.Ttf + describe "ContentType Txt" do + "unknown" `isnt` ContentType Type.MIME.Txt + "text/plain" `is` ContentType Type.MIME.Txt + describe "ContentType Vsd" do + "unknown" `isnt` ContentType Type.MIME.Vsd + "application/vnd.visio" `is` ContentType Type.MIME.Vsd + describe "ContentType Wav" do + "unknown" `isnt` ContentType Type.MIME.Wav + "audio/wav" `is` ContentType Type.MIME.Wav + describe "ContentType Weba" do + "unknown" `isnt` ContentType Type.MIME.Weba + "audio/webm" `is` ContentType Type.MIME.Weba + describe "ContentType Webm" do + "unknown" `isnt` ContentType Type.MIME.Webm + "video/webm" `is` ContentType Type.MIME.Webm + describe "ContentType Webp" do + "unknown" `isnt` ContentType Type.MIME.Webp + "image/webp" `is` ContentType Type.MIME.Webp + describe "ContentType Woff" do + "unknown" `isnt` ContentType Type.MIME.Woff + "font/woff" `is` ContentType Type.MIME.Woff + describe "ContentType Woff2" do + "unknown" `isnt` ContentType Type.MIME.Woff2 + "font/woff2" `is` ContentType Type.MIME.Woff2 + describe "ContentType Xhtml" do + "unknown" `isnt` ContentType Type.MIME.Xhtml + "application/xhtml+xml" `is` ContentType Type.MIME.Xhtml + describe "ContentType Xls" do + "unknown" `isnt` ContentType Type.MIME.Xls + "application/vnd.ms-excel" `is` ContentType Type.MIME.Xls + describe "ContentType Xlsx" do + "unknown" `isnt` ContentType Type.MIME.Xlsx + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" `is` ContentType Type.MIME.Xlsx + describe "ContentType Xml" do + "unknown" `isnt` ContentType Type.MIME.Xml + "application/xml" `is` ContentType Type.MIME.Xml + describe "ContentType Xul" do + "unknown" `isnt` ContentType Type.MIME.Xul + "application/vnd.mozilla.xul+xml" `is` ContentType Type.MIME.Xul + describe "ContentType Zip" do + "unknown" `isnt` ContentType Type.MIME.Zip + "application/zip" `is` ContentType Type.MIME.Zip + describe "ContentType Video3gp" do + "unknown" `isnt` ContentType Type.MIME.Video3gp + "video/3gpp" `is` ContentType Type.MIME.Video3gp + describe "ContentType Video3g2" do + "unknown" `isnt` ContentType Type.MIME.Video3g2 + "video/3gpp2" `is` ContentType Type.MIME.Video3g2 + describe "ContentType Archive7z" do + "unknown" `isnt` ContentType Type.MIME.Archive7z + "application/x-7z-compressed" `is` ContentType Type.MIME.Archive7z + describe "AccessControlAllowCredentials" do + "true" `is` AccessControlAllowCredentials + "" `isnt` AccessControlAllowCredentials + "false" `isnt` AccessControlAllowCredentials + describe "AccessControlAllowHeaders" do + "*" `is` AccessControlAllowHeaders (Left Wildcard) + " * " `is` AccessControlAllowHeaders (Left Wildcard) + "* " `is` AccessControlAllowHeaders (Left Wildcard) + "Vary" `is` AccessControlAllowHeaders (Right $ pure $ String.Lower.fromString "Vary") + " Vary" `is` AccessControlAllowHeaders (Right $ pure $ String.Lower.fromString "Vary") + " Vary " `is` AccessControlAllowHeaders (Right $ pure $ String.Lower.fromString "Vary") + "Accept, Vary, Content-Type" `is` AccessControlAllowHeaders (Right $ (pure "Accept" <> pure "Vary" <> pure "Content-Type") <#> String.Lower.fromString) + describe "AccessControlAllowMethods" do + "*" `is` AccessControlAllowMethods (Left Wildcard) + " * " `is` AccessControlAllowMethods (Left Wildcard) + "* " `is` AccessControlAllowMethods (Left Wildcard) + "GET" `is` AccessControlAllowMethods (Right $ pure GET) + "get" `is` AccessControlAllowMethods (Right $ pure GET) + "GET, PATCH" `is` AccessControlAllowMethods (Right $ pure GET <> pure PATCH) + " GET , PATCH " `is` AccessControlAllowMethods (Right $ pure GET <> pure PATCH) + describe "AccessControlAllowOrigin" do + "*" `is` AccessControlAllowOrigin (Left Wildcard) + " * " `is` AccessControlAllowOrigin (Left Wildcard) + "* " `is` AccessControlAllowOrigin (Left Wildcard) + "foo" `is` AccessControlAllowOrigin (Right "foo") + " foo " `is` AccessControlAllowOrigin (Right "foo") + "https://example.com" `is` AccessControlAllowOrigin (Right "https://example.com") + describe "AccessControlExposeHeaders" do + "*" `is` AccessControlExposeHeaders (Left Wildcard) + " * " `is` AccessControlExposeHeaders (Left Wildcard) + "* " `is` AccessControlExposeHeaders (Left Wildcard) + "Vary" `is` AccessControlExposeHeaders (Right $ pure $ String.Lower.fromString "Vary") + " Vary" `is` AccessControlExposeHeaders (Right $ pure $ String.Lower.fromString "Vary") + " Vary " `is` AccessControlExposeHeaders (Right $ pure $ String.Lower.fromString "Vary") + "Accept, Vary, Content-Type" `is` AccessControlExposeHeaders (Right $ (pure "Accept" <> pure "Vary" <> pure "Content-Type") <#> String.Lower.fromString) + describe "AccessControlMaxAge" do + " 123 " `is` AccessControlMaxAge 123 + " 0" `is` AccessControlMaxAge 0 + "23190" `is` AccessControlMaxAge 23190 + describe "AccessControlRequestHeaders" do + "Vary" `is` AccessControlRequestHeaders (pure $ String.Lower.fromString "Vary") + " Vary" `is` AccessControlRequestHeaders (pure $ String.Lower.fromString "Vary") + " Vary " `is` AccessControlRequestHeaders (pure $ String.Lower.fromString "Vary") + "Accept, Vary, Content-Type" `is` AccessControlRequestHeaders ((pure "Accept" <> pure "Vary" <> pure "Content-Type") <#> String.Lower.fromString) + describe "AccessControlRequestMethod" do + "GET" `is` AccessControlRequestMethod GET + "get" `is` AccessControlRequestMethod GET + " patCh " `is` AccessControlRequestMethod PATCH + describe "Age" do + " 123 " `is` Age 123 + " 0" `is` Age 0 + "23190" `is` Age 23190 + describe "Allow" do + "GET" `is` Allow (pure GET) + "get" `is` Allow (pure GET) + "GET, PATCH" `is` Allow (pure GET <> pure PATCH) + " GET , PATCH " `is` Allow (pure GET <> pure PATCH) + describe "Authorization" do + "Foo bar" `is` Authorization (AuthScheme "Foo") "bar" + "Bing bar" `is` Authorization (AuthScheme "Bing") "bar" + " Bing bar " `is` Authorization (AuthScheme "Bing") "bar" + "bar" `isnt` Authorization (AuthScheme "Foo") "bar" + describe "BasicAuth" do + "Basic ZGVtbzpwQDU1dzByZA==" `is` BasicAuth {username: "demo", password: "p@55w0rd"} + "Bearer ZGVtbzpwQDU1dzByZA==" `isnt` BasicAuth {username: "demo", password: "p@55w0rd"} + "Basic foo" `isnt` BasicAuth {username: "demo", password: "p@55w0rd"} + describe "BearerAuth" do + "Bearer foo" `is` BearerAuth "foo" + "Basic foo" `isnt` BearerAuth "foo" + "Bearer foo " `is` BearerAuth "foo" + describe "CacheControl" do + "max-age=604800" `is` CacheControl (cacheControlDefaults {maxAge = Just 604800}) + " max-age=604800 " `is` CacheControl (cacheControlDefaults {maxAge = Just 604800}) + "max-age=604800, must-revalidate" `is` CacheControl (cacheControlDefaults {maxAge = Just 604800, mustRevalidate = true}) + "max-age=20, s-maxage=10, no-cache, must-revalidate, proxy-revalidate, no-store, private, public, must-understand, no-transform, immutable, stale-while-revalidate, stale-if-error" + `is` + CacheControl (cacheControlDefaults {maxAge = Just 20, sMaxAge = Just 10, noCache = true, mustRevalidate = true, proxyRevalidate = true, noStore = true, private = true, public = true, mustUnderstand = true, noTransform = true, immutable = true, staleWhileRevalidate = true, staleIfError = true}) + "" `isnt` CacheControl cacheControlDefaults + " " `isnt` CacheControl cacheControlDefaults + "foo=bar" `isnt` CacheControl cacheControlDefaults + "foo" `isnt` CacheControl cacheControlDefaults + " foo=bar " `isnt` CacheControl cacheControlDefaults + " foo " `isnt` CacheControl cacheControlDefaults + describe "Connection" do + "" `isnt` Connection (Left ConnectionClose) + " " `isnt` Connection (Left ConnectionClose) + "close" `is` Connection (Left ConnectionClose) + " close " `is` Connection (Left ConnectionClose) + " cLoSe " `is` Connection (Left ConnectionClose) + "fuaiowf" `is` Connection (Right $ pure $ String.Lower.fromString "fuaiowf") + " a , b , c,d" `is` Connection (Right $ String.Lower.fromString <$> (pure "a" <> pure "b" <> pure "c" <> pure "d")) + describe "ContentDisposition" do + "form-data" `is` ContentDisposition (Either.Nested.in3 $ ContentDispositionFormData {filename: Nothing, name: Nothing}) + "form-data; name=\"foo\"" `is` ContentDisposition (Either.Nested.in3 $ ContentDispositionFormData {filename: Nothing, name: Just "foo"}) + "form-data; filename=\"foo.txt\"" `is` ContentDisposition (Either.Nested.in3 $ ContentDispositionFormData {filename: Just "foo.txt", name: Nothing}) + " form-data; filename=\"foo.txt\" ; name=\"foo\" " `is` ContentDisposition (Either.Nested.in3 $ ContentDispositionFormData {filename: Just "foo.txt", name: Just "foo"}) + + "attachment" `is` ContentDisposition (Either.Nested.in2 $ ContentDispositionAttachment {filename: Nothing}) + "attachment; filename=\"foo.txt\"" `is` ContentDisposition (Either.Nested.in2 $ ContentDispositionAttachment {filename: Just "foo.txt"}) + " attachment; filename=\"foo.txt\" " `is` ContentDisposition (Either.Nested.in2 $ ContentDispositionAttachment {filename: Just "foo.txt"}) + + "inline" `is` ContentDisposition (Either.Nested.in1 $ ContentDispositionInline) + "inline " `is` ContentDisposition (Either.Nested.in1 $ ContentDispositionInline) + " inline " `is` ContentDisposition (Either.Nested.in1 $ ContentDispositionInline) + describe "ContentEncoding" do + "gzip" `is` ContentEncoding (pure "gzip") + " gzip " `is` ContentEncoding (pure "gzip") + " gzip , deflate " `is` ContentEncoding (pure "gzip" <> pure "deflate") + describe "ContentLength" do + " 0 " `is` ContentLength 0 + " 1 " `is` ContentLength 1 + " 1212943817 " `is` ContentLength 1212943817 + describe "ContentLocation" $ pure unit + describe "ContentRange" $ pure unit + describe "Cookie" $ pure unit + describe "Date" $ pure unit + describe "ETag" $ pure unit + describe "ExpectContinue" $ pure unit + describe "Expires" $ pure unit + describe "Host" $ pure unit + describe "IfMatch" $ pure unit + describe "IfNoneMatch" $ pure unit + describe "IfModifiedSince" $ pure unit + describe "IfRange" $ pure unit + describe "IfUnmodifiedSince" $ pure unit + describe "LastModified" $ pure unit + describe "Origin" $ pure unit + describe "ProxyAuthorization" $ pure unit + describe "Range" $ pure unit + describe "Referer" $ pure unit + describe "ReferrerPolicy" $ pure unit + describe "RetryAfter" $ pure unit + describe "SecWebsocketKey" $ pure unit + describe "SecWebsocketAccept" $ pure unit + describe "SecWebsocketVersion" $ pure unit + describe "Server" $ pure unit + describe "SetCookie" $ pure unit + describe "StrictTransportSecurity" $ pure unit + describe "TE" $ pure unit + describe "TransferEncoding" $ pure unit + describe "Upgrade" $ pure unit + describe "UserAgent" $ pure unit + describe "Vary"$ pure unit diff --git a/test/Test/Axon.Header.purs b/test/Test/Axon.Header.purs new file mode 100644 index 0000000..7b114cb --- /dev/null +++ b/test/Test/Axon.Header.purs @@ -0,0 +1,10 @@ +module Test.Axon.Header where + +import Prelude + +import Test.Axon.Header.Typed as Typed +import Test.Spec (Spec, describe) + +spec :: Spec Unit +spec = describe "Header" do + Typed.spec diff --git a/test/Test/Axon.Request.Parts.purs b/test/Test/Axon.Request.Parts.purs index 5c9f225..ab18de9 100644 --- a/test/Test/Axon.Request.Parts.purs +++ b/test/Test/Axon.Request.Parts.purs @@ -2,19 +2,20 @@ module Test.Axon.Request.Parts where import Prelude -import Axon.Header.Typed (ContentType(..)) +import Axon.Header.Typed (ContentType) import Axon.Request (Request) import Axon.Request as Request import Axon.Request.Method (Method(..)) -import Axon.Request.Parts.Class (Header(..), Json(..), Patch(..), Path(..), Post(..), extractRequestParts) +import Axon.Request.Parts.Class (ExtractError(..), Header, Json(..), Patch, Path(..), Post(..), extractRequestParts, invokeHandler) import Axon.Request.Parts.Path (type (/), IgnoreRest) -import Control.Monad.Error.Class (liftEither, liftMaybe) +import Control.Monad.Error.Class (liftEither) import Data.Bifunctor (lmap) import Data.Either (Either(..)) import Data.Map as Map -import Data.Maybe (Maybe(..), fromJust) +import Data.Maybe (fromJust) import Data.Tuple.Nested (type (/\), (/\)) import Data.URL as URL +import Effect.Aff (Aff) import Effect.Class (liftEffect) import Effect.Exception (error) import Effect.Unsafe (unsafePerformEffect) @@ -38,9 +39,8 @@ spec = describe "Parts" do { address: "127.0.0.1", port: 81 } , method: GET } - void $ extractRequestParts @Request req <#> lmap (error <<< show) - >>= liftEither - >>= liftMaybe (error "was nothing") + _ :: Request <- invokeHandler req (pure @Aff) <#> lmap (error <<< show) >>= liftEither + pure unit it "extracts header, method, path, JSON body" do stream <- Buffer.fromString """{"firstName": "henry"}""" UTF8 @@ -55,12 +55,24 @@ spec = describe "Parts" do { address: "127.0.0.1", port: 81 } , method: PATCH } - a <- - extractRequestParts - @(Patch /\ Header (ContentType MIME.Json) /\ (Path ("users" / Int) _) /\ Json { firstName :: String }) - req <#> lmap (error <<< show) >>= liftEither >>= liftMaybe - (error "was nothing") - a `shouldEqual` (Patch /\ Header (ContentType MIME.Json) /\ Path 12 /\ Json { firstName: "henry" }) + + let + handler :: + Patch -> + Header (ContentType MIME.Json) -> + Path ("users" / Int) Int -> + Json { firstName :: String } -> + Aff String + handler _ _ (Path id) (Json {firstName}) = do + id `shouldEqual` 12 + firstName `shouldEqual` "henry" + pure firstName + + name <- invokeHandler req handler + <#> lmap (error <<< show) + >>= liftEither + + name `shouldEqual` "henry" describe "Path" do it "matches a route matching literal" do @@ -74,7 +86,6 @@ spec = describe "Parts" do } a <- extractRequestParts @(Path "foo" _) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Path unit) it "matches a route matching multiple literals" do @@ -90,7 +101,6 @@ spec = describe "Parts" do a <- extractRequestParts @(Path ("foo" / "bar" / "baz") _) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Path unit) it "does not partially match a route ..." do @@ -104,9 +114,7 @@ spec = describe "Parts" do , method: GET } a <- extractRequestParts @(Path ("foo" / "bar") _) req - <#> lmap (error <<< show) - >>= liftEither - a `shouldEqual` Nothing + a `shouldEqual` (Left ExtractNext) it "... but does if ends in IgnoreRest" do req <- liftEffect $ Request.make @@ -121,7 +129,6 @@ spec = describe "Parts" do a <- extractRequestParts @(Path ("foo" / "bar" / IgnoreRest) _) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Path unit) it "extracts an int" do @@ -137,7 +144,6 @@ spec = describe "Parts" do a <- extractRequestParts @(Path ("foo" / Int / "bar") _) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Path 123) it "extracts an int and a string" do @@ -153,7 +159,6 @@ spec = describe "Parts" do a <- extractRequestParts @(Path ("foo" / Int / "bar" / String) _) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Path $ 123 /\ "baz") describe "Body" do @@ -169,7 +174,6 @@ spec = describe "Parts" do a <- extractRequestParts @(Either Request.BodyStringError String) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Right "foo") it "extracts a string body from a readable stream" do @@ -186,12 +190,10 @@ spec = describe "Parts" do a <- extractRequestParts @(Either Request.BodyStringError String) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Right "foo") a' <- extractRequestParts @String req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a' `shouldEqual` "foo" it "extracts a string body from a buffer" do @@ -207,12 +209,10 @@ spec = describe "Parts" do a <- extractRequestParts @(Either Request.BodyStringError String) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Right "foo") a' <- extractRequestParts @String req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a' `shouldEqual` "foo" it "extracts a JSON body" do @@ -230,5 +230,4 @@ spec = describe "Parts" do a <- extractRequestParts @(Post /\ Json { foo :: Int, bar :: String }) req <#> lmap (error <<< show) >>= liftEither - >>= liftMaybe (error "was nothing") a `shouldEqual` (Post /\ Json { foo: 123, bar: "abc" }) diff --git a/test/Test/Main.purs b/test/Test/Main.purs index b52ba08..af552a2 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -4,6 +4,7 @@ import Prelude import Effect (Effect) import Test.Axon.Request as Test.Request +import Test.Axon.Header as Test.Header import Test.Spec (describe) import Test.Spec.Reporter (specReporter) import Test.Spec.Runner.Node (runSpecAndExitProcess) @@ -11,3 +12,4 @@ import Test.Spec.Runner.Node (runSpecAndExitProcess) main :: Effect Unit main = runSpecAndExitProcess [ specReporter ] $ describe "Axon" do Test.Request.spec + Test.Header.spec