diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+# Changelog
+
+`http-directory` uses [PVP Versioning](https://pvp.haskell.org).
+
+## 0.1.0
+- initial release with httpDirectory, httpFileSize, and httpRedirect(s)
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Jens Petersen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+# http-directory
+
+[![Hackage](https://img.shields.io/hackage/v/http-directory.svg)](https://hackage.haskell.org/package/http-directory)
+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
+[![Stackage Lts](http://stackage.org/package/http-directory/badge/lts)](http://stackage.org/lts/package/http-directory)
+[![Stackage Nightly](http://stackage.org/package/http-directory/badge/nightly)](http://stackage.org/nightly/package/http-directory)
+[![Build status](https://secure.travis-ci.org/juhp/http-directory.svg)](https://travis-ci.org/juhp/http-directory)
+
+A simple library for reading http directories.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/http-directory.cabal b/http-directory.cabal
new file mode 100644
--- /dev/null
+++ b/http-directory.cabal
@@ -0,0 +1,45 @@
+cabal-version:       1.18
+name:                http-directory
+version:             0.1.0
+synopsis:            http directory listing library
+description:
+            Library for listing http directories of files (links).
+            Also it can check the size of files, and url redirects.
+homepage:            https://github.com/juhp/http-directory
+bug-reports:         https://github.com/juhp/http-directory/issues
+license:             MIT
+license-file:        LICENSE
+author:              Jens Petersen
+maintainer:          juhpetersen@gmail.com
+copyright:           2019 Jens Petersen
+category:            Network
+build-type:          Simple
+extra-doc-files:     README.md
+                   , CHANGELOG.md
+tested-with:         GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2,
+                     GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.4
+
+source-repository head
+  type:                git
+  location:            https://github.com/juhp/http-directory.git
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Network.HTTP.Directory
+                       
+
+  build-depends:       base < 5,
+                       bytestring,
+                       html-conduit,
+                       http-client,
+                       http-types,
+                       text,
+                       xml-conduit
+  if impl(ghc<8.0)
+      Build-depends: semigroups
+
+  ghc-options:         -fwarn-missing-signatures
+                       -Wall
+
+  default-language:    Haskell2010
+  default-extensions:  OverloadedStrings
diff --git a/src/Network/HTTP/Directory.hs b/src/Network/HTTP/Directory.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/HTTP/Directory.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE CPP #-}
+
+{-|
+A library for listing "files" in an http "directory".
+
+@
+import Network.HTTP.Directory
+import qualified Data.Text as T
+
+main = do
+  files <- httpDirectory "https://example.com/some/dir/"
+  mapM_ T.putStrLn files
+  httpFileSize (head files) >>= print
+@
+-}
+
+module Network.HTTP.Directory
+       ( httpDirectory,
+         httpFileSize,
+         httpRedirect,
+         httpRedirects
+       ) where
+
+#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0))
+#else
+import Control.Applicative ((<$>))
+#endif
+
+import qualified Data.ByteString.Char8 as B
+import Data.Maybe
+import Data.Text (Text)
+
+import Network.HTTP.Client (hrRedirects, httpLbs, httpNoBody, Manager, method,
+                            parseRequest, responseBody, responseHeaders,
+                            responseOpenHistory, responseStatus)
+import Network.HTTP.Types (hContentLength, hLocation, statusCode)
+
+import Text.HTML.DOM (parseLBS)
+import Text.XML.Cursor
+
+-- | List the file links (hrefs) in an http directory
+--
+-- Note if the directory (webpage) url is redirected you may need to use
+-- 'httpRedirect' to determine the actual final url prefix for relative links
+-- (files).
+httpDirectory :: Manager -> String -> IO [Text]
+httpDirectory mgr url = do
+  request <- parseRequest url
+  response <- httpLbs request mgr
+  if statusCode (responseStatus response) /= 200
+    then error $ show $ responseStatus response
+    else do
+    let body = responseBody response
+        doc = parseLBS body
+        cursor = fromDocument doc
+    return $ concatMap (attribute "href") $ cursor $// element "a"
+
+-- | Try to get the filesize (Content-Length) of an http file
+httpFileSize :: Manager -> String -> IO (Maybe Integer)
+httpFileSize mgr url = do
+  request <- parseRequest url
+  response <- httpNoBody (request {method = "HEAD"}) mgr
+  if statusCode (responseStatus response) /= 200
+    then error $ show $ responseStatus response
+    else do
+    let headers = responseHeaders response
+    return $ read . B.unpack <$> lookup hContentLength headers
+
+-- | Returns the list of http redirects for an url in reverse order
+-- (ie last redirect is first)
+httpRedirects :: Manager -> String -> IO [B.ByteString]
+httpRedirects mgr url = do
+  request <- parseRequest url
+  respHist <- responseOpenHistory (request {method = "HEAD"}) mgr
+  return $ reverse $ mapMaybe (lookup hLocation . responseHeaders . snd) $ hrRedirects respHist
+
+-- | Return final redirect for an url
+httpRedirect :: Manager -> String -> IO (Maybe B.ByteString)
+httpRedirect mgr url =
+  listToMaybe <$> httpRedirects mgr url
