New upstream version 2.0.11
Emmanuel Bourg
5 years ago
1 | 1 | <parent> |
2 | 2 | <groupId>org.asynchttpclient</groupId> |
3 | 3 | <artifactId>async-http-client-project</artifactId> |
4 | <version>2.0.10</version> | |
4 | <version>2.0.11</version> | |
5 | 5 | </parent> |
6 | 6 | <modelVersion>4.0.0</modelVersion> |
7 | 7 | <artifactId>async-http-client</artifactId> |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
70 | 70 | int getPooledConnectionIdleTimeout(); |
71 | 71 | |
72 | 72 | /** |
73 | * @return the period in millis to clean the pool of dead and idle connections. | |
74 | */ | |
75 | int getConnectionPoolCleanerPeriod(); | |
76 | ||
77 | /** | |
73 | 78 | * Return the maximum time in millisecond an {@link AsyncHttpClient} waits until the response is completed. |
74 | 79 | * |
75 | 80 | * @return the maximum time in millisecond an {@link AsyncHttpClient} waits until the response is completed. |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
84 | 84 | // keep-alive |
85 | 85 | private final boolean keepAlive; |
86 | 86 | private final int pooledConnectionIdleTimeout; |
87 | private final int connectionPoolCleanerPeriod; | |
87 | 88 | private final int connectionTtl; |
88 | 89 | private final int maxConnections; |
89 | 90 | private final int maxConnectionsPerHost; |
154 | 155 | // keep-alive |
155 | 156 | boolean keepAlive,// |
156 | 157 | int pooledConnectionIdleTimeout,// |
158 | int connectionPoolCleanerPeriod,// | |
157 | 159 | int connectionTtl,// |
158 | 160 | int maxConnections,// |
159 | 161 | int maxConnectionsPerHost,// |
225 | 227 | // keep-alive |
226 | 228 | this.keepAlive = keepAlive; |
227 | 229 | this.pooledConnectionIdleTimeout = pooledConnectionIdleTimeout; |
230 | this.connectionPoolCleanerPeriod = connectionPoolCleanerPeriod; | |
228 | 231 | this.connectionTtl = connectionTtl; |
229 | 232 | this.maxConnections = maxConnections; |
230 | 233 | this.maxConnectionsPerHost = maxConnectionsPerHost; |
373 | 376 | } |
374 | 377 | |
375 | 378 | @Override |
379 | public int getConnectionPoolCleanerPeriod() { | |
380 | return connectionPoolCleanerPeriod; | |
381 | } | |
382 | ||
383 | @Override | |
376 | 384 | public int getConnectionTtl() { |
377 | 385 | return connectionTtl; |
378 | 386 | } |
602 | 610 | // keep-alive |
603 | 611 | private boolean keepAlive = defaultKeepAlive(); |
604 | 612 | private int pooledConnectionIdleTimeout = defaultPooledConnectionIdleTimeout(); |
613 | private int connectionPoolCleanerPeriod = defaultConnectionPoolCleanerPeriod(); | |
605 | 614 | private int connectionTtl = defaultConnectionTtl(); |
606 | 615 | private int maxConnections = defaultMaxConnections(); |
607 | 616 | private int maxConnectionsPerHost = defaultMaxConnectionsPerHost(); |
1091 | 1100 | shutdownTimeout, // |
1092 | 1101 | keepAlive, // |
1093 | 1102 | pooledConnectionIdleTimeout, // |
1103 | connectionPoolCleanerPeriod, // | |
1094 | 1104 | connectionTtl, // |
1095 | 1105 | maxConnections, // |
1096 | 1106 | maxConnectionsPerHost, // |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010-2013 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010-2013 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
36 | 36 | |
37 | 37 | public static int defaultPooledConnectionIdleTimeout() { |
38 | 38 | return AsyncHttpClientConfigHelper.getAsyncHttpClientConfig().getInt(ASYNC_CLIENT_CONFIG_ROOT + "pooledConnectionIdleTimeout"); |
39 | } | |
40 | ||
41 | public static int defaultConnectionPoolCleanerPeriod() { | |
42 | return AsyncHttpClientConfigHelper.getAsyncHttpClientConfig().getInt(ASYNC_CLIENT_CONFIG_ROOT + "connectionPoolCleanerPeriod"); | |
39 | 43 | } |
40 | 44 | |
41 | 45 | public static int defaultReadTimeout() { |
178 | 178 | for (HttpResponseBodyPart part : bodyParts) |
179 | 179 | target.put(part.getBodyPartBytes()); |
180 | 180 | |
181 | target.flip(); | |
181 | 182 | return target; |
182 | 183 | } |
183 | 184 |
53 | 53 | public DefaultChannelPool(AsyncHttpClientConfig config, Timer hashedWheelTimer) { |
54 | 54 | this(config.getPooledConnectionIdleTimeout(),// |
55 | 55 | config.getConnectionTtl(),// |
56 | hashedWheelTimer); | |
56 | hashedWheelTimer,// | |
57 | config.getConnectionPoolCleanerPeriod()); | |
57 | 58 | } |
58 | 59 | |
59 | 60 | private ChannelId channelId(Channel channel) { |
62 | 63 | |
63 | 64 | public DefaultChannelPool(int maxIdleTime,// |
64 | 65 | int connectionTtl,// |
65 | Timer nettyTimer) { | |
66 | Timer nettyTimer,// | |
67 | int cleanerPeriod) { | |
66 | 68 | this(maxIdleTime,// |
67 | 69 | connectionTtl,// |
68 | 70 | PoolLeaseStrategy.LIFO,// |
69 | nettyTimer); | |
71 | nettyTimer,// | |
72 | cleanerPeriod); | |
70 | 73 | } |
71 | 74 | |
72 | 75 | public DefaultChannelPool(int maxIdleTime,// |
73 | 76 | int connectionTtl,// |
74 | 77 | PoolLeaseStrategy poolLeaseStrategy,// |
75 | Timer nettyTimer) { | |
78 | Timer nettyTimer,// | |
79 | int cleanerPeriod) { | |
76 | 80 | this.maxIdleTime = (int) maxIdleTime; |
77 | 81 | this.connectionTtl = connectionTtl; |
78 | 82 | connectionTtlEnabled = connectionTtl > 0; |
81 | 85 | maxIdleTimeEnabled = maxIdleTime > 0; |
82 | 86 | this.poolLeaseStrategy = poolLeaseStrategy; |
83 | 87 | |
84 | cleanerPeriod = Math.min(connectionTtlEnabled ? connectionTtl : Integer.MAX_VALUE, maxIdleTimeEnabled ? maxIdleTime : Long.MAX_VALUE); | |
88 | this.cleanerPeriod = Math.min(cleanerPeriod, Math.min(connectionTtlEnabled ? connectionTtl : Integer.MAX_VALUE, maxIdleTimeEnabled ? maxIdleTime : Integer.MAX_VALUE)); | |
85 | 89 | |
86 | 90 | if (connectionTtlEnabled || maxIdleTimeEnabled) |
87 | 91 | scheduleNewIdleChannelDetector(new IdleChannelDetector()); |
152 | 156 | if (isIdleTimeoutExpired(idleChannel, now) || isRemotelyClosed(idleChannel.channel) || isTtlExpired(idleChannel.channel, now)) { |
153 | 157 | LOGGER.debug("Adding Candidate expired Channel {}", idleChannel.channel); |
154 | 158 | if (idleTimeoutChannels == null) |
155 | idleTimeoutChannels = new ArrayList<>(); | |
159 | idleTimeoutChannels = new ArrayList<>(1); | |
156 | 160 | idleTimeoutChannels.add(idleChannel); |
157 | 161 | } |
158 | 162 | } |
162 | 166 | |
163 | 167 | private final List<IdleChannel> closeChannels(List<IdleChannel> candidates) { |
164 | 168 | |
165 | // lazy create, only if we have a non-closeable channel | |
169 | // lazy create, only if we hit a non-closeable channel | |
166 | 170 | List<IdleChannel> closedChannels = null; |
167 | 171 | for (int i = 0; i < candidates.size(); i++) { |
168 | 172 | // We call takeOwnership here to avoid closing a channel that has just been taken out |
172 | 172 | Realm realm = null; |
173 | 173 | if (originalFuture != null) { |
174 | 174 | realm = originalFuture.getRealm(); |
175 | } else if (config.getRealm() != null) { | |
176 | realm = config.getRealm(); | |
177 | 175 | } else { |
178 | 176 | realm = request.getRealm(); |
177 | if (realm == null) { | |
178 | realm = config.getRealm(); | |
179 | } | |
179 | 180 | } |
180 | 181 | |
181 | 182 | Realm proxyRealm = null; |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | * Copyright 2010 Ning, Inc. | |
1 | * Copyright (c) 2016 AsyncHttpClient Project. All rights reserved. | |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
4 | * (the "License"); you may not use this file except in compliance with the | |
5 | * License. You may obtain a copy of the License at: | |
3 | * This program is licensed to you under the Apache License Version 2.0, | |
4 | * and you may not use this file except in compliance with the Apache License Version 2.0. | |
5 | * You may obtain a copy of the Apache License Version 2.0 at | |
6 | * http://www.apache.org/licenses/LICENSE-2.0. | |
6 | 7 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
12 | * License for the specific language governing permissions and limitations | |
13 | * under the License. | |
14 | * | |
8 | * Unless required by applicable law or agreed to in writing, | |
9 | * software distributed under the Apache License Version 2.0 is distributed on an | |
10 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. | |
15 | 12 | */ |
16 | 13 | package org.asynchttpclient.oauth; |
17 | 14 | |
23 | 20 | import java.util.Arrays; |
24 | 21 | import java.util.List; |
25 | 22 | import java.util.concurrent.ThreadLocalRandom; |
23 | import java.util.regex.Pattern; | |
26 | 24 | |
27 | 25 | import org.asynchttpclient.Param; |
28 | 26 | import org.asynchttpclient.Request; |
34 | 32 | import org.asynchttpclient.util.Utf8UrlEncoder; |
35 | 33 | |
36 | 34 | /** |
37 | * Simple OAuth signature calculator that can used for constructing client signatures | |
38 | * for accessing services that use OAuth for authorization. | |
39 | * <br> | |
40 | * Supports most common signature inclusion and calculation methods: HMAC-SHA1 for | |
41 | * calculation, and Header inclusion as inclusion method. Nonce generation uses | |
42 | * simple random numbers with base64 encoding. | |
35 | * Simple OAuth signature calculator that can used for constructing client signatures for accessing services that use OAuth for authorization. <br> | |
36 | * Supports most common signature inclusion and calculation methods: HMAC-SHA1 for calculation, and Header inclusion as inclusion method. Nonce generation uses simple random | |
37 | * numbers with base64 encoding. | |
43 | 38 | * |
44 | 39 | * @author tatu (tatu.saloranta@iki.fi) |
45 | 40 | */ |
71 | 66 | |
72 | 67 | /** |
73 | 68 | * @param consumerAuth Consumer key to use for signature calculation |
74 | * @param userAuth Request/access token to use for signature calculation | |
69 | * @param userAuth Request/access token to use for signature calculation | |
75 | 70 | */ |
76 | 71 | public OAuthSignatureCalculator(ConsumerKey consumerAuth, RequestToken userAuth) { |
77 | 72 | mac = new ThreadSafeHMAC(consumerAuth, userAuth); |
83 | 78 | public void calculateAndAddSignature(Request request, RequestBuilderBase<?> requestBuilder) { |
84 | 79 | String nonce = generateNonce(); |
85 | 80 | long timestamp = generateTimestamp(); |
86 | String signature = calculateSignature(request.getMethod(), request.getUri(), timestamp, nonce, request.getFormParams(), request.getQueryParams()); | |
81 | String signature = calculateSignature(request, timestamp, nonce); | |
87 | 82 | String headerValue = constructAuthHeader(signature, nonce, timestamp); |
88 | 83 | requestBuilder.setHeader(HEADER_AUTHORIZATION, headerValue); |
89 | 84 | } |
90 | 85 | |
86 | private String encodedParams(long oauthTimestamp, String nonce, List<Param> formParams, List<Param> queryParams) { | |
87 | /** | |
88 | * List of all query and form parameters added to this request; needed for calculating request signature | |
89 | */ | |
90 | int allParametersSize = 5 + (userAuth.getKey() != null ? 1 : 0) + (formParams != null ? formParams.size() : 0) + (queryParams != null ? queryParams.size() : 0); | |
91 | OAuthParameterSet allParameters = new OAuthParameterSet(allParametersSize); | |
92 | ||
93 | // start with standard OAuth parameters we need | |
94 | allParameters.add(KEY_OAUTH_CONSUMER_KEY, Utf8UrlEncoder.encodeQueryElement(consumerAuth.getKey())); | |
95 | allParameters.add(KEY_OAUTH_NONCE, Utf8UrlEncoder.encodeQueryElement(nonce)); | |
96 | allParameters.add(KEY_OAUTH_SIGNATURE_METHOD, OAUTH_SIGNATURE_METHOD); | |
97 | allParameters.add(KEY_OAUTH_TIMESTAMP, String.valueOf(oauthTimestamp)); | |
98 | if (userAuth.getKey() != null) { | |
99 | allParameters.add(KEY_OAUTH_TOKEN, Utf8UrlEncoder.encodeQueryElement(userAuth.getKey())); | |
100 | } | |
101 | allParameters.add(KEY_OAUTH_VERSION, OAUTH_VERSION_1_0); | |
102 | ||
103 | if (formParams != null) { | |
104 | for (Param param : formParams) { | |
105 | // formParams are not already encoded | |
106 | allParameters.add(Utf8UrlEncoder.encodeQueryElement(param.getName()), Utf8UrlEncoder.encodeQueryElement(param.getValue())); | |
107 | } | |
108 | } | |
109 | if (queryParams != null) { | |
110 | for (Param param : queryParams) { | |
111 | // queryParams are already form-url-encoded | |
112 | // but OAuth1 uses RFC3986_UNRESERVED_CHARS so * and + have to be encoded | |
113 | allParameters.add(percentEncodeAlreadyFormUrlEncoded(param.getName()), percentEncodeAlreadyFormUrlEncoded(param.getValue())); | |
114 | } | |
115 | } | |
116 | return allParameters.sortAndConcat(); | |
117 | } | |
118 | ||
91 | 119 | private String baseUrl(Uri uri) { |
92 | /* 07-Oct-2010, tatu: URL may contain default port number; if so, need to extract | |
93 | * from base URL. | |
120 | /* | |
121 | * 07-Oct-2010, tatu: URL may contain default port number; if so, need to remove from base URL. | |
94 | 122 | */ |
95 | 123 | String scheme = uri.getScheme(); |
96 | 124 | |
97 | 125 | StringBuilder sb = StringUtils.stringBuilder(); |
98 | 126 | sb.append(scheme).append("://").append(uri.getHost()); |
99 | ||
127 | ||
100 | 128 | int port = uri.getPort(); |
101 | 129 | if (scheme.equals("http")) { |
102 | 130 | if (port == 80) |
111 | 139 | |
112 | 140 | if (isNonEmpty(uri.getPath())) |
113 | 141 | sb.append(uri.getPath()); |
114 | ||
142 | ||
115 | 143 | return sb.toString(); |
116 | 144 | } |
117 | 145 | |
118 | private String encodedParams(long oauthTimestamp, String nonce, List<Param> formParams, List<Param> queryParams) { | |
119 | /** | |
120 | * List of all query and form parameters added to this request; needed | |
121 | * for calculating request signature | |
122 | */ | |
123 | int allParametersSize = 5 | |
124 | + (userAuth.getKey() != null ? 1 : 0) | |
125 | + (formParams != null ? formParams.size() : 0) | |
126 | + (queryParams != null ? queryParams.size() : 0); | |
127 | OAuthParameterSet allParameters = new OAuthParameterSet(allParametersSize); | |
128 | ||
129 | // start with standard OAuth parameters we need | |
130 | allParameters.add(KEY_OAUTH_CONSUMER_KEY, Utf8UrlEncoder.encodeQueryElement(consumerAuth.getKey())); | |
131 | allParameters.add(KEY_OAUTH_NONCE, Utf8UrlEncoder.encodeQueryElement(nonce)); | |
132 | allParameters.add(KEY_OAUTH_SIGNATURE_METHOD, OAUTH_SIGNATURE_METHOD); | |
133 | allParameters.add(KEY_OAUTH_TIMESTAMP, String.valueOf(oauthTimestamp)); | |
134 | if (userAuth.getKey() != null) { | |
135 | allParameters.add(KEY_OAUTH_TOKEN, Utf8UrlEncoder.encodeQueryElement(userAuth.getKey())); | |
136 | } | |
137 | allParameters.add(KEY_OAUTH_VERSION, OAUTH_VERSION_1_0); | |
138 | ||
139 | if (formParams != null) { | |
140 | for (Param param : formParams) { | |
141 | // formParams are not already encoded | |
142 | allParameters.add(Utf8UrlEncoder.encodeQueryElement(param.getName()), Utf8UrlEncoder.encodeQueryElement(param.getValue())); | |
143 | } | |
144 | } | |
145 | if (queryParams != null) { | |
146 | for (Param param : queryParams) { | |
147 | // queryParams are already encoded | |
148 | allParameters.add(param.getName(), param.getValue()); | |
149 | } | |
150 | } | |
151 | return allParameters.sortAndConcat(); | |
152 | } | |
153 | ||
154 | StringBuilder signatureBaseString(String method, Uri uri, long oauthTimestamp, String nonce, | |
155 | List<Param> formParams, List<Param> queryParams) { | |
156 | ||
146 | private static final Pattern STAR_CHAR_PATTERN = Pattern.compile("*", Pattern.LITERAL); | |
147 | private static final Pattern PLUS_CHAR_PATTERN = Pattern.compile("+", Pattern.LITERAL); | |
148 | private static final Pattern ENCODED_TILDE_PATTERN = Pattern.compile("%7E", Pattern.LITERAL); | |
149 | ||
150 | private String percentEncodeAlreadyFormUrlEncoded(String s) { | |
151 | s = STAR_CHAR_PATTERN.matcher(s).replaceAll("%2A"); | |
152 | s = PLUS_CHAR_PATTERN.matcher(s).replaceAll("%20"); | |
153 | s = ENCODED_TILDE_PATTERN.matcher(s).replaceAll("~"); | |
154 | return s; | |
155 | } | |
156 | ||
157 | StringBuilder signatureBaseString(Request request, long oauthTimestamp, String nonce) { | |
158 | ||
157 | 159 | // beware: must generate first as we're using pooled StringBuilder |
158 | String baseUrl = baseUrl(uri); | |
159 | String encodedParams = encodedParams(oauthTimestamp, nonce, formParams, queryParams); | |
160 | String baseUrl = baseUrl(request.getUri()); | |
161 | String encodedParams = encodedParams(oauthTimestamp, nonce, request.getFormParams(), request.getQueryParams()); | |
160 | 162 | |
161 | 163 | StringBuilder sb = StringUtils.stringBuilder(); |
162 | sb.append(method); // POST / GET etc (nothing to URL encode) | |
164 | sb.append(request.getMethod()); // POST / GET etc (nothing to URL encode) | |
163 | 165 | sb.append('&'); |
164 | 166 | Utf8UrlEncoder.encodeAndAppendQueryElement(sb, baseUrl); |
165 | ||
166 | 167 | |
167 | 168 | // and all that needs to be URL encoded (... again!) |
168 | 169 | sb.append('&'); |
169 | 170 | Utf8UrlEncoder.encodeAndAppendQueryElement(sb, encodedParams); |
170 | 171 | return sb; |
171 | 172 | } |
172 | ||
173 | /** | |
174 | * Method for calculating OAuth signature using HMAC/SHA-1 method. | |
175 | * | |
176 | * @param method the request methode | |
177 | * @param uri the request Uri | |
178 | * @param oauthTimestamp the timestamp | |
179 | * @param nonce the nonce | |
180 | * @param formParams the formParams | |
181 | * @param queryParams the query params | |
182 | * @return the signature | |
183 | */ | |
184 | public String calculateSignature(String method, Uri uri, long oauthTimestamp, String nonce, | |
185 | List<Param> formParams, List<Param> queryParams) { | |
186 | ||
187 | StringBuilder sb = signatureBaseString(method, uri, oauthTimestamp, nonce, formParams, queryParams); | |
173 | ||
174 | String calculateSignature(Request request, long oauthTimestamp, String nonce) { | |
175 | ||
176 | StringBuilder sb = signatureBaseString(request, oauthTimestamp, nonce); | |
188 | 177 | |
189 | 178 | ByteBuffer rawBase = StringUtils.charSequence2ByteBuffer(sb, UTF_8); |
190 | 179 | byte[] rawSignature = mac.digest(rawBase); |
224 | 213 | ThreadLocalRandom.current().nextBytes(nonceBuffer); |
225 | 214 | // let's use base64 encoding over hex, slightly more compact than hex or decimals |
226 | 215 | return Base64.encode(nonceBuffer); |
227 | // return String.valueOf(Math.abs(random.nextLong())); | |
216 | // return String.valueOf(Math.abs(random.nextLong())); | |
228 | 217 | } |
229 | 218 | |
230 | 219 | /** |
231 | * Container for parameters used for calculating OAuth signature. | |
232 | * About the only confusing aspect is that of whether entries are to be sorted | |
233 | * before encoded or vice versa: if my reading is correct, encoding is to occur | |
234 | * first, then sorting; although this should rarely matter (since sorting is primary | |
235 | * by key, which usually has nothing to encode)... of course, rarely means that | |
236 | * when it would occur it'd be harder to track down. | |
220 | * Container for parameters used for calculating OAuth signature. About the only confusing aspect is that of whether entries are to be sorted before encoded or vice versa: if | |
221 | * my reading is correct, encoding is to occur first, then sorting; although this should rarely matter (since sorting is primary by key, which usually has nothing to encode)... | |
222 | * of course, rarely means that when it would occur it'd be harder to track down. | |
237 | 223 | */ |
238 | 224 | final static class OAuthParameterSet { |
239 | 225 | private final ArrayList<Parameter> allParameters; |
268 | 254 | * Helper class for sorting query and form parameters that we need |
269 | 255 | */ |
270 | 256 | final static class Parameter implements Comparable<Parameter> { |
257 | ||
271 | 258 | private final String key, value; |
272 | 259 | |
273 | 260 | public Parameter(String key, String value) { |
299 | 286 | |
300 | 287 | @Override |
301 | 288 | public boolean equals(Object o) { |
302 | if (this == o) return true; | |
303 | if (o == null || getClass() != o.getClass()) return false; | |
289 | if (this == o) | |
290 | return true; | |
291 | if (o == null || getClass() != o.getClass()) | |
292 | return false; | |
304 | 293 | |
305 | 294 | Parameter parameter = (Parameter) o; |
306 | 295 | |
307 | if (!key.equals(parameter.key)) return false; | |
308 | if (!value.equals(parameter.value)) return false; | |
296 | if (!key.equals(parameter.key)) | |
297 | return false; | |
298 | if (!value.equals(parameter.value)) | |
299 | return false; | |
309 | 300 | |
310 | 301 | return true; |
311 | 302 | } |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | * Copyright 2010 Ning, Inc. | |
1 | * Copyright (c) 2016 AsyncHttpClient Project. All rights reserved. | |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
4 | * (the "License"); you may not use this file except in compliance with the | |
5 | * License. You may obtain a copy of the License at: | |
3 | * This program is licensed to you under the Apache License Version 2.0, | |
4 | * and you may not use this file except in compliance with the Apache License Version 2.0. | |
5 | * You may obtain a copy of the Apache License Version 2.0 at | |
6 | * http://www.apache.org/licenses/LICENSE-2.0. | |
6 | 7 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
12 | * License for the specific language governing permissions and limitations | |
13 | * under the License. | |
8 | * Unless required by applicable law or agreed to in writing, | |
9 | * software distributed under the Apache License Version 2.0 is distributed on an | |
10 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. | |
14 | 12 | */ |
15 | 13 | package org.asynchttpclient.util; |
16 | 14 |
2 | 2 | org.asynchttpclient.maxConnectionsPerHost=-1 |
3 | 3 | org.asynchttpclient.connectTimeout=5000 |
4 | 4 | org.asynchttpclient.pooledConnectionIdleTimeout=60000 |
5 | org.asynchttpclient.connectionPoolCleanerPeriod=1000 | |
5 | 6 | org.asynchttpclient.readTimeout=60000 |
6 | 7 | org.asynchttpclient.requestTimeout=60000 |
7 | 8 | org.asynchttpclient.connectionTtl=-1 |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010-2013 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | * Copyright 2010 Ning, Inc. | |
1 | * Copyright (c) 2016 AsyncHttpClient Project. All rights reserved. | |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
4 | * (the "License"); you may not use this file except in compliance with the | |
5 | * License. You may obtain a copy of the License at: | |
3 | * This program is licensed to you under the Apache License Version 2.0, | |
4 | * and you may not use this file except in compliance with the Apache License Version 2.0. | |
5 | * You may obtain a copy of the Apache License Version 2.0 at | |
6 | * http://www.apache.org/licenses/LICENSE-2.0. | |
6 | 7 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
12 | * License for the specific language governing permissions and limitations | |
13 | * under the License. | |
8 | * Unless required by applicable law or agreed to in writing, | |
9 | * software distributed under the Apache License Version 2.0 is distributed on an | |
10 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. | |
14 | 12 | */ |
15 | 13 | package org.asynchttpclient.oauth; |
16 | 14 | |
20 | 18 | |
21 | 19 | import java.io.UnsupportedEncodingException; |
22 | 20 | import java.net.URLDecoder; |
23 | import java.util.ArrayList; | |
24 | 21 | import java.util.List; |
25 | 22 | import java.util.regex.Matcher; |
26 | 23 | import java.util.regex.Pattern; |
27 | 24 | |
28 | 25 | import org.asynchttpclient.Param; |
29 | 26 | import org.asynchttpclient.Request; |
30 | import org.asynchttpclient.uri.Uri; | |
31 | 27 | import org.testng.annotations.Test; |
32 | 28 | |
33 | 29 | /** |
78 | 74 | OAuthSignatureCalculator calc = new OAuthSignatureCalculator(consumer, user); |
79 | 75 | |
80 | 76 | String signatureBaseString = calc.signatureBaseString(// |
81 | request.getMethod(),// | |
82 | request.getUri(),// | |
77 | request,// | |
83 | 78 | 137131201,// |
84 | "7d8f3e4a",// | |
85 | request.getFormParams(),// | |
86 | request.getQueryParams()).toString(); | |
79 | "7d8f3e4a").toString(); | |
87 | 80 | |
88 | 81 | assertEquals(signatureBaseString, "POST&" // |
89 | 82 | + "http%3A%2F%2Fexample.com%2Frequest" // |
107 | 100 | OAuthSignatureCalculator calc = new OAuthSignatureCalculator(consumer, user); |
108 | 101 | |
109 | 102 | String signatureBaseString = calc.signatureBaseString(// |
110 | request.getMethod(),// | |
111 | request.getUri(),// | |
103 | request,// | |
112 | 104 | 137131201,// |
113 | "ZLc92RAkooZcIO/0cctl0Q==",// | |
114 | request.getFormParams(),// | |
115 | request.getQueryParams()).toString(); | |
105 | "ZLc92RAkooZcIO/0cctl0Q==").toString(); | |
116 | 106 | |
117 | 107 | assertEquals(signatureBaseString, "POST&" // |
118 | 108 | + "http%3A%2F%2Fexample.com%2Frequest" // |
129 | 119 | + "oauth_version%3D1.0"); |
130 | 120 | } |
131 | 121 | |
132 | @Test(groups = "standalone") | |
122 | @Test | |
133 | 123 | public void testSignatureBaseStringWithProperlyEncodedUri() { |
134 | 124 | |
135 | 125 | Request request = post("http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b")// |
141 | 131 | testSignatureBaseStringWithEncodableOAuthToken(request); |
142 | 132 | } |
143 | 133 | |
144 | @Test(groups = "standalone") | |
134 | @Test | |
145 | 135 | public void testSignatureBaseStringWithRawUri() { |
146 | 136 | |
147 | 137 | // note: @ is legal so don't decode it into %40 because it won't be |
159 | 149 | |
160 | 150 | // based on the reference test case from |
161 | 151 | // http://oauth.pbwiki.com/TestCases |
162 | @Test(groups = "standalone") | |
152 | @Test | |
163 | 153 | public void testGetCalculateSignature() { |
164 | 154 | ConsumerKey consumer = new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET); |
165 | 155 | RequestToken user = new RequestToken(TOKEN_KEY, TOKEN_SECRET); |
166 | 156 | OAuthSignatureCalculator calc = new OAuthSignatureCalculator(consumer, user); |
167 | List<Param> queryParams = new ArrayList<>(); | |
168 | queryParams.add(new Param("file", "vacation.jpg")); | |
169 | queryParams.add(new Param("size", "original")); | |
170 | String url = "http://photos.example.net/photos"; | |
171 | String sig = calc.calculateSignature("GET", Uri.create(url), TIMESTAMP, NONCE, null, queryParams); | |
157 | ||
158 | Request request = get("http://photos.example.net/photos")// | |
159 | .addQueryParam("file", "vacation.jpg")// | |
160 | .addQueryParam("size", "original")// | |
161 | .build(); | |
162 | ||
163 | String sig = calc.calculateSignature(request, TIMESTAMP, NONCE); | |
172 | 164 | |
173 | 165 | assertEquals(sig, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); |
174 | 166 | } |
175 | 167 | |
176 | @Test(groups = "standalone") | |
177 | public void testPostCalculateSignature() { | |
168 | @Test | |
169 | public void testPostCalculateSignature() throws UnsupportedEncodingException { | |
178 | 170 | ConsumerKey consumer = new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET); |
179 | 171 | RequestToken user = new RequestToken(TOKEN_KEY, TOKEN_SECRET); |
180 | 172 | OAuthSignatureCalculator calc = new StaticOAuthSignatureCalculator(consumer, user, TIMESTAMP, NONCE); |
181 | 173 | |
182 | List<Param> formParams = new ArrayList<Param>(); | |
183 | formParams.add(new Param("file", "vacation.jpg")); | |
184 | formParams.add(new Param("size", "original")); | |
185 | String url = "http://photos.example.net/photos"; | |
186 | final Request req = post(url)// | |
187 | .setFormParams(formParams)// | |
174 | final Request req = post("http://photos.example.net/photos")// | |
175 | .addFormParam("file", "vacation.jpg")// | |
176 | .addFormParam("size", "original")// | |
188 | 177 | .setSignatureCalculator(calc)// |
189 | 178 | .build(); |
190 | 179 | |
197 | 186 | // header: OAuth |
198 | 187 | // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="wPkvxykrw%2BBTdCcGqKr%2B3I%2BPsiM%3D" |
199 | 188 | |
200 | String authHeader = req.getHeaders().get("Authorization"); | |
189 | String authHeader = req.getHeaders().get(AUTHORIZATION); | |
201 | 190 | Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); |
202 | 191 | assertEquals(m.find(), true); |
203 | 192 | String encodedSig = m.group(1); |
204 | String sig = null; | |
205 | try { | |
206 | sig = URLDecoder.decode(encodedSig, "UTF-8"); | |
207 | } catch (UnsupportedEncodingException e) { | |
208 | fail("bad encoding", e); | |
209 | } | |
193 | String sig = URLDecoder.decode(encodedSig, "UTF-8"); | |
210 | 194 | |
211 | 195 | assertEquals(sig, "wPkvxykrw+BTdCcGqKr+3I+PsiM="); |
212 | 196 | } |
213 | 197 | |
214 | @Test(groups = "standalone") | |
215 | public void testGetWithRequestBuilder() { | |
198 | @Test | |
199 | public void testGetWithRequestBuilder() throws UnsupportedEncodingException { | |
216 | 200 | ConsumerKey consumer = new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET); |
217 | 201 | RequestToken user = new RequestToken(TOKEN_KEY, TOKEN_SECRET); |
218 | 202 | OAuthSignatureCalculator calc = new StaticOAuthSignatureCalculator(consumer, user, TIMESTAMP, NONCE); |
219 | 203 | |
220 | List<Param> queryParams = new ArrayList<Param>(); | |
221 | queryParams.add(new Param("file", "vacation.jpg")); | |
222 | queryParams.add(new Param("size", "original")); | |
223 | String url = "http://photos.example.net/photos"; | |
224 | ||
225 | final Request req = get(url)// | |
226 | .setQueryParams(queryParams)// | |
204 | final Request req = get("http://photos.example.net/photos")// | |
205 | .addQueryParam("file", "vacation.jpg")// | |
206 | .addQueryParam("size", "original")// | |
227 | 207 | .setSignatureCalculator(calc)// |
228 | 208 | .build(); |
229 | 209 | |
239 | 219 | // Authorization header: OAuth |
240 | 220 | // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D" |
241 | 221 | |
242 | String authHeader = req.getHeaders().get("Authorization"); | |
222 | String authHeader = req.getHeaders().get(AUTHORIZATION); | |
243 | 223 | Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); |
244 | 224 | assertEquals(m.find(), true); |
245 | 225 | String encodedSig = m.group(1); |
246 | String sig = null; | |
247 | try { | |
248 | sig = URLDecoder.decode(encodedSig, "UTF-8"); | |
249 | } catch (UnsupportedEncodingException e) { | |
250 | fail("bad encoding", e); | |
251 | } | |
226 | String sig = URLDecoder.decode(encodedSig, "UTF-8"); | |
252 | 227 | |
253 | 228 | assertEquals(sig, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); |
254 | 229 | assertEquals(req.getUrl(), "http://photos.example.net/photos?file=vacation.jpg&size=original"); |
255 | 230 | } |
256 | 231 | |
257 | @Test(groups = "standalone") | |
258 | public void testGetWithRequestBuilderAndQuery() { | |
232 | @Test | |
233 | public void testGetWithRequestBuilderAndQuery() throws UnsupportedEncodingException { | |
259 | 234 | ConsumerKey consumer = new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET); |
260 | 235 | RequestToken user = new RequestToken(TOKEN_KEY, TOKEN_SECRET); |
261 | 236 | OAuthSignatureCalculator calc = new StaticOAuthSignatureCalculator(consumer, user, TIMESTAMP, NONCE); |
262 | 237 | |
263 | String url = "http://photos.example.net/photos?file=vacation.jpg&size=original"; | |
264 | ||
265 | final Request req = get(url)// | |
238 | final Request req = get("http://photos.example.net/photos?file=vacation.jpg&size=original")// | |
266 | 239 | .setSignatureCalculator(calc)// |
267 | 240 | .build(); |
268 | 241 | |
280 | 253 | |
281 | 254 | String authHeader = req.getHeaders().get(AUTHORIZATION); |
282 | 255 | Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); |
283 | assertEquals(m.find(), true); | |
256 | assertTrue(m.find()); | |
284 | 257 | String encodedSig = m.group(1); |
285 | String sig = null; | |
286 | try { | |
287 | sig = URLDecoder.decode(encodedSig, "UTF-8"); | |
288 | } catch (UnsupportedEncodingException e) { | |
289 | fail("bad encoding", e); | |
290 | } | |
258 | String sig = URLDecoder.decode(encodedSig, "UTF-8"); | |
291 | 259 | |
292 | 260 | assertEquals(sig, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); |
293 | 261 | assertEquals(req.getUrl(), "http://photos.example.net/photos?file=vacation.jpg&size=original"); |
296 | 264 | "OAuth oauth_consumer_key=\"dpf43f3p2l4k3l03\", oauth_token=\"nnch734d00sl2jdk\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D\", oauth_timestamp=\"1191242096\", oauth_nonce=\"kllo9940pd9333jh\", oauth_version=\"1.0\""); |
297 | 265 | } |
298 | 266 | |
299 | @Test(groups = "standalone") | |
267 | @Test | |
300 | 268 | public void testWithNullRequestToken() { |
301 | String url = "http://photos.example.net/photos?file=vacation.jpg&size=original"; | |
302 | 269 | ConsumerKey consumer = new ConsumerKey("9djdj82h48djs9d2", CONSUMER_SECRET); |
303 | 270 | RequestToken user = new RequestToken(null, null); |
304 | 271 | OAuthSignatureCalculator calc = new OAuthSignatureCalculator(consumer, user); |
305 | 272 | |
306 | final Request request = get(url)// | |
307 | .setSignatureCalculator(calc)// | |
308 | .build(); | |
273 | final Request request = get("http://photos.example.net/photos?file=vacation.jpg&size=original").build(); | |
309 | 274 | |
310 | 275 | String signatureBaseString = calc.signatureBaseString(// |
311 | request.getMethod(),// | |
312 | request.getUri(),// | |
276 | request,// | |
313 | 277 | 137131201,// |
314 | "ZLc92RAkooZcIO/0cctl0Q==",// | |
315 | request.getFormParams(),// | |
316 | request.getQueryParams()).toString(); | |
278 | "ZLc92RAkooZcIO/0cctl0Q==").toString(); | |
317 | 279 | |
318 | 280 | assertEquals(signatureBaseString, "GET&" + // |
319 | 281 | "http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" + // |
323 | 285 | "oauth_timestamp%3D137131201%26" + // |
324 | 286 | "oauth_version%3D1.0%26size%3Doriginal"); |
325 | 287 | } |
288 | ||
289 | @Test | |
290 | public void testWithStarQueryParameterValue() { | |
291 | ConsumerKey consumer = new ConsumerKey("key", "secret"); | |
292 | RequestToken user = new RequestToken(null, null); | |
293 | OAuthSignatureCalculator calc = new OAuthSignatureCalculator(consumer, user); | |
294 | ||
295 | final Request request = get("http://term.ie/oauth/example/request_token.php?testvalue=*").build(); | |
296 | ||
297 | String signatureBaseString = calc.signatureBaseString(// | |
298 | request,// | |
299 | 1469019732,// | |
300 | "6ad17f97334700f3ec2df0631d5b7511").toString(); | |
301 | ||
302 | assertEquals(signatureBaseString, "GET&" + // | |
303 | "http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Frequest_token.php&"// | |
304 | + "oauth_consumer_key%3Dkey%26"// | |
305 | + "oauth_nonce%3D6ad17f97334700f3ec2df0631d5b7511%26"// | |
306 | + "oauth_signature_method%3DHMAC-SHA1%26"// | |
307 | + "oauth_timestamp%3D1469019732%26"// | |
308 | + "oauth_version%3D1.0%26"// | |
309 | + "testvalue%3D%252A"); | |
310 | } | |
326 | 311 | } |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
0 | 0 | /* |
1 | 1 | * Copyright 2010 Ning, Inc. |
2 | 2 | * |
3 | * Ning licenses this file to you under the Apache License, version 2.0 | |
3 | * This program is licensed to you under the Apache License, version 2.0 | |
4 | 4 | * (the "License"); you may not use this file except in compliance with the |
5 | 5 | * License. You may obtain a copy of the License at: |
6 | 6 | * |
1 | 1 | <parent> |
2 | 2 | <groupId>org.asynchttpclient</groupId> |
3 | 3 | <artifactId>async-http-client-extras-parent</artifactId> |
4 | <version>2.0.10</version> | |
4 | <version>2.0.11</version> | |
5 | 5 | </parent> |
6 | 6 | <modelVersion>4.0.0</modelVersion> |
7 | 7 | <artifactId>async-http-client-extras-guava</artifactId> |
17 | 17 | <parent> |
18 | 18 | <artifactId>async-http-client-extras-parent</artifactId> |
19 | 19 | <groupId>org.asynchttpclient</groupId> |
20 | <version>2.0.10</version> | |
20 | <version>2.0.11</version> | |
21 | 21 | </parent> |
22 | 22 | <artifactId>async-http-client-extras-jdeferred</artifactId> |
23 | 23 | <name>Asynchronous Http Client JDeferred Extras</name> |
1 | 1 | <parent> |
2 | 2 | <groupId>org.asynchttpclient</groupId> |
3 | 3 | <artifactId>async-http-client-project</artifactId> |
4 | <version>2.0.10</version> | |
4 | <version>2.0.11</version> | |
5 | 5 | </parent> |
6 | 6 | <modelVersion>4.0.0</modelVersion> |
7 | 7 | <artifactId>async-http-client-extras-parent</artifactId> |
1 | 1 | <parent> |
2 | 2 | <groupId>org.asynchttpclient</groupId> |
3 | 3 | <artifactId>async-http-client-extras-parent</artifactId> |
4 | <version>2.0.10</version> | |
4 | <version>2.0.11</version> | |
5 | 5 | </parent> |
6 | 6 | <modelVersion>4.0.0</modelVersion> |
7 | 7 | <artifactId>async-http-client-extras-registry</artifactId> |
2 | 2 | <parent> |
3 | 3 | <artifactId>async-http-client-extras-parent</artifactId> |
4 | 4 | <groupId>org.asynchttpclient</groupId> |
5 | <version>2.0.10</version> | |
5 | <version>2.0.11</version> | |
6 | 6 | </parent> |
7 | 7 | <artifactId>async-http-client-extras-rxjava</artifactId> |
8 | 8 | <name>Asynchronous Http Client RxJava Extras</name> |
2 | 2 | <parent> |
3 | 3 | <artifactId>async-http-client-extras-parent</artifactId> |
4 | 4 | <groupId>org.asynchttpclient</groupId> |
5 | <version>2.0.10</version> | |
5 | <version>2.0.11</version> | |
6 | 6 | </parent> |
7 | 7 | <artifactId>async-http-client-extras-simple</artifactId> |
8 | 8 | <name>Asynchronous Http Simple Client</name> |
19 | 19 | <parent> |
20 | 20 | <groupId>org.asynchttpclient</groupId> |
21 | 21 | <artifactId>netty-bp</artifactId> |
22 | <version>2.0.10</version> | |
22 | <version>2.0.11</version> | |
23 | 23 | </parent> |
24 | 24 | |
25 | 25 | <artifactId>netty-codec-dns</artifactId> |
1 | 1 | <parent> |
2 | 2 | <groupId>org.asynchttpclient</groupId> |
3 | 3 | <artifactId>async-http-client-project</artifactId> |
4 | <version>2.0.10</version> | |
4 | <version>2.0.11</version> | |
5 | 5 | </parent> |
6 | 6 | <modelVersion>4.0.0</modelVersion> |
7 | 7 | <artifactId>netty-bp</artifactId> |
19 | 19 | <parent> |
20 | 20 | <groupId>org.asynchttpclient</groupId> |
21 | 21 | <artifactId>netty-bp</artifactId> |
22 | <version>2.0.10</version> | |
22 | <version>2.0.11</version> | |
23 | 23 | </parent> |
24 | 24 | |
25 | 25 | <artifactId>netty-resolver</artifactId> |
8 | 8 | <parent> |
9 | 9 | <groupId>org.asynchttpclient</groupId> |
10 | 10 | <artifactId>netty-bp</artifactId> |
11 | <version>2.0.10</version> | |
11 | <version>2.0.11</version> | |
12 | 12 | </parent> |
13 | 13 | |
14 | 14 | <artifactId>netty-resolver-dns</artifactId> |
41 | 41 | import io.netty.util.concurrent.FastThreadLocal; |
42 | 42 | import io.netty.util.concurrent.Future; |
43 | 43 | import io.netty.util.concurrent.Promise; |
44 | import io.netty.util.internal.EmptyArrays; | |
44 | 45 | import io.netty.util.internal.PlatformDependent; |
46 | import io.netty.util.internal.StringUtil2; | |
45 | 47 | import io.netty.util.internal.logging.InternalLogger; |
46 | 48 | import io.netty.util.internal.logging.InternalLoggerFactory; |
47 | 49 | |
50 | import java.lang.reflect.Method; | |
48 | 51 | import java.net.IDN; |
49 | 52 | import java.net.Inet4Address; |
50 | 53 | import java.net.InetAddress; |
66 | 69 | private static final InetAddress LOCALHOST_ADDRESS; |
67 | 70 | |
68 | 71 | static final InternetProtocolFamily[] DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[2]; |
72 | static final String[] DEFAULT_SEACH_DOMAINS; | |
69 | 73 | |
70 | 74 | static { |
71 | 75 | // Note that we did not use SystemPropertyUtil.getBoolean() here to emulate the behavior of JDK. |
82 | 86 | } |
83 | 87 | } |
84 | 88 | |
89 | static { | |
90 | String[] searchDomains; | |
91 | try { | |
92 | Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration"); | |
93 | Method open = configClass.getMethod("open"); | |
94 | Method nameservers = configClass.getMethod("searchlist"); | |
95 | Object instance = open.invoke(null); | |
96 | ||
97 | @SuppressWarnings("unchecked") | |
98 | List<String> list = (List<String>) nameservers.invoke(instance); | |
99 | searchDomains = list.toArray(new String[list.size()]); | |
100 | } catch (Exception ignore) { | |
101 | // Failed to get the system name search domain list. | |
102 | searchDomains = EmptyArrays.EMPTY_STRINGS; | |
103 | } | |
104 | DEFAULT_SEACH_DOMAINS = searchDomains; | |
105 | } | |
106 | ||
85 | 107 | private static final DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder(); |
86 | 108 | private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder(); |
87 | 109 | |
115 | 137 | private final int maxPayloadSize; |
116 | 138 | private final boolean optResourceEnabled; |
117 | 139 | private final HostsFileEntriesResolver hostsFileEntriesResolver; |
140 | private final String[] searchDomains; | |
141 | private final int ndots; | |
118 | 142 | |
119 | 143 | /** |
120 | 144 | * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers. |
133 | 157 | * @param maxPayloadSize the capacity of the datagram packet buffer |
134 | 158 | * @param optResourceEnabled if automatic inclusion of a optional records is enabled |
135 | 159 | * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases |
160 | * @param searchDomains the list of search domain | |
161 | * @param ndots the ndots value | |
136 | 162 | */ |
137 | 163 | public DnsNameResolver( |
138 | 164 | EventLoop eventLoop, |
146 | 172 | boolean traceEnabled, |
147 | 173 | int maxPayloadSize, |
148 | 174 | boolean optResourceEnabled, |
149 | HostsFileEntriesResolver hostsFileEntriesResolver) { | |
175 | HostsFileEntriesResolver hostsFileEntriesResolver, | |
176 | String[] searchDomains, | |
177 | int ndots) { | |
150 | 178 | |
151 | 179 | super(eventLoop); |
152 | 180 | checkNotNull(channelFactory, "channelFactory"); |
160 | 188 | this.optResourceEnabled = optResourceEnabled; |
161 | 189 | this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver"); |
162 | 190 | this.resolveCache = resolveCache; |
191 | this.searchDomains = checkNotNull(searchDomains, "searchDomains").clone(); | |
192 | this.ndots = checkPositive(ndots, "ndots"); | |
163 | 193 | |
164 | 194 | Bootstrap b = new Bootstrap(); |
165 | 195 | b.group(executor()); |
211 | 241 | |
212 | 242 | InternetProtocolFamily[] resolveAddressTypesUnsafe() { |
213 | 243 | return resolvedAddressTypes; |
244 | } | |
245 | ||
246 | final String[] searchDomains() { | |
247 | return searchDomains; | |
248 | } | |
249 | ||
250 | final int ndots() { | |
251 | return ndots; | |
214 | 252 | } |
215 | 253 | |
216 | 254 | /** |
374 | 412 | private void doResolveUncached(String hostname, |
375 | 413 | Promise<InetAddress> promise, |
376 | 414 | DnsCache resolveCache) { |
377 | final DnsNameResolverContext<InetAddress> ctx = | |
378 | new DnsNameResolverContext<InetAddress>(this, hostname, promise, resolveCache) { | |
379 | @Override | |
380 | protected boolean finishResolve( | |
381 | InternetProtocolFamily f, List<DnsCacheEntry> resolvedEntries) { | |
382 | ||
383 | final int numEntries = resolvedEntries.size(); | |
384 | for (int i = 0; i < numEntries; i++) { | |
385 | final InetAddress a = resolvedEntries.get(i).address(); | |
386 | if (addressMatchFamily(a, f)) { | |
387 | setSuccess(promise(), a); | |
388 | return true; | |
389 | } | |
390 | } | |
391 | return false; | |
392 | } | |
393 | }; | |
394 | ||
395 | ctx.resolve(); | |
415 | SingleResolverContext ctx = new SingleResolverContext(this, hostname, resolveCache); | |
416 | ctx.resolve(promise); | |
417 | } | |
418 | ||
419 | final class SingleResolverContext extends DnsNameResolverContext<InetAddress> { | |
420 | ||
421 | SingleResolverContext(DnsNameResolver parent, String hostname, DnsCache resolveCache) { | |
422 | super(parent, hostname, resolveCache); | |
423 | } | |
424 | ||
425 | @Override | |
426 | DnsNameResolverContext<InetAddress> newResolverContext(DnsNameResolver parent, | |
427 | String hostname, DnsCache resolveCache) { | |
428 | return new SingleResolverContext(parent, hostname, resolveCache); | |
429 | } | |
430 | ||
431 | @Override | |
432 | boolean finishResolve( | |
433 | InternetProtocolFamily f, List<DnsCacheEntry> resolvedEntries, | |
434 | Promise<InetAddress> promise) { | |
435 | ||
436 | final int numEntries = resolvedEntries.size(); | |
437 | for (int i = 0; i < numEntries; i++) { | |
438 | final InetAddress a = resolvedEntries.get(i).address(); | |
439 | if (addressMatchFamily(a, f)) { | |
440 | setSuccess(promise, a); | |
441 | return true; | |
442 | } | |
443 | } | |
444 | return false; | |
445 | } | |
396 | 446 | } |
397 | 447 | |
398 | 448 | @Override |
470 | 520 | return true; |
471 | 521 | } |
472 | 522 | |
473 | private void doResolveAllUncached(final String hostname, | |
474 | final Promise<List<InetAddress>> promise, | |
523 | final class ListResolverContext extends DnsNameResolverContext<List<InetAddress>> { | |
524 | ListResolverContext(DnsNameResolver parent, String hostname, DnsCache resolveCache) { | |
525 | super(parent, hostname, resolveCache); | |
526 | } | |
527 | ||
528 | @Override | |
529 | DnsNameResolverContext<List<InetAddress>> newResolverContext(DnsNameResolver parent, String hostname, | |
530 | DnsCache resolveCache) { | |
531 | return new ListResolverContext(parent, hostname, resolveCache); | |
532 | } | |
533 | ||
534 | @Override | |
535 | boolean finishResolve( | |
536 | InternetProtocolFamily f, List<DnsCacheEntry> resolvedEntries, | |
537 | Promise<List<InetAddress>> promise) { | |
538 | ||
539 | List<InetAddress> result = null; | |
540 | final int numEntries = resolvedEntries.size(); | |
541 | for (int i = 0; i < numEntries; i++) { | |
542 | final InetAddress a = resolvedEntries.get(i).address(); | |
543 | if (addressMatchFamily(a, f)) { | |
544 | if (result == null) { | |
545 | result = new ArrayList<InetAddress>(numEntries); | |
546 | } | |
547 | result.add(a); | |
548 | } | |
549 | } | |
550 | ||
551 | if (result != null) { | |
552 | promise.trySuccess(result); | |
553 | return true; | |
554 | } | |
555 | return false; | |
556 | } | |
557 | } | |
558 | ||
559 | private void doResolveAllUncached(String hostname, | |
560 | Promise<List<InetAddress>> promise, | |
475 | 561 | DnsCache resolveCache) { |
476 | final DnsNameResolverContext<List<InetAddress>> ctx = | |
477 | new DnsNameResolverContext<List<InetAddress>>(this, hostname, promise, resolveCache) { | |
478 | @Override | |
479 | protected boolean finishResolve( | |
480 | InternetProtocolFamily f, List<DnsCacheEntry> resolvedEntries) { | |
481 | ||
482 | List<InetAddress> result = null; | |
483 | final int numEntries = resolvedEntries.size(); | |
484 | for (int i = 0; i < numEntries; i++) { | |
485 | final InetAddress a = resolvedEntries.get(i).address(); | |
486 | if (addressMatchFamily(a, f)) { | |
487 | if (result == null) { | |
488 | result = new ArrayList<InetAddress>(numEntries); | |
489 | } | |
490 | result.add(a); | |
491 | } | |
492 | } | |
493 | ||
494 | if (result != null) { | |
495 | promise().trySuccess(result); | |
496 | return true; | |
497 | } | |
498 | return false; | |
499 | } | |
500 | }; | |
501 | ||
502 | ctx.resolve(); | |
562 | DnsNameResolverContext<List<InetAddress>> ctx = new ListResolverContext(this, hostname, resolveCache); | |
563 | ctx.resolve(promise); | |
503 | 564 | } |
504 | 565 | |
505 | 566 | private static String hostname(String inetHost) { |
506 | return IDN.toASCII(inetHost); | |
567 | String hostname = IDN.toASCII(inetHost); | |
568 | // Check for http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6894622 | |
569 | if (StringUtil2.endsWith(inetHost, '.') && !StringUtil2.endsWith(hostname, '.')) { | |
570 | hostname += "."; | |
571 | } | |
572 | return hostname; | |
507 | 573 | } |
508 | 574 | |
509 | 575 | /** |
+45
-1
48 | 48 | private int maxPayloadSize = 4096; |
49 | 49 | private boolean optResourceEnabled = true; |
50 | 50 | private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT; |
51 | private String[] searchDomains = DnsNameResolver.DEFAULT_SEACH_DOMAINS; | |
52 | private int ndots = 1; | |
51 | 53 | |
52 | 54 | /** |
53 | 55 | * Creates a new builder. |
282 | 284 | */ |
283 | 285 | public DnsNameResolverBuilder hostsFileEntriesResolver(HostsFileEntriesResolver hostsFileEntriesResolver) { |
284 | 286 | this.hostsFileEntriesResolver = hostsFileEntriesResolver; |
287 | return this; | |
288 | } | |
289 | ||
290 | /** | |
291 | * Set the list of search domains of the resolver. | |
292 | * | |
293 | * @param searchDomains the search domains | |
294 | * @return {@code this} | |
295 | */ | |
296 | public DnsNameResolverBuilder searchDomains(Iterable<String> searchDomains) { | |
297 | checkNotNull(searchDomains, "searchDomains"); | |
298 | ||
299 | final List<String> list = | |
300 | InternalThreadLocalMap.get().arrayList(4); | |
301 | ||
302 | for (String f : searchDomains) { | |
303 | if (f == null) { | |
304 | break; | |
305 | } | |
306 | ||
307 | // Avoid duplicate entries. | |
308 | if (list.contains(f)) { | |
309 | continue; | |
310 | } | |
311 | ||
312 | list.add(f); | |
313 | } | |
314 | ||
315 | this.searchDomains = list.toArray(new String[list.size()]); | |
316 | return this; | |
317 | } | |
318 | ||
319 | /** | |
320 | * Set the number of dots which must appear in a name before an initial absolute query is made. | |
321 | * | |
322 | * @param ndots the ndots value | |
323 | * @return {@code this} | |
324 | */ | |
325 | public DnsNameResolverBuilder ndots(int ndots) { | |
326 | this.ndots = ndots; | |
285 | 327 | return this; |
286 | 328 | } |
287 | 329 | |
311 | 353 | traceEnabled, |
312 | 354 | maxPayloadSize, |
313 | 355 | optResourceEnabled, |
314 | hostsFileEntriesResolver); | |
356 | hostsFileEntriesResolver, | |
357 | searchDomains, | |
358 | ndots); | |
315 | 359 | } |
316 | 360 | } |
+78
-41
34 | 34 | import io.netty.util.concurrent.FutureListener; |
35 | 35 | import io.netty.util.concurrent.Promise; |
36 | 36 | import io.netty.util.internal.StringUtil; |
37 | import io.netty.util.internal.StringUtil2; | |
37 | 38 | |
38 | 39 | import java.net.Inet4Address; |
39 | 40 | import java.net.Inet6Address; |
67 | 68 | |
68 | 69 | private final DnsNameResolver parent; |
69 | 70 | private final DnsServerAddressStream nameServerAddrs; |
70 | private final Promise<T> promise; | |
71 | 71 | private final String hostname; |
72 | 72 | private final DnsCache resolveCache; |
73 | 73 | private final boolean traceEnabled; |
85 | 85 | |
86 | 86 | protected DnsNameResolverContext(DnsNameResolver parent, |
87 | 87 | String hostname, |
88 | Promise<T> promise, | |
89 | 88 | DnsCache resolveCache) { |
90 | 89 | this.parent = parent; |
91 | this.promise = promise; | |
92 | 90 | this.hostname = hostname; |
93 | 91 | this.resolveCache = resolveCache; |
94 | 92 | |
99 | 97 | allowedQueries = maxAllowedQueries; |
100 | 98 | } |
101 | 99 | |
102 | protected Promise<T> promise() { | |
103 | return promise; | |
104 | } | |
105 | ||
106 | void resolve() { | |
100 | void resolve(Promise<T> promise) { | |
101 | boolean directSearch = parent.searchDomains().length == 0 || StringUtil2.endsWith(hostname, '.'); | |
102 | if (directSearch) { | |
103 | internalResolve(promise); | |
104 | } else { | |
105 | final Promise<T> original = promise; | |
106 | promise = parent.executor().newPromise(); | |
107 | promise.addListener(new FutureListener<T>() { | |
108 | int count; | |
109 | @Override | |
110 | public void operationComplete(Future<T> future) throws Exception { | |
111 | if (future.isSuccess()) { | |
112 | original.trySuccess(future.getNow()); | |
113 | } else if (count < parent.searchDomains().length) { | |
114 | String searchDomain = parent.searchDomains()[count++]; | |
115 | Promise<T> nextPromise = parent.executor().newPromise(); | |
116 | String nextHostname = DnsNameResolverContext.this.hostname + "." + searchDomain; | |
117 | DnsNameResolverContext<T> nextContext = newResolverContext(parent, | |
118 | nextHostname, resolveCache); | |
119 | nextContext.internalResolve(nextPromise); | |
120 | nextPromise.addListener(this); | |
121 | } else { | |
122 | original.tryFailure(future.cause()); | |
123 | } | |
124 | } | |
125 | }); | |
126 | int dots = 0; | |
127 | for (int idx = hostname.length() - 1; idx >= 0; idx--) { | |
128 | if (hostname.charAt(idx) == '.' && ++dots >= parent.ndots()) { | |
129 | internalResolve(promise); | |
130 | return; | |
131 | } | |
132 | } | |
133 | promise.tryFailure(new UnknownHostException(hostname)); | |
134 | } | |
135 | } | |
136 | ||
137 | private void internalResolve(Promise<T> promise) { | |
107 | 138 | InetSocketAddress nameServerAddrToTry = nameServerAddrs.next(); |
108 | 139 | for (InternetProtocolFamily f: resolveAddressTypes) { |
109 | 140 | final DnsRecordType type; |
118 | 149 | throw new Error(); |
119 | 150 | } |
120 | 151 | |
121 | query(nameServerAddrToTry, new DefaultDnsQuestion(hostname, type)); | |
122 | } | |
123 | } | |
124 | ||
125 | private void query(InetSocketAddress nameServerAddr, final DnsQuestion question) { | |
152 | query(nameServerAddrToTry, new DefaultDnsQuestion(hostname, type), promise); | |
153 | } | |
154 | } | |
155 | ||
156 | private void query(InetSocketAddress nameServerAddr, final DnsQuestion question, final Promise<T> promise) { | |
126 | 157 | if (allowedQueries == 0 || promise.isCancelled()) { |
127 | tryToFinishResolve(); | |
158 | tryToFinishResolve(promise); | |
128 | 159 | return; |
129 | 160 | } |
130 | 161 | |
144 | 175 | |
145 | 176 | try { |
146 | 177 | if (future.isSuccess()) { |
147 | onResponse(question, future.getNow()); | |
178 | onResponse(question, future.getNow(), promise); | |
148 | 179 | } else { |
149 | 180 | // Server did not respond or I/O error occurred; try again. |
150 | 181 | if (traceEnabled) { |
151 | 182 | addTrace(future.cause()); |
152 | 183 | } |
153 | query(nameServerAddrs.next(), question); | |
184 | query(nameServerAddrs.next(), question, promise); | |
154 | 185 | } |
155 | 186 | } finally { |
156 | tryToFinishResolve(); | |
187 | tryToFinishResolve(promise); | |
157 | 188 | } |
158 | 189 | } |
159 | 190 | }); |
160 | 191 | } |
161 | 192 | |
162 | void onResponse(final DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) { | |
193 | void onResponse(final DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope, | |
194 | Promise<T> promise) { | |
163 | 195 | try { |
164 | 196 | final DnsResponse res = envelope.content(); |
165 | 197 | final DnsResponseCode code = res.code(); |
166 | 198 | if (code == DnsResponseCode.NOERROR) { |
167 | 199 | final DnsRecordType type = question.type(); |
168 | 200 | if (type == DnsRecordType.A || type == DnsRecordType.AAAA) { |
169 | onResponseAorAAAA(type, question, envelope); | |
201 | onResponseAorAAAA(type, question, envelope, promise); | |
170 | 202 | } else if (type == DnsRecordType.CNAME) { |
171 | onResponseCNAME(question, envelope); | |
203 | onResponseCNAME(question, envelope, promise); | |
172 | 204 | } |
173 | 205 | return; |
174 | 206 | } |
181 | 213 | |
182 | 214 | // Retry with the next server if the server did not tell us that the domain does not exist. |
183 | 215 | if (code != DnsResponseCode.NXDOMAIN) { |
184 | query(nameServerAddrs.next(), question); | |
216 | query(nameServerAddrs.next(), question, promise); | |
185 | 217 | } |
186 | 218 | } finally { |
187 | 219 | ReferenceCountUtil.safeRelease(envelope); |
189 | 221 | } |
190 | 222 | |
191 | 223 | private void onResponseAorAAAA( |
192 | DnsRecordType qType, DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) { | |
224 | DnsRecordType qType, DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope, | |
225 | Promise<T> promise) { | |
193 | 226 | |
194 | 227 | // We often get a bunch of CNAMES as well when we asked for A/AAAA. |
195 | 228 | final DnsResponse response = envelope.content(); |
266 | 299 | |
267 | 300 | // We aked for A/AAAA but we got only CNAME. |
268 | 301 | if (!cnames.isEmpty()) { |
269 | onResponseCNAME(question, envelope, cnames, false); | |
270 | } | |
271 | } | |
272 | ||
273 | private void onResponseCNAME(DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope) { | |
274 | onResponseCNAME(question, envelope, buildAliasMap(envelope.content()), true); | |
302 | onResponseCNAME(question, envelope, cnames, false, promise); | |
303 | } | |
304 | } | |
305 | ||
306 | private void onResponseCNAME(DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope, | |
307 | Promise<T> promise) { | |
308 | onResponseCNAME(question, envelope, buildAliasMap(envelope.content()), true, promise); | |
275 | 309 | } |
276 | 310 | |
277 | 311 | private void onResponseCNAME( |
278 | 312 | DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> response, |
279 | Map<String, String> cnames, boolean trace) { | |
313 | Map<String, String> cnames, boolean trace, Promise<T> promise) { | |
280 | 314 | |
281 | 315 | // Resolve the host name in the question into the real host name. |
282 | 316 | final String name = question.name().toLowerCase(Locale.US); |
295 | 329 | } |
296 | 330 | |
297 | 331 | if (found) { |
298 | followCname(response.sender(), name, resolved); | |
332 | followCname(response.sender(), name, resolved, promise); | |
299 | 333 | } else if (trace && traceEnabled) { |
300 | 334 | addTrace(response.sender(), "no matching CNAME record found"); |
301 | 335 | } |
331 | 365 | return cnames != null? cnames : Collections.<String, String>emptyMap(); |
332 | 366 | } |
333 | 367 | |
334 | void tryToFinishResolve() { | |
368 | void tryToFinishResolve(Promise<T> promise) { | |
335 | 369 | if (!queriesInProgress.isEmpty()) { |
336 | 370 | // There are still some queries we did not receive responses for. |
337 | 371 | if (gotPreferredAddress()) { |
338 | 372 | // But it's OK to finish the resolution process if we got a resolved address of the preferred type. |
339 | finishResolve(); | |
373 | finishResolve(promise); | |
340 | 374 | } |
341 | 375 | |
342 | 376 | // We did not get any resolved address of the preferred type, so we can't finish the resolution process. |
349 | 383 | if (!triedCNAME) { |
350 | 384 | // As the last resort, try to query CNAME, just in case the name server has it. |
351 | 385 | triedCNAME = true; |
352 | query(nameServerAddrs.next(), new DefaultDnsQuestion(hostname, DnsRecordType.CNAME)); | |
386 | query(nameServerAddrs.next(), new DefaultDnsQuestion(hostname, DnsRecordType.CNAME), promise); | |
353 | 387 | return; |
354 | 388 | } |
355 | 389 | } |
356 | 390 | |
357 | 391 | // We have at least one resolved address or tried CNAME as the last resort.. |
358 | finishResolve(); | |
392 | finishResolve(promise); | |
359 | 393 | } |
360 | 394 | |
361 | 395 | private boolean gotPreferredAddress() { |
384 | 418 | return false; |
385 | 419 | } |
386 | 420 | |
387 | private void finishResolve() { | |
421 | private void finishResolve(Promise<T> promise) { | |
388 | 422 | if (!queriesInProgress.isEmpty()) { |
389 | 423 | // If there are queries in progress, we should cancel it because we already finished the resolution. |
390 | 424 | for (Iterator<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> i = queriesInProgress.iterator(); |
401 | 435 | if (resolvedEntries != null) { |
402 | 436 | // Found at least one resolved address. |
403 | 437 | for (InternetProtocolFamily f: resolveAddressTypes) { |
404 | if (finishResolve(f, resolvedEntries)) { | |
438 | if (finishResolve(f, resolvedEntries, promise)) { | |
405 | 439 | return; |
406 | 440 | } |
407 | 441 | } |
434 | 468 | promise.tryFailure(cause); |
435 | 469 | } |
436 | 470 | |
437 | protected abstract boolean finishResolve( | |
438 | InternetProtocolFamily f, List<DnsCacheEntry> resolvedEntries); | |
471 | abstract boolean finishResolve(InternetProtocolFamily f, List<DnsCacheEntry> resolvedEntries, | |
472 | Promise<T> promise); | |
473 | ||
474 | abstract DnsNameResolverContext<T> newResolverContext(DnsNameResolver parent, String hostname, | |
475 | DnsCache resolveCache); | |
439 | 476 | |
440 | 477 | static String decodeDomainName(ByteBuf in) { |
441 | 478 | in.markReaderIndex(); |
449 | 486 | } |
450 | 487 | } |
451 | 488 | |
452 | private void followCname(InetSocketAddress nameServerAddr, String name, String cname) { | |
489 | private void followCname(InetSocketAddress nameServerAddr, String name, String cname, Promise<T> promise) { | |
453 | 490 | |
454 | 491 | if (traceEnabled) { |
455 | 492 | if (trace == null) { |
466 | 503 | } |
467 | 504 | |
468 | 505 | final InetSocketAddress nextAddr = nameServerAddrs.next(); |
469 | query(nextAddr, new DefaultDnsQuestion(cname, DnsRecordType.A)); | |
470 | query(nextAddr, new DefaultDnsQuestion(cname, DnsRecordType.AAAA)); | |
506 | query(nextAddr, new DefaultDnsQuestion(cname, DnsRecordType.A), promise); | |
507 | query(nextAddr, new DefaultDnsQuestion(cname, DnsRecordType.AAAA), promise); | |
471 | 508 | } |
472 | 509 | |
473 | 510 | private void addTrace(InetSocketAddress nameServerAddr, String msg) { |
0 | /* | |
1 | * Copyright 2012 The Netty Project | |
2 | * | |
3 | * The Netty Project licenses this file to you under the Apache License, | |
4 | * version 2.0 (the "License"); you may not use this file except in compliance | |
5 | * with the License. You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
12 | * License for the specific language governing permissions and limitations | |
13 | * under the License. | |
14 | */ | |
15 | package io.netty.util.internal; | |
16 | ||
17 | /** | |
18 | * String utility class. | |
19 | */ | |
20 | public final class StringUtil2 { | |
21 | ||
22 | /** | |
23 | * Determine if the string {@code s} ends with the char {@code c}. | |
24 | * | |
25 | * @param s the string to test | |
26 | * @param c the tested char | |
27 | * @return true if {@code s} ends with the char {@code c} | |
28 | */ | |
29 | public static boolean endsWith(CharSequence s, char c) { | |
30 | int len = s.length(); | |
31 | return len > 0 && s.charAt(len - 1) == c; | |
32 | } | |
33 | ||
34 | private StringUtil2() { | |
35 | // Unused. | |
36 | } | |
37 | }⏎ |
+17
-198
27 | 27 | import io.netty.handler.codec.dns.DnsResponse; |
28 | 28 | import io.netty.handler.codec.dns.DnsResponseCode; |
29 | 29 | import io.netty.handler.codec.dns.DnsSection; |
30 | import io.netty.util.NetUtil; | |
31 | 30 | import io.netty.util.concurrent.Future; |
32 | 31 | import io.netty.util.internal.StringUtil; |
33 | import io.netty.util.internal.ThreadLocalRandom; | |
34 | 32 | import io.netty.util.internal.logging.InternalLogger; |
35 | 33 | import io.netty.util.internal.logging.InternalLoggerFactory; |
36 | import org.apache.directory.server.dns.DnsServer; | |
37 | import org.apache.directory.server.dns.io.encoder.DnsMessageEncoder; | |
38 | import org.apache.directory.server.dns.io.encoder.ResourceRecordEncoder; | |
39 | import org.apache.directory.server.dns.messages.DnsMessage; | |
40 | import org.apache.directory.server.dns.messages.QuestionRecord; | |
41 | import org.apache.directory.server.dns.messages.RecordClass; | |
42 | import org.apache.directory.server.dns.messages.RecordType; | |
43 | import org.apache.directory.server.dns.messages.ResourceRecord; | |
44 | import org.apache.directory.server.dns.messages.ResourceRecordModifier; | |
45 | import org.apache.directory.server.dns.protocol.DnsProtocolHandler; | |
46 | import org.apache.directory.server.dns.protocol.DnsUdpDecoder; | |
47 | import org.apache.directory.server.dns.protocol.DnsUdpEncoder; | |
48 | import org.apache.directory.server.dns.store.DnsAttribute; | |
49 | import org.apache.directory.server.dns.store.RecordStore; | |
50 | import org.apache.directory.server.protocol.shared.transport.UdpTransport; | |
51 | import org.apache.mina.core.buffer.IoBuffer; | |
52 | import org.apache.mina.core.session.IoSession; | |
53 | import org.apache.mina.filter.codec.ProtocolCodecFactory; | |
54 | import org.apache.mina.filter.codec.ProtocolCodecFilter; | |
55 | import org.apache.mina.filter.codec.ProtocolDecoder; | |
56 | import org.apache.mina.filter.codec.ProtocolEncoder; | |
57 | import org.apache.mina.filter.codec.ProtocolEncoderOutput; | |
58 | import org.apache.mina.transport.socket.DatagramAcceptor; | |
59 | import org.apache.mina.transport.socket.DatagramSessionConfig; | |
60 | 34 | import org.junit.AfterClass; |
61 | 35 | import org.junit.BeforeClass; |
62 | 36 | import org.junit.Test; |
63 | 37 | |
64 | import java.io.IOException; | |
65 | 38 | import java.net.InetAddress; |
66 | 39 | import java.net.InetSocketAddress; |
67 | 40 | import java.net.UnknownHostException; |
261 | 234 | StringUtil.EMPTY_STRING); |
262 | 235 | } |
263 | 236 | |
264 | private static final TestDnsServer dnsServer = new TestDnsServer(); | |
237 | private static final TestDnsServer dnsServer = new TestDnsServer(DOMAINS); | |
265 | 238 | private static final EventLoopGroup group = new NioEventLoopGroup(1); |
266 | 239 | |
267 | 240 | private static DnsNameResolverBuilder newResolver() { |
274 | 247 | |
275 | 248 | private static DnsNameResolverBuilder newResolver(InternetProtocolFamily... resolvedAddressTypes) { |
276 | 249 | return newResolver() |
250 | .resolvedAddressTypes(resolvedAddressTypes); | |
251 | } | |
252 | ||
253 | private static DnsNameResolverBuilder newNonCachedResolver(InternetProtocolFamily... resolvedAddressTypes) { | |
254 | return newResolver() | |
255 | .resolveCache(NoopDnsCache.INSTANCE) | |
277 | 256 | .resolvedAddressTypes(resolvedAddressTypes); |
278 | 257 | } |
279 | 258 | |
343 | 322 | DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv6).build(); |
344 | 323 | try { |
345 | 324 | testResolve0(resolver, EXCLUSIONS_RESOLVE_AAAA); |
325 | } finally { | |
326 | resolver.close(); | |
327 | } | |
328 | } | |
329 | ||
330 | @Test | |
331 | public void testNonCachedResolve() throws Exception { | |
332 | DnsNameResolver resolver = newNonCachedResolver(InternetProtocolFamily.IPv4).build(); | |
333 | try { | |
334 | testResolve0(resolver, EXCLUSIONS_RESOLVE_A); | |
346 | 335 | } finally { |
347 | 336 | resolver.close(); |
348 | 337 | } |
514 | 503 | futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX))); |
515 | 504 | } |
516 | 505 | |
517 | private static final class TestDnsServer extends DnsServer { | |
518 | private static final Map<String, byte[]> BYTES = new HashMap<String, byte[]>(); | |
519 | private static final String[] IPV6_ADDRESSES; | |
520 | static { | |
521 | BYTES.put("::1", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}); | |
522 | BYTES.put("0:0:0:0:0:0:1:1", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}); | |
523 | BYTES.put("0:0:0:0:0:1:1:1", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}); | |
524 | BYTES.put("0:0:0:0:1:1:1:1", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1}); | |
525 | BYTES.put("0:0:0:1:1:1:1:1", new byte[] {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
526 | BYTES.put("0:0:1:1:1:1:1:1", new byte[] {0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
527 | BYTES.put("0:1:1:1:1:1:1:1", new byte[] {0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
528 | BYTES.put("1:1:1:1:1:1:1:1", new byte[] {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
529 | ||
530 | IPV6_ADDRESSES = BYTES.keySet().toArray(new String[BYTES.size()]); | |
531 | } | |
532 | ||
533 | @Override | |
534 | public void start() throws IOException { | |
535 | InetSocketAddress address = new InetSocketAddress(NetUtil.LOCALHOST4, 0); | |
536 | UdpTransport transport = new UdpTransport(address.getHostName(), address.getPort()); | |
537 | setTransports(transport); | |
538 | ||
539 | DatagramAcceptor acceptor = transport.getAcceptor(); | |
540 | ||
541 | acceptor.setHandler(new DnsProtocolHandler(this, new TestRecordStore()) { | |
542 | @Override | |
543 | public void sessionCreated(IoSession session) throws Exception { | |
544 | // USe our own codec to support AAAA testing | |
545 | session.getFilterChain() | |
546 | .addFirst("codec", new ProtocolCodecFilter(new TestDnsProtocolUdpCodecFactory())); | |
547 | } | |
548 | }); | |
549 | ||
550 | ((DatagramSessionConfig) acceptor.getSessionConfig()).setReuseAddress(true); | |
551 | ||
552 | // Start the listener | |
553 | acceptor.bind(); | |
554 | } | |
555 | ||
556 | public InetSocketAddress localAddress() { | |
557 | return (InetSocketAddress) getTransports()[0].getAcceptor().getLocalAddress(); | |
558 | } | |
559 | ||
560 | /** | |
561 | * {@link ProtocolCodecFactory} which allows to test AAAA resolution. | |
562 | */ | |
563 | private static final class TestDnsProtocolUdpCodecFactory implements ProtocolCodecFactory { | |
564 | private final DnsMessageEncoder encoder = new DnsMessageEncoder(); | |
565 | private final TestAAAARecordEncoder recordEncoder = new TestAAAARecordEncoder(); | |
566 | ||
567 | @Override | |
568 | public ProtocolEncoder getEncoder(IoSession session) throws Exception { | |
569 | return new DnsUdpEncoder() { | |
570 | ||
571 | @Override | |
572 | public void encode(IoSession session, Object message, ProtocolEncoderOutput out) { | |
573 | IoBuffer buf = IoBuffer.allocate(1024); | |
574 | DnsMessage dnsMessage = (DnsMessage) message; | |
575 | encoder.encode(buf, dnsMessage); | |
576 | for (ResourceRecord record: dnsMessage.getAnswerRecords()) { | |
577 | // This is a hack to allow to also test for AAAA resolution as DnsMessageEncoder | |
578 | // does not support it and it is hard to extend, because the interesting methods | |
579 | // are private... | |
580 | // In case of RecordType.AAAA we need to encode the RecordType by ourselves. | |
581 | if (record.getRecordType() == RecordType.AAAA) { | |
582 | try { | |
583 | recordEncoder.put(buf, record); | |
584 | } catch (IOException e) { | |
585 | // Should never happen | |
586 | throw new IllegalStateException(e); | |
587 | } | |
588 | } | |
589 | } | |
590 | buf.flip(); | |
591 | ||
592 | out.write(buf); | |
593 | } | |
594 | }; | |
595 | } | |
596 | ||
597 | @Override | |
598 | public ProtocolDecoder getDecoder(IoSession session) throws Exception { | |
599 | return new DnsUdpDecoder(); | |
600 | } | |
601 | ||
602 | private static final class TestAAAARecordEncoder extends ResourceRecordEncoder { | |
603 | ||
604 | @Override | |
605 | protected void putResourceRecordData(IoBuffer ioBuffer, ResourceRecord resourceRecord) { | |
606 | byte[] bytes = BYTES.get(resourceRecord.get(DnsAttribute.IP_ADDRESS)); | |
607 | if (bytes == null) { | |
608 | throw new IllegalStateException(); | |
609 | } | |
610 | // encode the ::1 | |
611 | ioBuffer.put(bytes); | |
612 | } | |
613 | } | |
614 | } | |
615 | ||
616 | private static final class TestRecordStore implements RecordStore { | |
617 | private static final int[] NUMBERS = new int[254]; | |
618 | private static final char[] CHARS = new char[26]; | |
619 | ||
620 | static { | |
621 | for (int i = 0; i < NUMBERS.length; i++) { | |
622 | NUMBERS[i] = i + 1; | |
623 | } | |
624 | ||
625 | for (int i = 0; i < CHARS.length; i++) { | |
626 | CHARS[i] = (char) ('a' + i); | |
627 | } | |
628 | } | |
629 | ||
630 | private static int index(int arrayLength) { | |
631 | return Math.abs(ThreadLocalRandom.current().nextInt()) % arrayLength; | |
632 | } | |
633 | ||
634 | private static String nextDomain() { | |
635 | return CHARS[index(CHARS.length)] + ".netty.io"; | |
636 | } | |
637 | ||
638 | private static String nextIp() { | |
639 | return ipPart() + "." + ipPart() + '.' + ipPart() + '.' + ipPart(); | |
640 | } | |
641 | ||
642 | private static int ipPart() { | |
643 | return NUMBERS[index(NUMBERS.length)]; | |
644 | } | |
645 | ||
646 | private static String nextIp6() { | |
647 | return IPV6_ADDRESSES[index(IPV6_ADDRESSES.length)]; | |
648 | } | |
649 | ||
650 | @Override | |
651 | public Set<ResourceRecord> getRecords(QuestionRecord questionRecord) { | |
652 | String name = questionRecord.getDomainName(); | |
653 | if (DOMAINS.contains(name)) { | |
654 | ResourceRecordModifier rm = new ResourceRecordModifier(); | |
655 | rm.setDnsClass(RecordClass.IN); | |
656 | rm.setDnsName(name); | |
657 | rm.setDnsTtl(100); | |
658 | rm.setDnsType(questionRecord.getRecordType()); | |
659 | ||
660 | switch (questionRecord.getRecordType()) { | |
661 | case A: | |
662 | do { | |
663 | rm.put(DnsAttribute.IP_ADDRESS, nextIp()); | |
664 | } while (ThreadLocalRandom.current().nextBoolean()); | |
665 | break; | |
666 | case AAAA: | |
667 | do { | |
668 | rm.put(DnsAttribute.IP_ADDRESS, nextIp6()); | |
669 | } while (ThreadLocalRandom.current().nextBoolean()); | |
670 | break; | |
671 | case MX: | |
672 | int priority = 0; | |
673 | do { | |
674 | rm.put(DnsAttribute.DOMAIN_NAME, nextDomain()); | |
675 | rm.put(DnsAttribute.MX_PREFERENCE, String.valueOf(++priority)); | |
676 | } while (ThreadLocalRandom.current().nextBoolean()); | |
677 | break; | |
678 | default: | |
679 | return null; | |
680 | } | |
681 | return Collections.singleton(rm.getEntry()); | |
682 | } | |
683 | return null; | |
684 | } | |
685 | } | |
686 | } | |
687 | 506 | } |
0 | /* | |
1 | * Copyright 2016 The Netty Project | |
2 | * | |
3 | * The Netty Project licenses this file to you under the Apache License, | |
4 | * version 2.0 (the "License"); you may not use this file except in compliance | |
5 | * with the License. You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
12 | * License for the specific language governing permissions and limitations | |
13 | * under the License. | |
14 | */ | |
15 | package io.netty.resolver.dns; | |
16 | ||
17 | import io.netty.channel.EventLoopGroup; | |
18 | import io.netty.channel.nio.NioEventLoopGroup; | |
19 | import io.netty.channel.socket.nio.NioDatagramChannel; | |
20 | import io.netty.util.concurrent.Future; | |
21 | import org.junit.After; | |
22 | import org.junit.Before; | |
23 | import org.junit.Test; | |
24 | ||
25 | import java.net.InetAddress; | |
26 | import java.util.ArrayList; | |
27 | import java.util.Arrays; | |
28 | import java.util.Collections; | |
29 | import java.util.HashSet; | |
30 | import java.util.List; | |
31 | import java.util.Set; | |
32 | import java.util.concurrent.TimeUnit; | |
33 | ||
34 | import static org.junit.Assert.assertEquals; | |
35 | import static org.junit.Assert.assertFalse; | |
36 | import static org.junit.Assert.assertTrue; | |
37 | ||
38 | public class SearchDomainTest { | |
39 | ||
40 | private DnsNameResolverBuilder newResolver() { | |
41 | return new DnsNameResolverBuilder(group.next()) | |
42 | .channelType(NioDatagramChannel.class) | |
43 | .nameServerAddresses(DnsServerAddresses.singleton(dnsServer.localAddress())) | |
44 | .maxQueriesPerResolve(1) | |
45 | .optResourceEnabled(false); | |
46 | } | |
47 | ||
48 | private TestDnsServer dnsServer; | |
49 | private EventLoopGroup group; | |
50 | ||
51 | @Before | |
52 | public void before() { | |
53 | group = new NioEventLoopGroup(1); | |
54 | } | |
55 | ||
56 | @After | |
57 | public void destroy() { | |
58 | if (dnsServer != null) { | |
59 | dnsServer.stop(); | |
60 | dnsServer = null; | |
61 | } | |
62 | group.shutdownGracefully(); | |
63 | } | |
64 | ||
65 | @Test | |
66 | public void testResolve() throws Exception { | |
67 | Set<String> domains = new HashSet<String>(); | |
68 | domains.add("host1.foo.com"); | |
69 | domains.add("host1"); | |
70 | domains.add("host3"); | |
71 | domains.add("host4.sub.foo.com"); | |
72 | domains.add("host5.sub.foo.com"); | |
73 | domains.add("host5.sub"); | |
74 | ||
75 | TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); | |
76 | dnsServer = new TestDnsServer(store); | |
77 | dnsServer.start(); | |
78 | ||
79 | DnsNameResolver resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).build(); | |
80 | ||
81 | String a = "host1.foo.com"; | |
82 | String resolved = assertResolve(resolver, a); | |
83 | assertEquals(store.getAddress("host1.foo.com"), resolved); | |
84 | ||
85 | // host1 resolves host1.foo.com with foo.com search domain | |
86 | resolved = assertResolve(resolver, "host1"); | |
87 | assertEquals(store.getAddress("host1.foo.com"), resolved); | |
88 | ||
89 | // "host1." absolute query | |
90 | resolved = assertResolve(resolver, "host1."); | |
91 | assertEquals(store.getAddress("host1"), resolved); | |
92 | ||
93 | // "host2" not resolved | |
94 | assertNotResolve(resolver, "host2"); | |
95 | ||
96 | // "host3" does not contain a dot or is not absolute | |
97 | assertNotResolve(resolver, "host3"); | |
98 | ||
99 | // "host3." does not contain a dot but is absolute | |
100 | resolved = assertResolve(resolver, "host3."); | |
101 | assertEquals(store.getAddress("host3"), resolved); | |
102 | ||
103 | // "host4.sub" contains a dot but not resolved then resolved to "host4.sub.foo.com" with "foo.com" search domain | |
104 | resolved = assertResolve(resolver, "host4.sub"); | |
105 | assertEquals(store.getAddress("host4.sub.foo.com"), resolved); | |
106 | ||
107 | // "host5.sub" contains a dot and is resolved | |
108 | resolved = assertResolve(resolver, "host5.sub"); | |
109 | assertEquals(store.getAddress("host5.sub"), resolved); | |
110 | } | |
111 | ||
112 | @Test | |
113 | public void testResolveAll() throws Exception { | |
114 | Set<String> domains = new HashSet<String>(); | |
115 | domains.add("host1.foo.com"); | |
116 | domains.add("host1"); | |
117 | domains.add("host3"); | |
118 | domains.add("host4.sub.foo.com"); | |
119 | domains.add("host5.sub.foo.com"); | |
120 | domains.add("host5.sub"); | |
121 | ||
122 | TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains, 2); | |
123 | dnsServer = new TestDnsServer(store); | |
124 | dnsServer.start(); | |
125 | ||
126 | DnsNameResolver resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).build(); | |
127 | ||
128 | String a = "host1.foo.com"; | |
129 | List<String> resolved = assertResolveAll(resolver, a); | |
130 | assertEquals(store.getAddresses("host1.foo.com"), resolved); | |
131 | ||
132 | // host1 resolves host1.foo.com with foo.com search domain | |
133 | resolved = assertResolveAll(resolver, "host1"); | |
134 | assertEquals(store.getAddresses("host1.foo.com"), resolved); | |
135 | ||
136 | // "host1." absolute query | |
137 | resolved = assertResolveAll(resolver, "host1."); | |
138 | assertEquals(store.getAddresses("host1"), resolved); | |
139 | ||
140 | // "host2" not resolved | |
141 | assertNotResolveAll(resolver, "host2"); | |
142 | ||
143 | // "host3" does not contain a dot or is not absolute | |
144 | assertNotResolveAll(resolver, "host3"); | |
145 | ||
146 | // "host3." does not contain a dot but is absolute | |
147 | resolved = assertResolveAll(resolver, "host3."); | |
148 | assertEquals(store.getAddresses("host3"), resolved); | |
149 | ||
150 | // "host4.sub" contains a dot but not resolved then resolved to "host4.sub.foo.com" with "foo.com" search domain | |
151 | resolved = assertResolveAll(resolver, "host4.sub"); | |
152 | assertEquals(store.getAddresses("host4.sub.foo.com"), resolved); | |
153 | ||
154 | // "host5.sub" contains a dot and is resolved | |
155 | resolved = assertResolveAll(resolver, "host5.sub"); | |
156 | assertEquals(store.getAddresses("host5.sub"), resolved); | |
157 | } | |
158 | ||
159 | @Test | |
160 | public void testMultipleSearchDomain() throws Exception { | |
161 | Set<String> domains = new HashSet<String>(); | |
162 | domains.add("host1.foo.com"); | |
163 | domains.add("host2.bar.com"); | |
164 | domains.add("host3.bar.com"); | |
165 | domains.add("host3.foo.com"); | |
166 | ||
167 | TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); | |
168 | dnsServer = new TestDnsServer(store); | |
169 | dnsServer.start(); | |
170 | ||
171 | DnsNameResolver resolver = newResolver().searchDomains(Arrays.asList("foo.com", "bar.com")).build(); | |
172 | ||
173 | // "host1" resolves via the "foo.com" search path | |
174 | String resolved = assertResolve(resolver, "host1"); | |
175 | assertEquals(store.getAddress("host1.foo.com"), resolved); | |
176 | ||
177 | // "host2" resolves via the "bar.com" search path | |
178 | resolved = assertResolve(resolver, "host2"); | |
179 | assertEquals(store.getAddress("host2.bar.com"), resolved); | |
180 | ||
181 | // "host3" resolves via the the "foo.com" search path as it is the first one | |
182 | resolved = assertResolve(resolver, "host3"); | |
183 | assertEquals(store.getAddress("host3.foo.com"), resolved); | |
184 | ||
185 | // "host4" does not resolve | |
186 | assertNotResolve(resolver, "host4"); | |
187 | } | |
188 | ||
189 | @Test | |
190 | public void testSearchDomainWithNdots2() throws Exception { | |
191 | Set<String> domains = new HashSet<String>(); | |
192 | domains.add("host1.sub.foo.com"); | |
193 | domains.add("host2.sub.foo.com"); | |
194 | domains.add("host2.sub"); | |
195 | ||
196 | TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); | |
197 | dnsServer = new TestDnsServer(store); | |
198 | dnsServer.start(); | |
199 | ||
200 | DnsNameResolver resolver = newResolver().searchDomains(Collections.singleton("foo.com")).ndots(2).build(); | |
201 | ||
202 | String resolved = assertResolve(resolver, "host1.sub"); | |
203 | assertEquals(store.getAddress("host1.sub.foo.com"), resolved); | |
204 | ||
205 | // "host2.sub" is resolved with the foo.com search domain as ndots = 2 | |
206 | resolved = assertResolve(resolver, "host2.sub"); | |
207 | assertEquals(store.getAddress("host2.sub.foo.com"), resolved); | |
208 | } | |
209 | ||
210 | private void assertNotResolve(DnsNameResolver resolver, String inetHost) throws InterruptedException { | |
211 | Future<InetAddress> fut = resolver.resolve(inetHost); | |
212 | assertTrue(fut.await(10, TimeUnit.SECONDS)); | |
213 | assertFalse(fut.isSuccess()); | |
214 | } | |
215 | ||
216 | private void assertNotResolveAll(DnsNameResolver resolver, String inetHost) throws InterruptedException { | |
217 | Future<List<InetAddress>> fut = resolver.resolveAll(inetHost); | |
218 | assertTrue(fut.await(10, TimeUnit.SECONDS)); | |
219 | assertFalse(fut.isSuccess()); | |
220 | } | |
221 | ||
222 | private String assertResolve(DnsNameResolver resolver, String inetHost) throws InterruptedException { | |
223 | Future<InetAddress> fut = resolver.resolve(inetHost); | |
224 | assertTrue(fut.await(10, TimeUnit.SECONDS)); | |
225 | return fut.getNow().getHostAddress(); | |
226 | } | |
227 | ||
228 | private List<String> assertResolveAll(DnsNameResolver resolver, String inetHost) throws InterruptedException { | |
229 | Future<List<InetAddress>> fut = resolver.resolveAll(inetHost); | |
230 | assertTrue(fut.await(10, TimeUnit.SECONDS)); | |
231 | List<String> list = new ArrayList<String>(); | |
232 | for (InetAddress addr : fut.getNow()) { | |
233 | list.add(addr.getHostAddress()); | |
234 | } | |
235 | return list; | |
236 | } | |
237 | } |
0 | /* | |
1 | * Copyright 2016 The Netty Project | |
2 | * | |
3 | * The Netty Project licenses this file to you under the Apache License, | |
4 | * version 2.0 (the "License"); you may not use this file except in compliance | |
5 | * with the License. You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
11 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
12 | * License for the specific language governing permissions and limitations | |
13 | * under the License. | |
14 | */ | |
15 | package io.netty.resolver.dns; | |
16 | ||
17 | import io.netty.util.NetUtil; | |
18 | import io.netty.util.internal.ThreadLocalRandom; | |
19 | import org.apache.directory.server.dns.DnsException; | |
20 | import org.apache.directory.server.dns.DnsServer; | |
21 | import org.apache.directory.server.dns.io.encoder.DnsMessageEncoder; | |
22 | import org.apache.directory.server.dns.io.encoder.ResourceRecordEncoder; | |
23 | import org.apache.directory.server.dns.messages.DnsMessage; | |
24 | import org.apache.directory.server.dns.messages.QuestionRecord; | |
25 | import org.apache.directory.server.dns.messages.RecordClass; | |
26 | import org.apache.directory.server.dns.messages.RecordType; | |
27 | import org.apache.directory.server.dns.messages.ResourceRecord; | |
28 | import org.apache.directory.server.dns.messages.ResourceRecordImpl; | |
29 | import org.apache.directory.server.dns.messages.ResourceRecordModifier; | |
30 | import org.apache.directory.server.dns.protocol.DnsProtocolHandler; | |
31 | import org.apache.directory.server.dns.protocol.DnsUdpDecoder; | |
32 | import org.apache.directory.server.dns.protocol.DnsUdpEncoder; | |
33 | import org.apache.directory.server.dns.store.DnsAttribute; | |
34 | import org.apache.directory.server.dns.store.RecordStore; | |
35 | import org.apache.directory.server.protocol.shared.transport.UdpTransport; | |
36 | import org.apache.mina.core.buffer.IoBuffer; | |
37 | import org.apache.mina.core.session.IoSession; | |
38 | import org.apache.mina.filter.codec.ProtocolCodecFactory; | |
39 | import org.apache.mina.filter.codec.ProtocolCodecFilter; | |
40 | import org.apache.mina.filter.codec.ProtocolDecoder; | |
41 | import org.apache.mina.filter.codec.ProtocolEncoder; | |
42 | import org.apache.mina.filter.codec.ProtocolEncoderOutput; | |
43 | import org.apache.mina.transport.socket.DatagramAcceptor; | |
44 | import org.apache.mina.transport.socket.DatagramSessionConfig; | |
45 | ||
46 | import java.io.IOException; | |
47 | import java.net.InetSocketAddress; | |
48 | import java.util.ArrayList; | |
49 | import java.util.Collections; | |
50 | import java.util.HashMap; | |
51 | import java.util.LinkedHashSet; | |
52 | import java.util.List; | |
53 | import java.util.Map; | |
54 | import java.util.Set; | |
55 | ||
56 | final class TestDnsServer extends DnsServer { | |
57 | private static final Map<String, byte[]> BYTES = new HashMap<String, byte[]>(); | |
58 | private static final String[] IPV6_ADDRESSES; | |
59 | ||
60 | static { | |
61 | BYTES.put("::1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}); | |
62 | BYTES.put("0:0:0:0:0:0:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}); | |
63 | BYTES.put("0:0:0:0:0:1:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}); | |
64 | BYTES.put("0:0:0:0:1:1:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1}); | |
65 | BYTES.put("0:0:0:1:1:1:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
66 | BYTES.put("0:0:1:1:1:1:1:1", new byte[]{0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
67 | BYTES.put("0:1:1:1:1:1:1:1", new byte[]{0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
68 | BYTES.put("1:1:1:1:1:1:1:1", new byte[]{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); | |
69 | ||
70 | IPV6_ADDRESSES = BYTES.keySet().toArray(new String[BYTES.size()]); | |
71 | } | |
72 | ||
73 | private final RecordStore store; | |
74 | ||
75 | TestDnsServer(Set<String> domains) { | |
76 | this.store = new TestRecordStore(domains); | |
77 | } | |
78 | ||
79 | TestDnsServer(RecordStore store) { | |
80 | this.store = store; | |
81 | } | |
82 | ||
83 | @Override | |
84 | public void start() throws IOException { | |
85 | InetSocketAddress address = new InetSocketAddress(NetUtil.LOCALHOST4, 50000); | |
86 | UdpTransport transport = new UdpTransport(address.getHostName(), address.getPort()); | |
87 | setTransports(transport); | |
88 | ||
89 | DatagramAcceptor acceptor = transport.getAcceptor(); | |
90 | ||
91 | acceptor.setHandler(new DnsProtocolHandler(this, store) { | |
92 | @Override | |
93 | public void sessionCreated(IoSession session) throws Exception { | |
94 | // USe our own codec to support AAAA testing | |
95 | session.getFilterChain() | |
96 | .addFirst("codec", new ProtocolCodecFilter(new TestDnsProtocolUdpCodecFactory())); | |
97 | } | |
98 | }); | |
99 | ||
100 | ((DatagramSessionConfig) acceptor.getSessionConfig()).setReuseAddress(true); | |
101 | ||
102 | // Start the listener | |
103 | acceptor.bind(); | |
104 | } | |
105 | ||
106 | public InetSocketAddress localAddress() { | |
107 | return (InetSocketAddress) getTransports()[0].getAcceptor().getLocalAddress(); | |
108 | } | |
109 | ||
110 | /** | |
111 | * {@link ProtocolCodecFactory} which allows to test AAAA resolution. | |
112 | */ | |
113 | private static final class TestDnsProtocolUdpCodecFactory implements ProtocolCodecFactory { | |
114 | private final DnsMessageEncoder encoder = new DnsMessageEncoder(); | |
115 | private final TestAAAARecordEncoder recordEncoder = new TestAAAARecordEncoder(); | |
116 | ||
117 | @Override | |
118 | public ProtocolEncoder getEncoder(IoSession session) throws Exception { | |
119 | return new DnsUdpEncoder() { | |
120 | ||
121 | @Override | |
122 | public void encode(IoSession session, Object message, ProtocolEncoderOutput out) { | |
123 | IoBuffer buf = IoBuffer.allocate(1024); | |
124 | DnsMessage dnsMessage = (DnsMessage) message; | |
125 | encoder.encode(buf, dnsMessage); | |
126 | for (ResourceRecord record : dnsMessage.getAnswerRecords()) { | |
127 | // This is a hack to allow to also test for AAAA resolution as DnsMessageEncoder | |
128 | // does not support it and it is hard to extend, because the interesting methods | |
129 | // are private... | |
130 | // In case of RecordType.AAAA we need to encode the RecordType by ourselves. | |
131 | if (record.getRecordType() == RecordType.AAAA) { | |
132 | try { | |
133 | recordEncoder.put(buf, record); | |
134 | } catch (IOException e) { | |
135 | // Should never happen | |
136 | throw new IllegalStateException(e); | |
137 | } | |
138 | } | |
139 | } | |
140 | buf.flip(); | |
141 | ||
142 | out.write(buf); | |
143 | } | |
144 | }; | |
145 | } | |
146 | ||
147 | @Override | |
148 | public ProtocolDecoder getDecoder(IoSession session) throws Exception { | |
149 | return new DnsUdpDecoder(); | |
150 | } | |
151 | ||
152 | private static final class TestAAAARecordEncoder extends ResourceRecordEncoder { | |
153 | ||
154 | @Override | |
155 | protected void putResourceRecordData(IoBuffer ioBuffer, ResourceRecord resourceRecord) { | |
156 | byte[] bytes = BYTES.get(resourceRecord.get(DnsAttribute.IP_ADDRESS)); | |
157 | if (bytes == null) { | |
158 | throw new IllegalStateException(); | |
159 | } | |
160 | // encode the ::1 | |
161 | ioBuffer.put(bytes); | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | public static final class MapRecordStoreA implements RecordStore { | |
167 | ||
168 | private final Map<String, List<String>> domainMap; | |
169 | ||
170 | public MapRecordStoreA(Set<String> domains, int length) { | |
171 | domainMap = new HashMap<String, List<String>>(domains.size()); | |
172 | for (String domain : domains) { | |
173 | List<String> addresses = new ArrayList<String>(length); | |
174 | for (int i = 0; i < length; i++) { | |
175 | addresses.add(TestRecordStore.nextIp()); | |
176 | } | |
177 | domainMap.put(domain, addresses); | |
178 | } | |
179 | } | |
180 | ||
181 | public MapRecordStoreA(Set<String> domains) { | |
182 | this(domains, 1); | |
183 | } | |
184 | ||
185 | public String getAddress(String domain) { | |
186 | return domainMap.get(domain).get(0); | |
187 | } | |
188 | ||
189 | public List<String> getAddresses(String domain) { | |
190 | return domainMap.get(domain); | |
191 | } | |
192 | ||
193 | @Override | |
194 | public Set<ResourceRecord> getRecords(QuestionRecord questionRecord) throws DnsException { | |
195 | String name = questionRecord.getDomainName(); | |
196 | List<String> addresses = domainMap.get(name); | |
197 | if (addresses != null && questionRecord.getRecordType() == RecordType.A) { | |
198 | Set<ResourceRecord> records = new LinkedHashSet<ResourceRecord>(); | |
199 | for (String address : addresses) { | |
200 | HashMap<String, Object> attributes = new HashMap<String, Object>(); | |
201 | attributes.put(DnsAttribute.IP_ADDRESS.toLowerCase(), address); | |
202 | records.add(new ResourceRecordImpl(name, questionRecord.getRecordType(), | |
203 | RecordClass.IN, 100, attributes) { | |
204 | @Override | |
205 | public int hashCode() { | |
206 | return System.identityHashCode(this); | |
207 | } | |
208 | @Override | |
209 | public boolean equals(Object o) { | |
210 | return false; | |
211 | } | |
212 | }); | |
213 | } | |
214 | return records; | |
215 | } | |
216 | return null; | |
217 | } | |
218 | } | |
219 | ||
220 | private static final class TestRecordStore implements RecordStore { | |
221 | private static final int[] NUMBERS = new int[254]; | |
222 | private static final char[] CHARS = new char[26]; | |
223 | ||
224 | static { | |
225 | for (int i = 0; i < NUMBERS.length; i++) { | |
226 | NUMBERS[i] = i + 1; | |
227 | } | |
228 | ||
229 | for (int i = 0; i < CHARS.length; i++) { | |
230 | CHARS[i] = (char) ('a' + i); | |
231 | } | |
232 | } | |
233 | ||
234 | private static int index(int arrayLength) { | |
235 | return Math.abs(ThreadLocalRandom.current().nextInt()) % arrayLength; | |
236 | } | |
237 | ||
238 | private static String nextDomain() { | |
239 | return CHARS[index(CHARS.length)] + ".netty.io"; | |
240 | } | |
241 | ||
242 | private static String nextIp() { | |
243 | return ipPart() + "." + ipPart() + '.' + ipPart() + '.' + ipPart(); | |
244 | } | |
245 | ||
246 | private static int ipPart() { | |
247 | return NUMBERS[index(NUMBERS.length)]; | |
248 | } | |
249 | ||
250 | private static String nextIp6() { | |
251 | return IPV6_ADDRESSES[index(IPV6_ADDRESSES.length)]; | |
252 | } | |
253 | ||
254 | private final Set<String> domains; | |
255 | ||
256 | public TestRecordStore(Set<String> domains) { | |
257 | this.domains = domains; | |
258 | } | |
259 | ||
260 | @Override | |
261 | public Set<ResourceRecord> getRecords(QuestionRecord questionRecord) { | |
262 | String name = questionRecord.getDomainName(); | |
263 | if (domains.contains(name)) { | |
264 | ResourceRecordModifier rm = new ResourceRecordModifier(); | |
265 | rm.setDnsClass(RecordClass.IN); | |
266 | rm.setDnsName(name); | |
267 | rm.setDnsTtl(100); | |
268 | rm.setDnsType(questionRecord.getRecordType()); | |
269 | ||
270 | switch (questionRecord.getRecordType()) { | |
271 | case A: | |
272 | do { | |
273 | rm.put(DnsAttribute.IP_ADDRESS, nextIp()); | |
274 | } while (ThreadLocalRandom.current().nextBoolean()); | |
275 | break; | |
276 | case AAAA: | |
277 | do { | |
278 | rm.put(DnsAttribute.IP_ADDRESS, nextIp6()); | |
279 | } while (ThreadLocalRandom.current().nextBoolean()); | |
280 | break; | |
281 | case MX: | |
282 | int priority = 0; | |
283 | do { | |
284 | rm.put(DnsAttribute.DOMAIN_NAME, nextDomain()); | |
285 | rm.put(DnsAttribute.MX_PREFERENCE, String.valueOf(++priority)); | |
286 | } while (ThreadLocalRandom.current().nextBoolean()); | |
287 | break; | |
288 | default: | |
289 | return null; | |
290 | } | |
291 | return Collections.singleton(rm.getEntry()); | |
292 | } | |
293 | return null; | |
294 | } | |
295 | } | |
296 | } |
8 | 8 | <groupId>org.asynchttpclient</groupId> |
9 | 9 | <artifactId>async-http-client-project</artifactId> |
10 | 10 | <name>Asynchronous Http Client Project</name> |
11 | <version>2.0.10</version> | |
11 | <version>2.0.11</version> | |
12 | 12 | <packaging>pom</packaging> |
13 | 13 | <description> |
14 | 14 | The Async Http Client (AHC) library's purpose is to allow Java |
372 | 372 | <surefire.redirectTestOutputToFile>true</surefire.redirectTestOutputToFile> |
373 | 373 | <source.property>1.8</source.property> |
374 | 374 | <target.property>1.8</target.property> |
375 | <netty.version>4.0.38.Final</netty.version> | |
375 | <netty.version>4.0.39.Final</netty.version> | |
376 | 376 | <slf4j.version>1.7.21</slf4j.version> |
377 | 377 | <logback.version>1.1.7</logback.version> |
378 | 378 | <testng.version>6.9.10</testng.version> |