GithubHelp home page GithubHelp logo

tomas / needle Goto Github PK

View Code? Open in Web Editor NEW
1.6K 35.0 236.0 1.12 MB

Nimble, streamable HTTP client for Node.js. With proxy, iconv, cookie, deflate & multipart support.

Home Page: https://www.npmjs.com/package/needle

License: MIT License

JavaScript 76.82% HTML 23.18%

needle's Issues

Is there any way to track the progress?

I used needle to upload my file into my server.
There is any way to track upload progress ?

 var data = {
    fileUpload: { file: file, content_type: 'application/octet-stream' }
}; 
needle.post(config.URL+api, data, { multipart: true}, function(err, resp, body) {
...
});

Regression in receiving large payloads

Here's a block of code using the library pgte/nock (I think the version is irrelevant) and needle:

var needle = require("needle");
var nock = require("nock");

nock("http://www.abc.com").get("/abc").reply(200, (new Array(1024 * 1024 + 1)).join("."));
needle.get("http://www.abc.com/abc", function(e, r, b) { console.log("done, %d", b.length); });

The request gets intercepted by nock and a 1MB response is sent. In needle 0.6.6 this code behaves as expected, printing done, 1048576. In needle 0.7.0 and up, the response never arrives (and node doesn't know to wait, apparently, so the program exits without printing anything).

This could be related to this issue in nock, but it's nevertheless a clear regression in needle.

Boolean data in POST request gets converted as string

Hi tomas,
Sending boolean data as part of JSON data becomes string on the receiving side.
For Eg: data = {id:100, fname:'john', status:{isactive:true}}
here isactive:true becomes isactive:'true' <- string 'true'

Is this an expected behaviour or bug?

In, lib/needle.js lines 110 & 111

    post_data = (typeof(data) === "string") ? data : stringify(data);
    config.headers['Content-Type'] = 'application/x-www-form-urlencoded';

to make this work, if I change like following it works as expected

    post_data = (typeof(data) === "string") ? data : JSON.stringify(data);
    config.headers['Content-Type'] = 'application/json';

If bug, Can you please fix this?.

About digest authorization!

there are maybe a bug, when I use needle digest authorization.

it does't work for RFC 2069, only supports RFC 2617.

auth.js line 46 should be

if (typeof challenge.qop === 'string') {
    cnonce = md5(Math.random().toString(36)).substr(0, 8);
    nc = digest.update_nc(nc);
    resp = resp.concat(nc, cnonce);
    resp = resp.concat(challenge.qop, ha2);
} else {
    resp = resp.concat(ha2);
}

and

var ha1 = md5(user + ':' + challenge.realm + ':' + pass), ha2 = md5(method
        + ':' + path), resp = [ ha1, challenge.nonce ];

and can't be the parameters of function.

they should be get from the headers of response.

2-argument callback instead of 3-argument

Hi there,

Something that noticed was the callback that adds a third argument which is a shortcut to response.body. The node.js community has pretty much settled on the 2-argument callback, which in turn caused a whole ecosystem of utilities that make this assumption (eg node-async and Q).

What is the rationale behind this? And would you mind it if I made a patch that detects the arity of the callback and behave in a standard-compliant way again if the caller requests so?

BTW I must be overwhelming you a bit with all these requests but I think there really is a place in the node ecosystem for this library, since it is one of the few libraries that actually is pleasant to use. :)

Incompatible with node 0.4.x

Love the simplicity of needle, but it looks like needle doesn't really support 0.4.x. Since I'm hosting on heroku, I'm stuck with node 0.4.7.

The main issues are:

  • commit 36c797f1 requires a json file. node 0.4.12 can only import js files as modules. JSON import came with node 0.6.x. Not sure how to fix this short of just harcoding the version number and bumping it with each release.
  • commit d12105dd depends on the zlib module, which was also added in node 0.6.0. node-zlib exists and conflicts with the new 0.6.x module, but a better option might try/catching zlib and just saying zlib is only supported for 0.6.x.
  • commit 8642c3de doesn't quite fix paths for older node, since it drops the query string. As shown in the 0.6.x docs, path = pathname + search

I'll fork and submit a pull request for the latter two, but I'm not sure about the best course of action to fix the first issue.

needle.post() calling callback twice

I'm using needle.post() and passing in a callback function. The callback function is getting called twice and the body on the first time is a javascript object (as expected) and the second time the body is a JSON string. Using the latest version of needle.

Detect encoding from HTML/XML <meta> header

Some server does not notify charset in HTTP header, it's better to also sniff from the HTML/XML <meta> header.

I've done so using charset.

var encoding = 'utf8';  // defaults to 'utf8'
if (opts.encoding) {
  encoding = opts.encoding;
  console.info("Using encoding from options: " + encoding);
}
else {
  // detect charset from header or body (the first 1Kb)
  var cs = charset(res.headers, body.toString('utf8', 0, 1024));
  if (cs) {
    encoding = cs;
    console.info("Detected encoding: " + encoding);
  }
}
var str = iconv.decode(body, encoding);

Filenames not encoded

For example, one can add a filename containing a " or Unicode characters, resulting in an invalid request.

Digest Auth Fails

Not 100% sure, but when I was using this against the moodstock API, the digest auth was failing. I am pretty sure it was because the first authorisation response is setting nc=2 instead of 1. I simply set the nc to be 0 in the digest.generate function and it all worked fine for me.

Header host missing port

Just been looking at a request made from needle 0.5.9. It looks like the header's host value is missing the port, any chance this could be added?

Looks like the offending line may be lib\needle.js 172

opts.headers['Host'] = proxy ? url.parse(uri).hostname : remote.hostname;

Edit: Adding a thanks in advance/just for looking :)

Default charset set to UTF-8 and charset is not always the second param of header

ISO-8859 was the default of HTML4 but now it's UTF-8 for HTML5. I think it's a better idea now to set default charset to UTF-8. Besides, when detecting charset in parse_content_type(), charset is not always the second param. So it's a better to match it agains the whole header.

  parse_content_type: function(header) {
    if (!header || header == '') return {};

    var charset = 'utf-8';
    var arr = header.split(';');
    try { charset = header.match(/charset=([^;]+)/)[1] } catch (e) { /* not found */ }

    return { type: arr[0], charset: charset };
  },

Should use streams, not block

Use of readFileSync is a very bad idea, as it will block the whole process.
Instead, the client should accept a Stream (and internally can createReadStream from any passed file paths).
Ideally this stream would be piped straight into the request object, but even buffering it internally would be preferably to the the current blocking behaviour.

Compressed body isn't decompressed in stream

Correct me if I'm wrong, but it appears as if the resp.body is only unzipped if you use the callback approach and not when you're using the streaming approach:

This is where the data is pushed on the stream: https://github.com/tomas/needle/blob/master/lib/needle.js#L252

And this is where the compressed data is decoded:
https://github.com/tomas/needle/blob/master/lib/needle.js#L274

Is this conclusion correct? Is there a reason that this approach is chosen (a buffering-approach instead of a streams-approach)?

Anyway, I would like to make an attempt at patching this tomorrow.

What I was thinking about:

  • get rid the the 'chunks' array in send_request;
  • make the config.out stream the "primary" way for processing the body
  • if the client has requested a callback, "sniff" all data going over the stream and store it in resp.body

Getting the stream to decompress would then be a matter of piping config.out through zlib.

More than 10 requests at the same time fail!

Hello. I am trying to make 10 simultaneous POST requests to get some data from ElasticSearch

arrayWithFiveElements.forEach(function (country) {
    needle.post('http://localhost:9200/' + country + '/test_document1/_search?size=1000',
        '{"query": {"match_all": {}}}', function (err, response, body) {
            populateList(err, testData, body);
        });

    needle.post('http://localhost:9200/' + country + '/test_document2/_search?size=1000',
        '{"query": {"match_all": {}}}', function (err, response, body) {
            populateList(err, testData, body);
        });
});

and on the 9th and 10th request I always get this error:

Trace: [ERROR] No hits returned by Elastic Search for D1
    at populateMonthList (/opt/ddd/backend/backend.js:48:21)
    at /opt/ddd/backend/backend.js:74:17
    at error_stop (/opt/ddd/backend/node_modules/needle/lib/needle.js:206:9)
    at ClientRequest.<anonymous> (/opt/delivery-dashboard/backend/node_modules/needle/lib/needle.js:342:7)
    at ClientRequest.EventEmitter.emit (events.js:95:17)
    at Socket.socketErrorListener (http.js:1547:9)
    at Socket.EventEmitter.emit (events.js:95:17)
    at onwriteError (_stream_writable.js:239:10)
    at onwrite (_stream_writable.js:257:5)
    at WritableState.onwrite (_stream_writable.js:97:5)
{
  "code": "EPIPE",
  "errno": "EPIPE",
  "syscall": "write"
}
Trace: [ERROR] No hits returned by Elastic Search for D2
    at populateRWAMonthList (/opt/ddd/backend/backend.js:59:21)
    at error_stop (/opt/ddd/backend/node_modules/needle/lib/needle.js:206:9)
    at ClientRequest.<anonymous> (/opt/ddd/backend/node_modules/needle/lib/needle.js:342:7)
    at ClientRequest.EventEmitter.emit (events.js:95:17)
    at Socket.socketErrorListener (http.js:1547:9)
    at Socket.EventEmitter.emit (events.js:95:17)
    at onwriteError (_stream_writable.js:239:10)
    at onwrite (_stream_writable.js:257:5)
    at WritableState.onwrite (_stream_writable.js:97:5)
    at fireErrorCallbacks (net.js:437:13)
{
  "code": "EPIPE",
  "errno": "EPIPE",
  "syscall": "write"
}

The other 8 requests are totally fine.

Do you happen to know the solution to this issue?

Get callback get fired twice

I have this URL: http://panoramaproductions.electionemail.com/lt.php?nl=26&c=86&m=210&s=463b49e18dfd5a13da71ff10dc210d70&l=open

Callback get fired, first, with error from here:

 request.on('error', function(err) {
      debug('Request error', err);
      if (timer) clearTimeout(timer);

      error_stop(err || new Error('Unknown error when making request.'));
    });

and the error is { [Error: Parse Error] bytesParsed: 311, code: 'HPE_INVALID_CONSTANT' }

But it will be also fired with no error from here :

// Only aggregate the full body if a callback was requested.
      if (callback) {
        resp.body  = [];
        resp.bytes = 0;

        // Count the amount of (raw) bytes passed using a PassThrough stream.
        [...] 
          callback(null, resp, resp.body);
        });

      }

It may not seem like a real issue, but this bug makes needle problematic when using it with async, because triggers the async error:
if (called) throw new Error("Callback was already called.");

needle : 0.7.2
node : v0.10.19
OS: ubuntu x64 with kernel 3.8.0-29-generic

Limit download length (size) but still make use of decoder and parser

I have been using needle lately and come into a dilemma: it is possible to both cap download length and avoid having to deal with stream chunk myself?

From source code it seems that passing a callback will cause buffering but we have no way of knowing how much data has been buffered so far (thus not able to stop it). On the other hand if we handle readable stream ourselves I believe parser or decoder will be kick in (they need full buffer anyway).

Any suggestion?

PS: many thx for this package, i came from a curl background so understandably some features are not here for granted.

Needle parses in streams mode

Needle still parses JSON and XML data, even when no callback is provided. This causes the stream to be in object mode.

Is this expected behavior? In my mind, parsing should happen in streams mode. (But obviously this can trivially be disabled by passing in parsed: false)

Host header port incorrect

Without reverse proxy it works fine.

If you specify a reverse proxy with a port number not matching the port in the get url the port number in Host header is not correct in a GET request.

Example:
url request is http://testserver.testdomain.com/image.jpg
proxy = http://proxyserver.testdomain.com:8080

GET http://testserver.testdomain.com/image.jpg
Header:
Host: http://testserver.testdomain.com:8080

Needle.js
178 remote = proxy ? url.parse(proxy) : url.parse(uri);

182 opts.port = remote.port || (remote.protocol == 'https:' ? 443 : 80);

187 if (!opts.headers['Host']) {
188 opts.headers['Host'] = proxy ? url.parse(uri).hostname : remote.hostname;
189 if (opts.port != 80 && opts.port != 443)
190 opts.headers['Host'] += ':' + opts.port;
191 }

about DEBUG environment

When I'm using the 'debug' module which should set the DEBUG environment a value,'needle' module would output many debugging messages.So I can't find the debugging messages I want.

Sorry, my english is not well, so I don't know if you can understand.

Encoding not recognized: 'UTF-8' (searched as: 'utf8')

I am trying to work via proxy, but got this exception.
How to solve it?

Error: Encoding not recognized: '"UTF-8"' (searched as: '"utf8"')
at Object.module.exports.getCodec (/home/nodeuser/node_modules/needle/node_modules/iconv-lite/index.js:36:23)
at Object.module.exports.fromEncoding (/home/nodeuser/node_modules/needle/node_modules/iconv-lite/index.js:7:22)
at Object.Needle.response_end (/home/nodeuser/node_modules/needle/lib/needle.js:240:38)
at IncomingMessage. (/home/nodeuser/node_modules/needle/lib/needle.js:187:16)
at IncomingMessage.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:870:14
at process._tickCallback (node.js:415:13)

Decoder output garbled text on occasions

We are running into issues where some part of the response body are garbled upon decoding, and more annoyingly it does not always happen to the same page, not even the same text. So it's difficult for us to determine whether this is problem for needle or iconv-lite.

For example: http://www.huanqiukexue.com/html/newgc/2014/1215/25011.html is a page with charset gb2312, and upon multiple fetch we see different result on decode.

(text between question mark symbol are garbled, hopefully it show up the same on all OS)

attempt 1:

2005年,美国加利福尼亚大学河滨分校的心理学家索尼娅•柳�┟锥�斯基(Sonja Lyubomirsky)和她的同事一起,对多项研究的结论进行了综述,这些研究显示幸福与成功呈正比。

attempt 2:

2005年,美国加利福尼亚大学河滨分校的心理学家索尼娅•柳博米尔斯基(Sonja Lyubomirsky)和她的同事一起,对多项研究的结论进行了综述,这些研究显示幸福与成功呈正比。

There are also instance where both attempts result in the same garbled text.

attempt 1:

那些能够产生“心流”的任务不能太单调沉闷,�荒苋萌瞬�生挫败感;它还要具有充分的挑战性,要求一个人全神贯注。

attempt 2:

那些能够产生“心流”的任务不能太单调沉闷,�荒苋萌瞬�生挫败感;它还要具有充分的挑战性,要求一个人全神贯注。

and we use needle similar to this:

    stream.on('data', function(chunk) {
        if (chunk === null) {
            return;
        }

        raw.push(chunk);
    });

    stream.on('end', function(err) {
        body = Buffer.concat(raw).toString();
    });

Integers in JSON are converted to strings

I sent a POST request using needle like this:

needle.post(SERVICE_SOCKET + '/verify',
          {
            message: {
              value1: 1.2456,
              value2: -100,
              value3: 100,
            }
          },
          {},
          function (err, resp) {
            done(err);
          });

The receiver then got an object like this:

{
  value1: '1.2456',
  value2: '-100',
  value3: '100'
}

But I was expecting the following (as I get it using restify):

{
  value1: 1.2456,
  value2: -100,
  value3: 100
}

As we are using both needle and restify clients I have to handle both data formats which is quite dissatisfying. Am I missing an option or is this a bug?

Overriding supplied Content-Type header

I specify the content type header in the options object as shown below.

var options = {
    compressed : true,
    headers : {
        'Content-Type' : 'application/soap+xml"'
    }
}

I then make a POST request with a string as the data argument. Needle overrides my content type with application/x-www-form-urlencoded.

I can get around this with the following hack at line 114 of needle.js

if ( config.headers['Content-Type'] === undefined ) {
   config.headers['Content-Type'] = content_type;
}

Post Request with basic authentication fails

I am using needle API to make POST request to REST API and it always returns 401 error , where as if i use the same credentials to make GET request it is successful. I am not sure what am i doing wrong. Or is it some problem with Needle API. Below is my code

var needle = require('needle');
var async = require('async');

var credentials = {
username: "myusername",
password:"mypassword"

}

var data = {
name:"Test Repo",

}

needle.post('https://bitbucket.org/api/1.0/repositories/accountName/repoName/issues/10/comments',credentials,data,function(err,resp,body){
console.log(resp.statusCode) //always returns 401 error.
});

needle.get('https://bitbucket.org/api/1.0/repositories/accountName/repoName/issues/10/comments',credentials,function(err,resp,body){
console.log(resp.statusCode) //always returns 200 error.
});

Requires Node 0.10.x but package.json implies 0.4.x

Needle seems to require stream.Transform which is new to Node v0.10.

Using Node 0.8.26:

util.js:538
  ctor.prototype = Object.create(superCtor.prototype, {
                                          ^
TypeError: Cannot read property 'prototype' of undefined
    at exports.inherits (util.js:538:43)
    at Object.<anonymous> (/path/node_modules/needle/lib/decoder.js:5:1)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:362:17)
    at require (module.js:378:17)
    at Object.<anonymous> (/path/node_modules/needle/lib/needle.js:17:19)
    at Module._compile (module.js:449:26)

missing cookie header on redirect

Hello...
I am Heepan Kim.

First of all, thank you for sharing your code.

Original source code line 208

// if redirect code is found, send a GET request to that location if enabled via 'follow' option
if ([301, 302].indexOf(resp.statusCode) != -1 && headers.location) {
  if (count <= config.follow)
    return self.send_request(++count, 'GET', url.resolve(uri, headers.location), config, null, callback);
  else if (config.follow > 0)
    return callback(new Error('Max redirects reached. Possible loop in: ' + headers.location));
}

This code not working on cookie based server.

So I suggest my code.

// if redirect code is found, send a GET request to that location if enabled via 'follow' option
if ([301, 302].indexOf(resp.statusCode) != -1 && headers.location) {
  if (count <= config.follow) {
    // --------------------------------------------------
    // must set cookie
    var cookies = headers['set-cookie'];
    if ( cookies ) {
      config.base_opts.headers.Cookie = cookies[0];
    }
    // --------------------------------------------------
    return self.send_request(++count, 'GET', url.resolve(uri, headers.location), config, null, callback);
  }
  else if (config.follow > 0)
    return callback(new Error('Max redirects reached. Possible loop in: ' + headers.location));
}

Cannot use proxy option to send https to the proxy

When I run needle with the proxy option, I can only get needle to work when setting the proxy to http and not https.

Sample options are shown below.
rpurl = "https://endpointurl.com";

options = {
auth: 'auto',
proxy: 'http://sampleproxy.com:8080',
multipart: true
};

In this example, needle sends http protocol to the proxy even though the endpoint url is requiring https. So the proxy gets http and is required to translate to https. This is causing us some problems in negotiating cipher suites. We have a work around right now but would prefer to be able to run end to end https from needle to the proxy to the endpoint url.

When I try "proxy: 'https://sampleproxy.com:8080'", I receive the following error.

Error:
[Error: 140735290954512:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:766:

Note that when running curl behind a proxy, curl initiates the https session with the proxy which simply passes the session out to the endpoint url. Curl appears to use the /etc/environment file proxy settings.

Saving binary file

Hello,

i am trying to download and save binary file (image).

needle.get(url, { output: path }, function (err, resp, body) {

});

But I have got the error:

{ [Error: ENOENT, open '$PATH_TO_FILE$FILENAME']
errno: 34,
code: 'ENOENT',
path: '$PATH_TO_FILE$FILENAME' }

Could you suggest why I am getting this?

p.s. i have existing directories on the fie path.
By the way, is there possibility to enable creating missed directories
recursively?

needle.delete fails when data=null

when setting data=null on a needle.delete - nginx returns 411 error content-length not set. All works fine if you set data=" " - notice there a space inside the quotes.

Buffer

When I send a POST request I'm getting back a Buffer in the request body. How do I handle this?

{ ...
resume: [Function],
read: [Function],
bytes: 0,
body: < Buffer > }

Mocha is reporting global leaks in needle

I was using needle in my app and CI was reporting Error: global leaks detected: self, request_opts, protocol. On drilling down it appears to be originating from needle module. Switched to request and it's fine now.

Unexpected behaviour

If .post() method is called with empty data object and multipart option as true then in callback function err param will be null and resp will be [Error: Empty multipart body. Invalid data.]

Example:

needle.post('http://localhost', {}, { multipart: true }, function(err, resp, body) {
  console.log(err); // null
  console.log(resp); // [Error: Empty multipart body. Invalid data.]
});

timeout not work

I use
needle.get('some url'
,{"timeout":20000,"headers" : {"Depth" : "1", "Content-Type": 'text/xml; charset="utf-8"'}}
,function(err,res){
....do something....
}).pipe(out);

when downloading large file and network Interrupt,callback is not fired.and timeout is not working too.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.