Node.js: Cannot do PROFIND with request module - javascript

I'm trying to access content of my WebDAV server in node (server OK, I can net use it). I'm using the request module:
path = url.join(this._options.rootUrl, path || "");
var data =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<propfind xmlns=\"DAV:\">" +
"<propname/>" +
"</propfind>";
var headers = {
"Content-Type": "text/xml",
"Depth": depth || 1
};
var req = request({
url: path,
data: data,
headers: headers,
method: "PROPFIND",
json: false,
encoding: "utf8",
auth: {
user: this._options.user,
password: this._options.password
}
});
req.on("response", ...);
req.on("error", ...);
The problem is I'm getting HTTP 405 instead of the result. I've tried bundled https module, same result.
Is it possible that Node.js cannot invoke costom verbs like PROPFIND?
UPDATE 1:
This is stated that node can do PROFIND. So my question is, how do you people do correct WebDAV PROPFIND client request in node/io.js by using request module? (Or anything that works for this purpose.)

PROPFIND or any other verb is supported just fine for outbound requests in node and iojs. There is either something else wrong with your request (wrong/bad headers or payload for example) or configuration on the server.
Example of PROPFIND working:
// client.js
require('http').request({
host: '127.0.0.1',
port: 8000,
method: 'PROPFIND'
}, function(res) {
res.resume();
}).end();
// server.js
require('http').createServer(function(req,res) {
// echoes "PROPFIND" to stdout
console.log(req.method);
res.writeHead(200, { 'Connection': 'close' });
res.end();
}).listen(8000);

Related

No body present upon receipt with https POST with body using nodejs client and golang server

I am attempting to send a rest request over https using the following
// Construct the POST auth body and options
let postData = JSON.stringify({
username: this.state.username,
password: this.state.password
});
console.log(postData);
var options = {
host: this.state.peerAddress,
port: this.state.port,
path: "/api/" + this.state.channelName + "/user-authorisation",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": postData.length
}
};
console.log(options);
var postRequest = https.request(options, function(res) {
console.log("\nstatus code: ", res.statusCode);
res.setEncoding("utf8");
res.on("data", function(data) {
console.log(JSON.parse(data));
});
});
e.preventDefault();
// post the data
postRequest.write(postData);
postRequest.end();
The request is sent to the server but there is no body present. I have tested this by sending the same request over postman and it works fine. I am sending the request on Chrome if that makes any difference? I get the following errors on the console but these happen after the request has been sent I believe so that are not really an issue at this point.
request.js:149 OPTIONS https://<<address>>:<<port>><<path>> net::ERR_CONNECTION_REFUSED
localhost/:1 Uncaught (in promise) TypeError: Failed to fetch
Any help would be greatly appreciated!
Cheers

HTTPS Request (using https nodejs module) of type 'form-data/multipart' not working

I have the following curl request which is working nicely and as supposed:
curl --user 'api:MY_API_KEY'
https://api.mailgun.net/v3/mydomain/messages --form from='My Name
' --form to=validaddress#domain.com --form
subject='Hello 3!' --form text='Testing sending email!'
However I need to craft this request as a valid https request using the standard https module form nodejs, and I tried the following code, however I keep getting 400 (bad request) as response:
helpers.sendRequestFormData = function(protocol, port, hostname, method, path, contentType, auth, timeoutSeconds, postData, callback){
var from = 'My Name <info#mydomain>';
var to = 'validaddress#domain.com';
var subject = 'Email test';
var text = 'Testing sending email';
var stringPayload = `--${config.mailgun.boundary}
\nContent-Disposition: form-data; name="from";
\nContent-type: multipart/form-data;
\nfrom="${from}";
\n--${config.mailgun.boundary}
\nContent-Disposition: form-data; name="to";
\nContent-type: multipart/form-data;
\nto="${to}";
\n--${config.mailgun.boundary}
\nContent-Disposition: form-data; name="subject";
\nContent-type: multipart/form-data;
\nsubject="${subject}";
\n--${config.mailgun.boundary}
\nContent-Disposition: form-data; name="text";
\nContent-type: multipart/form-data;
\ntext="${text}";
\n--${config.mailgun.boundary}\n`;
// Construct the request
var requestDetails = {
'hostname' : hostname,
'port': port,
'method' : method,
'timeout' : timeoutSeconds * 1000,
'path' : path,
'headers' : {
'Authorization': auth,
'Content-Type': contentType,
'Content-Length': Buffer.byteLength(stringPayload)
}
};
// Instantiate the request object (using either the http or https module)
var _moduleToUse = protocol == 'http' ? http : https;
var req = _moduleToUse.request(requestDetails, function(res){
var responseStatus = res.statusCode;
console.log(responseStatus);
res.setEncoding('utf8');
res.on('data', function(data){
if(requestStatus == 200){
callback(false, parsedData);
}
});
});
// Bind to the error event so it doesn't get thrown
req.on('error',function(e){
console.log(e);
callback(true, {'Error': e});
});
// Bind to the timeout event
req.on('timeout',function(){
console.log('timeout');
callback(true, {'Error': 'The request took much time and got timeout.'})
});
// Add the payload
req.write(stringPayload);
// End the request
req.end();
};
Can someone please give me some hints, guidance, or tips? I am getting a bit overwhelmed about this, I am sure it might be something simple, been doing trial and error with semi-colons and dashes on the boundary but still didn't got a 200 status response code.
Thank you so much in advance!
I made it working, the current code is the following:
helpers.sendRequest = function(protocol, port, hostname, method, path,
contentType, auth, timeoutSeconds, postData, callback){
var stringPayload = querystring.stringify(postData);
// Construct the request
var requestDetails = {
'hostname' : hostname,
'port': port,
'method' : method,
'timeout' : timeoutSeconds * 1000,
'path' : path
};
// Instantiate the request object (using either the http or https module)
var _moduleToUse = protocol == 'http' ? http : https;
var req = _moduleToUse.request(requestDetails, function(res){
res.on('data', (d) => {
if(res.statusCode == 200){
callback(false);
}else{
console.log(res.statusCode);
callback(true);
}
});
});
req.setHeader('Authorization', auth);
req.setHeader('Content-Type', contentType);
req.setHeader('Content-Length', Buffer.byteLength(stringPayload));
// Add the payload
req.write(stringPayload);
// Bind to the error event so it doesn't get thrown
req.on('error',function(e){
console.log(e);
callback(true, {'Error': e});
});
// Bind to the timeout event
req.on('timeout',function(){
console.log('timeout');
callback(true, {'Error': 'The request took much time and got timeout.'})
});
// End the request
req.end();
};
And I call the method like this:
genericHelper.sendRequest('https', '443', 'api.mailgun.net', 'POST', '/v3/sandbox0630029a67f24517a9c3e383d2c6098e.mailgun.org/messages',
'application/x-www-form-urlencoded', ('Basic ' + Buffer.from(('api:'+ config.mailgun.ApiKeyTest)).toString('base64')), 5, emailRequestObject, function(err, data){
// Here I do what I need after sending the http request with success
});
I hope it helps someone out, so the problem was the content-type, I had to change it to 'application/x-www-form-urlencoded' and then also the authorization I had to convert to Base64 and include Basic before the pair in base64.

request to server node.js

I am learning about requests in node. I have created simple server
var http=require("http");
var fs=require("fs");
http.createServer(function(req,res){
switch(req.url){
case '/redirect' :
res.writeHead({"Location ":'/'});
res.end();
break;
case '/form.html' :
res.writeHead(200,{'Content-Type': 'text/html'});
req.setEncoding("utf-8");
var ws=fs.createReadStream("form.html")
var body="";
ws.pipe(body);
res.end(JSON.stringify(body));
break;
default:
res.writeHead(200,{"Content-Type": 'text/plain'});
res.end(JSON.stringify({
url:req.url,
method:req.method,
headers:req.headers
}));
}
}).listen(4001)
and request script
var request=require("request");
var inspect = require("util").inspect;
request('http://localhost:4001/form.html',function(err,res,body){
if(err){
throw err;
}
console.log(inspect({
err:err,
res:{
statusCode:res.statusCode
},
body:JSON.parse(body)
}))
});
If i understood it correctly (logic behind requests) =
1)the request script makes request(default get) to the server
2)The server script will notice request , and deterime what to do by request.url , here we are making request on /form.html
3) Server script will do /form/html case = put header into response, create read Stream and send the value into response
4) The response is sent into request script where it is represented by body argument in callback function
5) The script now do adction in callback function = console.log the response.
form.html contains simple htmp form , but why the output of request script is
{ err: null,
res: { statusCode: 200 },
body:
{ url: '/form.html',
method: 'GET',
headers: { host: 'localhost:4001', connection: 'close' } } }
and not (in body) the text code that contains form.html?
I'm not sure I understand exactly what you're trying to do in the form.html branch of your switch, but if you're just trying to serve the form.html file, you can do this:
case '/form.html' :
res.writeHead(200,{'Content-Type': 'text/html'});
var ws = fs.createReadStream("form.html");
ws.pipe(res);
break;
This will serve the contents of the local file form.html to the browser.

nodejs http post request throws TypeError

I am trying to make a simple server that use google oauth (without express and passportjs, as I want to study the data exchanged).
When my program attempts to send a post request to google, nodejs throws:
http.js:593 throw new TypeError('first argument must be a string or Buffer');
I have checked and make sure that all parameters in query and option are all string, but the error still persist. What could I have missed here?
Here is my code:
// Load the http module to create an http server.
var http = require('http');
var url = require('url');
var fs = require('fs');
var querystring = require('querystring');
var content;
fs.readFile('./test.html',function(err,data){
content = data;
});
// Configure our HTTP server to respond with Hello World to all requests.
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
var path = url.parse(request.url).pathname;
var query = querystring.parse(url.parse(request.url).query);
var code;
if (query!=null) {
code = query.code;
};
if ('/auth/google/callback'==path){
var data = querystring.stringify({
'code': ''+code,
'client_id': 'id',
'client_secret': 'secret',
'redirect_uri': 'http://localhost:8999/auth/google/code/callback',
'grant_type': 'authorization_code'
});
var options = {
hostname: 'accounts.google.com',
port:'80',
path: '/o/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': ''+data.length
}
};
debugger;
var post = http.request(options, function(res){
response.write(res);
response.end();
});
debugger;
post.write(data);
debugger;
post.end();
}
else if (path=='/auth/google/code/callback'){
console.log(request.headers);
console.log(request.url);
}
else response.end(content);
console.log(request.headers);
console.log(request.url);
});
// Listen on port 8000, IP defaults to 127.0.0.1
server.listen(8999);
// Put a friendly message on the terminal
console.log("Server running at http://127.0.0.1:8000/");
Many thanks,
I think problem is when you are saying
response.write(res); //it needs a string
I think res is an object here.
try
response.write(JSON.stringify(res));
When you write response or request. It should contain string so you need to change it to
response.write(querystring.stringify(res));
or
response.write(JSON.stringify(res));

How to post to a request using node.js

I am trying to post some json to a URL. I saw various other questions about this on stackoverflow but none of them seemed to be clear or work. This is how far I got, I modified the example on the api docs:
var http = require('http');
var google = http.createClient(80, 'server');
var request = google.request('POST', '/get_stuff',
{'host': 'sever', 'content-type': 'application/json'});
request.write(JSON.stringify(some_json),encoding='utf8'); //possibly need to escape as well?
request.end();
request.on('response', function (response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
When I post this to the server I get an error telling me that it's not of the json format or that it's not utf8, which they should be. I tried to pull the request url but it is null. I am just starting with nodejs so please be nice.
The issue is that you are setting Content-Type in the wrong place. It is part of the request headers, which have their own key in the options object, the first parameter of the request() method. Here's an implementation using ClientRequest() for a one-time transaction (you can keep createClient() if you need to make multiple connections to the same server):
var http = require('http')
var body = JSON.stringify({
foo: "bar"
})
var request = new http.ClientRequest({
hostname: "SERVER_NAME",
port: 80,
path: "/get_stuff",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body)
}
})
request.end(body)
The rest of the code in the question is correct (request.on() and below).
Jammus got this right. If the Content-Length header is not set, then the body will contain some kind of length at the start and a 0 at the end.
So when I was sending from Node:
{"email":"joe#bloggs.com","passwd":"123456"}
my rails server was receiving:
"2b {"email":"joe#bloggs.com","passwd":"123456"} 0 "
Rails didn't understand the 2b, so it wouldn't interpret the results.
So, for passing params via JSON, set the Content-Type to application/json, and always give the Content-Length.
To send JSON as POST to an external API with NodeJS... (and "http" module)
var http = require('http');
var post_req = null,
post_data = '{"login":"toto","password":"okay","duration":"9999"}';
var post_options = {
hostname: '192.168.1.1',
port : '8080',
path : '/web/authenticate',
method : 'POST',
headers : {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Content-Length': post_data.length
}
};
post_req = http.request(post_options, function (res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ', chunk);
});
});
post_req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
post_req.write(post_data);
post_req.end();
There is a very good library that support sending POST request in Nodejs:
Link: https://github.com/mikeal/request
Sample code:
var request = require('request');
//test data
var USER_DATA = {
"email": "email#mail.com",
"password": "a075d17f3d453073853f813838c15b8023b8c487038436354fe599c3942e1f95"
}
var options = {
method: 'POST',
url: 'URL:PORT/PATH',
headers: {
'Content-Type': 'application/json'
},
json: USER_DATA
};
function callback(error, response, body) {
if (!error) {
var info = JSON.parse(JSON.stringify(body));
console.log(info);
}
else {
console.log('Error happened: '+ error);
}
}
//send request
request(options, callback);
Try including the content length.
var body = JSON.stringify(some_json);
var request = google.request('POST', '/get_stuff', {
host: 'server',
'Content-Length': Buffer.byteLength(body),
'Content-Type': 'application/json'
});
request.write(body);
request.end();
This might not solve your problem, but javascript doesn't support named arguments, so where you say:
request.write(JSON.stringify(some_json),encoding='utf8');
You should be saying:
request.write(JSON.stringify(some_json),'utf8');
The encoding= is assigning to a global variable, so it's valid syntax but probably not doing what you intend.
Probably non-existent at the time this question was asked, you could use nowadays a higher level library for handling http requests, such as https://github.com/mikeal/request. Node's built-in http module is too low level for beginners to start with.
Mikeal's request module has built-in support for directly handling JSON (see the documentation, especially https://github.com/mikeal/request#requestoptions-callback).
var request = google.request(
'POST',
'/get_stuff',
{
'host': 'sever',
**'headers'**:
{
'content-type': 'application/json'
}
}
);

Categories