Vert.x httpClient/webClient process response chunk by chunk or as stream - jvm

I have application with vert.x and asyncHttpClient. I want to replace asyncHttpClient with vert.x httpClient (webClient), but i need to have ability to process response by chunks or by small parts of data (like org.asynchttpclient.AsyncHandler in AsyncHttpClient) or like a stream. Is it possible? I look at custom BodyCodec, but i can`t understand data flow in custom WriteStream.

You can't do this easily with Vert.x Web Client which is designed for buffering responses.
However you can do this with the Vert.x Http Client:
client.request(HttpMethod.GET, "some-uri", ar1 -> {
if (ar1.succeeded()) {
HttpClientRequest request = ar1.result();
request.send(ar2 -> {
HttpClientResponse response = ar2.result();
response.handler(buffer -> {
System.out.println("Received a part of the response body: " + buffer);
});
});
}
});

Related

Asp.net core endpoint "hangs" when sending response as chunked

I'm trying to send a response as Transfer-Encoding: chunked in asp.net core but it fails. Browser hangs (request remains pending) and then says ERR_INCOMPLETE_CHUNKED_ENCODING. curl says that connection was closed. How to send body chunked correctly ?
This is my endpoint:
endpoints.MapGet("/GetMessage", async context =>
{
context.Response.Headers["Transfer-Encoding"] = "chunked";
await context.Response.WriteAsync("Hello World!");
// send the first part
await context.Response.Body.FlushAsync();
// delay to simulate 'work'
await Task.Delay(2000);
// send the second part
await context.Response.WriteAsync("Hello World!");
});
EDIT (my findings):
The reason I need chunked transfer is because I'm using a technique called http streaming. It is leaving response open and sending parts of file as they become available.
What I've learned is that I don't need to set Transfer-Encoding header, I don't need to 'flush' and I need to use Response.Body.WriteAsync since I need to write bytes because file is audio (not text). The final piece is that I need to give it some audio mime type or browser will wait for the entire body before calling progress callback function. There's some under the hood stuff happening that I don't understand but this is good enough for me.
My server test code:
endpoints.MapGet("/GetMessage", async context =>
{
context.Response.Headers["Content-Type"] = "audio/aac";
var buf = System.Text.Encoding.ASCII.GetBytes("Hellow world !");
await context.Response.Body.WriteAsync(buf, 0, buf.Length);
await Task.Delay(3000);
await context.Response.Body.WriteAsync(buf, 0, buf.Length);
});
Html test page:
<html>
<body>
<h3>Hello word</h3>
<script>
let xhr = new XMLHttpRequest()
xhr.onprogress = e => console.log("progress " + e.currentTarget.response)
xhr.onload = e => console.log("load " + e.currentTarget.response)
xhr.open("GET", "/GetMessage");
xhr.send();
</script>
</body>
</html>
This works as expected. Console prints first part right away and the entire body at the end.

hapi 18 eventsourcing not working without stream.end()

Try to archive:
I try to use the HTML5 EventSourcing API https://developer.mozilla.org/de/docs/Web/API/EventSource to push events to my client application (javascript).
working example code with plain node http:
With a plain example node implementation it works perfectly and as expected. Example code: https://www.html5rocks.com/en/tutorials/eventsource/basics/
Problem:
When i try to integrate EventSourcing (or SSE) into my API endpoint which is based on hapi (currently using latest - 18.1.0) it does not work.
My route handler code mixed with some code i found:
const Stream = require('stream');
class ResponseStream extends Stream.PassThrough {
setCompressor (compressor) {
this._compressor = compressor;
}
}
const stream = new ResponseStream();
let data = 0;
setInterval(() => {
data++;
stream.write('event: message\n');
stream.write('data:' + data + '\n\n');
console.log('write data...', data);
// stream.end();
}, 1000);
return h
.response(stream)
.type('text/event-stream')
.header('Connection', 'keep-alive')
.header('Cache-Control', 'no-cache')
Findings:
I already searched and it seems since hapi 17.x there they exposed the flush method for the compressor < https://github.com/hapijs/hapi/issues/3658 >, section features.
But it still does not working.
They only way it sends a message is to uncomment the stream.end() line after sending the data. The problem obviously is that i cant send further data if i close the stream :/.
If i kill the server (with stream.end() line commented) the data gets transmitted to the client in a "single transmission". I think the problem is is still somewhere with the gzip buffering even when flushing the stream.
There are some code examples in the hapi github but i got none working with hapi 17 or 18 (all exmaples where hapi =< 16) :/
Someone know how to solve the problem or has a working EventSource example with latest hapi? I would kindly appreciate any help or suggestions.
Edit - Solution
The solution from the post below does work but i had also an nginx reverse proxy in front of my api endpoint it seems the main problem was not my code it was the nginx which had also buffered the eventsource messages.
To avoid this sort of problem add in your hapi: X-Accel-Buffering: no; and it works flawless
Well I just tested with Hapi 18.1.0 and managed to create a working example.
This is my handler code:
handler: async (request, h) => {
class ResponseStream extends Stream.PassThrough {
setCompressor(compressor) {
this._compressor = compressor;
}
}
const stream = new ResponseStream();
let data = 0;
setInterval(() => {
data++;
stream.write('event: message\n');
stream.write('data:' + data + '\n\n');
console.log('write data...', data);
stream._compressor.flush();
}, 1000);
return h.response(stream)
.type('text/event-stream')
}
and this is client code just to test
var evtSource = new EventSource("http://localhost/");
evtSource.onmessage = function(e) {
console.log("Data", + e.data);
};
evtSource.onerror = function(e) {
console.log("EventSource failed.", e);
};
These are the resources that where I found my way to working example
https://github.com/hapijs/hapi/blob/70f777bd2fbe6e2462847f05ee10a7206571e280/test/transmit.js#L1816
https://github.com/hapijs/hapi/issues/3599#issuecomment-485190525

How to consume WCF soap web service in node.js

I tried lot of examples available in the net using node module wcf.js. But could not get any appropriate result. I'm using the below url
https://webservice.kareo.com/services/soap/2.1/KareoServices.svc?wsdl
Any one who can explain me with the help of code will be really helpful. I want to know how to access the wsdl in node.js
Thanks.
Please have a look at wcf.js
In short you can follow these steps:
npm install wcf.js
Write your code like this:
code
var Proxy = require('wcf.js').Proxy;
var BasicHttpBinding = require('wcf.js').BasicHttpBinding;
var binding = new BasicHttpBinding();
//Ensure the proxy variable created below has a working wsdl link that actually loads wsdl
var proxy = new Proxy(binding, "http://YourHost/YourService.svc?wsdl");
/*Ensure your message below looks like a valid working SOAP UI request*/
var message = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:sil='http://YourNamespace'>" +
"<soapenv:Header/>" +
"<soapenv:Body>" +
"<sil:YourMethod>" +
"<sil:YourParameter1>83015348-b9dc-41e5-afe2-85e19d3703f9</sil:YourParameter1>" +
"<sil:YourParameter2>IMUT</sil:YourParameter2>" +
"</sil:YourMethod>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";
/*The message that you created above, ensure it works properly in SOAP UI rather copy a working request from SOAP UI*/
/*proxy.send's second argument is the soap action; you can find the soap action in your wsdl*/
proxy.send(message, "http://YourNamespace/IYourService/YourMethod", function (response, ctx) {
console.log(response);
/*Your response is in xml and which can either be used as it is of you can parse it to JSON etc.....*/
});
You don't have that many options.
You'll probably want to use one of:
node-soap
douche
soapjs
i tried node-soap to get INR USD rate with following code.
app.get('/getcurr', function(req, res) {
var soap = require('soap');
var args = {FromCurrency: 'USD', ToCurrency: 'INR'};
var url = "http://www.webservicex.net/CurrencyConvertor.asmx?WSDL";
soap.createClient(url, function(err, client) {
client.ConversionRate(args, function(err, result) {
console.log(result);
});
});
});
Code Project has got a neat sample which uses wcf.js for which api's are wcf like so no need to learn new paradigm.
I think that an alternative would be to:
use a tool such as SoapUI to record input and output xml messages
use node request to form input xml message to send (POST) the request to the web service (note that standard javascript templating mechanisms such as ejs or mustache could help you here) and finally
use an XML parser to deserialize response data to JavaScript objects
Yes, this is a rather dirty and low level approach but it should work without problems
You'll probably want to use one of:
node-soap
douche
soapjs
Aslo, there's an existing question.
In my case, I used https://www.npmjs.com/package/soap. By default forceSoap12Headers option was set to false which prevented node-soap to generate correct soap message according to SOAP 1.2. Check for more details: I am confused about SOAP namespaces. After I set it to true, I was able to make a call to .NET WCF service. Here is a TypeScript code snipper that worked for me.
import * as soap from 'soap';
import { IOptions } from 'soap';
// ...
const url = 'https://www.your-domain.com/stock.svc?wsdl';
const opt: IOptions = {
forceSoap12Headers: true,
};
soap.createClient(url, opt, (err, client: soap.Client) => {
if (err) {
throw err;
}
const wsSecurityOptions = {
hasTimeStamp: false,
};
const wsSecurity = new soap.WSSecurity('username', 'password', wsSecurityOptions);
client.setSecurity(wsSecurity);
client.addSoapHeader({ Action: 'http://tempuri.org/API/GetStockDetail' }, undefined, 'wsa', 'http://www.w3.org/2005/08/addressing');
client.addSoapHeader({ To: 'https://www.your-domain.com/stock.svc' }, undefined, 'wsa', 'http://www.w3.org/2005/08/addressing');
const args = {
symbol: 'GOOG',
};
client.GetStockDetail(
args,
(requestErr, result) => {
if (requestErr) {
throw requestErr;
}
console.log(result);
},
);
});
Here couple links to the documentation of node-soap usage:
https://github.com/vpulim/node-soap/tree/master/test
https://github.com/vpulim/node-soap

Calling wcf webservice from node js with module or plugins

I have a requirement to call WCF dotnet webservice from node js , Can any of you suggest the best module avaiable now.
Just some references should be enough
Thanks
If the WCF service has an endpoint that returns JSON you should be able to hit it with a basic node.js HTTP request.
Below is a simple sample to execute make an HTTP request from node. This should get you started. Replace "www.google.com" with the name of the server where the WCF server lives and update the path to point to the svc file:
var http = require('http');
var options = {
hostname: 'www.google.com',
port: 80,
path: '/',
method: 'GET'
};
var req = http.request(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('CHUNK: ' + chunk);
});
res.on('end', function () {
console.log('DONE');
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.end();
If this is a simple service (simple xml, no advanced message level security) then I recommend to just send the raw Xml using request.
If the xml is complex then you can use node-soap.
If there is message-level security use wcf.js.

Using Node JS to proxy http and modify response

I'm trying to write a front end to an API service with Node JS.
I'd like to be able to have a user point their browser at my node server and make a request. The node script would modify the input to the request, call the api service, then modify the output and pass back to the user.
I like the solution here (with Express JS and node-http-proxy) as it passes the cookies and headers directly from the user through my site to the api server.
proxy request in node.js / express
I see how to modify the input to the request, but i can't figure out how to modify the response. Any suggestions?
transformer-proxy could be useful here. I'm the author of this plugin and I'm answering here because I found this page when looking for the same question and wasn't satisfied with harmon as I don't want to manipulate HTML.
Maybe someone else is looking for this and finds it useful.
Harmon is designed to plug into node-http-proxy https://github.com/No9/harmon
It uses trumpet and so is stream based to work around any buffering problems.
It uses an element and attribute selector to enable manipulation of a response.
This can be used to modify output response.
See here: https://github.com/nodejitsu/node-http-proxy/issues/382#issuecomment-14895039
http-proxy-interceptor is a middleware I wrote for this very purpose. It allows you to modify the http response using one or more transform streams. There are tons of stream-based packages available (like trumpet, which harmon uses), and by using streams you can avoid buffering the entire response.
var httpProxy = require('http-proxy');
var modifyResponse = require('http-proxy-response-rewrite');
var proxy = httpProxy.createServer({
target:'target server IP here',
});
proxy.listen(8001);
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
proxy.on('proxyRes', function (proxyRes, req, res) {
modifyResponse(res, proxyRes.headers['content-encoding'], function (body) {
if (body && (body.indexOf("<process-order-response>")!= -1)) {
var beforeTag = "</receipt-text>"; //tag after which u can add data to
// response
var beforeTagBody = body.substring(0,(body.indexOf(beforeTag) + beforeTag.length));
var requiredXml = " <ga-loyalty-rewards>\n"+
"<previousBalance>0</previousBalance>\n"+
"<availableBalance>0</availableBalance>\n"+
"<accuruedAmount>0</accuruedAmount>\n"+
"<redeemedAmount>0</redeemedAmount>\n"+
"</ga-loyalty-rewards>";
var afterTagBody = body.substring(body.indexOf(beforeTag)+ beforeTag.length)+
var res = [];
res.push(beforeTagBody, requiredXml, afterTagBody);
console.log(res.join(""));
return res.join("");
}
return body;
});
});