use of org.jboss.netty.handler.codec.http.HttpChunk 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.handler.codec.http.HttpChunk in project load-balancer by RestComm.
the class HttpResponseHandler method handleHttpResponse.
private void handleHttpResponse(ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
if (e.getMessage() instanceof HttpChunkTrailer) {
HttpChunkTrailer chunk = (HttpChunkTrailer) e.getMessage();
balancerRunner.balancerContext.httpBytesToClient.addAndGet(chunk.getContent().capacity());
if (chunk.isLast())
readingChunks = false;
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel channel = null;
if (ac != null)
channel = ac.getChannel();
if (channel != null) {
if (logger.isDebugEnabled())
logger.debug("Send chunked response from : " + e.getChannel().getRemoteAddress() + " to : " + channel.getRemoteAddress() + " capacity : " + chunk.getContent().capacity());
channel.write(chunk);
}
} else if (!readingChunks || !(e.getMessage() instanceof DefaultHttpChunk)) {
response = (HttpResponse) e.getMessage();
int stsusCode = response.getStatus().getCode();
if (stsusCode > 399 && stsusCode < 600) {
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
if (ac != null && ac.isCheckNeed()) {
InetSocketAddress address = (InetSocketAddress) e.getChannel().getRemoteAddress();
InvocationContext invocationContext = balancerRunner.getLatestInvocationContext();
KeySip keySip = new KeySip(address.getHostString(), address.getPort(), false);
Node currNode = invocationContext.sipNodeMap(false).get(keySip);
if (currNode != null) {
currNode.setBad(true);
String instanseId = currNode.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID);
if (instanseId != null)
invocationContext.httpNodeMap.get(new KeyHttp(currNode.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID))).setBad(true);
logger.error("Error code [" + stsusCode + "] detected in HTTP response. From node : " + currNode + ". This node will marked as bad.");
String currInstanceId = (String) currNode.getProperties().get("Restcomm-Instance-Id");
if (currInstanceId != null)
logger.warn("Node : " + invocationContext.httpNodeMap.remove(new KeyHttp(currInstanceId)) + " from httpNodeMap");
// invocationContext.badSipNodeMap(false).put(keySip, currNode);
invocationContext.balancerAlgorithm.nodeRemoved(currNode);
}
// TODO CHECK REQUEST AND REMOVE NODE
}
}
updateStatistic(response);
balancerRunner.balancerContext.httpBytesToClient.addAndGet(response.getContent().capacity());
if (response.isChunked()) {
readingChunks = true;
}
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel channel = null;
if (ac != null)
channel = ac.getChannel();
if (channel != null) {
if (logger.isDebugEnabled())
logger.debug("Send response from : " + e.getChannel().getRemoteAddress() + " to : " + channel.getRemoteAddress() + " capacity : " + response.getContent().capacity());
channel.write(response);
}
Set<String> headers = response.getHeaderNames();
if (headers.contains("Sec-WebSocket-Protocol")) {
if (response.getHeader("Sec-WebSocket-Protocol").equalsIgnoreCase("sip")) {
if (logger.isDebugEnabled()) {
logger.debug("WebSocket response");
}
wsVersion = response.getHeader(Names.SEC_WEBSOCKET_VERSION);
// Modify the Server pipeline
ChannelPipeline p = channel.getPipeline();
websocketModifyServerPipelineFactory = new WebsocketModifyServerPipelineFactory();
websocketModifyServerPipelineFactory.upgradeServerPipelineFactory(p, wsVersion);
}
}
} else {
HttpChunk chunk = (HttpChunk) e.getMessage();
balancerRunner.balancerContext.httpBytesToClient.addAndGet(chunk.getContent().capacity());
if (chunk.isLast())
readingChunks = false;
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel channel = null;
if (ac != null)
channel = ac.getChannel();
if (channel != null) {
if (logger.isDebugEnabled())
logger.debug("Send chunked response from : " + e.getChannel().getRemoteAddress() + " to : " + channel.getRemoteAddress() + " capacity : " + chunk.getContent().capacity());
channel.write(chunk);
}
}
}
use of org.jboss.netty.handler.codec.http.HttpChunk in project load-balancer by RestComm.
the class HttpRequestHandler method handleHttpRequest.
private void handleHttpRequest(ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
String currAddress = e.getRemoteAddress().toString();
semaphore = semaphoreMap.get(currAddress);
if (semaphore == null) {
semaphore = new Semaphore(1);
Semaphore tempSemaphore = semaphoreMap.putIfAbsent(currAddress, semaphore);
if (tempSemaphore != null)
semaphore = tempSemaphore;
}
try {
semaphore.acquire();
} catch (InterruptedException ex) {
}
if (!readingChunks && e.getMessage() instanceof HttpRequest) {
request = (HttpRequest) e.getMessage();
if (logger.isDebugEnabled()) {
logger.debug("Request URI accessed: " + request.getUri() + " channel " + e.getChannel());
}
if (HttpChannelAssociations.urlRewriteFilter != null)
HttpChannelAssociations.urlRewriteFilter.doFilter(request, e);
AdvancedChannel currentAC = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel associatedChannel = null;
if (currentAC != null)
associatedChannel = currentAC.getChannel();
InvocationContext invocationContext = balancerRunner.getLatestInvocationContext();
// SIPNode node = null;
try {
// TODO: If WebSocket request, choose a NODE that is able to
// handle WebSocket requests (has a websocket connector)
node = invocationContext.balancerAlgorithm.processHttpRequest(request);
} catch (Exception ex) {
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
logger.warn("Problem in balancer algorithm", ex);
writeResponse(e, HttpResponseStatus.INTERNAL_SERVER_ERROR, "Load Balancer Error: Exception in the balancer algorithm:\n" + sw.toString());
return;
}
if (node == null) {
if (logger.isInfoEnabled()) {
logger.info("Service unavailable. No server is available.");
}
writeResponse(e, HttpResponseStatus.SERVICE_UNAVAILABLE, IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream("500.html")));
return;
}
if (associatedChannel != null && associatedChannel.isConnected()) {
semaphore.release();
associatedChannel.write(request);
} else {
e.getChannel().getCloseFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture arg0) throws Exception {
closeChannelPair(arg0.getChannel());
}
});
// Start the connection attempt.
ChannelFuture future = null;
Set<String> headers = request.getHeaderNames();
if (headers.contains("Sec-WebSocket-Protocol")) {
if (request.getHeader("Sec-WebSocket-Protocol").equalsIgnoreCase("sip")) {
if (logger.isDebugEnabled()) {
logger.debug("New SIP over WebSocket request. WebSocket uri: " + request.getUri());
logger.debug("Dispatching WebSocket request to node: " + node.getIp() + " port: " + node.getProperties().get("wsPort"));
}
wsrequest = true;
wsVersion = request.getHeader(Names.SEC_WEBSOCKET_VERSION);
websocketServerPipelineFactory = new WebsocketModifyClientPipelineFactory();
future = HttpChannelAssociations.inboundBootstrap.connect(new InetSocketAddress(node.getIp(), Integer.parseInt(node.getProperties().get("wsPort"))));
}
} else {
if (!isSecured) {
if (logger.isDebugEnabled()) {
logger.debug("Dispatching HTTP request to node: " + node.getIp() + " port: " + node.getProperties().get("httpPort"));
}
future = HttpChannelAssociations.inboundBootstrap.connect(new InetSocketAddress(node.getIp(), Integer.parseInt(node.getProperties().get("httpPort"))));
} else {
if (logger.isDebugEnabled()) {
logger.debug("Dispatching HTTPS request to node: " + node.getIp() + " port: " + node.getProperties().get("sslPort"));
}
future = HttpChannelAssociations.inboundSecureBootstrap.connect(new InetSocketAddress(node.getIp(), Integer.parseInt(node.getProperties().get("sslPort"))));
}
}
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture arg0) throws Exception {
Channel channel = arg0.getChannel();
if (pattern != null && pattern.matcher(request.getUri()).find()) {
logger.info("request : " + request.getUri() + " matches to pattern : " + pattern);
HttpChannelAssociations.channels.put(new AdvancedChannel(e.getChannel(), true), new AdvancedChannel(channel, true));
HttpChannelAssociations.channels.put(new AdvancedChannel(channel, true), new AdvancedChannel(e.getChannel(), true));
} else {
HttpChannelAssociations.channels.put(new AdvancedChannel(e.getChannel(), false), new AdvancedChannel(channel, false));
HttpChannelAssociations.channels.put(new AdvancedChannel(channel, false), new AdvancedChannel(e.getChannel(), false));
}
if (request.isChunked()) {
readingChunks = true;
}
semaphore.release();
channel.write(request);
if (wsrequest) {
if (logger.isDebugEnabled()) {
logger.debug("This is a websocket request, changing the pipeline");
}
// Modify the Client Pipeline - Phase 1
ChannelPipeline p = channel.getPipeline();
websocketServerPipelineFactory.upgradeClientPipelineFactoryPhase1(p, wsVersion);
}
channel.getCloseFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture arg0) throws Exception {
closeChannelPair(arg0.getChannel());
}
});
}
});
}
} else {
HttpChunk chunk = (HttpChunk) e.getMessage();
if (chunk.isLast()) {
readingChunks = false;
}
semaphore.release();
HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel())).getChannel().write(chunk);
if (logger.isDebugEnabled())
logger.debug("Send chunked request from : " + e.getChannel().getLocalAddress() + " to : " + e.getChannel().getRemoteAddress() + " capacity : " + chunk.getContent().capacity());
}
}
use of org.jboss.netty.handler.codec.http.HttpChunk in project cdap by caskdata.
the class HttpRequestHandler method messageReceived.
@Override
public void messageReceived(final ChannelHandlerContext ctx, MessageEvent event) throws Exception {
if (channelClosed) {
return;
}
final Channel inboundChannel = event.getChannel();
Object msg = event.getMessage();
if (msg instanceof HttpChunk) {
// This case below should never happen this would mean we get Chunks before HTTPMessage.
if (chunkSender == null) {
throw new HandlerException(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Chunk received and event sender is null");
}
chunkSender.send(msg);
} else if (msg instanceof HttpRequest) {
// Discover and forward event.
HttpRequest request = (HttpRequest) msg;
request = applyProxyRules(request);
// Suspend incoming traffic until connected to the outbound service.
inboundChannel.setReadable(false);
WrappedDiscoverable discoverable = getDiscoverable(request, (InetSocketAddress) inboundChannel.getLocalAddress());
// If no event sender, make new connection, otherwise reuse existing one.
MessageSender sender = discoveryLookup.get(discoverable);
if (sender == null || !sender.isConnected()) {
InetSocketAddress address = discoverable.getSocketAddress();
ChannelFuture future = clientBootstrap.connect(address);
final Channel outboundChannel = future.getChannel();
outboundChannel.getPipeline().addAfter("request-encoder", "outbound-handler", new OutboundHandler(inboundChannel));
if (Arrays.equals(Constants.Security.SSL_URI_SCHEME.getBytes(), discoverable.getPayload())) {
SSLContext clientContext;
try {
clientContext = SSLContext.getInstance("TLS");
clientContext.init(null, PermissiveTrustManagerFactory.getTrustManagers(), null);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new RuntimeException("SSL is enabled for app-fabric but failed to create SSLContext in the router " + "client.", e);
}
SSLEngine engine = clientContext.createSSLEngine();
engine.setUseClientMode(true);
engine.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" });
outboundChannel.getPipeline().addFirst("ssl", new SslHandler(engine));
LOG.trace("Adding ssl handler to the pipeline.");
}
sender = new MessageSender(inboundChannel, future);
discoveryLookup.put(discoverable, sender);
// Remember the in-flight outbound channel
inboundChannel.setAttachment(outboundChannel);
outboundChannel.getCloseFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
inboundChannel.getPipeline().execute(new Runnable() {
@Override
public void run() {
// close the inbound channel as well if it carries the in-flight request
if (outboundChannel.equals(inboundChannel.getAttachment())) {
closeOnFlush(inboundChannel);
}
}
});
}
});
} else {
Channel outboundChannel = (Channel) inboundChannel.getAttachment();
if (outboundChannel != null) {
// Set outbound channel to be readable in case previous request has set it as non-readable
outboundChannel.setReadable(true);
}
}
// Send the message.
sender.send(request);
inboundChannel.setReadable(true);
//Save the channelFuture for subsequent chunks
if (request.isChunked()) {
chunkSender = sender;
}
} else {
super.messageReceived(ctx, event);
}
}
use of org.jboss.netty.handler.codec.http.HttpChunk in project cdap by caskdata.
the class IdleEventProcessor method messageReceived.
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Object message = e.getMessage();
if (message instanceof HttpResponse) {
HttpResponse response = (HttpResponse) message;
if (!response.isChunked()) {
requestInProgress = false;
}
} else if (message instanceof HttpChunk) {
HttpChunk chunk = (HttpChunk) message;
if (chunk.isLast()) {
requestInProgress = false;
}
}
ctx.sendUpstream(e);
}
Aggregations