Skip to content

Commit 77832c3

Browse files
Merge pull request #120 from chrisdone/cd/2026-04-25-warp
Add HTTP server support
2 parents adfbd16 + 75c0a95 commit 77832c3

5 files changed

Lines changed: 84 additions & 6 deletions

File tree

examples/43-http.hell

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
main = do
2+
Http.run 8080 \request respond ->
3+
if Eq.eq (Http.pathInfo request) []
4+
then
5+
case List.lookup (CI.mk $ Text.encodeUtf8 "Content-Type") (Http.requestHeaders request) of
6+
Maybe.Just _x ->
7+
respond $ Http.responseBuilder (Http.mkStatus 200 "OK") [] $
8+
Builder.byteString $ Text.encodeUtf8 "Hello, World!"
9+
Maybe.Nothing ->
10+
respond $ Http.responseBuilder (Http.mkStatus 500 "Error") [] $
11+
Builder.byteString $ Text.encodeUtf8 "Wobble"
12+
else
13+
respond $
14+
Http.responseFile (Http.mkStatus 400 "Not Found")
15+
[(CI.mk (Text.encodeUtf8 "Content-Type"), Text.encodeUtf8 "text/markdown")]
16+
"docs/readme.md"
17+
Maybe.Nothing

examples/44-char.hell

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
main = do
2+
line <- Text.getLine
3+
IO.print $ Text.all (Eq.eq 'x') line

hell.cabal

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ executable hell
2525
, async
2626
, base >=4.17.2.1 && <4.22
2727
, bytestring
28+
, case-insensitive
2829
, constraints
2930
, containers
3031
, criterion
@@ -33,6 +34,7 @@ executable hell
3334
, ghc-prim
3435
, haskell-src-exts
3536
, hspec
37+
, http-types
3638
, lucid2
3739
, mtl
3840
, optparse-applicative
@@ -47,6 +49,8 @@ executable hell
4749
, typed-process
4850
, unliftio
4951
, vector
52+
, wai
53+
, warp
5054
default-language: Haskell2010
5155

5256
test-suite hell-test
@@ -61,6 +65,7 @@ test-suite hell-test
6165
, async
6266
, base >=4.17.2.1 && <4.22
6367
, bytestring
68+
, case-insensitive
6469
, constraints
6570
, containers
6671
, criterion
@@ -69,6 +74,7 @@ test-suite hell-test
6974
, ghc-prim
7075
, haskell-src-exts
7176
, hspec
77+
, http-types
7278
, lucid2
7379
, mtl
7480
, optparse-applicative
@@ -83,4 +89,6 @@ test-suite hell-test
8389
, typed-process
8490
, unliftio
8591
, vector
92+
, wai
93+
, warp
8694
default-language: Haskell2010

package.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ dependencies:
3232
- temporary
3333
- these
3434
- time
35+
- warp
36+
- wai
37+
- http-types
38+
- case-insensitive
3539

3640
ghc-options:
3741
- -Wall

src/Hell.hs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ import Control.Monad
5050
-- e.g. 'Data.Graph' becomes 'Graph', and are then exposed to the Hell
5151
-- guest language as such.
5252

53+
import qualified Data.CaseInsensitive as CI
54+
import Data.CaseInsensitive (CI, FoldCase)
55+
import qualified Network.HTTP.Types as Http
56+
import qualified Network.Wai as Wai
57+
import qualified Network.Wai.Handler.Warp as Warp
58+
import Data.ByteString.Builder (Builder)
59+
import qualified Data.ByteString.Builder as Builder
5360
import Control.Applicative (Alternative (..), optional)
5461
import qualified Control.Concurrent as Concurrent
5562
import Control.Exception (evaluate)
@@ -837,6 +844,7 @@ instances =
837844
Map.fromList
838845
[ entail1 @Show @[],
839846
entail1 @Show @Set,
847+
entail1 @Show @CI,
840848
entail1 @Show @Tree,
841849
entail1 @Show @Maybe,
842850
entail1 @Show @Vector,
@@ -852,8 +860,10 @@ instances =
852860
instance0 @Show @Char,
853861
instance0 @Show @Text,
854862
instance0 @Show @ByteString,
863+
instance0 @Show @Builder,
855864
instance0 @Show @ExitCode,
856865
instance0 @Show @Value,
866+
entail1 @Eq @CI,
857867
entail1 @Eq @[],
858868
entail1 @Eq @Set,
859869
entail1 @Eq @Maybe,
@@ -874,6 +884,7 @@ instances =
874884
instance0 @Eq @ExitCode,
875885
entail1 @Ord @[],
876886
entail1 @Ord @Set,
887+
entail1 @Ord @CI,
877888
entail1 @Ord @Maybe,
878889
entail2 @Ord @Either,
879890
entail2 @Ord @(,),
@@ -920,7 +931,9 @@ instances =
920931
instance2 @Semigroup @Options.Mod,
921932
instance0 @Semigroup @Text,
922933
instance1 @Semigroup @Vector,
923-
instance1 @Semigroup @[]
934+
instance1 @Semigroup @[],
935+
instance0 @FoldCase @Text,
936+
instance0 @FoldCase @ByteString
924937
]
925938

926939
--------------------------------------------------------------------------------
@@ -1693,6 +1706,8 @@ supportedTypeConstructors =
16931706
("Day", SomeTypeRep $ typeRep @Day),
16941707
("UTCTime", SomeTypeRep $ typeRep @UTCTime),
16951708
("TimeOfDay", SomeTypeRep $ typeRep @TimeOfDay),
1709+
("Builder", SomeTypeRep $ typeRep @Builder),
1710+
("CI", SomeTypeRep $ typeRep @CI),
16961711
-- Internal, hidden types
16971712
("hell:Hell.NilL", SomeTypeRep $ typeRep @('NilL)),
16981713
("hell:Hell.ConsL", SomeTypeRep $ typeRep @('ConsL)),
@@ -1770,10 +1785,11 @@ supportedLits =
17701785
lit' "Text.reverse" Text.reverse,
17711786
lit' "Text.toLower" Text.toLower,
17721787
lit' "Text.toUpper" Text.toUpper,
1773-
-- Needs Char operations.
1774-
-- ("Text.any", lit' Text.any),
1775-
-- ("Text.all", lit' Text.all),
1776-
-- ("Text.filter", lit' Text.filter),
1788+
lit' "Text.any" Text.any,
1789+
lit' "Text.unpack" Text.unpack,
1790+
lit' "Text.pack" Text.pack,
1791+
lit' "Text.all" Text.all,
1792+
lit' "Text.filter" Text.filter,
17771793
lit' "Text.take" Text.take,
17781794
lit' "Text.splitOn" Text.splitOn,
17791795
lit' "Text.takeEnd" Text.takeEnd,
@@ -1883,7 +1899,21 @@ supportedLits =
18831899
-- Options
18841900
lit' "Options.switch" Options.switch,
18851901
lit' "Options.strOption" (Options.strOption @Text),
1886-
lit' "Options.strArgument" (Options.strArgument @Text)
1902+
lit' "Options.strArgument" (Options.strArgument @Text),
1903+
-- Http
1904+
lit' "Http.run" warp_run,
1905+
lit' "Http.responseBuilder" Wai.responseBuilder,
1906+
lit' "Http.responseStream" Wai.responseStream,
1907+
lit' "Http.responseFile" wai_responseFile,
1908+
lit' "Http.mkStatus" http_mkStatus,
1909+
lit' "Http.pathInfo" Wai.pathInfo,
1910+
lit' "Http.FilePart" Wai.FilePart,
1911+
lit' "Http.requestHeaders" Wai.requestHeaders,
1912+
lit' "Http.queryString" Wai.queryString,
1913+
lit' "Http.getRequestBodyChunk" Wai.getRequestBodyChunk,
1914+
lit' "Http.consumeRequestBodyStrict" (fmap L.toStrict . Wai.consumeRequestBodyStrict),
1915+
-- Builder
1916+
lit' "Builder.byteString" Builder.byteString
18871917
]
18881918
where
18891919
lit' :: forall a. (Type.Typeable a) => String -> a -> (String, (UTerm (), SomeTypeRep))
@@ -2150,6 +2180,10 @@ polyLits =
21502180
"Exit.exitWith" Exit.exitWith :: forall a. ExitCode -> IO a
21512181
"Exit.exitCode" exit_exitCode :: forall a. a -> (Int -> a) -> ExitCode -> a
21522182

2183+
-- CI
2184+
"CI.foldedCase" CI.foldedCase :: forall s. CI s -> s
2185+
"CI.mk" CI.mk :: forall s. CI.FoldCase s => s -> CI s
2186+
21532187
-- Exceptions
21542188
"Error.error" (error . Text.unpack) :: forall a. Text -> a
21552189

@@ -2500,6 +2534,18 @@ t_appendFile fp t = ByteString.appendFile (Text.unpack fp) (Text.encodeUtf8 t)
25002534
t_readFile :: Text -> IO Text
25012535
t_readFile fp = fmap Text.decodeUtf8 (ByteString.readFile (Text.unpack fp))
25022536

2537+
-- Same as Warp.run, but with HTTP/2 support disabled.
2538+
-- Stick to HTTP/1.2; simpler, fewer moving parts.
2539+
warp_run :: Int -> Wai.Application -> IO ()
2540+
warp_run p = Warp.runSettings (Warp.setHTTP2Disabled $ Warp.setPort p $ Warp.defaultSettings)
2541+
2542+
-- No point using ByteString here.
2543+
http_mkStatus :: Int -> Text -> Http.Status
2544+
http_mkStatus i = Http.mkStatus i . Text.encodeUtf8
2545+
2546+
wai_responseFile :: Http.Status -> Http.ResponseHeaders -> Text -> Maybe Wai.FilePart -> Wai.Response
2547+
wai_responseFile s r f = Wai.responseFile s r (Text.unpack f)
2548+
25032549
--------------------------------------------------------------------------------
25042550
-- JSON operations
25052551

0 commit comments

Comments
 (0)