简体   繁体   中英

Custom node.js web server doesn't finish loading CSS and JavaScript

For self-betterment, I'm trying to write a pure node.js web server. The idea is to completely avoid using pre-written middleware or any non-default node.js modules for loading static HTML, CSS, and JavaScript, and force myself to learn all of the ins and outs of doing it the purist node.js way.

I've got it up and running, and tested it a few times, and it seemed to be working great until I decided to stress test it a bit by hitting refresh quickly on a static page a few times.

The first 3-4 reloads work fine. After that, none of the CSS or JavaScript files called by the static HTML file load.

Here's my file reading code:

fs.readFile(filename, function(err, contents) {
    if(err) {
        throw err;
    } else {
    res.setHeader("Content-Length", contents.length);
        res.setHeader("Content-Type", validExtensions[ext]);
        res.statusCode = 200;
        res.write(contents);
        res.end();
    }
});

( validExtensions is just an array of paired extensions and their appropriate mime types.)

This function will be called three times during a page load when there's the HTML file, a CSS file, and a JavaScript file. Again, it works fine until it suddenly doesn't. Then, if I refresh once more, everything is fine again.

I suspect what's going on is that sometimes it finishes the HTML file before the CSS or JavaScript file has finished.

The only workarounds I can think of are pretty nasty. I could create a separate function for processing all files that aren't HTML, and having that process keep a count of the remaining files. Then I could use a while loop in the function that processes HTML and prevents it from doing res.end() until that count is zero, but that sounds both ugly and possibly resource-draining.

Or I could use the (even worse) workaround of loading all non-HTML files synchronously, but since that call would be from within an otherwise-asynchronously processing function, I really don't want to do that.

If I were just trying to build a static site, I'd use http-server or express or one of the many excellent solutions out there for serving static files, but they all have extensive module dependencies, and I'm trying to get this done without them.

Is there an elegant and purist way to ensure res.end() doesn't happen for the HTML being loaded until all non-HTML files are finished doing their things?

Edit : So anyone can see the entire context, the whole project, what there is of it, is up on github here: https://github.com/GoodDamon/customserver

Your issue lies with customServer.url . If you were to open your browser's dev tools and examine the contents of app.css , you'd find that occasionally it gets populated with only the following line:

console.log("CustomServer successfully loaded!");

...which happens to be the same as the contents of your app.js file. Coincidence? Nope. If you take a look at your node console output, from time to time you'll see this:

10/11/2015 5:42:09 PM: New client request: /css/app.css - Method: GET 
10/11/2015 5:42:09 PM: Client requested a file. Testing extension validity... 
10/11/2015 5:42:09 PM: File extension .css is VALID. Testing availability... 
10/11/2015 5:42:09 PM: New client request: /js/app.js - Method: GET 
10/11/2015 5:42:09 PM: Client requested a file. Testing extension validity... 
10/11/2015 5:42:09 PM: File extension .js is VALID. Testing availability... 
10/11/2015 5:42:09 PM: File is AVAILABLE. Providing to client. 
10/11/2015 5:42:09 PM: File is AVAILABLE. Providing to client.

As you can see, the browser is requesting app.css first, immediately followed by app.js . Because both "Providing to Client" lines are at the bottom, you know that your node server was not done processing the first request. What the client ends up receiving is app.js twice, sent as both a .css and a .js file.

Why is this? Each client request that hits the node server should be theoretically be isolated to it's own event loop. There should be no cross-contamination. The issue lies with customServer.url , which is what you are assigning the incoming req.url values to. Because it lies outside the scope of both client requests, every request overwrites the value of the previous.

This would not be an issue with synchronous programming, but node.js is completely async and will execute all requests as fast as it receives them, regardless if there are any requests still pending. Your fs.stat() call takes a bit of time to execute and there's a good chance that another request will have hit the server before the success/error callback is executed.

tl;dr: This line- customServer.getFile(customServer.url, res, ext); sends the same file to 2 different requests because you are overwriting customServer.url with every new request. Track your request URL within the scope of the request!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM