I'm teaching myself Coffeescript/node and, of course, the only way to do this is with TDD. That means I'm also teaching myself vows. There are, I think, at least two problems. One is -- where does the asynchronous code to get the HTTP response lie? And the other is -- why won't the server send me a response back? The console displays "Request sent" but it does not display "Request received."
Here is the test file:
vows = require 'vows'
assert = require 'assert'
EventEmitter = require('events').EventEmitter
Server = require('./web').WebServer
Client = require('../lib/client').Client
Request = require('../lib/request').Request
PORT = 8080
SERVER = new Server PORT
SERVER.start()
CLIENT = new Client PORT, 'localhost'
REQUEST = new Request 'GET', '/'
vows
.describe('Sending a request to the server')
.addBatch
'The request is sent':
topic: ->
CLIENT.transmit(REQUEST, #callback)
return
'The response should be what the server sent back': (err, request) ->
body = ""
request.on 'response', (response) ->
response.on 'data', (chunk) -> body += chunk
assert.equal body, /Ooga/
.export(module)
Here is the WebServer object:
Http = require('http')
exports.WebServer = class WebServer
processRequest = (request, response) ->
console.log 'Request received!'
console.log request
response.writeHead 200, {'Content-Type':'text/plain'} #, 'Content-Length':'6'}
response.write 'Ha-ha!'
response.end
constructor: (#port) ->
#server = Http.createServer processRequest
start: ->
#server.listen #port
stop: ->
#server.close()
Next up is the client code - also very simple.
Http = require 'http'
Request = require('./request').Request
exports.Client = class Client
constructor: (#port, #host) ->
#httpClient = Http.createClient #port, #host
#sentence = "I am a Client"
transmit: (request, callback = null) ->
req = #httpClient.request request.method, request.pathName
req.end
console.log "Request sent!"
if callback
callback(null, req)
#req.on 'response', (res) ->
# callback(null, res)
#request.on 'data', (chunk) -> callback(null, chunk)
#callback(null, request)
And finally, the 'request' object.
exports.Request = class Request
constructor: (#method, #pathName) ->
method: ->
#method
pathName: ->
#pathname
responseBody: ->
#body
setResponseBody: (body) ->
#body = body
appendToResponseBody: (chunk) ->
#body += chunk
All this is so simple that I really can't tell why the server doesn't seem to be working. I'm not even worried, yet, about where the asynchronous code should be to get the information back from the server, but I'd like to figure that out too..
Ah, you've made a classic mistake: You wrote
req.end
where you meant
req.end()
So, your request wasn't actually being sent at all, despite your console's claims to the contrary! (I see a response.end in your code as well.)
Incidentally, Vows test code is pretty, but it's also a complex framework with some subtle gotchas. You may want to try something simpler, like nodeunit.
Oh, and if you really hate parentheses, you can write do req.end instead of req.end(), but this isn't a common style.
Related
I'm writing a application using sockets, but can't seem to get the initial handshake to work. I'm using WebSockets + React on my front-end, running on PORT 8080, and Node.js socket on the backend running on PORT 5000.
The front-end handshake is done through my component like so:
componentDidMount(){
this.socket = new WebSocket('ws://localhost:5000', ['json']);
this.socket.onerror = err => {
console.log(err)
}
this.socket.onmessage = e => {
let res = JSON.parse(e.data);
console.log(e, res);
let copyArr = [...this.state.message]
copyArr.push(res);
this.setState({
message: copyArr
});
}
}
On my Node server, I do:
const server = http.createServer();
server.on('upgrade', (req, socket) => {
if(req.headers['upgrade'] !== "websocket"){
socket.end('HTTP/1.1 400 Bad Request');
return;
}
const acceptKey = req.headers['sec-websocket-key'];
const acceptHash = generateValue(acceptKey);
console.log('accepkey', acceptKey, 'hash', acceptHash);
const resHeaders = [ 'HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', `Sec-WebSocket-Accept: ${acceptHash}` ];
console.log(resHeaders);
let protocols = req.headers['sec-websocket-protocol'];
protocols = !protocols ? [] : protocols.split(',').map(name => name.trim());
if(protocols.includes('json')){
console.log('json here');
resHeaders.push(`Sec-WebSocket-Protocol: json`);
}
socket.write(resHeaders.join('\r\n') + '\r\n\r\n');
})
function generateValue(key){
return crypto
.createHash('sha1')
.update(key + '258EAFA5-E914–47DA-95CA-C5AB0DC85B11', 'binary')
.digest('base64');
}
When my React component mounts, it tries to establish the initial handshake but fails with the error: WebSocket connection to 'ws://localhost:5000/' failed: Error during WebSocket handshake: Incorrect 'Sec-WebSocket-Accept' header value. I've checked using Chrome developer tool and found this
While on the backend, when logging the request accept-key header, and response headers, I saw this:
So, unless I'm mistaken about these headers, it seems that the request and response accept-key header somehow changes when making it's way from the client to the server, and vice versa. How is this happening? Or have I misunderstood what's going on. Why exactly is the initial handshake not working?
There is a en dash – instead of hyphen - in 258EAFA5-E914–47DA-95CA-C5AB0DC85B11 after E914
So replace it with hyphen -
reference https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-WebSocket-Accept
I believe the generateValue function is wrong, you pass binary as the inputData encoding which is a keyword for latin1 according to the docs. But I believe it is UTF-8 string, not latin1, so the result hash is wrong. So just try to use update(key + '258EAFA5-E914–47DA-95CA-C5AB0DC85B11', 'utf8') or even without the second utf8 argument since it is a default.
I have a simple HTTPS request -
https://api.pro.coinbase.com/products/btc-eur/ticker
In the browser this returns one object. What's the simplest code that will allow me to retrieve and display this object (as is) in the terminal of Node?
const https = require('https')
const url = https.get('https://api.pro.coinbase.com/products/btc-eur/ticker')
const myObject = JSON.parse(url)
console.log(myObject)
A simple copy / paste of the above code in VSC returns the error SyntaxError: Unexpected token o in JSON at position 1.
#mamba76, welcome to the SO community. Please use Node.js node-fetch package. It is much simpler to use. You can install it using npm install.
Following code might help:
"use strict";
const fetch = require('node-fetch')
async function getValue() {
// Invoke the API.
// Wait until data is fetched.
let response = await fetch('https://api.pro.coinbase.com/products/btc-eur/ticker');
let value = await response.json();
return value;
}
getValue().then(result => {console.log(result.price);});
As a good practice, always assume that API calls over the HTTP (whether in your own network or outside) might take time to return data and hence you should use async-await pattern to make these requests.
Extending #Akshay.N's answer and without using external dependencies,
const https = require('https')
https.get("https://api.pro.coinbase.com/products/btc-eur/ticker",res=>{
let body = '';
res.on('data', (chunk) => { body += chunk; });
res.on('end', () => {
const myObject = JSON.parse(body);
console.log(myObject);
})
})
Now, what we're doing here is waiting on the data event as long as the data is coming in, and appending it to the variable body. Once the end event is encountered, we take that as a signal that all data has been received and we can proceed to parse the body into an object using JSON.parse (assuming the data was serialized in JSON; if it wasn't JSON.parse will throw an error).
This tutorial is helpful: https://davidwalsh.name/nodejs-http-request
try something like this:-
https.get("https://api.pro.coinbase.com/products/btc-eur/ticker",res=>{
res.on('data', (chunk) => { console.log(JSON.parse(chunk))})
})
With node (you need request module) :
// display object
(require("request")).get({
url: "myurl",
json: true
}, function(e,r,b){
console.log(b);
});
// display as string
(require("request")).get({
url: "myurl",
json: false
}, function(e,r,b){
console.log(b);
});
With just curl in your terminal (without node)
curl myurl
I would like to POST two data in multipart / form-data format using Node-RED.
(One for text data, one for voice data)
I set the function node and http request node as follows, but it does not seem to be POST.
I think that it is necessary to create a multi-part body and assign it to msg.body, but I do not know how to create a multi-part body of the voice data.
I do not know how to solve it, so someone would like to tell me.
function node
var request = global.get('requestModule');
var fs = global.get('fsModule');
msg.body = {};
msg.body = {
'apikey' : "**********",
'wav' : fs.createReadStream('/tmp/testtest.wav')
};
msg.headers = {};
msg.headers['Content-type'] = 'multipart/form-data';
return msg
http request(Property)
method ⇒ POST
URL ⇒ https://xxxxyyyzzz/
SSL/TLS ⇒ No
Basic ⇒ No
Output ⇒ JSON
The http request Node-Red core node support multipart/form-data POST out of the box.
Add a function node before your http request with this Function :
msg.headers = {};
msg.headers['Content-Type'] = 'multipart/form-data';
msg.headers['Accept'] = 'application/json';
msg.payload = {
'apikey': msg.apiKey,
'wav': {
value: msg.payload.invoice.file,
options: {
filename: 'testtest.wav',
contentType: 'audio/wav', // This is optionnal
}
}
}
return msg;
The http request node use Request nodejs library under the hood, and this one use form-data library for handling multipart, so all the options supported by these works.
The source code of the relevant part of http request handling multipart.
I'm using mocha to test some classes and I need to create a stub of request library.
I'm using sinon, and I'm able to create a stub of the request.get method but I'm not able to create a stub of the request method (the http calls try to connect to a server). As I have read, request.get is an alias for request but when I stub request.get it has no effect over request calls.
This code works (using request.get):
In tests:
request = require 'request'
describe "User test", ->
user = {}
before (done) ->
user = new test.user('Ander', 18)
sinon.stub(request, 'get').yields(null, {statusCode: 200}, 'foo')
done()
after (done) ->
request.get.restore()
done()
it "testing server response", ->
user.getData().should.equal 'ander'
In source:
request = require 'request'
class User
contructor(#name, #age): ->
getData: ->
mydata = ''
request.get 'http://127.0.0.1:8080/', (err, response, body) ->
if not err and response.statusCode == 200
mydata = body
else
err = throw new Error "Errorea"
mydata
But this doesn't work (tries to connect to the supplied url):
In tests:
request = require 'request'
describe "User test", ->
user = {}
before (done) ->
user = new test.user('Ander', 18)
sinon.stub(request, 'Request').yields(null, {statusCode: 200}, 'foo')
#Creating the stub this way, doesn't work neither
#sinon.stub(request, 'get').yields(null, {statusCode: 200}, 'foo')
done()
after (done) ->
request.Request.restore()
done()
it "testing server response", ->
user.getData().should.equal 'ander'
In source:
request = require 'request'
class User
contructor(#name, #age): ->
getData: ->
mydata = ''
request 'http://127.0.0.1:8080/', (err, response, body) ->
if not err and response.statusCode == 200
mydata = body
else
err = throw new Error "Errorea"
mydata
Which is the right way to create a stub for request call? Which is the method to be stubed?
Although request is a great library, it is not a good example of well structured API. And because module request is defined as a function with additional methods (similarly like express), as what I know you can't create stub for function request with sinon.
The best thing you can do is to avoid to use request function in your code and use only request.get, request.post, etc., which you can easily stub.
Creating stub for Request in your second example doesn't help because Request is not a method, see source code.
If anyone is still looking for an answer for this, it looks like you can create a stub for request using sinon:
before(function(done){
sinon
.stub(request, 'get')
.yields(null, null, JSON.stringify({login: "bulkan"}));
done();
});
more details can be found here
Another workaround would be generating a stub using sinon module and the request dependency in the corresponding module can be overridden using proxyquire.
var sinon = require('sinon');
var proxyquire = require('proxyquire');
describe('something', function(){
var request;
var overriddenModule;
before(function(){
request = sinon.stub();
// overriding the 'request' dependency with the stub
overriddenModule = proxyquire('path_to_module_using_request', {'request': request});
});
it("should do something",function(done){
// stubbing the request(options,callback) method
request.withArgs(sinon.match.any,sinon.match.any).yields(null,null,responseBody);
overriddenModule.method_which_is_doing_request;
// our logic and assertions before calling done()
});
});
For more info, check this article on Unit Testing with Mocks in Node
As mentioned in one of the answers to the question How to mock request and response in nodejs to test middleware/controllers?, the node-mocks-http package provides a way to build request and response mocks.
I wonder why such simple http request is not working...
http = require("http")
url = "http://nodejs.org/"
console.log "Try a request to #{url}..."
reqHttp = http.request url, (response) ->
console.log "Request to #{url}"
response.on 'data', (chunk) -> console.log "chunk: ", chunk
reqHttp.on 'error', (error) -> console.log "reqHttp error", error
After a minute or so it returns:
reqHttp error { [Error: socket hang up] code: 'ECONNRESET' }
To make sure it is not a problem on my environment, I tried the request module and worked just fine:
request = require("request")
url = "http://nodejs.org/"
request url, (error, response, body) ->
console.log body if not error and response.statusCode is 200
It seems I'm not the only one.
So, I have a workaround for my problem (using request module), but I'd like to know why I can't use the buind-in http request. Is it buggy or unreliable? (Node.js version 0.8.21)
OK, this is really simple. You are constructing an http request but did not finish sending it. From the link you gave itself:
req.write('data\n'); //Write some data into request
req.write('data\n');
req.end(); //Finish sending request let request go. Please do this
Since you never used req.end(), it hung up since it never got completed. Node reset the inactive request
reqHttp error { [Error: socket hang up] code: 'ECONNRESET' }