i am using netty 4.1 embeded in Java and trying to retrive Data from a clients POST request in the pipeline. I tried several options i found online but nothing works...
Maybe someone has a useful thought on this.
Regards and thanks for everyone who helps.
Pipeline:
p.addLast ("codec", new HttpServerCodec ());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("encoder", new HttpRequestEncoder());
p.addLast("handler",new InboundHandlerA());
Handler:
private static class InboundHandlerA extends ChannelInboundHandlerAdapter{
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Connected!");
ctx.fireChannelActive();
}
public void channelRead (ChannelHandlerContext channelHandlerCtxt, Object msg) throws Exception {
System.out.println(msg);
}
}
Recieving HTTP requests using netty is simple, you can do this with the following pipeline:
// Provides support for http objects:
p.addLast("codec", new HttpServerCodec());
// Deals with fragmentation in http traffic:
p.addLast("aggregator", new HttpObjectAggregator(Short.MAX_VALUE));
// Deals with optional compression of responses:
// p.addLast("aggregator", new HttpContentCompressor());
p.addLast("handler",new InboundHandlerA());
This can be used with a custom SimpleChannelInboundHandler<FullHttpRequest>:
public class InboundHandlerA extends SimpleChannelInboundHandler<FullHttpRequest> {
#Override
public void channelActive(ChannelHandlerContext ctx) {
super.channelActive(ctx);
System.out.println("Connected!");
}
// Please keep in mind that this method
will be renamed to messageReceived(ChannelHandlerContext, I) in 5.0.
#Override
public void channelRead0 (ChannelHandlerContext ctx,
FullHttpRequest msg) throws Exception {
// Check for invalid http data:
if(msg.getDecoderResult() != DecoderResult.SUCCESS ) {
ctx.close();
return;
}
System.out.println("Recieved request!");
System.out.println("HTTP Method: " + msg.getMethod());
System.out.println("HTTP Version: " + msg.getProtocolVersion());
System.out.println("URI: " + msg.getUri());
System.out.println("Headers: " + msg.headers());
System.out.println("Trailing headers: " + msg.trailingHeaders());
ByteBuf data = msg.content();
System.out.println("POST/PUT length: " + data.readableBytes());
System.out.println("POST/PUT as string: ");
System.out.println("-- DATA --");
System.out.println(data.toString(StandardCharsets.UTF_8));
System.out.println("-- DATA END --");
// Send response back so the browser won't timeout
ByteBuf responseBytes = ctx.alloc().buffer();
responseBytes.writeBytes("Hello World".getBytes());
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, responseBytes);
response.headers().set(HttpHeaders.Names.CONTENT_TYPE,
"text/plain");
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
response.content().readableBytes());
response.headers().set(HttpHeaders.Names.CONNECTION,
HttpHeaders.Values.KEEP_ALIVE);
ctx.write(response);
}
}
The code above is printing out all the details on a incoming message, including the post data. If you require only the post data, you can add a simple if-statement to filter on a POST response type
Related
Im trying to create a netty based server to use SSE especification on client
First I create a handler(NotifyHandler) that extends from SimpleChannelInboundHandler, and extends from my own Pub system, when a notification arrives at onNotificationRecibed is written on context output channel.
private ChannelHandlerContext context = null;
private Publisher p = null;
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
this.context = ctx;
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK);
HttpHeaders headers = response.headers();
headers.set(HttpHeaders.Names.CONTENT_TYPE, "text/event-stream");
headers.set(HttpHeaders.Names.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate");
headers.set(HttpHeaders.Names.PRAGMA, HttpHeaders.Values.NO_CACHE);
headers.set(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
ctx.writeAndFlush(response);
Pub.getInstance().suscribe(this);
}
#Override
public void onNotificationRecibed(String type, Map<String, Object> data) {
context.writeAndFlush("event:"+type);
context.writeAndFlush("data:"+data.toString());
context.flush();
}
On the initializer:
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new NotifyHandler());
}
I canĀ“t make it work, i trying to find some examples or usages on this streams but nothing seems to work. Anyone can point me on the right direction?. Sorry for my english, and thanks for your time.
I had the same problem. As it is described in specification, each field has to be separated by new line and each message by another new line.
#Override
public void onNotificationRecibed(String type, Map<String, Object> data) {
context.writeAndFlush("event:" + type + "\n");
context.writeAndFlush("data:" + data.toString() + "\n\n");
context.flush();
}
In addition to #MPazik's suggestion, as you're using HttpResponseEncoder, you will need to write HttpContent to the ChannelHandlerContext. For example:
#Override
public void onNotificationRecibed(String type, Map<String, Object> data) {
final StringBuilder msg = new StringBuilder(1024); // 1kb
msg.append("event:").append(type).append("\n");
msg.append"data:").append(data.toString().append("\n\n"));
final ByteBuf buffer = Unpooled.copiedBuffer(msg.toString(), StandardCharsets.UTF_8);
context.writeAndFlush(new DefaultHttpContent(buffer));
}
All of the classes above are either standard JDK or Netty.
I have just started netty and I am really disappointed with the documentation present on
their website.
I am trying to connect to an URL using Netty.. I took the time client example from their website and changed it as per my requirement..
Code :
public class NettyClient {
public static void main(String[] args) throws Exception {
String host = "myUrl.com/v1/parma?param1=value";
int port = 443;
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ClientHandler());
ch.pipeline().addLast("encoder", new HttpRequestEncoder());
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
But the problem is that that it expects only the url without the query parameters.. How can I pass query parameters with the URL?
and please provide me some link of a good documentation for Netty 4..
EDIT
Client code after referring the example mentioned in the answer :
URI uri = new URI("myUrl.com/v1/parma?param1=value");
String scheme = uri.getScheme() == null? "http" : uri.getScheme();
String host = "myUrl.com";
int port = 443;
boolean ssl = "https".equalsIgnoreCase(scheme);
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new NettyClientInitializer(ssl));
// Make the connection attempt.
Channel ch = b.connect(host, port).sync().channel();
// Prepare the HTTP request.
HttpRequest request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
request.headers().set(HttpHeaders.Names.HOST, host);
request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
//request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
/*// Set some example cookies.
request.headers().set(
HttpHeaders.Names.COOKIE,
ClientCookieEncoder.encode(
new DefaultCookie("my-cookie", "foo"),
new DefaultCookie("another-cookie", "bar")));
*/
// Send the HTTP request.
ch.writeAndFlush(request);
// Wait for the server to close the connection.
ch.closeFuture().sync();
} finally {
// Shut down executor threads to exit.
group.shutdownGracefully();
}
handler code :
public class ClientHandler extends SimpleChannelInboundHandler<HttpObject> {
#Override
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse response = (HttpResponse) msg;
System.out.println("STATUS: " + response.getStatus());
System.out.println("VERSION: " + response.getProtocolVersion());
System.out.println();
if (!response.headers().isEmpty()) {
for (String name: response.headers().names()) {
for (String value: response.headers().getAll(name)) {
System.out.println("HEADER: " + name + " = " + value);
}
}
System.out.println();
}
if (HttpHeaders.isTransferEncodingChunked(response)) {
System.out.println("CHUNKED CONTENT {");
} else {
System.out.println("CONTENT {");
}
}
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
System.out.print(content.content().toString(CharsetUtil.UTF_8));
System.out.flush();
if (content instanceof LastHttpContent) {
System.out.println("} END OF CONTENT");
}
}
}
#Override
public void exceptionCaught(
ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
initializer code :
public class NettyClientInitializer extends ChannelInitializer<SocketChannel> {
private final boolean ssl;
public NettyClientInitializer(boolean ssl) {
this.ssl = ssl;
}
#Override
public void initChannel(SocketChannel ch) throws Exception {
// Create a default pipeline implementation.
ChannelPipeline p = ch.pipeline();
p.addLast("log", new LoggingHandler(LogLevel.INFO));
// Enable HTTPS if necessary.
/*
if (ssl) {
SSLEngine engine =
SecureChatSslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);
p.addLast("ssl", new SslHandler(engine));
}
*/
p.addLast("codec", new HttpClientCodec());
// Remove the following line if you don't want automatic content decompression.
// p.addLast("inflater", new HttpContentDecompressor());
// Uncomment the following line if you don't want to handle HttpChunks.
p.addLast("aggregator", new HttpObjectAggregator(1048576));
p.addLast("handler", new ClientHandler());
}
}
Your code only handles the low-level connection at the moment. Indeed at this level only the hostname and port can be used.
For the HTTP request You have to construct an HttpRequest object and send it over the channel. In this request object You define the query parameters and all such things.
There is a bunch of example code about HTTP client functionality on Netty website - have a a look!
In this example the problem lies with the constructor for the DefaultHttpRequest parameter of uri.getRawPath(). The invocation of this method does NOT return the query parameters. It works in this case as there were no query parameters in the Snoop example. By substituting uri.toASCIIString() returns the encoded uri complete with the query parameters. To prove this to yourself, rather than having a method invocation within a method invocation (a bad idea for just this reason, add the statement
String url = uri.getRawPath();
and look at the string url.
I had the exact same problem. I've done this natively in servlets for years but now was trying to do it in a Netty app.
Consequently the new code would be:
String path = uri.toASCIIString();
// Prepare the HTTP request.
HttpRequest request = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, path);
When you build the request, you need to add the query to the path. Instead of
uri.getRawPath()
use
uri.getRawPath() + "?" + uri.getRawQuery()
It is in AKKA documentation written that
... Actors should not block (i.e. passively wait while occupying a Thread) on some external entity, which might be a lock, a network socket, etc. The blocking operations should be done in some special-cased thread which sends messages to the actors which shall act on them.
source http://doc.akka.io/docs/akka/2.0/general/actor-systems.html#Actor_Best_Practices
I have found the following information at the moment :
I read Sending outbound HTTP request from Akka / Scala and checked the example at https://github.com/dsciamma/fbgl1
I found following article http://nurkiewicz.blogspot.de/2012/11/non-blocking-io-discovering-akka.html explaining how to use https://github.com/AsyncHttpClient/async-http-client non blocking http client with akka. But is written in Scala.
How can i write an actor that make non-blocking http requests?
It must downlad a remote url page as file and than send the generated file object to the master actor. master actor then sends this request to parser actor to parse the file...
In the last response, Koray is using a wrong reference for the sender, the correct way to do it is:
public class ReduceActor extends UntypedActor {
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof URI) {
URI url = (URI) message;
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
final ActorRef sender = getSender();
asyncHttpClient.prepareGet(url.toURL().toString()).execute(new AsyncCompletionHandler<Response>() {
#Override
public Response onCompleted(Response response) throws Exception {
File f = new File("e:/tmp/crawler/" + UUID.randomUUID().toString() + ".html");
// Do something with the Response
// ...
// System.out.println(response1.getStatusLine());
FileOutputStream fao = new FileOutputStream(f);
IOUtils.copy(response.getResponseBodyAsStream(), fao);
System.out.println("File downloaded " + f);
sender.tell(new WordCount(f));
return response;
}
#Override
public void onThrowable(Throwable t) {
// Something wrong happened.
}
});
} else
unhandled(message);
}
Checkout this other thread of akka: https://stackoverflow.com/a/11899690/575746
I have implemented this in this way.
public class ReduceActor extends UntypedActor {
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof URI) {
URI url = (URI) message;
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
asyncHttpClient.prepareGet(url.toURL().toString()).execute(new AsyncCompletionHandler<Response>() {
#Override
public Response onCompleted(Response response) throws Exception {
File f = new File("e:/tmp/crawler/" + UUID.randomUUID().toString() + ".html");
// Do something with the Response
// ...
// System.out.println(response1.getStatusLine());
FileOutputStream fao = new FileOutputStream(f);
IOUtils.copy(response.getResponseBodyAsStream(), fao);
System.out.println("File downloaded " + f);
getSender().tell(new WordCount(f));
return response;
}
#Override
public void onThrowable(Throwable t) {
// Something wrong happened.
}
});
} else
unhandled(message);
}
I'm trying create a simple audio stream server like a concept proof, but I'm having some dificulties.
I'm streaming a single file to start, I searched but didn't found enought information to create a audio stream server, so I just created a simple server based on my little knowledge about servers. I've created it with netty passing the stream to ChunkedStream object and wrote it on channel:
public class CastServerHandler extends SimpleChannelHandler {
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
HttpRequest request = (HttpRequest) e.getMessage();
if (request.getMethod() != GET) {
sendError(ctx, METHOD_NOT_ALLOWED);
return;
}
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
System.out.println(response.toString());
Channel channel = e.getChannel();
channel.write(response);
ChannelFuture writeFuture;
StreamSource source = StreamSource.getInstance();
ChunkedStream stream = new ChunkedStream(source.getLiveStream());
writeFuture = channel.write(stream);
writeFuture.addListener(new ChannelFutureProgressListener() {
public void operationComplete(ChannelFuture future) {
System.out.println("terminou");
future.getChannel().close();
}
public void operationProgressed(ChannelFuture future, long amount,
long current, long total) {
System.out.println("Transferido: " + current + " de " + total);
}
});
if (!isKeepAlive(request)) {
writeFuture.addListener(ChannelFutureListener.CLOSE);
}
}
private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
response.setContent(ChannelBuffers.copiedBuffer(
"Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8));
// Close the connection as soon as the error message is sent.
ctx.getChannel().write(response)
.addListener(ChannelFutureListener.CLOSE);
}
private void writeLiveStream(Channel channel) {
StreamSource source = StreamSource.getInstance();
ChunkedStream stream = new ChunkedStream(source.getLiveStream());
channel.write(stream);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
e.getChannel().close();
}
}
Ufortunately, I didn't successfully streamed the audio directly to web browser, so I tryied to figure out what icecast returns as response to web browser, and it return these properties in header:
Cache-Control:no-cache
Content-Type:application/ogg
Server:Icecast 2.3.2
ice-audio-info:samplerate=44100;channels=2;quality=3%2e00
icy-description:Stream de teste
icy-genre:Rock
icy-name:Radio teste Brevleq
icy-pub:0
Is there a simple way netty use to put these content in HttpResponse header (specially Content-type:applicatio/ogg)?? I hope this is the problem...
See the API of HttpResponse.
It has setHeader method.
I'd consider going with a straight binary protocol, and creating an HTTP interface only for a proxy. There's no reason to deal with a text based protocol for something like this.
I'm using GWT and creating a HTTP request but I'm having issues accessing the file from the production version, even though it's working fine in development. My main program has the following for the request on the client side.
static final String dataURL = GWT.getModuleBaseURL() + "interpretData";
public void onModuleLoad() {
requestData(dataURL, new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
RootPanel.get(holderId).add(new Label(error + ": Asynchronous call failed - " + caught.getLocalizedMessage()));
return;
}
public void onSuccess(String JSON){
try{
// code executed on success
} catch (Exception e) {
RootPanel.get(holderId).add(new Label(error + ": " + e.getMessage()));
return;
}
}
});
}
public static void requestData(final String url, final AsyncCallback<String> callback) {
// create a request for the xml data on the server
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
builder.setCallback(new RequestCallback() {
public void onError(Request request, Throwable exception) {
callback.onFailure(exception);
}
public void onResponseReceived(Request request, Response response) {
try {
final int responseCode = response.getStatusCode() / 100;
if (url.startsWith("file:/") || (responseCode == 2) || (responseCode == 0)){
callback.onSuccess(response.getText());
} else {
callback.onFailure(new IllegalStateException(" Http Error: #" + response.getStatusCode() + " - " + response.getStatusText()));
}
} catch (Throwable e) {
callback.onFailure(e);
}
}
});
On the server side, I have:
public class interpretData extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
response.setContentType("application/json");
// code to return a String
}
Finally, my XML file has the following in it:
<servlet>
<servlet-name>interpretData</servlet-name>
<servlet-class>com.gmod.caeli.server.interpretData</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>interpretData</servlet-name>
<url-pattern>/caeli/interpretData</url-pattern>
</servlet-mapping>
In the end, I can access the file from: http://127.0.0.1:8888/caeli/interpretData so the development version is completely fine, but I don't know how to get it to work in production (the URL I'm calling for production is file:///~/workspace/Caeli/war/caeli/interpretData) I've searched for examples, but I haven't found any clues to what I'm doing wrong. I tried using setting it up with tomcat and I got a 404 error there too. I feel like I'm missing something small, so hopefully this is enough information for someone to notice something wrong.
From my experience and research, the URL that your attempting to request in production (file:///...) cannot be requested by the web browser via an Ajax call, anchor tag, javascript etc. It might be a little confusing/misleading as you can enter that URL into your browser manually and get the expected result, however this local resource request is not allowed by the browser.