I would like to write an HTTP server that answer to request using a non-standard HTTP method (verb). For instance, the client would make a request like FOO / HTTP/.1.1. And on the server side, this request would be handled by something like:
var express = require('express');
var app = express.createServer();
app.configure(function(){
app.use(express.logger({ format: ':method :url' }));
app.use(express.methodOverride());
});
app.foo('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
I appended my non-standard method to the array exported in ExpressJS's lib/router/methods.js. This allow me to write my server code as expected. When using express.methodOverride() and a POST request with _method=foo, it works. But an actual FOO request doesn't work. As soon as the client send the first line of the request the connection is closed by the server:
$telnet localhost 3000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
FOO / HTTP/1.1
Connection closed by foreign host.
I would like to be able to implement this with ExpressJS and without avoid hacking into its core file.
Any idea if this is possible and how?
Short answer: No, it's not possible. Not without implementing your own HTTP module.
To test, start a barebones HTTP server ...
$ node
> require('http').createServer(function(req, res) {
... console.log(req.method);
... res.end();
... }).listen(8080);
Then (as you've already done) telnet to it and issue a GET and FOO request ...
$ telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked
0
FOO / HTTP/1.1
Connection closed by foreign host.
$
In node console you'll see
GET
... but no FOO. So, node's native HTTP module, which Express uses, does not make these requests available.
Node has a hard-coded whitelist of acceptable HTTP verbs in C.
In order to accept custom verbs, you must modify the HTTP parser and recompile node.
You mentioned that you're trying to implement PURGE, which was added to the whitelist in v0.7.5.
As others have said, Node.js' HTTP server library is configured to accept only specific verbs. Ben Noordius' suggestion of using Parsley doesn't work either, since that library accepts an even smaller whitelist of verbs. (It also hasn't been maintained in quite some time.)
At this stage, if we want to support oddball requests, we have to take more drastic measures. Here's a nice ugly hack for you that involves duck punching some internal behavior. This works on v0.10.x of Node.js, but test carefully on newer versions as they become available.
In my case, I needed to support not only a non-standard verb, but a non-standard protocol version identifier as well, and a missing Content-Length header for Icecast source streams:
SOURCE /live ICE/1.0
The following should get you started:
server.on('connection', function (socket) {
var originalOnDataFunction = socket.ondata;
var newLineOffset;
var receiveBuffer = new Buffer(0);
socket.ondata = function (d, start, end) {
receiveBuffer = Buffer.concat([receiveBuffer, d.slice(start, end)]);
if ((newLineOffset = receiveBuffer.toString('ascii').indexOf('\n')) > -1) {
var firstLineParts = receiveBuffer.slice(0, newLineOffset).toString().split(' ');
firstLineParts[0] = firstLineParts[0].replace(/^SOURCE$/ig, 'PUT');
firstLineParts[2] = firstLineParts[2].replace(/^ICE\//ig, 'HTTP/');
receiveBuffer = Buffer.concat([
new Buffer(
firstLineParts.join(' ') + '\r\n' +
'Content-Length: 9007199254740992\r\n'
),
receiveBuffer.slice(newLineOffset +1)
]);
socket.ondata = originalOnDataFunction;
socket.ondata.apply(this, [receiveBuffer, 0, receiveBuffer.length]);
}
};
}
It's ugly, but works. I'm not particularly happy about it, but when choosing between a rough built-from-the-ground-up HTTP parser or tweaking an existing one, I choose to tweak in this instance.
For anyone who needs it, there is http-parser-js, which replaces Node's built-in HTTP parser.
Their README contains an example of monkey-patching the parser, though I find that it wasn't enough, as both the http-parser-js and the http modules have a hardcoded list of methods.
So, you have to replace the parser and edit the list of methods:
const { HTTPParser } = require('http-parser-js');
HTTPParser.methods.push('FOOBAR');
const binding = process.binding('http_parser');
binding.HTTPParser = HTTPParser;
binding.methods = HTTPParser.methods;
require('http').METHODS = HTTPParser.methods;
Later Node versions may not support process.binding, in which case, you can use the --expose-internals flag for Node (see this issue):
const { internalBinding } = require('internal/test/binding');
const binding = internalBinding('http_parser');
From the looks of it, the http2 module's parser accepts any method, in case that's an option. See this issue about invalid HTTP methods. Unfortunately, express and the likes do not use http2.
And for anyone who was in my shoes, proxying requests to a legacy server in Create React App, use the above snippet in webpack-dev-server, at the top of Server.js, in order to monkey-patch the parser. Hopefully everything switches to http2 soon...
Related
Need help in wiring the grpc client/server to listen to rest requests. Want to execute a post request
Any help is much appreciated..
digestor.proto
import "google/api/annotations.proto";
service Digestor{
rpc GetDigestor(DigestMessage) returns (DigestedMessage) {}
}
service DigestorRest {
rpc GetDigestor(DigestMessage) returns (DigestedMessage) {
option (google.api.http) = {
get: "/v1/digest"
body: "*"
};
}
}
after the pb2 files get generated.
grpc - request works perfectly ok
curr_client = DigestorClient()
print(curr_client.get_digest("Test Message"))
My rest/get request:
requests.get(url='http://localhost:46001/v1/digest')
should return the correct response however I get
requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x04\x00#\x00\x00\x00\x05\x00#\x00\x00\x00\x06\x00\x00 \x00รพ\x03\x00\x00\x00\x01\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00?\x00\x01\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
I presume you are using the GCP Transcoding feature. If you haven't tried the tutorial, please try to go through the example steps.
The gRPC protocol is based on HTTP2 instead of HTTP1. The requests library is an HTTP1. The exception reported by requests looks like protocol mismatch. So, it is likely that there is an issue in your GCP Endpoints setting.
I try to implement a websocket client (with libwebsockets in C and it's not an option). As example, i used the test-client.c given with the library.
My websocket-client actualy works fine with the test-server.c . But i experienced some complications to communicate with an nginx server.
As i understand, the handshack doesn't end up well because nginx doesn't know my websocket client's sub-protocol.
Well, it appears, like in test-client.c i'm implementing my own sub-protocol (with its own name, its own callback function).
My questions are :
Is there a way to not use a specific websocket sub-protocol with libwebsockets ?
If not, am i supposed to implement an existing one (client side) like WAMP or something in this list? (I do not want to reinvent the wheel...)
If not, does it exist a "default" websocket subprotocol that i can specify to nginx and in which it could be compatible with my websocket-client ? (I'm only doing some simple send/receive action with my client. Implementing a libwebsockets client seems completly useless if it can only communicate with a libwebsockets server)
Are my questions relevant? If not why ? What am i missing ?
Any help is appreciated.
Thanks!
As discussed with Andy, the libwebsockets designer (https://github.com/warmcat/libwebsockets/issues/834), the simpliest way to make it works is to not named the sub-protocol in the structure defining the websocket sub-protocol client's side :
/* list of supported sub-protocols and callbacks */
static struct lws_protocols ws_protocols[] = {
{ NULL, ws_callback, 0, 128, },
{ NULL, NULL, 0, 0 } /* end */
};
Libwebsockets client doesn't try to negociate with sec-websocket-protocol in the header, the handshake works just fine with nginx.
I've wondered how to identify the current protocol if it's using websocket or polling.
-- in the client. (appended for certainty)
I've found a valid information from the debug console.
Meteor.connection._stream.socket.protocol
and it seems to have one value among...
['websocket',
'xdr-streaming',
'xhr-streaming',
'iframe-eventsource',
'iframe-htmlfile',
'xdr-polling',
'xhr-polling',
'iframe-xhr-polling',
'jsonp-polling'];
is there more grace way to identify the current protocol?
and when would it be the fastest timing to detect the protocol?
By the way, I need it to use a different DDP server when the sticky sessions needed since AWS ELB doesn't support sticky sessions and websocket at the same time.
Meteor uses DDP protocol. To connect to a different Meteor server and call its methods, use DDP.connect as follows.
import { DDP } from 'meteor/ddp-client'
DDP.connect(url)
Unfortunately, there is no graceful to get the protocol. onConnection returns an object which has some info.
Meteor.onConnection(obj =>
{ console.log(obj.httpHeaders.x-forwarded-proto); });
This returns 'ws' for websocket. This way of getting the protocol is not graceful!
Meteor.status() gives a reactive data source.
(https://docs.meteor.com/api/connections.html#DDP-connect)
if (Meteor.isClient) {
Tracker.autorun(() => {
const stat = Meteor.status();
if (stat.status === 'connected') {
console.log(Meteor.connection._stream.socket.protocol);
}
});
}
something like that will give the current protocol in the client side.
I am trying to use SockJS from my Meteor to connect to another service but I can't get a reference to SockJS within meteor client or server. Does anyone have a good example of using SockJS to connect to other service or streaming API's from Meteor?
I have tried to accomplish this two ways but 'socket' is always undefined:
var socket = sockjs.createServer({ sockjs_url: 'http://api.hitbtc.com:8081' });
socket.onmessage = function(msg) {
var data = JSON.parse(msg.data);
console.log("CONNECTED!!" + data)
};
var socket = new SockJS('http://api.hitbtc.com:8081');
socket.onmessage = function(msg) {
var data = JSON.parse(msg.data);
console.log("CONNECTED!!" + data)
};
Even though SockJS is used by the Meteor itself it's hidden deeply inside the ddp package and it's not really exposed to the users. So basically, you have two options here:
You can either put another copy of SockJS into your app, ...
... or you can teach your custom server to understand DDP protocol, then you will be able to use DDP.connect to establish a new connection.
The second solution does not make sense of course if you are using 3rd party service. The first solution feels ugly because of the data redundancy, but I am afraid it's the only way out if 2. is not acceptable.
In the server:
execute Npm.require('./') and observe the path informed in the error, from it you can point to the packages from the depths of Meteor, in the case of SockJS the path (in version 1.10.2 of Meteor) is:
Npm.require('./meteor/ddp-server/node_modules/sockjs');
In the specific case of Sockjs, its use is slightly different from that presented on the Github page, as follows:
const sockjs = Npm.require('./meteor/ddp-server/node_modules/sockjs');
const echo = sockjs.listen(WebApp.httpServer, {prefix: '/echo'});
echo.on('connection', function(conn) {
conn.on ('data', function(message) {
conn.write(message);
});
conn.on('close', function(){});
});
I didn't find the sockjs "client" package in these files, because the sockjs-client package is specific to the browser. So I downloaded from the CDN that the "echo" output provided, I use "--exclude-archs web.browser.legacy" in my test environment, but from what I read out there the sockjs-client package is available if you don't use it this parameter.
Sockjs relies on "faye-websocket" which has both a client and a websocket server designed to run on NodeJs, here is the suggestion.
Ps: I didn't find an equivalent form on the client side (there is no Npm.require)
I am looking to create a websocket on Meteor Server (not client) to connect to an external site. I know the URL I am going to be hitting as well as what data to expect, but I am unclear as to how exactly to create the websocket itself. All the searching I do presents me with solutions for the client, but I have yet to run into anything that serves as a server solution.
Is there anything out there I missed that fills this purpose? Atmosherejs.com doesn't list anything, and searching around on google/github didn't reveal anything either. Is there something built into Meteor that already accomplishes this?
The following code is for opening a Socket in Meteor on Port 3003. It convert the data from the socket (sendet from client) to a JSON-Object. So this means, the following code is a socket, which receive JSON.
Fiber = Npm.require('fibers')
// server
Npm.require('net').createServer(function (socket) {
console.log("connected");
socket.on('data', function (data) {
socket.write("hello!");
var o = JSON.parse(data.toString());
console.log(o);
Fiber(function() {
console.log('Meteor code is executing');
//=> Meteor code
}).run();
//console.log(data.toString());
//socket.close();
});
})
.listen(3003);