Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/httpserver/ExchangeImpl.java
38918 views
/*1* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.httpserver;2627import java.io.*;28import java.net.*;29import javax.net.ssl.*;30import java.util.*;31import java.util.logging.Logger;32import java.text.*;33import com.sun.net.httpserver.*;3435class ExchangeImpl {3637Headers reqHdrs, rspHdrs;38Request req;39String method;40boolean writefinished;41URI uri;42HttpConnection connection;43long reqContentLen;44long rspContentLen;45/* raw streams which access the socket directly */46InputStream ris;47OutputStream ros;48Thread thread;49/* close the underlying connection when this exchange finished */50boolean close;51boolean closed;52boolean http10 = false;5354/* for formatting the Date: header */55private static final String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz";56private static final TimeZone gmtTZ = TimeZone.getTimeZone("GMT");57private static final ThreadLocal<DateFormat> dateFormat =58new ThreadLocal<DateFormat>() {59@Override protected DateFormat initialValue() {60DateFormat df = new SimpleDateFormat(pattern, Locale.US);61df.setTimeZone(gmtTZ);62return df;63}64};6566private static final String HEAD = "HEAD";6768/* streams which take care of the HTTP protocol framing69* and are passed up to higher layers70*/71InputStream uis;72OutputStream uos;73LeftOverInputStream uis_orig; // uis may have be a user supplied wrapper74PlaceholderOutputStream uos_orig;7576boolean sentHeaders; /* true after response headers sent */77Map<String,Object> attributes;78int rcode = -1;79HttpPrincipal principal;80ServerImpl server;8182ExchangeImpl (83String m, URI u, Request req, long len, HttpConnection connection84) throws IOException {85this.req = req;86this.reqHdrs = req.headers();87this.rspHdrs = new Headers();88this.method = m;89this.uri = u;90this.connection = connection;91this.reqContentLen = len;92/* ros only used for headers, body written directly to stream */93this.ros = req.outputStream();94this.ris = req.inputStream();95server = getServerImpl();96server.startExchange();97}9899public Headers getRequestHeaders () {100return new UnmodifiableHeaders (reqHdrs);101}102103public Headers getResponseHeaders () {104return rspHdrs;105}106107public URI getRequestURI () {108return uri;109}110111public String getRequestMethod (){112return method;113}114115public HttpContextImpl getHttpContext (){116return connection.getHttpContext();117}118119private boolean isHeadRequest() {120return HEAD.equals(getRequestMethod());121}122123public void close () {124if (closed) {125return;126}127closed = true;128129/* close the underlying connection if,130* a) the streams not set up yet, no response can be sent, or131* b) if the wrapper output stream is not set up, or132* c) if the close of the input/outpu stream fails133*/134try {135if (uis_orig == null || uos == null) {136connection.close();137return;138}139if (!uos_orig.isWrapped()) {140connection.close();141return;142}143if (!uis_orig.isClosed()) {144uis_orig.close();145}146uos.close();147} catch (IOException e) {148connection.close();149}150}151152public InputStream getRequestBody () {153if (uis != null) {154return uis;155}156if (reqContentLen == -1L) {157uis_orig = new ChunkedInputStream (this, ris);158uis = uis_orig;159} else {160uis_orig = new FixedLengthInputStream (this, ris, reqContentLen);161uis = uis_orig;162}163return uis;164}165166LeftOverInputStream getOriginalInputStream () {167return uis_orig;168}169170public int getResponseCode () {171return rcode;172}173174public OutputStream getResponseBody () {175/* TODO. Change spec to remove restriction below. Filters176* cannot work with this restriction177*178* if (!sentHeaders) {179* throw new IllegalStateException ("headers not sent");180* }181*/182if (uos == null) {183uos_orig = new PlaceholderOutputStream (null);184uos = uos_orig;185}186return uos;187}188189190/* returns the place holder stream, which is the stream191* returned from the 1st call to getResponseBody()192* The "real" ouputstream is then placed inside this193*/194PlaceholderOutputStream getPlaceholderResponseBody () {195getResponseBody();196return uos_orig;197}198199public void sendResponseHeaders (int rCode, long contentLen)200throws IOException201{202if (sentHeaders) {203throw new IOException ("headers already sent");204}205this.rcode = rCode;206String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n";207OutputStream tmpout = new BufferedOutputStream (ros);208PlaceholderOutputStream o = getPlaceholderResponseBody();209tmpout.write (bytes(statusLine, 0), 0, statusLine.length());210boolean noContentToSend = false; // assume there is content211rspHdrs.set ("Date", dateFormat.get().format (new Date()));212213/* check for response type that is not allowed to send a body */214215if ((rCode>=100 && rCode <200) /* informational */216||(rCode == 204) /* no content */217||(rCode == 304)) /* not modified */218{219if (contentLen != -1) {220Logger logger = server.getLogger();221String msg = "sendResponseHeaders: rCode = "+ rCode222+ ": forcing contentLen = -1";223logger.warning (msg);224}225contentLen = -1;226}227228if (isHeadRequest()) {229/* HEAD requests should not set a content length by passing it230* through this API, but should instead manually set the required231* headers.*/232if (contentLen >= 0) {233final Logger logger = server.getLogger();234String msg =235"sendResponseHeaders: being invoked with a content length for a HEAD request";236logger.warning (msg);237}238noContentToSend = true;239contentLen = 0;240} else { /* not a HEAD request */241if (contentLen == 0) {242if (http10) {243o.setWrappedStream (new UndefLengthOutputStream (this, ros));244close = true;245} else {246rspHdrs.set ("Transfer-encoding", "chunked");247o.setWrappedStream (new ChunkedOutputStream (this, ros));248}249} else {250if (contentLen == -1) {251noContentToSend = true;252contentLen = 0;253}254rspHdrs.set("Content-length", Long.toString(contentLen));255o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));256}257}258write (rspHdrs, tmpout);259this.rspContentLen = contentLen;260tmpout.flush() ;261tmpout = null;262sentHeaders = true;263if (noContentToSend) {264WriteFinishedEvent e = new WriteFinishedEvent (this);265server.addEvent (e);266closed = true;267}268server.logReply (rCode, req.requestLine(), null);269}270271void write (Headers map, OutputStream os) throws IOException {272Set<Map.Entry<String,List<String>>> entries = map.entrySet();273for (Map.Entry<String,List<String>> entry : entries) {274String key = entry.getKey();275byte[] buf;276List<String> values = entry.getValue();277for (String val : values) {278int i = key.length();279buf = bytes (key, 2);280buf[i++] = ':';281buf[i++] = ' ';282os.write (buf, 0, i);283buf = bytes (val, 2);284i = val.length();285buf[i++] = '\r';286buf[i++] = '\n';287os.write (buf, 0, i);288}289}290os.write ('\r');291os.write ('\n');292}293294private byte[] rspbuf = new byte [128]; // used by bytes()295296/**297* convert string to byte[], using rspbuf298* Make sure that at least "extra" bytes are free at end299* of rspbuf. Reallocate rspbuf if not big enough.300* caller must check return value to see if rspbuf moved301*/302private byte[] bytes (String s, int extra) {303int slen = s.length();304if (slen+extra > rspbuf.length) {305int diff = slen + extra - rspbuf.length;306rspbuf = new byte [2* (rspbuf.length + diff)];307}308char c[] = s.toCharArray();309for (int i=0; i<c.length; i++) {310rspbuf[i] = (byte)c[i];311}312return rspbuf;313}314315public InetSocketAddress getRemoteAddress (){316Socket s = connection.getChannel().socket();317InetAddress ia = s.getInetAddress();318int port = s.getPort();319return new InetSocketAddress (ia, port);320}321322public InetSocketAddress getLocalAddress (){323Socket s = connection.getChannel().socket();324InetAddress ia = s.getLocalAddress();325int port = s.getLocalPort();326return new InetSocketAddress (ia, port);327}328329public String getProtocol (){330String reqline = req.requestLine();331int index = reqline.lastIndexOf (' ');332return reqline.substring (index+1);333}334335public SSLSession getSSLSession () {336SSLEngine e = connection.getSSLEngine();337if (e == null) {338return null;339}340return e.getSession();341}342343public Object getAttribute (String name) {344if (name == null) {345throw new NullPointerException ("null name parameter");346}347if (attributes == null) {348attributes = getHttpContext().getAttributes();349}350return attributes.get (name);351}352353public void setAttribute (String name, Object value) {354if (name == null) {355throw new NullPointerException ("null name parameter");356}357if (attributes == null) {358attributes = getHttpContext().getAttributes();359}360attributes.put (name, value);361}362363public void setStreams (InputStream i, OutputStream o) {364assert uis != null;365if (i != null) {366uis = i;367}368if (o != null) {369uos = o;370}371}372373/**374* PP375*/376HttpConnection getConnection () {377return connection;378}379380ServerImpl getServerImpl () {381return getHttpContext().getServerImpl();382}383384public HttpPrincipal getPrincipal () {385return principal;386}387388void setPrincipal (HttpPrincipal principal) {389this.principal = principal;390}391392static ExchangeImpl get (HttpExchange t) {393if (t instanceof HttpExchangeImpl) {394return ((HttpExchangeImpl)t).getExchangeImpl();395} else {396assert t instanceof HttpsExchangeImpl;397return ((HttpsExchangeImpl)t).getExchangeImpl();398}399}400}401402/**403* An OutputStream which wraps another stream404* which is supplied either at creation time, or sometime later.405* If a caller/user tries to write to this stream before406* the wrapped stream has been provided, then an IOException will407* be thrown.408*/409class PlaceholderOutputStream extends java.io.OutputStream {410411OutputStream wrapped;412413PlaceholderOutputStream (OutputStream os) {414wrapped = os;415}416417void setWrappedStream (OutputStream os) {418wrapped = os;419}420421boolean isWrapped () {422return wrapped != null;423}424425private void checkWrap () throws IOException {426if (wrapped == null) {427throw new IOException ("response headers not sent yet");428}429}430431public void write(int b) throws IOException {432checkWrap();433wrapped.write (b);434}435436public void write(byte b[]) throws IOException {437checkWrap();438wrapped.write (b);439}440441public void write(byte b[], int off, int len) throws IOException {442checkWrap();443wrapped.write (b, off, len);444}445446public void flush() throws IOException {447checkWrap();448wrapped.flush();449}450451public void close() throws IOException {452checkWrap();453wrapped.close();454}455}456457458