1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix;
19
20 import java.io.UnsupportedEncodingException;
21 import java.net.SocketException;
22 import java.net.URI;
23 import java.net.URLDecoder;
24 import java.net.URLEncoder;
25 import java.util.Objects;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
30
31
32
33
34
35
36 @SuppressFBWarnings("REDOS")
37 public final class HostAndPort {
38 private static final Pattern PAT_HOST_AND_PORT = Pattern.compile(
39 "^//((?<userinfo>[^/\\@]*)\\@)?(?<host>[^/\\:]+)(?:\\:(?<port>[0-9]+))?");
40 private final String hostname;
41 private final int port;
42
43
44
45
46
47
48
49 public HostAndPort(String hostname, int port) {
50 this.hostname = hostname;
51 this.port = port;
52 }
53
54 @Override
55 public int hashCode() {
56 final int prime = 31;
57 int result = 1;
58 result = prime * result + ((getHostname() == null) ? 0 : getHostname().hashCode());
59 result = prime * result + getPort();
60 return result;
61 }
62
63 @Override
64 public boolean equals(Object obj) {
65 if (this == obj) {
66 return true;
67 }
68 if (!(obj instanceof HostAndPort)) {
69 return false;
70 }
71 HostAndPort other = (HostAndPort) obj;
72 if (getHostname() == null) {
73 if (other.getHostname() != null) {
74 return false;
75 }
76 } else if (!getHostname().equals(other.getHostname())) {
77 return false;
78 }
79
80 return getPort() == other.getPort();
81 }
82
83 @Override
84 public String toString() {
85 if (getPort() == -1) {
86 return getHostname();
87 } else {
88 return getHostname() + ":" + getPort();
89 }
90 }
91
92
93
94
95
96
97
98
99 public static HostAndPort parseFrom(URI u) throws SocketException {
100 String host = u.getHost();
101 if (host != null) {
102 return new HostAndPort(host, u.getPort());
103 }
104 String raw = u.getRawSchemeSpecificPart();
105 Matcher m = PAT_HOST_AND_PORT.matcher(raw);
106 if (!m.find()) {
107 throw new SocketException("Cannot parse URI: " + u);
108 }
109 host = urlDecode(m.group("host"));
110
111 String portStr = m.group("port");
112 int port;
113 if (portStr == null) {
114 port = -1;
115 } else {
116 port = Integer.parseInt(portStr);
117 }
118
119 return new HostAndPort(host, port);
120 }
121
122 @SuppressWarnings("PMD.UseStandardCharsets")
123 private static String urlDecode(String s) {
124 try {
125 return URLDecoder.decode(s, "UTF-8");
126 } catch (UnsupportedEncodingException e) {
127 throw new IllegalStateException(e);
128 }
129 }
130
131 @SuppressWarnings("PMD.UseStandardCharsets")
132 private static String urlEncode(String s) {
133 try {
134 return URLEncoder.encode(s, "UTF-8");
135 } catch (UnsupportedEncodingException e) {
136 throw new IllegalStateException(e);
137 }
138 }
139
140
141
142
143
144
145 public String getHostname() {
146 return hostname;
147 }
148
149
150
151
152
153
154 public int getPort() {
155 return port;
156 }
157
158
159
160
161
162
163
164 public URI toURI(String scheme) {
165 return toURI(scheme, null, null, null, null);
166 }
167
168
169
170
171
172
173
174
175
176 public URI toURI(String scheme, URI template) {
177 if (template == null) {
178 return toURI(scheme, null, null, null, null);
179 }
180
181 String rawAuthority = template.getRawAuthority();
182 int at = rawAuthority.indexOf('@');
183 if (at >= 0) {
184 rawAuthority = rawAuthority.substring(0, at);
185 } else if (rawAuthority.length() > 0 && template.getHost() == null) {
186
187 rawAuthority = null;
188 } else if (rawAuthority.length() > 0 && template.getAuthority().equals(template.getHost())) {
189
190 rawAuthority = null;
191 } else if (rawAuthority.length() > 0 && template.getAuthority().equals(template.getHost() + ":"
192 + template.getPort())) {
193
194 rawAuthority = null;
195 }
196
197 return toURI(scheme, rawAuthority, template.getRawPath(), template.getRawQuery(), template
198 .getRawFragment());
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212 public URI toURI(String scheme, String rawAuthority, String rawPath, String rawQuery,
213 String rawFragment) {
214 Objects.requireNonNull(scheme);
215 if (rawPath != null && !rawPath.isEmpty()) {
216 if (!rawPath.startsWith("/")) {
217 throw new IllegalArgumentException("Path must be absolute: " + rawPath);
218 }
219 }
220
221 return URI.create(scheme + "://" + (rawAuthority == null ? "" : rawAuthority + "@") + urlEncode(
222 getHostname()).replace("%2C", ",") + (port <= 0 ? "" : (":" + port)) + (rawPath == null ? ""
223 : rawPath) + (rawQuery == null ? "" : "?" + rawQuery) + (rawFragment == null ? "" : "#"
224 + rawFragment));
225 }
226 }