Skip to content

Instantly share code, notes, and snippets.

@rybla
Created September 21, 2024 16:14
Show Gist options
  • Save rybla/a98686895528aa7839e98ffb4bc4894c to your computer and use it in GitHub Desktop.
Save rybla/a98686895528aa7839e98ffb4bc4894c to your computer and use it in GitHub Desktop.
simple http server in Purescript
- console
- debug
- effect
- node-fs
- node-http
- prelude
import Prelude
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Effect.Class.Console as Console
import Node.Encoding (Encoding(..))
import Node.EventEmitter (on_, once_)
import Node.FS.Sync as FS
import Node.HTTP (createServer)
import Node.HTTP.IncomingMessage as IncomingMessage
import Node.HTTP.OutgoingMessage as OutgoingMessage
import Node.HTTP.Server (requestH, toNetServer)
import Node.HTTP.ServerResponse as ServerResponse
import Node.Net.Server (listenTcp, listeningH)
import Node.Path as Path
import Node.Stream as Stream
host :: String
host = "localhost"
port :: Int
port = 8080
uri_base :: String
uri_base = "http://" <> host <> ":" <> show port
main :: Effect Unit
main = launchAff_ do
Console.log "entrypoint: Gos.Server.Main.main"
server <- createServer # liftEffect
server # toNetServer
# once_ listeningH (Console.log $ "listening at " <> uri_base)
# liftEffect
server
# on_ requestH
( \in_msg res -> do
let out_msg = res # ServerResponse.toOutgoingMessage
let out_stream = out_msg # OutgoingMessage.toWriteable
let url = in_msg # IncomingMessage.url
case in_msg # IncomingMessage.method of
"GET" -> do
let filepath = "./public" <> url
exists <- FS.exists filepath
let extname = Path.extname filepath
if exists then do
out_msg # OutgoingMessage.setHeader "Content-Type"
case extname of
"" -> "text/html"
".html" -> "text/html"
".css" -> "text/css"
".json" -> "application/json"
".png" -> "image/png"
_ -> "text/plain"
content <- FS.readTextFile UTF8 (filepath <> if extname == "" then "/index.html" else "")
Stream.writeString out_stream UTF8 content # void
Stream.end out_stream
Console.log $ "served " <> filepath
else do
if extname == "" || extname == ".html" then do
out_msg # OutgoingMessage.setHeader "Content-Type" "text/html"
content <- FS.readTextFile UTF8 "./public/404.html"
Stream.writeString out_stream UTF8 content # void
else do
ServerResponse.setStatusCode 404 res
Stream.end out_stream
Console.log $ "not found " <> filepath
_ -> pure unit
)
# liftEffect
listenTcp (server # toNetServer) { host, port } # liftEffect
@rybla
Copy link
Author

rybla commented Sep 21, 2024

servers any file from ./public, with some handling for different file types

if a .http file isn't found, then serves ./public/404.html. if a non-.http file isn't found, just serves 404 error code.

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