1
2
3
4
5
6
7
8
9
10
11
12 package org.eclipse.jgit.transport;
13
14 import java.io.IOException;
15 import java.net.URISyntaxException;
16 import java.text.MessageFormat;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.function.Supplier;
22
23 import org.eclipse.jgit.annotations.NonNull;
24 import org.eclipse.jgit.errors.ConfigInvalidException;
25 import org.eclipse.jgit.internal.JGitText;
26 import org.eclipse.jgit.lib.Config;
27 import org.eclipse.jgit.lib.StoredConfig;
28 import org.eclipse.jgit.util.StringUtils;
29 import org.eclipse.jgit.util.SystemReader;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33
34
35
36
37
38
39
40
41 public class HttpConfig {
42
43 private static final Logger LOG = LoggerFactory.getLogger(HttpConfig.class);
44
45 private static final String FTP = "ftp";
46
47
48 public static final String HTTP = "http";
49
50
51 public static final String FOLLOW_REDIRECTS_KEY = "followRedirects";
52
53
54 public static final String MAX_REDIRECTS_KEY = "maxRedirects";
55
56
57 public static final String POST_BUFFER_KEY = "postBuffer";
58
59
60 public static final String SSL_VERIFY_KEY = "sslVerify";
61
62
63
64
65
66
67 public static final String USER_AGENT = "userAgent";
68
69
70
71
72
73
74 public static final String EXTRA_HEADER = "extraHeader";
75
76
77
78
79
80
81 public static final String COOKIE_FILE_KEY = "cookieFile";
82
83
84
85
86
87
88 public static final String SAVE_COOKIES_KEY = "saveCookies";
89
90
91
92
93
94
95
96 public static final String COOKIE_FILE_CACHE_LIMIT_KEY = "cookieFileCacheLimit";
97
98 private static final int DEFAULT_COOKIE_FILE_CACHE_LIMIT = 10;
99
100 private static final String MAX_REDIRECT_SYSTEM_PROPERTY = "http.maxRedirects";
101
102 private static final int DEFAULT_MAX_REDIRECTS = 5;
103
104 private static final int MAX_REDIRECTS = (new Supplier<Integer>() {
105
106 @Override
107 public Integer get() {
108 String rawValue = SystemReader.getInstance()
109 .getProperty(MAX_REDIRECT_SYSTEM_PROPERTY);
110 Integer value = Integer.valueOf(DEFAULT_MAX_REDIRECTS);
111 if (rawValue != null) {
112 try {
113 value = Integer.valueOf(Integer.parseUnsignedInt(rawValue));
114 } catch (NumberFormatException e) {
115 LOG.warn(MessageFormat.format(
116 JGitText.get().invalidSystemProperty,
117 MAX_REDIRECT_SYSTEM_PROPERTY, rawValue, value));
118 }
119 }
120 return value;
121 }
122 }).get().intValue();
123
124 private static final String ENV_HTTP_USER_AGENT = "GIT_HTTP_USER_AGENT";
125
126
127
128
129 public enum HttpRedirectMode implements Config.ConfigEnum {
130
131
132 TRUE("true"),
133
134
135
136
137 INITIAL("initial"),
138
139 FALSE("false");
140
141 private final String configValue;
142
143 private HttpRedirectMode(String configValue) {
144 this.configValue = configValue;
145 }
146
147 @Override
148 public String toConfigValue() {
149 return configValue;
150 }
151
152 @Override
153 public boolean matchConfigValue(String s) {
154 return configValue.equals(s);
155 }
156 }
157
158 private int postBuffer;
159
160 private boolean sslVerify;
161
162 private HttpRedirectMode followRedirects;
163
164 private int maxRedirects;
165
166 private String userAgent;
167
168 private List<String> extraHeaders;
169
170 private String cookieFile;
171
172 private boolean saveCookies;
173
174 private int cookieFileCacheLimit;
175
176
177
178
179
180
181 public int getPostBuffer() {
182 return postBuffer;
183 }
184
185
186
187
188
189
190 public boolean isSslVerify() {
191 return sslVerify;
192 }
193
194
195
196
197
198
199 public HttpRedirectMode getFollowRedirects() {
200 return followRedirects;
201 }
202
203
204
205
206
207
208 public int getMaxRedirects() {
209 return maxRedirects;
210 }
211
212
213
214
215
216
217
218 public String getUserAgent() {
219 return userAgent;
220 }
221
222
223
224
225
226
227
228 @NonNull
229 public List<String> getExtraHeaders() {
230 return extraHeaders == null ? Collections.emptyList() : extraHeaders;
231 }
232
233
234
235
236
237
238
239
240 public String getCookieFile() {
241 return cookieFile;
242 }
243
244
245
246
247
248
249
250
251 public boolean getSaveCookies() {
252 return saveCookies;
253 }
254
255
256
257
258
259
260
261
262
263 public int getCookieFileCacheLimit() {
264 return cookieFileCacheLimit;
265 }
266
267
268
269
270
271
272
273
274
275
276 public HttpConfig(Config config, URIish uri) {
277 init(config, uri);
278 }
279
280
281
282
283
284
285
286
287 public HttpConfig(URIish uri) {
288 StoredConfig userConfig = null;
289 try {
290 userConfig = SystemReader.getInstance().getUserConfig();
291 } catch (IOException | ConfigInvalidException e) {
292
293 LOG.error(e.getMessage(), e);
294 init(new Config(), uri);
295 return;
296 }
297 init(userConfig, uri);
298 }
299
300 private void init(Config config, URIish uri) {
301
302 int postBufferSize = config.getInt(HTTP, POST_BUFFER_KEY,
303 1 * 1024 * 1024);
304 boolean sslVerifyFlag = config.getBoolean(HTTP, SSL_VERIFY_KEY, true);
305 HttpRedirectMode followRedirectsMode = config.getEnum(
306 HttpRedirectMode.values(), HTTP, null,
307 FOLLOW_REDIRECTS_KEY, HttpRedirectMode.INITIAL);
308 int redirectLimit = config.getInt(HTTP, MAX_REDIRECTS_KEY,
309 MAX_REDIRECTS);
310 if (redirectLimit < 0) {
311 redirectLimit = MAX_REDIRECTS;
312 }
313 String agent = config.getString(HTTP, null, USER_AGENT);
314 if (agent != null) {
315 agent = UserAgent.clean(agent);
316 }
317 userAgent = agent;
318 String[] headers = config.getStringList(HTTP, null, EXTRA_HEADER);
319
320
321 int start = findLastEmpty(headers) + 1;
322 if (start > 0) {
323 headers = Arrays.copyOfRange(headers, start, headers.length);
324 }
325 extraHeaders = Arrays.asList(headers);
326 cookieFile = config.getString(HTTP, null, COOKIE_FILE_KEY);
327 saveCookies = config.getBoolean(HTTP, SAVE_COOKIES_KEY, false);
328 cookieFileCacheLimit = config.getInt(HTTP, COOKIE_FILE_CACHE_LIMIT_KEY,
329 DEFAULT_COOKIE_FILE_CACHE_LIMIT);
330 String match = findMatch(config.getSubsections(HTTP), uri);
331
332 if (match != null) {
333
334 postBufferSize = config.getInt(HTTP, match, POST_BUFFER_KEY,
335 postBufferSize);
336 sslVerifyFlag = config.getBoolean(HTTP, match, SSL_VERIFY_KEY,
337 sslVerifyFlag);
338 followRedirectsMode = config.getEnum(HttpRedirectMode.values(),
339 HTTP, match, FOLLOW_REDIRECTS_KEY, followRedirectsMode);
340 int newMaxRedirects = config.getInt(HTTP, match, MAX_REDIRECTS_KEY,
341 redirectLimit);
342 if (newMaxRedirects >= 0) {
343 redirectLimit = newMaxRedirects;
344 }
345 String uriSpecificUserAgent = config.getString(HTTP, match,
346 USER_AGENT);
347 if (uriSpecificUserAgent != null) {
348 userAgent = UserAgent.clean(uriSpecificUserAgent);
349 }
350 String[] uriSpecificExtraHeaders = config.getStringList(HTTP, match,
351 EXTRA_HEADER);
352 if (uriSpecificExtraHeaders.length > 0) {
353 start = findLastEmpty(uriSpecificExtraHeaders) + 1;
354 if (start > 0) {
355 uriSpecificExtraHeaders = Arrays.copyOfRange(
356 uriSpecificExtraHeaders, start,
357 uriSpecificExtraHeaders.length);
358 }
359 extraHeaders = Arrays.asList(uriSpecificExtraHeaders);
360 }
361 String urlSpecificCookieFile = config.getString(HTTP, match,
362 COOKIE_FILE_KEY);
363 if (urlSpecificCookieFile != null) {
364 cookieFile = urlSpecificCookieFile;
365 }
366 saveCookies = config.getBoolean(HTTP, match, SAVE_COOKIES_KEY,
367 saveCookies);
368 }
369
370 agent = SystemReader.getInstance().getenv(ENV_HTTP_USER_AGENT);
371 if (!StringUtils.isEmptyOrNull(agent)) {
372 userAgent = UserAgent.clean(agent);
373 }
374 postBuffer = postBufferSize;
375 sslVerify = sslVerifyFlag;
376 followRedirects = followRedirectsMode;
377 maxRedirects = redirectLimit;
378 }
379
380 private int findLastEmpty(String[] values) {
381 for (int i = values.length - 1; i >= 0; i--) {
382 if (values[i] == null) {
383 return i;
384 }
385 }
386 return -1;
387 }
388
389
390
391
392
393
394
395
396
397
398
399
400 private String findMatch(Set<String> names, URIish uri) {
401 String bestMatch = null;
402 int bestMatchLength = -1;
403 boolean withUser = false;
404 String uPath = uri.getPath();
405 boolean hasPath = !StringUtils.isEmptyOrNull(uPath);
406 if (hasPath) {
407 uPath = normalize(uPath);
408 if (uPath == null) {
409
410 return null;
411 }
412 }
413 for (String s : names) {
414 try {
415 URIish candidate = new URIish(s);
416
417 if (!compare(uri.getScheme(), candidate.getScheme())
418 || !compare(uri.getHost(), candidate.getHost())) {
419 continue;
420 }
421
422 if (defaultedPort(uri.getPort(),
423 uri.getScheme()) != defaultedPort(candidate.getPort(),
424 candidate.getScheme())) {
425 continue;
426 }
427
428 boolean hasUser = false;
429 if (candidate.getUser() != null) {
430 if (!candidate.getUser().equals(uri.getUser())) {
431 continue;
432 }
433 hasUser = true;
434 }
435
436 String cPath = candidate.getPath();
437 int matchLength = -1;
438 if (StringUtils.isEmptyOrNull(cPath)) {
439 matchLength = 0;
440 } else {
441 if (!hasPath) {
442 continue;
443 }
444
445 matchLength = segmentCompare(uPath, cPath);
446 if (matchLength < 0) {
447 continue;
448 }
449 }
450
451
452
453 if (matchLength > bestMatchLength
454 || (!withUser && hasUser && matchLength >= 0
455 && matchLength == bestMatchLength)) {
456 bestMatch = s;
457 bestMatchLength = matchLength;
458 withUser = hasUser;
459 }
460 } catch (URISyntaxException e) {
461 LOG.warn(MessageFormat
462 .format(JGitText.get().httpConfigInvalidURL, s));
463 }
464 }
465 return bestMatch;
466 }
467
468 private boolean compare(String a, String b) {
469 if (a == null) {
470 return b == null;
471 }
472 return a.equalsIgnoreCase(b);
473 }
474
475 private int defaultedPort(int port, String scheme) {
476 if (port >= 0) {
477 return port;
478 }
479 if (FTP.equalsIgnoreCase(scheme)) {
480 return 21;
481 } else if (HTTP.equalsIgnoreCase(scheme)) {
482 return 80;
483 } else {
484 return 443;
485 }
486 }
487
488 static int segmentCompare(String uriPath, String m) {
489
490
491 String matchPath = normalize(m);
492 if (matchPath == null || !uriPath.startsWith(matchPath)) {
493 return -1;
494 }
495
496
497
498 int uLength = uriPath.length();
499 int mLength = matchPath.length();
500 if (mLength == uLength || matchPath.charAt(mLength - 1) == '/'
501 || (mLength < uLength && uriPath.charAt(mLength) == '/')) {
502 return mLength;
503 }
504 return -1;
505 }
506
507 static String normalize(String path) {
508
509 int i = 0;
510 int length = path.length();
511 StringBuilder builder = new StringBuilder(length);
512 builder.append('/');
513 if (length > 0 && path.charAt(0) == '/') {
514 i = 1;
515 }
516 while (i < length) {
517 int slash = path.indexOf('/', i);
518 if (slash < 0) {
519 slash = length;
520 }
521 if (slash == i || (slash == i + 1 && path.charAt(i) == '.')) {
522
523 } else if (slash == i + 2 && path.charAt(i) == '.'
524 && path.charAt(i + 1) == '.') {
525
526 int l = builder.length() - 2;
527 while (l >= 0 && builder.charAt(l) != '/') {
528 l--;
529 }
530 if (l < 0) {
531 LOG.warn(MessageFormat.format(
532 JGitText.get().httpConfigCannotNormalizeURL, path));
533 return null;
534 }
535 builder.setLength(l + 1);
536 } else {
537
538 builder.append(path, i, Math.min(length, slash + 1));
539 }
540 i = slash + 1;
541 }
542 if (builder.length() > 1 && builder.charAt(builder.length() - 1) == '/'
543 && length > 0 && path.charAt(length - 1) != '/') {
544
545
546 builder.setLength(builder.length() - 1);
547 }
548 return builder.toString();
549 }
550 }