Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Commit a829b2c

Browse files
committed
copy pasted repo, small refactorings.
0 parents  commit a829b2c

File tree

4 files changed

+581
-0
lines changed

4 files changed

+581
-0
lines changed

.gitignore

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
dist
2+
dist-*
3+
cabal-dev
4+
*.o
5+
*.hi
6+
*.chi
7+
*.chs.h
8+
*.dyn_o
9+
*.dyn_hi
10+
.hpc
11+
.hsenv
12+
.cabal-sandbox/
13+
cabal.sandbox.config
14+
*.prof
15+
*.aux
16+
*.hp
17+
*.eventlog
18+
.stack-work/
19+
cabal.project.local
20+
*~

Dockerfile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
FROM ubuntu:16.04
2+
3+
# Get dumb-init to avoid Ctrl-C issues. See:
4+
# http://engineeringblog.yelp.com/2016/01/dumb-init-an-init-for-docker.html
5+
ADD https://github.com/Yelp/dumb-init/releases/download/v1.1.3/dumb-init_1.1.3_amd64 /usr/local/bin/dumb-init
6+
RUN chmod +x /usr/local/bin/dumb-init
7+
8+
# Set up Haskell Stack, the Haskell build tool.
9+
# Stack is the only dependency we have to run our application.
10+
# Once available, it will grab everything else we need
11+
# (compiler, libraries, etc).
12+
ADD https://get.haskellstack.org/get-stack.sh /usr/local/bin/
13+
RUN sh /usr/local/bin/get-stack.sh
14+
15+
# Copy over the source code and make it executable.
16+
COPY FileHandler.hs /usr/local/bin/file-handler
17+
RUN chmod +x /usr/local/bin/file-handler
18+
19+
# Create a new user account and directory to run from, and then
20+
# run everything else as that user.
21+
RUN useradd -m www && mkdir -p /workdir && chown www /workdir
22+
USER www
23+
24+
# We run our application with "sanity" to force it to install all of
25+
# its dependencies during Docker image build time, making the Docker
26+
# image launch much faster.
27+
RUN /usr/local/bin/file-handler sanity
28+
29+
# We're all ready, now just configure our image to run the server on
30+
# launch from the correct working directory.
31+
CMD ["/usr/local/bin/dumb-init", "/usr/local/bin/file-handler"]
32+
WORKDIR /workdir
33+
EXPOSE 5000

FileHandler.hs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env stack
2+
{- stack
3+
--resolver lts-6.11
4+
--install-ghc
5+
runghc
6+
--package shakespeare
7+
--package wai-app-static
8+
--package wai-extra
9+
--package warp
10+
-}
11+
12+
-- The code above is used for Haskell Stack's script interpreter
13+
-- feature. For more information, see:
14+
-- https://docs.haskellstack.org/en/stable/GUIDE/#script-interpreter
15+
--
16+
-- Note how we explicitly list an LTS Haskell snapshot
17+
-- (https://www.stackage.org/lts-6.11) to ensure reproducibility. We
18+
-- then state which packages need to be present to run this code.
19+
20+
-- Enable the OverloadedStrings extension, a commonly used feature.
21+
{-# LANGUAGE OverloadedStrings #-}
22+
23+
-- We use the QuasiQuotes to embed Hamlet HTML templates inside
24+
-- our source file.
25+
{-# LANGUAGE QuasiQuotes #-}
26+
27+
-- Import the various modules that we'll use in our code.
28+
import qualified Data.ByteString.Char8 as S8
29+
import qualified Data.ByteString.Lazy as L
30+
import Data.Functor.Identity
31+
import Network.HTTP.Types
32+
import Network.Wai
33+
import Network.Wai.Application.Static
34+
import Network.Wai.Handler.Warp
35+
import Network.Wai.Parse
36+
import System.Environment
37+
import System.FilePath
38+
import Text.Blaze.Html.Renderer.Utf8
39+
import Text.Hamlet
40+
41+
-- | Entrypoint to our application
42+
main :: IO ()
43+
main = do
44+
-- For ease of setup, we want to have a "sanity" command line
45+
-- argument. We'll see how this is used in the Dockerfile
46+
-- later. Desired behavior:
47+
--
48+
-- * If we have the argument "sanity", immediately exit
49+
-- * If we have no arguments, run the server
50+
-- * Otherwise, error out
51+
args <- getArgs
52+
case args of
53+
["sanity"] -> putStrLn "Sanity check passed, ready to roll!"
54+
[] -> do
55+
putStrLn "Launching DataHandler."
56+
-- Run our application (defined below) on port 5000
57+
run 5000 app
58+
_ -> error $ "Unknown arguments: " ++ show args
59+
60+
-- | Our main application
61+
app :: Application
62+
app req send =
63+
-- Route the request based on the path requested
64+
case pathInfo req of
65+
-- "/": send the HTML homepage contents
66+
[] -> send $ responseBuilder
67+
status200
68+
[("Content-Type", "text/html; charset=utf-8")]
69+
(renderHtmlBuilder homepage)
70+
71+
-- "/browse/...": use the file server to allow directory
72+
-- listings and downloading files
73+
("browse":rest) ->
74+
-- We create a modified request that strips off the
75+
-- "browse" component of the path, so that the file server
76+
-- does not need to look inside a /browse/ directory
77+
let req' = req { pathInfo = rest }
78+
in fileServer req' send
79+
80+
-- "/upload": handle a file upload
81+
["upload"] -> upload req send
82+
83+
-- anything else: 404
84+
_ -> send $ responseLBS
85+
status404
86+
[("Content-Type", "text/plain; charset=utf-8")]
87+
"Not found"
88+
89+
-- | Create an HTML page which links to the /browse URL, and allows
90+
-- for a file upload
91+
homepage :: Html
92+
homepage = [shamlet|
93+
$doctype 5
94+
<html>
95+
<head>
96+
<title>File server
97+
<body>
98+
<h1>File server
99+
<p>
100+
<a href=/browse/>Browse available files
101+
102+
<form method=POST action=/upload enctype=multipart/form-data>
103+
<p>Upload a new file
104+
<input type=file name=file>
105+
<input type=submit>
106+
|]
107+
108+
-- | Use the standard file server settings to serve files from the
109+
-- current directory
110+
fileServer :: Application
111+
fileServer = staticApp (defaultFileServerSettings ".")
112+
113+
-- | Handle file uploads, storing the file in the current directory
114+
upload :: Application
115+
upload req send = do
116+
-- Parse the request body. We'll ignore parameters and just look
117+
-- at the files
118+
(_params, files) <- parseRequestBody lbsBackEnd req
119+
120+
-- Look for the file parameter called "file"
121+
case lookup "file" files of
122+
-- Not found, so return a 400 response
123+
Nothing -> send $ responseLBS
124+
status400
125+
[("Content-Type", "text/plain; charset=utf-8")]
126+
"No file parameter found"
127+
-- Got it!
128+
Just file -> do
129+
let
130+
-- Determine the name of the file to write out
131+
name = takeFileName $ S8.unpack $ fileName file
132+
-- and grab the content
133+
content = fileContent file
134+
-- Write it out
135+
L.writeFile name content
136+
137+
-- Send a 303 response to redirect back to the homepage
138+
send $ responseLBS
139+
status303
140+
[ ("Content-Type", "text/plain: charset=utf-8")
141+
, ("Location", "/")
142+
]
143+
"Upload successful!"

0 commit comments

Comments
 (0)