use of org.jboss.netty.channel.ExceptionEvent in project graylog2-server by Graylog2.
the class NettyTransport method getBaseChannelHandlers.
/**
* Subclasses can override this to add additional ChannelHandlers to the pipeline to support additional features.
* <p/>
* Some common use cases are to add SSL/TLS, connection counters or throttling traffic shapers.
*
* @param input
* @return the list of initial channelhandlers to add to the {@link org.jboss.netty.channel.ChannelPipelineFactory}
*/
protected LinkedHashMap<String, Callable<? extends ChannelHandler>> getBaseChannelHandlers(final MessageInput input) {
LinkedHashMap<String, Callable<? extends ChannelHandler>> handlerList = Maps.newLinkedHashMap();
handlerList.put("exception-logger", new Callable<ChannelHandler>() {
@Override
public ChannelHandler call() throws Exception {
return new SimpleChannelUpstreamHandler() {
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
if ("Connection reset by peer".equals(e.getCause().getMessage())) {
log.trace("{} in Input [{}/{}] (channel {})", e.getCause().getMessage(), input.getName(), input.getId(), e.getChannel());
} else {
log.error("Error in Input [{}/{}] (channel {})", input.getName(), input.getId(), e.getChannel(), e.getCause());
}
super.exceptionCaught(ctx, e);
}
};
}
});
handlerList.put("packet-meta-dumper", new Callable<ChannelHandler>() {
@Override
public ChannelHandler call() throws Exception {
return new PacketInformationDumper(input);
}
});
handlerList.put("traffic-counter", Callables.returning(throughputCounter));
return handlerList;
}
use of org.jboss.netty.channel.ExceptionEvent in project druid by druid-io.
the class NettyHttpClient method go.
@Override
public <Intermediate, Final> ListenableFuture<Final> go(final Request request, final HttpResponseHandler<Intermediate, Final> handler, final Duration requestReadTimeout) {
final HttpMethod method = request.getMethod();
final URL url = request.getUrl();
final Multimap<String, String> headers = request.getHeaders();
final String requestDesc = method + " " + url;
if (log.isDebugEnabled()) {
log.debug("[%s] starting", requestDesc);
}
// Block while acquiring a channel from the pool, then complete the request asynchronously.
final Channel channel;
final String hostKey = getPoolKey(url);
final ResourceContainer<ChannelFuture> channelResourceContainer = pool.take(hostKey);
final ChannelFuture channelFuture = channelResourceContainer.get().awaitUninterruptibly();
if (!channelFuture.isSuccess()) {
// Some other poor sap will have to deal with it...
channelResourceContainer.returnResource();
return Futures.immediateFailedFuture(new ChannelException("Faulty channel in resource pool", channelFuture.getCause()));
} else {
channel = channelFuture.getChannel();
// In case we get a channel that never had its readability turned back on.
channel.setReadable(true);
}
final String urlFile = StringUtils.nullToEmptyNonDruidDataString(url.getFile());
final HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, method, urlFile.isEmpty() ? "/" : urlFile);
if (!headers.containsKey(HttpHeaders.Names.HOST)) {
httpRequest.headers().add(HttpHeaders.Names.HOST, getHost(url));
}
// If Accept-Encoding is set in the Request, use that. Otherwise use the default from "compressionCodec".
if (!headers.containsKey(HttpHeaders.Names.ACCEPT_ENCODING)) {
httpRequest.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, compressionCodec.getEncodingString());
}
for (Map.Entry<String, Collection<String>> entry : headers.asMap().entrySet()) {
String key = entry.getKey();
for (String obj : entry.getValue()) {
httpRequest.headers().add(key, obj);
}
}
if (request.hasContent()) {
httpRequest.setContent(request.getContent());
}
final long readTimeout = getReadTimeout(requestReadTimeout);
final SettableFuture<Final> retVal = SettableFuture.create();
if (readTimeout > 0) {
channel.getPipeline().addLast(READ_TIMEOUT_HANDLER_NAME, new ReadTimeoutHandler(timer, readTimeout, TimeUnit.MILLISECONDS));
}
channel.getPipeline().addLast(LAST_HANDLER_NAME, new SimpleChannelUpstreamHandler() {
private volatile ClientResponse<Intermediate> response = null;
// Chunk number most recently assigned.
private long currentChunkNum = 0;
// Suspend and resume watermarks (respectively: last chunk number that triggered a suspend, and that was
// provided to the TrafficCop's resume method). Synchronized access since they are not always accessed
// from an I/O thread. (TrafficCops can be called from any thread.)
private final Object watermarkLock = new Object();
private long suspendWatermark = -1;
private long resumeWatermark = -1;
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
if (log.isDebugEnabled()) {
log.debug("[%s] messageReceived: %s", requestDesc, e.getMessage());
}
try {
Object msg = e.getMessage();
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
if (log.isDebugEnabled()) {
log.debug("[%s] Got response: %s", requestDesc, httpResponse.getStatus());
}
HttpResponseHandler.TrafficCop trafficCop = resumeChunkNum -> {
synchronized (watermarkLock) {
resumeWatermark = Math.max(resumeWatermark, resumeChunkNum);
if (suspendWatermark >= 0 && resumeWatermark >= suspendWatermark) {
suspendWatermark = -1;
channel.setReadable(true);
long backPressureDuration = System.nanoTime() - backPressureStartTimeNs;
log.debug("[%s] Resumed reads from channel (chunkNum = %,d).", requestDesc, resumeChunkNum);
return backPressureDuration;
}
}
// If we didn't resume, don't know if backpressure was happening
return 0;
};
response = handler.handleResponse(httpResponse, trafficCop);
if (response.isFinished()) {
retVal.set((Final) response.getObj());
}
assert currentChunkNum == 0;
possiblySuspendReads(response);
if (!httpResponse.isChunked()) {
finishRequest();
}
} else if (msg instanceof HttpChunk) {
HttpChunk httpChunk = (HttpChunk) msg;
if (log.isDebugEnabled()) {
log.debug("[%s] Got chunk: %sB, last=%s", requestDesc, httpChunk.getContent().readableBytes(), httpChunk.isLast());
}
if (httpChunk.isLast()) {
finishRequest();
} else {
response = handler.handleChunk(response, httpChunk, ++currentChunkNum);
if (response.isFinished() && !retVal.isDone()) {
retVal.set((Final) response.getObj());
}
possiblySuspendReads(response);
}
} else {
throw new ISE("Unknown message type[%s]", msg.getClass());
}
} catch (Exception ex) {
log.warn(ex, "[%s] Exception thrown while processing message, closing channel.", requestDesc);
if (!retVal.isDone()) {
retVal.set(null);
}
channel.close();
channelResourceContainer.returnResource();
throw ex;
}
}
private void possiblySuspendReads(ClientResponse<?> response) {
if (!response.isContinueReading()) {
synchronized (watermarkLock) {
suspendWatermark = Math.max(suspendWatermark, currentChunkNum);
if (suspendWatermark > resumeWatermark) {
channel.setReadable(false);
backPressureStartTimeNs = System.nanoTime();
log.debug("[%s] Suspended reads from channel (chunkNum = %,d).", requestDesc, currentChunkNum);
}
}
}
}
private void finishRequest() {
ClientResponse<Final> finalResponse = handler.done(response);
if (!finalResponse.isFinished() || !finalResponse.isContinueReading()) {
throw new ISE("[%s] Didn't get a completed ClientResponse Object from [%s] (finished = %s, continueReading = %s)", requestDesc, handler.getClass(), finalResponse.isFinished(), finalResponse.isContinueReading());
}
if (!retVal.isDone()) {
retVal.set(finalResponse.getObj());
}
removeHandlers();
channel.setReadable(true);
channelResourceContainer.returnResource();
}
@Override
public void exceptionCaught(ChannelHandlerContext context, ExceptionEvent event) {
if (log.isDebugEnabled()) {
final Throwable cause = event.getCause();
if (cause == null) {
log.debug("[%s] Caught exception", requestDesc);
} else {
log.debug(cause, "[%s] Caught exception", requestDesc);
}
}
retVal.setException(event.getCause());
// response is non-null if we received initial chunk and then exception occurs
if (response != null) {
handler.exceptionCaught(response, event.getCause());
}
try {
if (channel.isOpen()) {
channel.close();
}
} catch (Exception e) {
log.warn(e, "Error while closing channel");
} finally {
channelResourceContainer.returnResource();
}
}
@Override
public void channelDisconnected(ChannelHandlerContext context, ChannelStateEvent event) {
if (log.isDebugEnabled()) {
log.debug("[%s] Channel disconnected", requestDesc);
}
// response is non-null if we received initial chunk and then exception occurs
if (response != null) {
handler.exceptionCaught(response, new ChannelException("Channel disconnected"));
}
channel.close();
channelResourceContainer.returnResource();
if (!retVal.isDone()) {
log.warn("[%s] Channel disconnected before response complete", requestDesc);
retVal.setException(new ChannelException("Channel disconnected"));
}
}
private void removeHandlers() {
if (readTimeout > 0) {
channel.getPipeline().remove(READ_TIMEOUT_HANDLER_NAME);
}
channel.getPipeline().remove(LAST_HANDLER_NAME);
}
});
channel.write(httpRequest).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
channel.close();
channelResourceContainer.returnResource();
if (!retVal.isDone()) {
retVal.setException(new ChannelException(StringUtils.format("[%s] Failed to write request to channel", requestDesc), future.getCause()));
}
}
}
});
return retVal;
}
use of org.jboss.netty.channel.ExceptionEvent in project druid by druid-io.
the class ChannelResourceFactory method generate.
@Override
public ChannelFuture generate(final String hostname) {
log.debug("Generating: %s", hostname);
URL url;
try {
url = new URL(hostname);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
final String host = url.getHost();
final int port = url.getPort() == -1 ? url.getDefaultPort() : url.getPort();
final ChannelFuture retVal;
final ChannelFuture connectFuture;
if (proxyConfig != null) {
final ChannelFuture proxyFuture = bootstrap.connect(new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort()));
connectFuture = Channels.future(proxyFuture.getChannel());
final String proxyUri = StringUtils.format("%s:%d", host, port);
DefaultHttpRequest connectRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, proxyUri);
if (proxyConfig.getUser() != null) {
connectRequest.headers().add("Proxy-Authorization", Request.makeBasicAuthenticationString(proxyConfig.getUser(), proxyConfig.getPassword()));
}
proxyFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f1) {
if (f1.isSuccess()) {
final Channel channel = f1.getChannel();
channel.getPipeline().addLast(DRUID_PROXY_HANDLER, new SimpleChannelUpstreamHandler() {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Object msg = e.getMessage();
final ChannelPipeline pipeline = ctx.getPipeline();
pipeline.remove(DRUID_PROXY_HANDLER);
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
if (HttpResponseStatus.OK.equals(httpResponse.getStatus())) {
// When the HttpClientCodec sees the CONNECT response complete, it goes into a "done"
// mode which makes it just do nothing. Swap it with a new instance that will cover
// subsequent requests
pipeline.replace("codec", "codec", new HttpClientCodec());
connectFuture.setSuccess();
} else {
connectFuture.setFailure(new ChannelException(StringUtils.format("Got status[%s] from CONNECT request to proxy[%s]", httpResponse.getStatus(), proxyUri)));
}
} else {
connectFuture.setFailure(new ChannelException(StringUtils.format("Got message of type[%s], don't know what to do.", msg.getClass())));
}
}
});
channel.write(connectRequest).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f2) {
if (!f2.isSuccess()) {
connectFuture.setFailure(new ChannelException(StringUtils.format("Problem with CONNECT request to proxy[%s]", proxyUri), f2.getCause()));
}
}
});
} else {
connectFuture.setFailure(new ChannelException(StringUtils.format("Problem connecting to proxy[%s]", proxyUri), f1.getCause()));
}
}
});
} else {
connectFuture = bootstrap.connect(new InetSocketAddress(host, port));
}
if ("https".equals(url.getProtocol())) {
if (sslContext == null) {
throw new IllegalStateException("No sslContext set, cannot do https");
}
final SSLEngine sslEngine = sslContext.createSSLEngine(host, port);
final SSLParameters sslParameters = new SSLParameters();
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
sslEngine.setSSLParameters(sslParameters);
sslEngine.setUseClientMode(true);
final SslHandler sslHandler = new SslHandler(sslEngine, SslHandler.getDefaultBufferPool(), false, timer, sslHandshakeTimeout);
// https://github.com/netty/netty/issues/160
sslHandler.setCloseOnSSLException(true);
final ChannelFuture handshakeFuture = Channels.future(connectFuture.getChannel());
connectFuture.getChannel().getPipeline().addLast("connectionErrorHandler", new SimpleChannelUpstreamHandler() {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
final Channel channel = ctx.getChannel();
if (channel == null) {
// For the case where this pipeline is not attached yet.
handshakeFuture.setFailure(new ChannelException(StringUtils.format("Channel is null. The context name is [%s]", ctx.getName())));
return;
}
handshakeFuture.setFailure(e.getCause());
if (channel.isOpen()) {
channel.close();
}
}
});
connectFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f) {
if (f.isSuccess()) {
final ChannelPipeline pipeline = f.getChannel().getPipeline();
pipeline.addFirst("ssl", sslHandler);
sslHandler.handshake().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f2) {
if (f2.isSuccess()) {
handshakeFuture.setSuccess();
} else {
handshakeFuture.setFailure(new ChannelException(StringUtils.format("Failed to handshake with host[%s]", hostname), f2.getCause()));
}
}
});
} else {
handshakeFuture.setFailure(new ChannelException(StringUtils.format("Failed to connect to host[%s]", hostname), f.getCause()));
}
}
});
retVal = handshakeFuture;
} else {
retVal = connectFuture;
}
return retVal;
}
Aggregations