Adds TLS support

master
Abhinav Sarkar 2018-07-21 19:00:42 +05:30
parent 72009b5db2
commit 0c1345b877
4 changed files with 59 additions and 22 deletions

View File

@ -1,20 +1,19 @@
# hastatic
[![Docker Build Status](https://img.shields.io/docker/build/abhin4v/hastatic.svg?style=flat-square)](https://hub.docker.com/r/abhin4v/hastatic/)
![MicroBadger Size](https://img.shields.io/microbadger/image-size/abhin4v/hastatic.svg?style=flat-square)
[![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)
_hastatic_ is a tiny static web server for Docker.
## 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.
- Built for Docker.
- Supports HTTPS.
- Supports custom 404 file.
- Supports custom index files for URLs ending with "/".
- Takes care to not serve hidden files.
- 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
@ -30,10 +29,14 @@ CMD ["/usr/bin/hastatic"]
Build and run:
```
```bash
$ 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
$ # run with HTTPS support
$ docker run -e TLS_CERT_FILE=certificate.pem -e TLS_KEY_FILE=key.pem -p 443:3000 mywebsite
```
## 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
- NF_FILE: name of the custom 404 file, default: `404.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
_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.

View File

@ -2,7 +2,7 @@
--
-- see: https://github.com/sol/hpack
--
-- hash: e49204f2f26a2ed8552e95f4f543b1cb4148cc509790f5d550ab98b70a722323
-- hash: e7a5e0192bba9ed055c360b88189436d278d96883c36e66a4a17b70f134f4654
name: hastatic
version: 0.9.0
@ -31,4 +31,5 @@ executable hastatic
, wai
, wai-middleware-static
, warp
, warp-tls
default-language: Haskell2010

View File

@ -25,6 +25,7 @@ executables:
dependencies:
- wai
- warp
- warp-tls
- http-types
- wai-middleware-static
- text

View File

@ -5,12 +5,16 @@ import Data.Maybe (fromMaybe)
import qualified Data.List as List
import qualified Data.Text as T
import Network.Wai
import qualified Network.Wai.Handler.WarpTLS as TLS
import Network.Wai.Middleware.Static
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 Text.Read (readMaybe)
data TLS = Okay TLS.TLSSettings | Error String | None
indexHTML :: T.Text -> Middleware
indexHTML indexFile app req respond =
let path = pathInfo req
@ -22,22 +26,48 @@ indexHTML indexFile app req respond =
[x] -> if "." `T.isInfixOf` x then [x] else [x, indexFile]
(x:xs) -> x : fixPath xs
notFoundHandler :: FilePath -> Application
notFoundHandler notFoundFile _ respond = respond $
responseFile status404 [("Content-Type", "text/html")] notFoundFile Nothing
main = do
mPort <- lookupEnv "PORT"
let port = fromMaybe 3000 (readMaybe =<< mPort)
mNotFoundFile <- lookupEnv "NF_FILE"
let notFoundFile = fromMaybe "404.html" mNotFoundFile
mIndexFile <- lookupEnv "IDX_FILE"
let indexFile = T.pack $ fromMaybe "index.html" mIndexFile
getTLSSettings :: IO TLS
getTLSSettings = do
tlsCertFile <- lookupEnv "TLS_CERT_FILE"
tlsKeyFile <- lookupEnv "TLS_KEY_FILE"
cache <- initCaching PublicStaticCaching
putStrLn $ "Starting server on port: " <> show port
run port
$ indexHTML indexFile
$ staticPolicy' cache (predicate noDot)
$ notFoundHandler notFoundFile
case (tlsCertFile, tlsKeyFile) of
(Nothing, Nothing) -> return None
(Just cert, Just key) -> return $ Okay $ TLS.tlsSettings cert key
_ -> return $ Error "Certificate file or Key file is missing"
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
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