Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Stream and Buffer based read/write loop.

- We moved to sendfile before node.JS had binary buffers. A read write
  loop is a lot faster now than back when node was converting everything
  to UTF-8 strings. Sendfile isn't needed.

Sendfile(2) was causing all sorts of hassles
- Connections would timeout, then sendfile would try and send out a null
  file descriptor
- Sendfile(2) has crap error messages for when the shit hits the fan.
  'bad argument'
  • Loading branch information...
commit b56f626dddf2f0469eca8a05eaa0bc1ff029afc2 1 parent a6ecb61
Mark Hansen authored

Showing 2 changed files with 30 additions and 73 deletions. Show diff stats Hide diff stats

  1. +29 72 lib/antinode.js
  2. +1 1  package.json
101 lib/antinode.js
@@ -9,6 +9,7 @@ var http = require('http'),
9 9 Script = process.binding('evals').Script;
10 10
11 11 exports.default_settings = {
  12 + "max_bytes_per_read": 4 * 1024,
12 13 "timeout_milliseconds": 1000 * 30,
13 14 "hosts" : {},
14 15 "port" : 8080,
@@ -128,78 +129,34 @@ function handle_request(path, req, resp) {
128 129 });
129 130
130 131 function stream_file(file, stats) {
131   - fs.open(file,'r', 0660, function(err, fd) {
132   - if (err) {
133   - log.debug("fs.open(",file,") error: ",err.message);
134   - return file_not_found();
135   - }
136   - log.debug("opened", path, "on fd", fd);
137   - var fd_open = true;
138   - req.connection.addListener('timeout', function() {
139   - if (fd_open) {
140   - // close file descriptors so we don't run out of them.
141   - // only close the file once. If our timeout handler runs
142   - // after we've finished sending the file, the fd will
143   - // already be closed and it could be reused by another
144   - // request. It would interrupt the other request if we
145   - // closed a fd twice.
146   - close(fd);
147   - fd_open = false;
148   - }
149   - });
150   -
151   - send_headers(200, stats.size, mime.mime_type(file), stats.mtime);
152   - resp._send(''); // HACK force a flush
153   -
154   - send_chunk(0, stats.size, function() {
155   - finish(resp);
156   - close(fd);
157   - fd_open = false;
158   - });
159   -
160   - // For large files, sendfile(2) may only send a small chunk of the
161   - // file (even when we request it send more) due to TCP's flow
162   - // control so we need to keep sending till the whole file's sent.
163   - function send_chunk(offset, bytes_to_write, callback) {
164   - log.debug('sending chunk of', file, offset, bytes_to_write);
165   - if (!req.connection.fd) return callback(); //the connection timed out
166   - fs.sendfile(req.connection.fd, fd, offset, bytes_to_write,
167   - function (err, bytes_written) {
168   - if (err) {
169   - switch (err.errno) {
170   - case process.EAGAIN:
171   - // write would have blocked, try again later
172   - bytes_written = 0;
173   - break;
174   -
175   - default:
176   - log.error("sendfile(",file,") failed: ",err.message);
177   - // fall through
178   -
179   - case process.EBADF:
180   - // don't write a log message, the timeout handler
181   - // already did
182   - callback();
183   - return;
184   - }
185   - }
186   - if (bytes_written > 0) {
187   - // this isn't an idle connection. Bump the connection
188   - // timeout. Usually this would be done for us in the
189   - // response.write() call but we use sendfile(), so we
190   - // need to update this manually.
191   - req.connection.setTimeout(settings.timeout_milliseconds);
192   - }
193   - bytes_to_write -= bytes_written;
194   - offset += bytes_written;
195   - if (bytes_to_write < 1) {
196   - callback();
197   - }
198   - else {
199   - send_chunk(offset, bytes_to_write, callback);
200   - }
201   - });
202   - }
  132 + try {
  133 + var readStream = fs.createReadStream(file);
  134 + }
  135 + catch (err) {
  136 + log.debug("fs.createReadStream(",file,") error: ",sys.inspect(err));
  137 + return file_not_found();
  138 + }
  139 +
  140 + req.connection.addListener('timeout', function() {
  141 + log.debug('timed out. destroying file read stream');
  142 + readStream.destroy();
  143 + });
  144 +
  145 + log.debug("opened",path);
  146 + send_headers(200, stats.size, mime.mime_type(file), stats.mtime);
  147 + req.connection.addListener('drain', function() {
  148 + // it is safe to write
  149 + });
  150 + readStream.addListener('data', function (data) {
  151 + // send it out
  152 + resp.write(data);
  153 + });
  154 + readStream.addListener('error', function (err) {
  155 + log.error('error reading',file,sys.inspect(err));
  156 + finish(resp);
  157 + });
  158 + readStream.addListener('end', function () {
  159 + finish(resp);
203 160 });
204 161 }
205 162
2  package.json
... ... @@ -1,6 +1,6 @@
1 1 { "name" : "antinode"
2 2 , "description" : "A simple web server for node.js"
3   -, "version" : "2.0.1"
  3 +, "version" : "2.0.3"
4 4 , "author" : "Mark Hansen <mark@markhansen.co.nz>"
5 5 , "contributors" :
6 6 [ "Ben Noordhuis <info@bnoordhuis.nl>"

0 comments on commit b56f626

Please sign in to comment.
Something went wrong with that request. Please try again.