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