Merge "Refactor Additional header for netconf hello message."
[controller.git] / opendaylight / netconf / netconf-util / src / main / java / org / opendaylight / controller / netconf / util / handler / NetconfXMLToHelloMessageDecoder.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.netconf.util.handler;
9
10 import java.nio.ByteBuffer;
11 import java.util.Arrays;
12 import java.util.List;
13
14 import org.opendaylight.controller.netconf.api.NetconfMessage;
15 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
16 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
17 import org.w3c.dom.Document;
18
19 import com.google.common.base.Charsets;
20 import com.google.common.collect.ImmutableList;
21
22 /**
23  * Customized NetconfXMLToMessageDecoder that reads additional header with
24  * session metadata from
25  * {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage}
26  * . Used by netconf server to retrieve information about session metadata.
27  */
28 public class NetconfXMLToHelloMessageDecoder extends NetconfXMLToMessageDecoder {
29
30     private static final List<byte[]> POSSIBLE_ENDS = ImmutableList.of(
31             new byte[] { ']', '\n' },
32             new byte[] { ']', '\r', '\n' });
33     private static final List<byte[]> POSSIBLE_STARTS = ImmutableList.of(
34             new byte[] { '[' },
35             new byte[] { '\r', '\n', '[' },
36             new byte[] { '\n', '[' });
37
38     private String additionalHeaderCache;
39
40     @Override
41     protected byte[] preprocessMessageBytes(byte[] bytes) {
42         // Extract bytes containing header with additional metadata
43
44         if (startsWithAdditionalHeader(bytes)) {
45             // Auth information containing username, ip address... extracted for monitoring
46             int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
47             if (endOfAuthHeader > -1) {
48                 byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
49                 additionalHeaderCache = additionalHeaderToString(additionalHeaderBytes);
50                 bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
51             }
52         }
53
54         return bytes;
55     }
56
57     @Override
58     protected void cleanUpAfterDecode() {
59         additionalHeaderCache = null;
60     }
61
62     @Override
63     protected NetconfMessage buildNetconfMessage(Document doc) {
64         return new NetconfHelloMessage(doc, additionalHeaderCache == null ? null
65                 : NetconfHelloMessageAdditionalHeader.fromString(additionalHeaderCache));
66     }
67
68     private int getAdditionalHeaderEndIndex(byte[] bytes) {
69         for (byte[] possibleEnd : POSSIBLE_ENDS) {
70             int idx = findByteSequence(bytes, possibleEnd);
71
72             if (idx != -1) {
73                 return idx;
74             }
75         }
76
77         return -1;
78     }
79
80     private static int findByteSequence(final byte[] bytes, final byte[] sequence) {
81         if (bytes.length < sequence.length) {
82             throw new IllegalArgumentException("Sequence to be found is longer than the given byte array.");
83         }
84         if (bytes.length == sequence.length) {
85             if (Arrays.equals(bytes, sequence)) {
86                 return 0;
87             } else {
88                 return -1;
89             }
90         }
91         int j = 0;
92         for (int i = 0; i < bytes.length; i++) {
93             if (bytes[i] == sequence[j]) {
94                 j++;
95                 if (j == sequence.length) {
96                     return i - j + 1;
97                 }
98             } else {
99                 j = 0;
100             }
101         }
102         return -1;
103     }
104
105     private boolean startsWithAdditionalHeader(byte[] bytes) {
106         for (byte[] possibleStart : POSSIBLE_STARTS) {
107             int i = 0;
108             for (byte b : possibleStart) {
109                 if(bytes[i++] != b)
110                     break;
111
112                 if(i == possibleStart.length)
113                     return true;
114             }
115         }
116
117         return false;
118     }
119
120     private String additionalHeaderToString(byte[] bytes) {
121         return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
122     }
123
124 }