Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding Node.js HTTP parser

I am using Node's basic http.request() function without problem on normal HTTP servers. I need to use http.request() (or similar) with SHOUTcast servers. The SHOUTcast "protocol" is fully compatible with HTTP, except for one detail... the first response line.

Normal HTTP servers respond with:

HTTP/1.1 200 OK

SHOUTcast servers respond with:

ICY 200 OK

Again, the rest of the protocol is the same. The only difference is HTTP/1.x vs. ICY.

I would like to extend, subclass, or somehow modify Node's http.request() function so that I can make it work with SHOUTcast servers. Connecting to SHOUTcast with Node has been done before, but only by re-inventing the entire wheel. I'd rather not do that for what amounts to be a minor protocol difference.

My Question: Is there a way to do one of the following?

  1. Extend or override the relevant parts of Node's HTTP parser. (I doubt this is possible, as it seems the parser is native code.)

  2. Create my own bit of code that parses the relevant parts of HTTP, but reuses as many existing Node components for HTTP as possible.

  3. Create a simple internal proxy (or somehow relay data) so that I can modify that first server response line prior to it reaching Node's HTTP parser.

  4. Something else?

I have also considered using Shred, but it does not offer an option to stream the response. (It waits for the entire server response to be complete before firing an event, which won't work for streaming servers where the data can run indefinitely.) Along those same lines, I tried Request, but it uses Node's own HTTP parser, so I get the same parse errors as I would with the native HTTP client.

like image 685
Brad Avatar asked Mar 26 '12 23:03

Brad


People also ask

How do I override a node JS method?

override using a query value To use a query string value to override the method, specify the query string key as a string argument to the methodOverride function. To then make the call, send a POST request to a URL with the overridden method as the value of that query string key.

What is HTTP createServer?

The http. createServer() method turns your computer into an HTTP server. The http. createServer() method creates an HTTP Server object. The HTTP Server object can listen to ports on your computer and execute a function, a requestListener, each time a request is made.

What is setHeader in node JS?

setHeader(name, value) (Added in v0. 4.0) method is an inbuilt application programming interface of the 'http' module which sets a single header value for implicit headers. If this header already exists in the to-be-sent headers, its value will be replaced.

What does res writeHead do?

The res. writeHead method is for returning a status code to the browser, and the browser will throw an error if it is a client-side status code or server-side status code.


1 Answers

I've come up with another way to do this, similar to the internal proxy, but without the extra connection. It seems that it is possible to override the internally used socket for the HTTP client. When this is done, it is easy to hook in and modify data before passing it off to the original internal socket ondata function.

Usage

var httpicy = new HttpIcyClient();
httpicy.request(/* your normal request parameters here */);

Source

var http = require('http');

var HttpIcyClient = function () {};
HttpIcyClient.prototype.request = function (options, callback) {
    var req = http.request(options, callback),
        originalOnDataFunction,
        receiveBuffer = new Buffer(0);

    req.on('socket', function (socket) {
        originalOnDataFunction = socket.ondata;
        socket.ondata = function (d, start, end) {
            receiveBuffer = Buffer.concat([receiveBuffer, d.slice(start, end)]);
            if (receiveBuffer.length >= 4) {
                socket.ondata = originalOnDataFunction;
                if (receiveBuffer.toString('ascii', 0, 4) === 'ICY ') {
                    receiveBuffer = Buffer.concat([new Buffer('HTTP/1.0 ', 'ascii'), receiveBuffer.slice(4)]);
                }
                socket.ondata.apply(this, [receiveBuffer, 0, receiveBuffer.length]);
            }
        };
    });
    return req;
}
like image 78
Brad Avatar answered Oct 12 '22 18:10

Brad