Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StaticFileHandler weird behaviour on old browsers #33

Open
xavierpantet opened this issue Sep 9, 2019 · 9 comments
Open

StaticFileHandler weird behaviour on old browsers #33

xavierpantet opened this issue Sep 9, 2019 · 9 comments

Comments

@xavierpantet
Copy link

Hello,

I am developing a web platform that I need to work with iPads running iOS9.
I tried accessing it with Safari, Firefox and Chrome but it seems to me that StaticFileHandler does not work properly. Indeed, some of my JS and CSS files and my images are not always loaded. I inspected the requests sent by the browser and it seems that some of them randomly never complete, then the browser waits indefinitely for the response.

I did not have any problems with newer versions of iOS. I also tried to load the resources from an Apache server and it works fine.

Do you have any idea why this happens?
Thanks!

@kjessup
Copy link
Member

kjessup commented Sep 9, 2019

Ensure you are passing allowResponseFilters: true when you make the StaticFileHandler. If you are using TLS or compression then this needs to be true. I think it does this automatically when TLS is in effect but SFH doesn't know about content compression, so it needs to be set explicitly.

https://github.com/PerfectlySoft/PerfectTemplate/blob/master/Sources/PerfectTemplate/main.swift#L41

@xavierpantet
Copy link
Author

allowResponseFilters is indeed set to true.

I also encounter this kind of errors:

[Error] Did not parse stylesheet at 'someFile.css' because non CSS MIME types are not allowed in strict mode.

Also, some of my JS files are sometimes incorrectly decoded.

I wonder if this can be related to the cache because it eventually works after I refresh the page multiple times without changing anything in my code.

@xavierpantet
Copy link
Author

Update: I dug a little bit deeper using Wireshark and discovered an interesting phenomenon.

I think Perfect might be mixing responses and requests. Indeed, I realised that I sometimes get a JS file as response to a PNG request... any idea why?

Again, it all works fine if I load my resources from an Apache server, which makes be think that the problem indeed comes from Perfect and not the client.

Thanks!

@kjessup
Copy link
Member

kjessup commented Sep 11, 2019

Do you have content compression enabled? If so, remove that filter and see if it has an effect. I'm just trying to eliminate certain causes at this point. If you can, paste in your code for launching the server, just so I can see if there's anything unusual there.

@xavierpantet
Copy link
Author

I tried both, with and without compression. It has no effect.

Sure thing, here is the code:

let server = HTTPServer()
let sessionDriver = SessionMemoryDriver()

let routes = [
    Route(method: .get, uri: "/**", handler: try! HTTPHandler.staticFiles(data: [
        "documentRoot": "./webroot",
        "allowResponseFilters": true
    ])),
    Route(method: .get, uri: "/", handler:homeController.indexAction),
     // more non-static files routes...
]

server.setRequestFilters([sessionDriver.requestFilter])
server.setResponseFilters([sessionDriver.responseFilter, (Finalizer(), .high)])
/*
    With compression:
    server.setResponseFilters([sessionDriver.responseFilter, (Finalizer(), .high), (try PerfectHTTPServer.HTTPFilter.contentCompression(data: [:]), HTTPFilterPriority.high)])
*/
server.serverPort = 8181
server.addRoutes(routes)
server.serverName = "localhost"
try! server.start()

Here is my response filter (Finalizer):

class Finalizer: HTTPResponseFilter {
    
    public func filterHeaders(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
        if 400...500 ~= response.status.code {
            let pageContent = "An error occured"
            response.setBody(string: pageContent)
            response.setHeader(.contentLength, value: String(pageContent.count))
            callback(.done)
        }
        else {
            callback(.continue)
        }
    }
    
    public func filterBody(response: HTTPResponse, callback: (HTTPResponseFilterResult) -> ()) {
        callback(.continue)
    }
}

I hope this helps!
Thank you!

@kjessup
Copy link
Member

kjessup commented Sep 13, 2019

I'm at a bit of a loss here. I don't know of anything that would cause what you are experiencing and was unable to reproduce the issue with your code. I banged away on an iOS 9.3.5 iPad and never saw any anomalies with the requests/responses.

Can you check the package versions you are working with? Should be HTTPServer@3.0.23, HTTP@3.3.0, PerfectSession@3.1.5.

@xavierpantet
Copy link
Author

Thank you for your reply!

I am indeed using HTTPServer@3.0.23 and PerfectSession@3.1.5. However HTTPServer@3.0.23 is targeting HTTP@3.0.12, hence this is the version I am using instead of 3.3.0.

I managed to design a minimal server needed to reproduce the bug.

Here is my Package.swift:

// swift-tools-version:4.2

import PackageDescription

let package = Package(
    name: "RunnifySwift",
    dependencies: [
        .package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", from: "3.0.23"),
        .package(url: "https://github.com/PerfectlySoft/Perfect-Mustache.git", from: "3.0.2")
    ],
    targets: [
        .target(
            name: "RunnifySwift",
            dependencies: ["PerfectHTTPServer", "PerfectMustache"]),
        .testTarget(
            name: "RunnifySwiftTests",
            dependencies: ["RunnifySwift"]),
    ]
)

My server contains 2 Swift files:
main.swift:

import PerfectHTTP
import PerfectHTTPServer
import PerfectMustache
import Foundation

    
let server = HTTPServer()
let WEBROOT = "[path-to-webroot]"

func handlePage(_ controller: @escaping (HTTPRequest) throws -> (String, [String: Any])) -> (HTTPRequest, HTTPResponse) -> Void {
    return { req, res in
        do {
            let (view, data) = try controller(req)
            mustacheRequest(request: req, response: res, handler: PageHandler(values: data, sessionData: [:]), templatePath: WEBROOT + view)
        }
        catch(let error) {
            res.setBody(string: error.localizedDescription)
            res.completed(status: .internalServerError)
        }
    }
}

func indexAction(request: HTTPRequest) throws -> (String, [String: Any]) {
    return ("pages/Index/index.html.mustache", ["listRaces": []])
}

let routes = Routes([
    Route(method: .get, uri: "/**", handler: try! HTTPHandler.staticFiles(data: [
        "documentRoot": WEBROOT,
        "allowResponseFilters": true
        ])),
    Route(method: .get, uri: "/", handler: handlePage(indexAction))
])

server.serverPort = 80
server.addRoutes(routes)
server.serverName = "localhost"
try! server.start()

And PageHandler.swift:

import Foundation
import PerfectMustache
import PerfectHTTP

struct PageHandler: MustachePageHandler {
    let values: MustacheEvaluationContext.MapType
    let sessionData: [String: Any]
    
    func extendValuesForResponse(context contxt: MustacheWebEvaluationContext, collector: MustacheEvaluationOutputCollector) {
        contxt.extendValues(with: values)
        contxt.extendValues(with: sessionData)
        do{ try contxt.requestCompleted(withCollector: collector) }
        catch {
            let response = contxt.webResponse
            response.status = .internalServerError
            response.setBody(string: "\(error)")
            response.completed()
        }
    }
}

You can find the content of my webroot here (I removed everything that is not necessary).

I am running the server on an Early-2015 MacBook Pro (macOS Mojave 10.14.6) and the bug occurs when I try to access the root of the website from Safari, Firefox and Google Chrome running on both an iPad 2 and iPad 3 running iOS 9.5.3 (latest available version for both models).

Note that the bug does not occur every time. Best way to trigger it is to empty the cache and reload the page. If it does not occur, it is sometimes necessary to repeat the operation 3-4 times.

I hope it helps! 😄
Thank you!

@kjessup
Copy link
Member

kjessup commented Sep 16, 2019

That was very helpful, thank you. I am able to reproduce the problem and will report back when I get to the bottom of what's happening.

@kjessup
Copy link
Member

kjessup commented Sep 17, 2019

So… still not sure exactly what's happening, but I have been able to eliminate several possibilities. It's not mustache; I removed that and just serve the static index page and the problem is still there. It's nothing internal to Perfect mixing up the requests/responses. I added some debugging there and the proper files are being read and returned with the proper mime type and content-length.

The strangest bit though is that if I comment out <script src="/vendors/fontawesome/js/all.js"></script> it all loads fine. I switched it to use all.min.js and it still hangs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants