5

I'm prototyping an app using the native mongo rest api where Node returns about 400K of json. I use the following to maket he request to mongo's native api and return the result:

http.request(options, function(req)
  {
    req.on('data', function(data)
      {
console.log(data,data.rows);
        response.send( 200, data );
      }
    );
  }
)
.on('error', function(error)
  {
console.log('error\t',error);
    response.send(500, error);
  }
)
.end();

When I hit http://localhost:8001/api/testdata via curl, the response is proper (both what is outputted to Node's console from the console.log and what is received by curl). But when I hit it via ajax in my app, the stream is…interupted, even data outputted to Node's console (Terminal) is odd: It has multiple EOFs, and the Network > response for the call in chrome's dev tools ends at the first EOF.

One other strange thing: data looks like:

{
    "offset": 0,
    "rows": [ … ]
}

but in neither Node nor client-side (angular) can I reference data.rows (it returns undefined). typeof data returns [object Object].

EDIT The request headers for both curl and angular (as reported by Node) are:

req.headers: {
  'x-action': '',
  'x-ns': 'test.headends',
  'content-type': 'text/plain;charset=utf-8',
  connection: 'close',
  'content-length': '419585'
}

EDIT I checked response headers in both angular and curl directly (instead of from Node), annnd there's a disagreement (same output from both curl and angular directly instead of from node):

access-control-allow-headers: "Origin, X-Requested-With, Content-Type, Accept"
access-control-allow-methods: "OPTIONS,GET,POST,PUT,DELETE"
access-control-allow-origin: "*"
connection: "keep-alive"
content-length: "65401" // <---------------- too small!
content-type: "application/octet-stream"
//             ^-- if i force "application/json"
// with response.json() instead of response.send() in Node,
// the client displays octets (and it takes 8s instead of 0s)
date: "Mon, 15 Jul 2013 18:36:50 GMT"
etag: ""-207110537""
x-powered-by: "Express"
Jakob Jingleheimer
  • 30,952
  • 27
  • 76
  • 126
  • 1
    I would try to read the headers and request body in Node and have it trace/print out what's coming through in both cases. That is try to understand what the diff is of the request information or the way the requests are occurring, then search for specific answers to ways to resolve the differences between your curl request and the ajax request (setting headers in angular etc.) – shaunhusain Jul 12 '13 at 19:44
  • @shaunhusain, I checked the headers, and they're the same, so I also checked the entire `req` object (with FileMerge) and it returns _0 differences_ – Jakob Jingleheimer Jul 15 '13 at 18:16
  • 1
    well that is spooky :) sorry I don't know what to tell you, if the data sent to a machine is exactly the same and it responds in two different ways I have no idea what's going on. Perhaps we have angered the node gods or the angular gods (perhaps some lower-level OSI god) and we should repent. How are you capturing your data? I've used Charles with lots of success in the past (are you sure this is happening?) – shaunhusain Jul 15 '13 at 18:30
  • @shaunhusain, HAHA! too funny. Well, I just tried spitting out the response headers from Angular, and they are different from what Node reports. Updated my question. Is charles a packet sniffer (like Wireshark)? – Jakob Jingleheimer Jul 15 '13 at 19:20
  • I think the console output from node was a red herring: it is appended immediately after the last character of the previous log, making it appear as if it were one chuck when in fact it is 7 chucks of lengths: `65401, 65536, 65536, 65536, 65536, 65536, 26504`, which coincidentally total to 419585. – Jakob Jingleheimer Jul 15 '13 at 19:37
  • :) Yes Charles is like Wireshark it just works very well with AMF encoded Data (stuff from Flash) and deals with SSL nicely so I used it a lot. It also does lots of nice stuff to give you files to save from streams and decodes just about everything for you (images music etc. to some degree). It's free for like 30min at a time, license is about $50 I think, I paid for it eventually cause I used it daily. It acts as a software proxy though and I'm pretty sure is available on all OSes – shaunhusain Jul 15 '13 at 19:41
  • 1
    Sorry forgot the link you can get Charles here: http://www.charlesproxy.com/ it'll show you the messages as simply requests and responses all wrapped up, you can make multiple sessions and use the filter to show only traffic you're interested in (particular protocol and domains etc.) You can also right click the rows you see in charles and save the request/response or have it repeat the query 100 times if you want. It's come in handy tons of times. – shaunhusain Jul 15 '13 at 19:48

2 Answers2

11

Node's http.request() returns data in chunks for streaming (would be nice if they explicitly state this). Thus it's necessary to write each chunk to the body of Express's response, listen for the end of the http request (which is not really documented), and then call response.end() to actually finish the response.

var req = http.request(options, function(res)
  {
    res.on( 'data', function(chunk) { response.write(chunk); } );
    res.on( 'end', function() { response.end(); } );
  }
);
req.on('error', function(error) { … });
req.end();

Where response is Express's response the the initial client request (curl or angular's ajax call).

Community
  • 1
  • 1
Jakob Jingleheimer
  • 30,952
  • 27
  • 76
  • 126
  • 1
    i think this just saved my life along with [AngularUI typeahead conflict with $resource](http://stackoverflow.com/questions/15930339/how-to-tie-angular-uis-typeahead-with-a-server-via-http-for-server-side-optimi) – Ayush Oct 13 '16 at 18:36
  • 1
    You can actually use pipe() for a streamlined implementation. There's a tiny npm package that will handle the boilerplating (i think it's called Request). – Jakob Jingleheimer Oct 13 '16 at 18:53
  • @jacod any npm link for that, this is what I get on [npmjs](https://www.npmjs.com/package/pipe) – Ayush Oct 13 '16 at 19:55
  • @AyushKSingh The package I used is [Request](https://www.npmjs.com/package/request), but there's another one that is much simpler. I'll try to find it. – Jakob Jingleheimer Oct 14 '16 at 04:14
  • 2
    @AyushKSingh Found it: [proxy-middleware](https://www.npmjs.com/package/proxy-middleware) – Jakob Jingleheimer Oct 14 '16 at 05:15
0
resp.set('content-type' , 'application/json'); 
          
const stream = db.Country.findAllWithStream();

// console.log(stream);
stream.on('data', chunk => {
  stream.pipe(resp); 
});
          
stream.on('end', () => {
  console.log('\n\nEND!!!!!');
  resp.end()
});
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
  • 1
    Your answer could be improved by adding more information on what the code does and how it helps the OP. – Tyler2P Sep 22 '22 at 15:06