1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.websocketx.extensions;
17
18 import io.netty.handler.codec.http.HttpHeaderNames;
19 import io.netty.handler.codec.http.HttpHeaderValues;
20 import io.netty.handler.codec.http.HttpHeaders;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 public final class WebSocketExtensionUtil {
32
33 private static final String EXTENSION_SEPARATOR = ",";
34 private static final String PARAMETER_SEPARATOR = ";";
35 private static final char PARAMETER_EQUAL = '=';
36
37 private static final Pattern PARAMETER = Pattern.compile("^([^=]+)(=[\\\"]?([^\\\"]+)[\\\"]?)?$");
38
39 static boolean isWebsocketUpgrade(HttpHeaders headers) {
40
41
42 return headers.contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true) &&
43 headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true);
44 }
45
46 public static List<WebSocketExtensionData> extractExtensions(String extensionHeader) {
47 String[] rawExtensions = extensionHeader.split(EXTENSION_SEPARATOR);
48 if (rawExtensions.length > 0) {
49 List<WebSocketExtensionData> extensions = new ArrayList<>(rawExtensions.length);
50 for (String rawExtension : rawExtensions) {
51 String[] extensionParameters = rawExtension.split(PARAMETER_SEPARATOR);
52 String name = extensionParameters[0].trim();
53 Map<String, String> parameters;
54 if (extensionParameters.length > 1) {
55 parameters = new LinkedHashMap<>(extensionParameters.length - 1);
56 for (int i = 1; i < extensionParameters.length; i++) {
57 String parameter = extensionParameters[i].trim();
58 Matcher parameterMatcher = PARAMETER.matcher(parameter);
59 if (parameterMatcher.matches() && parameterMatcher.group(1) != null) {
60 parameters.put(parameterMatcher.group(1), parameterMatcher.group(3));
61 }
62 }
63 } else {
64 parameters = Collections.emptyMap();
65 }
66 extensions.add(new WebSocketExtensionData(name, parameters));
67 }
68 return extensions;
69 } else {
70 return Collections.emptyList();
71 }
72 }
73
74 static String computeMergeExtensionsHeaderValue(String userDefinedHeaderValue,
75 List<WebSocketExtensionData> extraExtensions) {
76 List<WebSocketExtensionData> userDefinedExtensions =
77 userDefinedHeaderValue != null ?
78 extractExtensions(userDefinedHeaderValue) :
79 Collections.emptyList();
80
81 for (WebSocketExtensionData userDefined: userDefinedExtensions) {
82 WebSocketExtensionData matchingExtra = null;
83 int i;
84 for (i = 0; i < extraExtensions.size(); i ++) {
85 WebSocketExtensionData extra = extraExtensions.get(i);
86 if (extra.name().equals(userDefined.name())) {
87 matchingExtra = extra;
88 break;
89 }
90 }
91 if (matchingExtra == null) {
92 extraExtensions.add(userDefined);
93 } else {
94
95 Map<String, String> mergedParameters = new LinkedHashMap<>(matchingExtra.parameters());
96 mergedParameters.putAll(userDefined.parameters());
97 extraExtensions.set(i, new WebSocketExtensionData(matchingExtra.name(), mergedParameters));
98 }
99 }
100
101 StringBuilder sb = new StringBuilder(150);
102
103 for (WebSocketExtensionData data: extraExtensions) {
104 sb.append(data.name());
105 for (Entry<String, String> parameter : data.parameters().entrySet()) {
106 sb.append(PARAMETER_SEPARATOR);
107 sb.append(parameter.getKey());
108 if (parameter.getValue() != null) {
109 sb.append(PARAMETER_EQUAL);
110 sb.append(parameter.getValue());
111 }
112 }
113 sb.append(EXTENSION_SEPARATOR);
114 }
115
116 if (!extraExtensions.isEmpty()) {
117 sb.setLength(sb.length() - EXTENSION_SEPARATOR.length());
118 }
119
120 return sb.toString();
121 }
122
123 private WebSocketExtensionUtil() {
124
125 }
126 }