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