2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.nettyutil;
10 import static org.mockito.ArgumentMatchers.any;
11 import static org.mockito.ArgumentMatchers.eq;
12 import static org.mockito.Mockito.doNothing;
13 import static org.mockito.Mockito.doReturn;
14 import static org.mockito.Mockito.mock;
15 import static org.mockito.Mockito.timeout;
16 import static org.mockito.Mockito.times;
17 import static org.mockito.Mockito.verify;
18 import static org.opendaylight.netconf.nettyutil.AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR;
19 import static org.opendaylight.netconf.nettyutil.AbstractChannelInitializer.NETCONF_MESSAGE_FRAME_ENCODER;
21 import io.netty.buffer.ByteBuf;
22 import io.netty.buffer.Unpooled;
23 import io.netty.channel.ChannelHandlerContext;
24 import io.netty.channel.ChannelInboundHandlerAdapter;
25 import io.netty.channel.ChannelOutboundHandler;
26 import io.netty.channel.ChannelOutboundHandlerAdapter;
27 import io.netty.channel.ChannelPromise;
28 import io.netty.channel.embedded.EmbeddedChannel;
29 import io.netty.handler.ssl.SslHandler;
30 import io.netty.util.HashedWheelTimer;
31 import io.netty.util.concurrent.Future;
32 import io.netty.util.concurrent.Promise;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Optional;
37 import org.junit.Assert;
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 import org.mockito.Mock;
42 import org.mockito.Mockito;
43 import org.mockito.junit.MockitoJUnitRunner;
44 import org.opendaylight.netconf.api.NetconfSessionListener;
45 import org.opendaylight.netconf.api.NetconfSessionPreferences;
46 import org.opendaylight.netconf.api.messages.NetconfHelloMessage;
47 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
48 import org.opendaylight.netconf.api.xml.XmlUtil;
49 import org.opendaylight.netconf.nettyutil.handler.ChunkedFramingMechanismEncoder;
50 import org.opendaylight.netconf.nettyutil.handler.EOMFramingMechanismEncoder;
51 import org.opendaylight.netconf.nettyutil.handler.FramingMechanismHandlerFactory;
52 import org.opendaylight.netconf.nettyutil.handler.NetconfChunkAggregator;
53 import org.opendaylight.netconf.nettyutil.handler.NetconfEOMAggregator;
54 import org.opendaylight.netconf.nettyutil.handler.NetconfXMLToHelloMessageDecoder;
55 import org.opendaylight.netconf.util.messages.FramingMechanism;
57 @RunWith(MockitoJUnitRunner.StrictStubs.class)
58 public class AbstractNetconfSessionNegotiatorTest {
61 private NetconfSessionListener<TestingNetconfSession> listener;
63 private Promise<TestingNetconfSession> promise;
65 private SslHandler sslHandler;
66 private EmbeddedChannel channel;
67 private AbstractNetconfSessionNegotiator negotiator;
68 private NetconfHelloMessage hello;
69 private NetconfHelloMessage helloBase11;
70 private NetconfXMLToHelloMessageDecoder xmlToHello;
71 private NetconfSessionPreferences prefs;
74 public void setUp() throws Exception {
75 channel = new EmbeddedChannel();
76 xmlToHello = new NetconfXMLToHelloMessageDecoder();
77 channel.pipeline().addLast(AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER,
78 new ChannelInboundHandlerAdapter());
79 channel.pipeline().addLast(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, xmlToHello);
80 channel.pipeline().addLast(NETCONF_MESSAGE_FRAME_ENCODER,
81 FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM));
82 channel.pipeline().addLast(NETCONF_MESSAGE_AGGREGATOR, new NetconfEOMAggregator());
83 hello = NetconfHelloMessage.createClientHello(Collections.emptySet(), Optional.empty());
84 helloBase11 = NetconfHelloMessage.createClientHello(Collections
85 .singleton(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1), Optional.empty());
86 prefs = new NetconfSessionPreferences(helloBase11);
87 doReturn(promise).when(promise).setFailure(any());
88 negotiator = new TestSessionNegotiator(prefs, promise, channel, new HashedWheelTimer(), listener, 100L);
92 public void testStartNegotiation() throws Exception {
93 negotiator.startNegotiation();
94 Assert.assertEquals(helloBase11, channel.readOutbound());
98 public void testStartNegotiationSsl() throws Exception {
99 doReturn(true).when(sslHandler).isSharable();
100 doNothing().when(sslHandler).handlerAdded(any());
101 doNothing().when(sslHandler).write(any(), any(), any());
102 final Future<EmbeddedChannel> handshakeFuture = channel.eventLoop().newSucceededFuture(channel);
103 doReturn(handshakeFuture).when(sslHandler).handshakeFuture();
104 channel.pipeline().addLast(sslHandler);
105 negotiator.startNegotiation();
106 verify(sslHandler, timeout(1000)).write(any(), eq(helloBase11), any());
111 public void testStartNegotiationNotEstablished() throws Exception {
112 final ChannelOutboundHandler closedDetector = Mockito.spy(new CloseDetector());
113 channel.pipeline().addLast("closedDetector", closedDetector);
114 doReturn(false).when(promise).isDone();
115 doReturn(false).when(promise).isCancelled();
116 negotiator.startNegotiation();
117 verify(closedDetector, timeout(2000)).close(any(), any());
121 public void testGetSessionPreferences() throws Exception {
122 Assert.assertEquals(prefs, negotiator.getSessionPreferences());
126 public void testGetSessionForHelloMessage() throws Exception {
127 negotiator.startNegotiation();
128 final AbstractNetconfSession session = negotiator.getSessionForHelloMessage(hello);
129 Assert.assertNotNull(session);
130 Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_AGGREGATOR) instanceof NetconfEOMAggregator);
131 Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_FRAME_ENCODER) instanceof EOMFramingMechanismEncoder);
135 public void testGetSessionForHelloMessageBase11() throws Exception {
136 negotiator.startNegotiation();
137 final AbstractNetconfSession session = negotiator.getSessionForHelloMessage(helloBase11);
138 Assert.assertNotNull(session);
139 Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_AGGREGATOR) instanceof NetconfChunkAggregator);
140 Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_FRAME_ENCODER)
141 instanceof ChunkedFramingMechanismEncoder);
145 public void testReplaceHelloMessageInboundHandler() throws Exception {
146 final List<Object> out = new ArrayList<>();
147 final byte[] msg = "<rpc/>".getBytes();
148 final ByteBuf msgBuf = Unpooled.wrappedBuffer(msg);
149 final ByteBuf helloBuf = Unpooled.wrappedBuffer(XmlUtil.toString(hello.getDocument()).getBytes());
150 negotiator.startNegotiation();
151 xmlToHello.decode(null, helloBuf, out);
152 xmlToHello.decode(null, msgBuf, out);
153 final AbstractNetconfSession session = mock(AbstractNetconfSession.class);
154 doNothing().when(session).handleMessage(any());
155 negotiator.replaceHelloMessageInboundHandler(session);
156 verify(session, times(1)).handleMessage(any());
160 public void testNegotiationFail() throws Exception {
161 negotiator.startNegotiation();
162 final RuntimeException cause = new RuntimeException("failure cause");
163 channel.pipeline().fireExceptionCaught(cause);
164 verify(promise).setFailure(cause);
167 private static class CloseDetector extends ChannelOutboundHandlerAdapter {
169 public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
170 // Override needed so @Skip from superclass is not effective