FlyingFox icon indicating copy to clipboard operation
FlyingFox copied to clipboard

Directory Handler for static files

Open gotev opened this issue 2 years ago • 2 comments

First of all, thanks for this really good library!

With this setup:

let server = HTTPServer(port: 8080)

let root = URL(fileURLWithPath: "/Users/myUser/myStaticDir")

let route = HTTPRoute(method: .GET, path: "/static/*")
let handler = DirectoryHTTPHandler(root: root, serverPath: "static")

await server.appendRoute(route, to: handler)

try await server.start()

When I perform:

curl -L -vv http://localhost:8080/static/index.html

I'm always getting a 404.

That's because: https://github.com/swhitty/FlyingFox/blob/8ef9eddda8f4ea370233454eded2ceded1fb76ee/FlyingFox/Sources/Handlers/DirectoryHTTPHandler.swift#L51 never matches, because request.path will always be /static/index.html, so I find this example to be wrong: https://github.com/swhitty/FlyingFox#directoryhttphandler

To make it work I had to write the handler like this:

let handler = DirectoryHTTPHandler(root: root, serverPath: "/static/")

But then I found out that if I want to also handle the default file to be index.html when not explicitly specifying it:

curl -L -vv http://localhost:8080/static/

or

curl -L -vv http://localhost:8080/static

I have to also add:

let fileRoute = HTTPRoute(method: .GET, path: "/static")
let fileHandler = FileHTTPHandler(path: root.appendingPathComponent("index.html"), contentType: "text/html")
await server.appendRoute(fileRoute, to: fileHandler)

Then I thought this is a very common scenario one would expect to work with minimal work, so I written this extension:

extension HTTPServer {
    func appendStaticRoute(_ route: String, root: URL, defaultFileName: String = "index.html", withContentType contentType: String = "text/html") {
        appendRoute(HTTPRoute(method: .GET, path: "/\(route)/*"), to: DirectoryHTTPHandler(root: root, serverPath: "/\(route)/"))
        appendRoute(HTTPRoute(method: .GET, path: "/\(route)/"), to: FileHTTPHandler(path: root.appendingPathComponent(defaultFileName), contentType: contentType))
        appendRoute(HTTPRoute(method: .GET, path: "/\(route)"), to: .redirect(to: "/\(route)/"))
    }

    func appendStaticRoute(_ route: String, bundle: Bundle = .main, defaultFileName: String = "index.html", withContentType contentType: String = "text/html") throws {
        guard let root = bundle.resourceURL else { throw HTTPUnhandledError() }
        appendStaticRoute(route, root: root, defaultFileName: defaultFileName, withContentType: contentType)
    }
}

Which allows you to achieve the same with only:

let server = HTTPServer(port: 8080)

let root = URL(fileURLWithPath: "/Users/myUser/myStaticDir")

await server.appendStaticRoute("static", root: root)

try await server.start()

And so here I am, posting this. If you think it's a valuable addition, feel free to add it in the library 🍻

gotev avatar Dec 09 '22 19:12 gotev

Excellent! Just what I needed! Many thanks.

dom-do-more avatar Mar 04 '23 19:03 dom-do-more

Thank you for reporting this discrepancy in the docs and annoyance.

I have merged a change that is more lenient when processing the serverPath making the leading and trialing / delimiters optional. https://github.com/swhitty/FlyingFox/pull/55

I will have a think about severing a default file within the handler.

swhitty avatar Mar 05 '23 05:03 swhitty