Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/net/www/MessageHeader.java
41159 views
1
/*
2
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
/*-
27
* news stream opener
28
*/
29
30
package sun.net.www;
31
32
import java.io.*;
33
import java.util.Collections;
34
import java.util.*;
35
36
/** An RFC 844 or MIME message header. Includes methods
37
for parsing headers from incoming streams, fetching
38
values, setting values, and printing headers.
39
Key values of null are legal: they indicate lines in
40
the header that don't have a valid key, but do have
41
a value (this isn't legal according to the standard,
42
but lines like this are everywhere). */
43
public
44
class MessageHeader {
45
private String keys[];
46
private String values[];
47
private int nkeys;
48
49
public MessageHeader () {
50
grow();
51
}
52
53
public MessageHeader (InputStream is) throws java.io.IOException {
54
parseHeader(is);
55
}
56
57
/**
58
* Returns list of header names in a comma separated list
59
*/
60
public synchronized String getHeaderNamesInList() {
61
StringJoiner joiner = new StringJoiner(",");
62
for (int i=0; i<nkeys; i++) {
63
joiner.add(keys[i]);
64
}
65
return joiner.toString();
66
}
67
68
/**
69
* Reset a message header (all key/values removed)
70
*/
71
public synchronized void reset() {
72
keys = null;
73
values = null;
74
nkeys = 0;
75
grow();
76
}
77
78
/**
79
* Find the value that corresponds to this key.
80
* It finds only the first occurrence of the key.
81
* @param k the key to find.
82
* @return null if not found.
83
*/
84
public synchronized String findValue(String k) {
85
if (k == null) {
86
for (int i = nkeys; --i >= 0;)
87
if (keys[i] == null)
88
return values[i];
89
} else
90
for (int i = nkeys; --i >= 0;) {
91
if (k.equalsIgnoreCase(keys[i]))
92
return values[i];
93
}
94
return null;
95
}
96
97
// return the location of the key
98
public synchronized int getKey(String k) {
99
for (int i = nkeys; --i >= 0;)
100
if ((keys[i] == k) ||
101
(k != null && k.equalsIgnoreCase(keys[i])))
102
return i;
103
return -1;
104
}
105
106
public synchronized String getKey(int n) {
107
if (n < 0 || n >= nkeys) return null;
108
return keys[n];
109
}
110
111
public synchronized String getValue(int n) {
112
if (n < 0 || n >= nkeys) return null;
113
return values[n];
114
}
115
116
/** Deprecated: Use multiValueIterator() instead.
117
*
118
* Find the next value that corresponds to this key.
119
* It finds the first value that follows v. To iterate
120
* over all the values of a key use:
121
* <pre>
122
* for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) {
123
* ...
124
* }
125
* </pre>
126
*/
127
public synchronized String findNextValue(String k, String v) {
128
boolean foundV = false;
129
if (k == null) {
130
for (int i = nkeys; --i >= 0;)
131
if (keys[i] == null)
132
if (foundV)
133
return values[i];
134
else if (values[i] == v)
135
foundV = true;
136
} else
137
for (int i = nkeys; --i >= 0;)
138
if (k.equalsIgnoreCase(keys[i]))
139
if (foundV)
140
return values[i];
141
else if (values[i] == v)
142
foundV = true;
143
return null;
144
}
145
146
/**
147
* Removes bare Negotiate and Kerberos headers when an "NTLM ..."
148
* appears. All Performed on headers with key being k.
149
* @return true if there is a change
150
*/
151
public boolean filterNTLMResponses(String k) {
152
boolean found = false;
153
for (int i=0; i<nkeys; i++) {
154
if (k.equalsIgnoreCase(keys[i])
155
&& values[i] != null && values[i].length() > 5
156
&& values[i].substring(0, 5).equalsIgnoreCase("NTLM ")) {
157
found = true;
158
break;
159
}
160
}
161
if (found) {
162
int j = 0;
163
for (int i=0; i<nkeys; i++) {
164
if (k.equalsIgnoreCase(keys[i]) && (
165
"Negotiate".equalsIgnoreCase(values[i]) ||
166
"Kerberos".equalsIgnoreCase(values[i]))) {
167
continue;
168
}
169
if (i != j) {
170
keys[j] = keys[i];
171
values[j] = values[i];
172
}
173
j++;
174
}
175
if (j != nkeys) {
176
nkeys = j;
177
return true;
178
}
179
}
180
return false;
181
}
182
183
class HeaderIterator implements Iterator<String> {
184
int index = 0;
185
int next = -1;
186
String key;
187
boolean haveNext = false;
188
Object lock;
189
190
public HeaderIterator (String k, Object lock) {
191
key = k;
192
this.lock = lock;
193
}
194
public boolean hasNext () {
195
synchronized (lock) {
196
if (haveNext) {
197
return true;
198
}
199
while (index < nkeys) {
200
if (key.equalsIgnoreCase (keys[index])) {
201
haveNext = true;
202
next = index++;
203
return true;
204
}
205
index ++;
206
}
207
return false;
208
}
209
}
210
public String next() {
211
synchronized (lock) {
212
if (haveNext) {
213
haveNext = false;
214
return values [next];
215
}
216
if (hasNext()) {
217
return next();
218
} else {
219
throw new NoSuchElementException ("No more elements");
220
}
221
}
222
}
223
public void remove () {
224
throw new UnsupportedOperationException ("remove not allowed");
225
}
226
}
227
228
/**
229
* return an Iterator that returns all values of a particular
230
* key in sequence
231
*/
232
public Iterator<String> multiValueIterator (String k) {
233
return new HeaderIterator (k, this);
234
}
235
236
public synchronized Map<String, List<String>> getHeaders() {
237
return getHeaders(null);
238
}
239
240
public synchronized Map<String, List<String>> getHeaders(String[] excludeList) {
241
return filterAndAddHeaders(excludeList, null);
242
}
243
244
public synchronized Map<String, List<String>> filterAndAddHeaders(
245
String[] excludeList, Map<String, List<String>> include) {
246
boolean skipIt = false;
247
Map<String, List<String>> m = new HashMap<>();
248
for (int i = nkeys; --i >= 0;) {
249
if (excludeList != null) {
250
// check if the key is in the excludeList.
251
// if so, don't include it in the Map.
252
for (int j = 0; j < excludeList.length; j++) {
253
if ((excludeList[j] != null) &&
254
(excludeList[j].equalsIgnoreCase(keys[i]))) {
255
skipIt = true;
256
break;
257
}
258
}
259
}
260
if (!skipIt) {
261
List<String> l = m.get(keys[i]);
262
if (l == null) {
263
l = new ArrayList<>();
264
m.put(keys[i], l);
265
}
266
l.add(values[i]);
267
} else {
268
// reset the flag
269
skipIt = false;
270
}
271
}
272
273
if (include != null) {
274
for (Map.Entry<String,List<String>> entry: include.entrySet()) {
275
List<String> l = m.get(entry.getKey());
276
if (l == null) {
277
l = new ArrayList<>();
278
m.put(entry.getKey(), l);
279
}
280
l.addAll(entry.getValue());
281
}
282
}
283
284
for (String key : m.keySet()) {
285
m.put(key, Collections.unmodifiableList(m.get(key)));
286
}
287
288
return Collections.unmodifiableMap(m);
289
}
290
291
/** Check if a line of message header looks like a request line.
292
* This method does not perform a full validation but simply
293
* returns false if the line does not end with 'HTTP/[1-9].[0-9]'
294
* @param line the line to check.
295
* @return true if the line might be a request line.
296
*/
297
private static boolean isRequestline(String line) {
298
String k = line.trim();
299
int i = k.lastIndexOf(' ');
300
if (i <= 0) return false;
301
int len = k.length();
302
if (len - i < 9) return false;
303
304
char c1 = k.charAt(len-3);
305
char c2 = k.charAt(len-2);
306
char c3 = k.charAt(len-1);
307
if (c1 < '1' || c1 > '9') return false;
308
if (c2 != '.') return false;
309
if (c3 < '0' || c3 > '9') return false;
310
311
return (k.substring(i+1, len-3).equalsIgnoreCase("HTTP/"));
312
}
313
314
/** Prints the key-value pairs represented by this
315
header. Also prints the RFC required blank line
316
at the end. Omits pairs with a null key. Omits
317
colon if key-value pair is the requestline. */
318
public void print(PrintStream p) {
319
// no synchronization: use cloned arrays instead.
320
String[] k; String[] v; int n;
321
synchronized (this) { n = nkeys; k = keys.clone(); v = values.clone(); }
322
print(n, k, v, p);
323
}
324
325
326
/** Prints the key-value pairs represented by this
327
header. Also prints the RFC required blank line
328
at the end. Omits pairs with a null key. Omits
329
colon if key-value pair is the requestline. */
330
private static void print(int nkeys, String[] keys, String[] values, PrintStream p) {
331
for (int i = 0; i < nkeys; i++)
332
if (keys[i] != null) {
333
StringBuilder sb = new StringBuilder(keys[i]);
334
if (values[i] != null) {
335
sb.append(": " + values[i]);
336
} else if (i != 0 || !isRequestline(keys[i])) {
337
sb.append(":");
338
}
339
p.print(sb.append("\r\n"));
340
}
341
p.print("\r\n");
342
p.flush();
343
}
344
345
/** Adds a key value pair to the end of the
346
header. Duplicates are allowed */
347
public synchronized void add(String k, String v) {
348
grow();
349
keys[nkeys] = k;
350
values[nkeys] = v;
351
nkeys++;
352
}
353
354
/** Prepends a key value pair to the beginning of the
355
header. Duplicates are allowed */
356
public synchronized void prepend(String k, String v) {
357
grow();
358
for (int i = nkeys; i > 0; i--) {
359
keys[i] = keys[i-1];
360
values[i] = values[i-1];
361
}
362
keys[0] = k;
363
values[0] = v;
364
nkeys++;
365
}
366
367
/** Overwrite the previous key/val pair at location 'i'
368
* with the new k/v. If the index didn't exist before
369
* the key/val is simply tacked onto the end.
370
*/
371
372
public synchronized void set(int i, String k, String v) {
373
grow();
374
if (i < 0) {
375
return;
376
} else if (i >= nkeys) {
377
add(k, v);
378
} else {
379
keys[i] = k;
380
values[i] = v;
381
}
382
}
383
384
385
/** grow the key/value arrays as needed */
386
387
private void grow() {
388
if (keys == null || nkeys >= keys.length) {
389
String[] nk = new String[nkeys + 4];
390
String[] nv = new String[nkeys + 4];
391
if (keys != null)
392
System.arraycopy(keys, 0, nk, 0, nkeys);
393
if (values != null)
394
System.arraycopy(values, 0, nv, 0, nkeys);
395
keys = nk;
396
values = nv;
397
}
398
}
399
400
/**
401
* Remove the key from the header. If there are multiple values under
402
* the same key, they are all removed.
403
* Nothing is done if the key doesn't exist.
404
* After a remove, the other pairs' order are not changed.
405
* @param k the key to remove
406
*/
407
public synchronized void remove(String k) {
408
if(k == null) {
409
for (int i = 0; i < nkeys; i++) {
410
while (keys[i] == null && i < nkeys) {
411
for(int j=i; j<nkeys-1; j++) {
412
keys[j] = keys[j+1];
413
values[j] = values[j+1];
414
}
415
nkeys--;
416
}
417
}
418
} else {
419
for (int i = 0; i < nkeys; i++) {
420
while (k.equalsIgnoreCase(keys[i]) && i < nkeys) {
421
for(int j=i; j<nkeys-1; j++) {
422
keys[j] = keys[j+1];
423
values[j] = values[j+1];
424
}
425
nkeys--;
426
}
427
}
428
}
429
}
430
431
/** Sets the value of a key. If the key already
432
exists in the header, it's value will be
433
changed. Otherwise a new key/value pair will
434
be added to the end of the header. */
435
public synchronized void set(String k, String v) {
436
for (int i = nkeys; --i >= 0;)
437
if (k.equalsIgnoreCase(keys[i])) {
438
values[i] = v;
439
return;
440
}
441
add(k, v);
442
}
443
444
/** Set's the value of a key only if there is no
445
* key with that value already.
446
*/
447
448
public synchronized void setIfNotSet(String k, String v) {
449
if (findValue(k) == null) {
450
add(k, v);
451
}
452
}
453
454
/** Convert a message-id string to canonical form (strips off
455
leading and trailing {@literal <>s}) */
456
public static String canonicalID(String id) {
457
if (id == null)
458
return "";
459
int st = 0;
460
int len = id.length();
461
boolean substr = false;
462
int c;
463
while (st < len && ((c = id.charAt(st)) == '<' ||
464
c <= ' ')) {
465
st++;
466
substr = true;
467
}
468
while (st < len && ((c = id.charAt(len - 1)) == '>' ||
469
c <= ' ')) {
470
len--;
471
substr = true;
472
}
473
return substr ? id.substring(st, len) : id;
474
}
475
476
/** Parse a MIME header from an input stream. */
477
public void parseHeader(InputStream is) throws java.io.IOException {
478
synchronized (this) {
479
nkeys = 0;
480
}
481
mergeHeader(is);
482
}
483
484
/** Parse and merge a MIME header from an input stream. */
485
@SuppressWarnings("fallthrough")
486
public void mergeHeader(InputStream is) throws java.io.IOException {
487
if (is == null)
488
return;
489
char s[] = new char[10];
490
int firstc = is.read();
491
while (firstc != '\n' && firstc != '\r' && firstc >= 0) {
492
int len = 0;
493
int keyend = -1;
494
int c;
495
boolean inKey = firstc > ' ';
496
s[len++] = (char) firstc;
497
parseloop:{
498
while ((c = is.read()) >= 0) {
499
switch (c) {
500
case ':':
501
if (inKey && len > 0)
502
keyend = len;
503
inKey = false;
504
break;
505
case '\t':
506
c = ' ';
507
/*fall through*/
508
case ' ':
509
inKey = false;
510
break;
511
case '\r':
512
case '\n':
513
firstc = is.read();
514
if (c == '\r' && firstc == '\n') {
515
firstc = is.read();
516
if (firstc == '\r')
517
firstc = is.read();
518
}
519
if (firstc == '\n' || firstc == '\r' || firstc > ' ')
520
break parseloop;
521
/* continuation */
522
c = ' ';
523
break;
524
}
525
if (len >= s.length) {
526
char ns[] = new char[s.length * 2];
527
System.arraycopy(s, 0, ns, 0, len);
528
s = ns;
529
}
530
s[len++] = (char) c;
531
}
532
firstc = -1;
533
}
534
while (len > 0 && s[len - 1] <= ' ')
535
len--;
536
String k;
537
if (keyend <= 0) {
538
k = null;
539
keyend = 0;
540
} else {
541
k = String.copyValueOf(s, 0, keyend);
542
if (keyend < len && s[keyend] == ':')
543
keyend++;
544
while (keyend < len && s[keyend] <= ' ')
545
keyend++;
546
}
547
String v;
548
if (keyend >= len)
549
v = new String();
550
else
551
v = String.copyValueOf(s, keyend, len - keyend);
552
add(k, v);
553
}
554
}
555
556
public synchronized String toString() {
557
String result = super.toString() + nkeys + " pairs: ";
558
for (int i = 0; i < keys.length && i < nkeys; i++) {
559
result += "{"+keys[i]+": "+values[i]+"}";
560
}
561
return result;
562
}
563
}
564
565