Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java
41159 views
1
/*
2
* Copyright (c) 2005, 2021, 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
package sun.net.httpserver;
27
28
import java.io.*;
29
import java.net.*;
30
import javax.net.ssl.*;
31
import java.util.*;
32
import java.lang.System.Logger;
33
import java.lang.System.Logger.Level;
34
import java.text.*;
35
import java.time.Instant;
36
import java.time.ZoneId;
37
import java.time.format.DateTimeFormatter;
38
import java.util.stream.Stream;
39
import com.sun.net.httpserver.*;
40
41
class ExchangeImpl {
42
43
Headers reqHdrs, rspHdrs;
44
Request req;
45
String method;
46
boolean writefinished;
47
URI uri;
48
HttpConnection connection;
49
long reqContentLen;
50
long rspContentLen;
51
/* raw streams which access the socket directly */
52
InputStream ris;
53
OutputStream ros;
54
Thread thread;
55
/* close the underlying connection when this exchange finished */
56
boolean close;
57
boolean closed;
58
boolean http10 = false;
59
60
/* for formatting the Date: header */
61
private static final DateTimeFormatter FORMATTER;
62
static {
63
String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
64
FORMATTER = DateTimeFormatter.ofPattern(pattern, Locale.US)
65
.withZone(ZoneId.of("GMT"));
66
}
67
68
private static final String HEAD = "HEAD";
69
70
/* streams which take care of the HTTP protocol framing
71
* and are passed up to higher layers
72
*/
73
InputStream uis;
74
OutputStream uos;
75
LeftOverInputStream uis_orig; // uis may have be a user supplied wrapper
76
PlaceholderOutputStream uos_orig;
77
78
boolean sentHeaders; /* true after response headers sent */
79
Map<String,Object> attributes;
80
int rcode = -1;
81
HttpPrincipal principal;
82
ServerImpl server;
83
84
ExchangeImpl (
85
String m, URI u, Request req, long len, HttpConnection connection
86
) throws IOException {
87
this.req = req;
88
this.reqHdrs = new UnmodifiableHeaders(req.headers());
89
this.rspHdrs = new Headers();
90
this.method = m;
91
this.uri = u;
92
this.connection = connection;
93
this.reqContentLen = len;
94
/* ros only used for headers, body written directly to stream */
95
this.ros = req.outputStream();
96
this.ris = req.inputStream();
97
server = getServerImpl();
98
server.startExchange();
99
}
100
101
public Headers getRequestHeaders () {
102
return reqHdrs;
103
}
104
105
public Headers getResponseHeaders () {
106
return rspHdrs;
107
}
108
109
public URI getRequestURI () {
110
return uri;
111
}
112
113
public String getRequestMethod (){
114
return method;
115
}
116
117
public HttpContextImpl getHttpContext (){
118
return connection.getHttpContext();
119
}
120
121
private boolean isHeadRequest() {
122
return HEAD.equals(getRequestMethod());
123
}
124
125
public void close () {
126
if (closed) {
127
return;
128
}
129
closed = true;
130
131
/* close the underlying connection if,
132
* a) the streams not set up yet, no response can be sent, or
133
* b) if the wrapper output stream is not set up, or
134
* c) if the close of the input/outpu stream fails
135
*/
136
try {
137
if (uis_orig == null || uos == null) {
138
connection.close();
139
return;
140
}
141
if (!uos_orig.isWrapped()) {
142
connection.close();
143
return;
144
}
145
if (!uis_orig.isClosed()) {
146
uis_orig.close();
147
}
148
uos.close();
149
} catch (IOException e) {
150
connection.close();
151
}
152
}
153
154
public InputStream getRequestBody () {
155
if (uis != null) {
156
return uis;
157
}
158
if (reqContentLen == -1L) {
159
uis_orig = new ChunkedInputStream (this, ris);
160
uis = uis_orig;
161
} else {
162
uis_orig = new FixedLengthInputStream (this, ris, reqContentLen);
163
uis = uis_orig;
164
}
165
return uis;
166
}
167
168
LeftOverInputStream getOriginalInputStream () {
169
return uis_orig;
170
}
171
172
public int getResponseCode () {
173
return rcode;
174
}
175
176
public OutputStream getResponseBody () {
177
/* TODO. Change spec to remove restriction below. Filters
178
* cannot work with this restriction
179
*
180
* if (!sentHeaders) {
181
* throw new IllegalStateException ("headers not sent");
182
* }
183
*/
184
if (uos == null) {
185
uos_orig = new PlaceholderOutputStream (null);
186
uos = uos_orig;
187
}
188
return uos;
189
}
190
191
192
/* returns the place holder stream, which is the stream
193
* returned from the 1st call to getResponseBody()
194
* The "real" ouputstream is then placed inside this
195
*/
196
PlaceholderOutputStream getPlaceholderResponseBody () {
197
getResponseBody();
198
return uos_orig;
199
}
200
201
public void sendResponseHeaders (int rCode, long contentLen)
202
throws IOException
203
{
204
final Logger logger = server.getLogger();
205
if (sentHeaders) {
206
throw new IOException ("headers already sent");
207
}
208
this.rcode = rCode;
209
String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n";
210
OutputStream tmpout = new BufferedOutputStream (ros);
211
PlaceholderOutputStream o = getPlaceholderResponseBody();
212
tmpout.write (bytes(statusLine, 0), 0, statusLine.length());
213
boolean noContentToSend = false; // assume there is content
214
boolean noContentLengthHeader = false; // must not send Content-length is set
215
rspHdrs.set("Date", FORMATTER.format(Instant.now()));
216
217
/* check for response type that is not allowed to send a body */
218
219
if ((rCode>=100 && rCode <200) /* informational */
220
||(rCode == 204) /* no content */
221
||(rCode == 304)) /* not modified */
222
{
223
if (contentLen != -1) {
224
String msg = "sendResponseHeaders: rCode = "+ rCode
225
+ ": forcing contentLen = -1";
226
logger.log (Level.WARNING, msg);
227
}
228
contentLen = -1;
229
noContentLengthHeader = (rCode != 304);
230
}
231
232
if (isHeadRequest() || rCode == 304) {
233
/* HEAD requests or 304 responses should not set a content length by passing it
234
* through this API, but should instead manually set the required
235
* headers.*/
236
if (contentLen >= 0) {
237
String msg =
238
"sendResponseHeaders: being invoked with a content length for a HEAD request";
239
logger.log (Level.WARNING, msg);
240
}
241
noContentToSend = true;
242
contentLen = 0;
243
} else { /* not a HEAD request or 304 response */
244
if (contentLen == 0) {
245
if (http10) {
246
o.setWrappedStream (new UndefLengthOutputStream (this, ros));
247
close = true;
248
} else {
249
rspHdrs.set ("Transfer-encoding", "chunked");
250
o.setWrappedStream (new ChunkedOutputStream (this, ros));
251
}
252
} else {
253
if (contentLen == -1) {
254
noContentToSend = true;
255
contentLen = 0;
256
}
257
if (!noContentLengthHeader) {
258
rspHdrs.set("Content-length", Long.toString(contentLen));
259
}
260
o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));
261
}
262
}
263
264
// A custom handler can request that the connection be
265
// closed after the exchange by supplying Connection: close
266
// to the response header. Nothing to do if the exchange is
267
// already set up to be closed.
268
if (!close) {
269
Stream<String> conheader =
270
Optional.ofNullable(rspHdrs.get("Connection"))
271
.map(List::stream).orElse(Stream.empty());
272
if (conheader.anyMatch("close"::equalsIgnoreCase)) {
273
logger.log (Level.DEBUG, "Connection: close requested by handler");
274
close = true;
275
}
276
}
277
278
write (rspHdrs, tmpout);
279
this.rspContentLen = contentLen;
280
tmpout.flush() ;
281
tmpout = null;
282
sentHeaders = true;
283
logger.log(Level.TRACE, "Sent headers: noContentToSend=" + noContentToSend);
284
if (noContentToSend) {
285
WriteFinishedEvent e = new WriteFinishedEvent (this);
286
server.addEvent (e);
287
closed = true;
288
}
289
server.logReply (rCode, req.requestLine(), null);
290
}
291
292
void write (Headers map, OutputStream os) throws IOException {
293
Set<Map.Entry<String,List<String>>> entries = map.entrySet();
294
for (Map.Entry<String,List<String>> entry : entries) {
295
String key = entry.getKey();
296
byte[] buf;
297
List<String> values = entry.getValue();
298
for (String val : values) {
299
int i = key.length();
300
buf = bytes (key, 2);
301
buf[i++] = ':';
302
buf[i++] = ' ';
303
os.write (buf, 0, i);
304
buf = bytes (val, 2);
305
i = val.length();
306
buf[i++] = '\r';
307
buf[i++] = '\n';
308
os.write (buf, 0, i);
309
}
310
}
311
os.write ('\r');
312
os.write ('\n');
313
}
314
315
private byte[] rspbuf = new byte [128]; // used by bytes()
316
317
/**
318
* convert string to byte[], using rspbuf
319
* Make sure that at least "extra" bytes are free at end
320
* of rspbuf. Reallocate rspbuf if not big enough.
321
* caller must check return value to see if rspbuf moved
322
*/
323
private byte[] bytes (String s, int extra) {
324
int slen = s.length();
325
if (slen+extra > rspbuf.length) {
326
int diff = slen + extra - rspbuf.length;
327
rspbuf = new byte [2* (rspbuf.length + diff)];
328
}
329
char c[] = s.toCharArray();
330
for (int i=0; i<c.length; i++) {
331
rspbuf[i] = (byte)c[i];
332
}
333
return rspbuf;
334
}
335
336
public InetSocketAddress getRemoteAddress (){
337
Socket s = connection.getChannel().socket();
338
InetAddress ia = s.getInetAddress();
339
int port = s.getPort();
340
return new InetSocketAddress (ia, port);
341
}
342
343
public InetSocketAddress getLocalAddress (){
344
Socket s = connection.getChannel().socket();
345
InetAddress ia = s.getLocalAddress();
346
int port = s.getLocalPort();
347
return new InetSocketAddress (ia, port);
348
}
349
350
public String getProtocol (){
351
String reqline = req.requestLine();
352
int index = reqline.lastIndexOf (' ');
353
return reqline.substring (index+1);
354
}
355
356
public SSLSession getSSLSession () {
357
SSLEngine e = connection.getSSLEngine();
358
if (e == null) {
359
return null;
360
}
361
return e.getSession();
362
}
363
364
public Object getAttribute (String name) {
365
if (name == null) {
366
throw new NullPointerException ("null name parameter");
367
}
368
if (attributes == null) {
369
attributes = getHttpContext().getAttributes();
370
}
371
return attributes.get (name);
372
}
373
374
public void setAttribute (String name, Object value) {
375
if (name == null) {
376
throw new NullPointerException ("null name parameter");
377
}
378
if (attributes == null) {
379
attributes = getHttpContext().getAttributes();
380
}
381
attributes.put (name, value);
382
}
383
384
public void setStreams (InputStream i, OutputStream o) {
385
assert uis != null;
386
if (i != null) {
387
uis = i;
388
}
389
if (o != null) {
390
uos = o;
391
}
392
}
393
394
/**
395
* PP
396
*/
397
HttpConnection getConnection () {
398
return connection;
399
}
400
401
ServerImpl getServerImpl () {
402
return getHttpContext().getServerImpl();
403
}
404
405
public HttpPrincipal getPrincipal () {
406
return principal;
407
}
408
409
void setPrincipal (HttpPrincipal principal) {
410
this.principal = principal;
411
}
412
413
static ExchangeImpl get (HttpExchange t) {
414
if (t instanceof HttpExchangeImpl) {
415
return ((HttpExchangeImpl)t).getExchangeImpl();
416
} else {
417
assert t instanceof HttpsExchangeImpl;
418
return ((HttpsExchangeImpl)t).getExchangeImpl();
419
}
420
}
421
}
422
423
/**
424
* An OutputStream which wraps another stream
425
* which is supplied either at creation time, or sometime later.
426
* If a caller/user tries to write to this stream before
427
* the wrapped stream has been provided, then an IOException will
428
* be thrown.
429
*/
430
class PlaceholderOutputStream extends java.io.OutputStream {
431
432
OutputStream wrapped;
433
434
PlaceholderOutputStream (OutputStream os) {
435
wrapped = os;
436
}
437
438
void setWrappedStream (OutputStream os) {
439
wrapped = os;
440
}
441
442
boolean isWrapped () {
443
return wrapped != null;
444
}
445
446
private void checkWrap () throws IOException {
447
if (wrapped == null) {
448
throw new IOException ("response headers not sent yet");
449
}
450
}
451
452
public void write(int b) throws IOException {
453
checkWrap();
454
wrapped.write (b);
455
}
456
457
public void write(byte b[]) throws IOException {
458
checkWrap();
459
wrapped.write (b);
460
}
461
462
public void write(byte b[], int off, int len) throws IOException {
463
checkWrap();
464
wrapped.write (b, off, len);
465
}
466
467
public void flush() throws IOException {
468
checkWrap();
469
wrapped.flush();
470
}
471
472
public void close() throws IOException {
473
checkWrap();
474
wrapped.close();
475
}
476
}
477
478