/** * Sends this response to all the passed channels as a {@link TextWebSocketFrame} * @param listener A channel future listener to attach to each channel future. Ignored if null. * @param channels The channels to send this response to * @return An array of the futures for the write of this response to each channel written to */ public ChannelFuture[] send(ChannelFutureListener listener, Channel...channels) { if(channels!=null && channels.length>0) { Set<ChannelFuture> futures = new HashSet<ChannelFuture>(channels.length); if(opCode==null) { opCode = "ok"; } TextWebSocketFrame frame = new TextWebSocketFrame(this.toChannelBuffer()); for(Channel channel: channels) { if(channel!=null && channel.isWritable()) { ChannelFuture cf = Channels.future(channel); if(listener!=null) cf.addListener(listener); channel.getPipeline().sendDownstream(new DownstreamMessageEvent(channel, cf, frame, channel.getRemoteAddress())); futures.add(cf); } } return futures.toArray(new ChannelFuture[futures.size()]); } return EMPTY_CHANNEL_FUTURE_ARR; }
/** * Downstream handler which takes care of the POJO to network packet translation */ @Override public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if(e.getMessage() instanceof BGPv4Packet) { ChannelBuffer buffer = ((BGPv4Packet)e.getMessage()).encodePacket(); log.info("writing packet " + e.getMessage()); if(buffer != null) { ctx.sendDownstream(new DownstreamMessageEvent(e.getChannel(), e.getFuture(), buffer, e.getRemoteAddress())); } } else { log.error("expected a {} message payload, got a {} message payload", BGPv4Packet.class.getName(), e.getMessage().getClass().getName()); } }
@Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { // If this tunnel has already been opened then don't try open it if (tunnelId == null) { if (LOG.isDebugEnabled()) LOG.debug("connection to " + e.getValue() + " succeeded - sending open tunnel request"); final HttpRequest request = HttpTunnelMessageUtils.createOpenTunnelRequest(tunnelChannel.getServerHostName(), tunnelChannel.getUserAgent()); final Channel channel = ctx.getChannel(); final DownstreamMessageEvent event = new DownstreamMessageEvent(channel, Channels.future(channel), request, channel.getRemoteAddress()); queuedWrites.offer(new TimedMessageEventWrapper(event, requestTimer.time())); pendingRequestCount.incrementAndGet(); } // Send our first chunk of data this.sendQueuedData(ctx); }
@Override public void log(ChannelEvent e) { if (e instanceof MessageEvent) { MessageEvent event = (MessageEvent) e; StringBuilder msg = new StringBuilder(); msg.append("[").append(String.format("%08X", e.getChannel().getId())).append(": "); msg.append(((InetSocketAddress) e.getChannel().getLocalAddress()).getPort()); if (e instanceof DownstreamMessageEvent) { msg.append(" > "); } else { msg.append(" < "); } if (event.getRemoteAddress() != null) { msg.append(((InetSocketAddress) event.getRemoteAddress()).getHostString()); } else { msg.append("null"); } msg.append("]"); if (event.getMessage() instanceof ChannelBuffer) { msg.append(" HEX: "); msg.append(ChannelBuffers.hexDump((ChannelBuffer) event.getMessage())); } Log.debug(msg.toString()); } }
/** * {@inheritDoc} * @see org.jboss.netty.channel.ChannelDownstreamHandler#handleDownstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent) */ @Override public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { Channel channel = e.getChannel(); if(!channel.isOpen()) return; if(!(e instanceof MessageEvent)) { ctx.sendDownstream(e); return; } Object message = ((MessageEvent)e).getMessage(); if((message instanceof HttpResponse) || (message instanceof WebSocketFrame)) { ctx.sendDownstream(e); return; } if((message instanceof ChannelBuffer)) { ctx.sendDownstream(new DownstreamMessageEvent(channel, Channels.future(channel), new TextWebSocketFrame((ChannelBuffer)message), channel.getRemoteAddress())); } else if((message instanceof JsonNode)) { String json = marshaller.writeValueAsString(message); ctx.sendDownstream(new DownstreamMessageEvent(channel, Channels.future(channel), new TextWebSocketFrame(json), channel.getRemoteAddress())); } else if((message instanceof ChannelBufferizable)) { ctx.sendDownstream(new DownstreamMessageEvent(channel, Channels.future(channel), new TextWebSocketFrame(((Netty3ChannelBufferizable)message).toChannelBuffer()), channel.getRemoteAddress())); } else if((message instanceof CharSequence)) { ctx.sendDownstream(new DownstreamMessageEvent(channel, Channels.future(channel), new TextWebSocketFrame(marshaller.writeValueAsString(message)), channel.getRemoteAddress())); } else if((message instanceof JSONResponse)) { ObjectMapper mapper = (ObjectMapper)((JSONResponse)message).getChannelOption("mapper", TSDBTypeSerializer.DEFAULT.getMapper()); ctx.sendDownstream(new DownstreamMessageEvent(channel, Channels.future(channel), new TextWebSocketFrame(mapper.writeValueAsString(message)), channel.getRemoteAddress())); } else { ctx.sendUpstream(e); } }
@Override public void log(ChannelEvent e) { if(e instanceof MessageEvent) { Object message = ((MessageEvent)e).getMessage(); if(message instanceof RemoteRun.AgentToMaster) { log.debug("{} {}: {}", e.getChannel().toString(), e instanceof DownstreamMessageEvent ? "WRITE" : "RECEIVED", toString((RemoteRun.AgentToMaster)message)); } else if(message instanceof RemoteRun.MasterToAgent) { log.debug("{} {}: {}", e.getChannel().toString(), e instanceof DownstreamMessageEvent ? "WRITE" : "RECEIVED", toString((RemoteRun.MasterToAgent)message)); } else { log.debug("{}", e); } } }
@SuppressWarnings("ThrowableResultOfMethodCallIgnored") @Test public void shouldFailOriginalConnectFutureAndCloseChannelIfHandshakeWriteFutureFails() throws Exception { ChannelFuture originalConnectFuture = Channels.future(channel); // pretend that a connectRequested event was sent over this channel handler.setConnectFutureForUnitTestOnly(originalConnectFuture); // signal that the connect succeeded handler.channelConnected(ctx, new UpstreamChannelStateEvent(channel, ChannelState.CONNECTED, new InetSocketAddress(0))); // instead of passing events upstream, the handler should // attempt to write an handshake out and send nothing upstream ArgumentCaptor<DownstreamMessageEvent> downstreamEvent = ArgumentCaptor.forClass(DownstreamMessageEvent.class); verify(ctx, atLeastOnce()).getChannel(); verify(ctx).sendDownstream(downstreamEvent.capture()); verifyNoMoreInteractions(ctx); // fail the write DownstreamMessageEvent handshakeEvent = downstreamEvent.getValue(); IOException cause = new IOException(); handshakeEvent.getFuture().setFailure(cause); // verify that the original future failed as well assertThat(originalConnectFuture.isDone(), equalTo(true)); assertThat(originalConnectFuture.isSuccess(), equalTo(false)); assertThat((IOException) originalConnectFuture.getCause(), is(cause)); // and that the channel was closed verify(channel).close(); }
@Override public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("request to send data for tunnel " + tunnelId); final ChannelFuture future = e.getFuture(); if (disconnecting.get()) { if (LOG.isWarnEnabled()) LOG.warn("rejecting write request for tunnel " + tunnelId + " received after disconnect requested"); final Exception error = new IllegalStateException("tunnel is closing"); future.setFailure(error); return; } final ChannelBuffer data = (ChannelBuffer) e.getMessage(); final HttpRequest request = HttpTunnelMessageUtils.createSendDataRequest(tunnelChannel.getServerHostName(), tunnelId, data, tunnelChannel.getUserAgent()); final Channel channel = ctx.getChannel(); final DownstreamMessageEvent translatedEvent = new DownstreamMessageEvent(channel, future, request, channel.getRemoteAddress()); queuedWrites.offer(new TimedMessageEventWrapper(translatedEvent, requestTimer.time())); if (pendingRequestCount.incrementAndGet() == 1) this.sendQueuedData(ctx); else { if (LOG.isDebugEnabled()) LOG.debug("write request for tunnel " + tunnelId + " queued"); } }
private HttpRequest checkIsMessageEventContainingHttpRequest( ChannelEvent event) { assertTrue(event instanceof DownstreamMessageEvent); DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event; assertTrue(messageEvent.getMessage() instanceof HttpRequest); return (HttpRequest) messageEvent.getMessage(); }
private void checkIsSendDataRequestWithData(ChannelEvent event, ChannelBuffer data) { assertTrue(event instanceof DownstreamMessageEvent); DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event; assertTrue(messageEvent.getMessage() instanceof HttpRequest); HttpRequest request = (HttpRequest) messageEvent.getMessage(); assertTrue(HttpTunnelMessageUtils.isSendDataRequest(request, USER_AGENT)); assertEquals(data.readableBytes(), HttpHeaders.getContentLength(request)); ChannelBuffer content = request.getContent(); NettyTestUtils.assertEquals(data, content); }
protected void writeDownstream(ChannelHandlerContext ctx, Object data) { ChannelFuture f = Channels.succeededFuture(ctx.getChannel()); SocketAddress address = ctx.getChannel().getRemoteAddress(); Channel c = ctx.getChannel(); ctx.sendDownstream(new DownstreamMessageEvent(c, f, data, address)); }
@Test public void shouldIndicateThatConnectSucceededIfHandshakeWriteSucceeds() throws Exception { ChannelFuture originalConnectFuture = Mockito.mock(ChannelFuture.class); InetSocketAddress connectedAddress = new InetSocketAddress(0); // pretend that a connectRequested event was sent over this channel handler.setConnectFutureForUnitTestOnly(originalConnectFuture); // signal that the connect succeeded handler.channelConnected(ctx, new UpstreamChannelStateEvent(channel, ChannelState.CONNECTED, connectedAddress)); // check the order of operations for the handshake write ArgumentCaptor<DownstreamMessageEvent> downstreamEvent = ArgumentCaptor.forClass(DownstreamMessageEvent.class); InOrder preWriteEventOrder = Mockito.inOrder(ctx); preWriteEventOrder.verify(ctx, atLeastOnce()).getChannel(); preWriteEventOrder.verify(ctx).sendDownstream(downstreamEvent.capture()); preWriteEventOrder.verifyNoMoreInteractions(); // check that the handshake is valid DownstreamMessageEvent handshakeEvent = downstreamEvent.getValue(); assertThat(Handshakers.getServerIdFromHandshake((ChannelBuffer) handshakeEvent.getMessage(), mapper), equalTo(SELF)); // mark the handshake write as having succeeded handshakeEvent.getFuture().setSuccess(); // check the order of operations after the handshake write succeeded // the following actions must be performed, in order: // 1. the handler is removed // 2. the original connect future is triggered // 3. a channelConnected event is forwarded on ArgumentCaptor<UpstreamChannelStateEvent> upstreamEvent = ArgumentCaptor.forClass(UpstreamChannelStateEvent.class); InOrder postWriteEventOrder = Mockito.inOrder(originalConnectFuture, ctx, pipeline); postWriteEventOrder.verify(ctx).getPipeline(); postWriteEventOrder.verify(pipeline).remove(handler); postWriteEventOrder.verify(originalConnectFuture).setSuccess(); postWriteEventOrder.verify(ctx).sendUpstream(upstreamEvent.capture()); postWriteEventOrder.verifyNoMoreInteractions(); // and that an appropriate upstream event was sent UpstreamChannelStateEvent connectedEvent = upstreamEvent.getValue(); assertThat(connectedEvent.getChannel(), is(channel)); assertThat(connectedEvent.getState(), is(ChannelState.CONNECTED)); assertThat((InetSocketAddress) connectedEvent.getValue(), is(connectedAddress)); }
private void writeDownstream(final ChannelHandlerContext ctx, final Object data) { ChannelFuture f = Channels.succeededFuture(ctx.getChannel()); SocketAddress address = ctx.getChannel().getRemoteAddress(); Channel c = ctx.getChannel(); ctx.sendDownstream(new DownstreamMessageEvent(c, f, data, address)); }
public static <T> T checkIsDownstreamMessageEvent(ChannelEvent event, Class<T> expectedMessageType) { assertTrue(event instanceof DownstreamMessageEvent); DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event; assertTrue(expectedMessageType.isInstance(messageEvent.getMessage())); return expectedMessageType.cast(messageEvent.getMessage()); }