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 # 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.

View File

@ -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

View File

@ -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

View File

@ -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