Adds TLS support
parent
72009b5db2
commit
0c1345b877
19
README.md
19
README.md
|
@ -1,20 +1,19 @@
|
||||||
# hastatic
|
# hastatic
|
||||||
|
|
||||||
[![Docker Build Status](https://img.shields.io/docker/build/abhin4v/hastatic.svg?style=flat-square)](https://hub.docker.com/r/abhin4v/hastatic/)
|
[![Docker Build Status](https://img.shields.io/docker/build/abhin4v/hastatic.svg?style=flat-square)](https://hub.docker.com/r/abhin4v/hastatic/) ![Docker Pulls](https://img.shields.io/docker/pulls/abhin4v/hastatic.svg?style=flat-square) ![MicroBadger Size](https://img.shields.io/microbadger/image-size/abhin4v/hastatic.svg?style=flat-square)
|
||||||
![MicroBadger Size](https://img.shields.io/microbadger/image-size/abhin4v/hastatic.svg?style=flat-square)
|
|
||||||
|
|
||||||
_hastatic_ is a tiny static web server for Docker.
|
_hastatic_ is a tiny static web server for Docker.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- A tiny web server, just 3.5MB in size.
|
- A tiny web server, just 5 MB in size.
|
||||||
- Statically compiled binary with no dependencies.
|
- Statically compiled binary with no dependencies.
|
||||||
- Built for Docker.
|
- Built for Docker.
|
||||||
|
- Supports HTTPS.
|
||||||
- Supports custom 404 file.
|
- Supports custom 404 file.
|
||||||
- Supports custom index files for URLs ending with "/".
|
- Supports custom index files for URLs ending with "/".
|
||||||
- Takes care to not serve hidden files.
|
- Takes care to not serve hidden files.
|
||||||
- Adds caching headers automatically.
|
- Adds caching headers automatically.
|
||||||
- Does not support HTTPS. It is expected that you run it behind a reverse-proxy server with HTTPS support, like nginx.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -30,10 +29,14 @@ CMD ["/usr/bin/hastatic"]
|
||||||
|
|
||||||
Build and run:
|
Build and run:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
$ docker build -t mywebsite .
|
$ docker build -t mywebsite .
|
||||||
$ docker run -p 8080:3000 mywebsite # run with default configs
|
$ # run with default configs
|
||||||
|
$ docker run -p 8080:3000 mywebsite
|
||||||
|
$ # run with custom configs
|
||||||
$ docker run -e PORT=2000 -e NF_FILE=404.html -e IDX_FILE=index.html -p 8080:2000 mywebsite
|
$ docker run -e PORT=2000 -e NF_FILE=404.html -e IDX_FILE=index.html -p 8080:2000 mywebsite
|
||||||
|
$ # run with HTTPS support
|
||||||
|
$ docker run -e TLS_CERT_FILE=certificate.pem -e TLS_KEY_FILE=key.pem -p 443:3000 mywebsite
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
@ -43,7 +46,9 @@ The Docker image supports these environment variable for configuration:
|
||||||
- PORT: the port to run the web server on, default: 3000
|
- PORT: the port to run the web server on, default: 3000
|
||||||
- NF_FILE: name of the custom 404 file, default: `404.html`
|
- NF_FILE: name of the custom 404 file, default: `404.html`
|
||||||
- IDX_FILE: name of the custom index files, default: `index.html`
|
- IDX_FILE: name of the custom index files, default: `index.html`
|
||||||
|
- TLS_CERT_FILE: TLS certification file, optional, required for HTTPS support
|
||||||
|
- TLS_KEY_FILE: TLS key file, optional, required for HTTPS support
|
||||||
|
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
_hastatic_ is written in Haskell, just 30 lines of it. It uses the excellent [Warp](https://hackage.haskell.org/package/warp) server underneath.
|
_hastatic_ is written in Haskell, just 60 lines of it. It uses the excellent [Warp](https://hackage.haskell.org/package/warp) server underneath with the [warp-tls](https://hackage.haskell.org/package/warp-tls) package for TLS support.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
--
|
--
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
--
|
--
|
||||||
-- hash: e49204f2f26a2ed8552e95f4f543b1cb4148cc509790f5d550ab98b70a722323
|
-- hash: e7a5e0192bba9ed055c360b88189436d278d96883c36e66a4a17b70f134f4654
|
||||||
|
|
||||||
name: hastatic
|
name: hastatic
|
||||||
version: 0.9.0
|
version: 0.9.0
|
||||||
|
@ -31,4 +31,5 @@ executable hastatic
|
||||||
, wai
|
, wai
|
||||||
, wai-middleware-static
|
, wai-middleware-static
|
||||||
, warp
|
, warp
|
||||||
|
, warp-tls
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
|
@ -25,6 +25,7 @@ executables:
|
||||||
dependencies:
|
dependencies:
|
||||||
- wai
|
- wai
|
||||||
- warp
|
- warp
|
||||||
|
- warp-tls
|
||||||
- http-types
|
- http-types
|
||||||
- wai-middleware-static
|
- wai-middleware-static
|
||||||
- text
|
- text
|
||||||
|
|
58
src/Main.hs
58
src/Main.hs
|
@ -5,12 +5,16 @@ import Data.Maybe (fromMaybe)
|
||||||
import qualified Data.List as List
|
import qualified Data.List as List
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
|
import qualified Network.Wai.Handler.WarpTLS as TLS
|
||||||
import Network.Wai.Middleware.Static
|
import Network.Wai.Middleware.Static
|
||||||
import Network.HTTP.Types (status404)
|
import Network.HTTP.Types (status404)
|
||||||
import Network.Wai.Handler.Warp (run)
|
import Network.Wai.Handler.Warp (run, defaultSettings, setPort)
|
||||||
|
import System.Exit (die)
|
||||||
import System.Environment (lookupEnv)
|
import System.Environment (lookupEnv)
|
||||||
import Text.Read (readMaybe)
|
import Text.Read (readMaybe)
|
||||||
|
|
||||||
|
data TLS = Okay TLS.TLSSettings | Error String | None
|
||||||
|
|
||||||
indexHTML :: T.Text -> Middleware
|
indexHTML :: T.Text -> Middleware
|
||||||
indexHTML indexFile app req respond =
|
indexHTML indexFile app req respond =
|
||||||
let path = pathInfo req
|
let path = pathInfo req
|
||||||
|
@ -22,22 +26,48 @@ indexHTML indexFile app req respond =
|
||||||
[x] -> if "." `T.isInfixOf` x then [x] else [x, indexFile]
|
[x] -> if "." `T.isInfixOf` x then [x] else [x, indexFile]
|
||||||
(x:xs) -> x : fixPath xs
|
(x:xs) -> x : fixPath xs
|
||||||
|
|
||||||
|
notFoundHandler :: FilePath -> Application
|
||||||
notFoundHandler notFoundFile _ respond = respond $
|
notFoundHandler notFoundFile _ respond = respond $
|
||||||
responseFile status404 [("Content-Type", "text/html")] notFoundFile Nothing
|
responseFile status404 [("Content-Type", "text/html")] notFoundFile Nothing
|
||||||
|
|
||||||
main = do
|
getTLSSettings :: IO TLS
|
||||||
mPort <- lookupEnv "PORT"
|
getTLSSettings = do
|
||||||
let port = fromMaybe 3000 (readMaybe =<< mPort)
|
tlsCertFile <- lookupEnv "TLS_CERT_FILE"
|
||||||
mNotFoundFile <- lookupEnv "NF_FILE"
|
tlsKeyFile <- lookupEnv "TLS_KEY_FILE"
|
||||||
let notFoundFile = fromMaybe "404.html" mNotFoundFile
|
|
||||||
mIndexFile <- lookupEnv "IDX_FILE"
|
|
||||||
let indexFile = T.pack $ fromMaybe "index.html" mIndexFile
|
|
||||||
|
|
||||||
cache <- initCaching PublicStaticCaching
|
case (tlsCertFile, tlsKeyFile) of
|
||||||
putStrLn $ "Starting server on port: " <> show port
|
(Nothing, Nothing) -> return None
|
||||||
run port
|
(Just cert, Just key) -> return $ Okay $ TLS.tlsSettings cert key
|
||||||
$ indexHTML indexFile
|
_ -> return $ Error "Certificate file or Key file is missing"
|
||||||
$ staticPolicy' cache (predicate noDot)
|
|
||||||
$ notFoundHandler notFoundFile
|
application :: [FilePath] -> IO Application
|
||||||
|
application excludedPaths = do
|
||||||
|
notFoundFile <- fromMaybe "404.html" <$> lookupEnv "NF_FILE"
|
||||||
|
indexFile <- T.pack . fromMaybe "index.html" <$> lookupEnv "IDX_FILE"
|
||||||
|
cache <- initCaching PublicStaticCaching
|
||||||
|
return
|
||||||
|
. indexHTML indexFile
|
||||||
|
. staticPolicy' cache polcy
|
||||||
|
. notFoundHandler
|
||||||
|
$ notFoundFile
|
||||||
where
|
where
|
||||||
noDot = not . List.isPrefixOf "."
|
noDot = not . List.isPrefixOf "."
|
||||||
|
polcy = predicate noDot >-> predicate (not . flip elem excludedPaths)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
mPort <- lookupEnv "PORT"
|
||||||
|
let port = fromMaybe 3000 (readMaybe =<< mPort)
|
||||||
|
tlsSettings <- getTLSSettings
|
||||||
|
|
||||||
|
case tlsSettings of
|
||||||
|
Okay tls -> do
|
||||||
|
app <- application [TLS.certFile tls, TLS.keyFile tls]
|
||||||
|
putStrLn $ "Starting HTTPS server on port: " <> show port
|
||||||
|
TLS.runTLS tls (setPort port defaultSettings) app
|
||||||
|
None -> do
|
||||||
|
app <- application []
|
||||||
|
putStrLn $ "Starting HTTP server on port: " <> show port
|
||||||
|
run port app
|
||||||
|
Error msg ->
|
||||||
|
die $ "Error starting server: " <> msg
|
||||||
|
|
Loading…
Reference in New Issue