src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java
changeset 47216 71c04702a3d5
parent 41592 855537e5ad9c
child 50681 4254bed3c09d
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2005, 2010, 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 com.sun.net.httpserver.*;
       
    36 
       
    37 class ExchangeImpl {
       
    38 
       
    39     Headers reqHdrs, rspHdrs;
       
    40     Request req;
       
    41     String method;
       
    42     boolean writefinished;
       
    43     URI uri;
       
    44     HttpConnection connection;
       
    45     long reqContentLen;
       
    46     long rspContentLen;
       
    47     /* raw streams which access the socket directly */
       
    48     InputStream ris;
       
    49     OutputStream ros;
       
    50     Thread thread;
       
    51     /* close the underlying connection when this exchange finished */
       
    52     boolean close;
       
    53     boolean closed;
       
    54     boolean http10 = false;
       
    55 
       
    56     /* for formatting the Date: header */
       
    57     private static final String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
       
    58     private static final TimeZone gmtTZ = TimeZone.getTimeZone("GMT");
       
    59     private static final ThreadLocal<DateFormat> dateFormat =
       
    60          new ThreadLocal<DateFormat>() {
       
    61              @Override protected DateFormat initialValue() {
       
    62                  DateFormat df = new SimpleDateFormat(pattern, Locale.US);
       
    63                  df.setTimeZone(gmtTZ);
       
    64                  return df;
       
    65          }
       
    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 = 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 new UnmodifiableHeaders (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         if (sentHeaders) {
       
   205             throw new IOException ("headers already sent");
       
   206         }
       
   207         this.rcode = rCode;
       
   208         String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n";
       
   209         OutputStream tmpout = new BufferedOutputStream (ros);
       
   210         PlaceholderOutputStream o = getPlaceholderResponseBody();
       
   211         tmpout.write (bytes(statusLine, 0), 0, statusLine.length());
       
   212         boolean noContentToSend = false; // assume there is content
       
   213         rspHdrs.set ("Date", dateFormat.get().format (new Date()));
       
   214 
       
   215         /* check for response type that is not allowed to send a body */
       
   216 
       
   217         if ((rCode>=100 && rCode <200) /* informational */
       
   218             ||(rCode == 204)           /* no content */
       
   219             ||(rCode == 304))          /* not modified */
       
   220         {
       
   221             if (contentLen != -1) {
       
   222                 Logger logger = server.getLogger();
       
   223                 String msg = "sendResponseHeaders: rCode = "+ rCode
       
   224                     + ": forcing contentLen = -1";
       
   225                 logger.log (Level.WARNING, msg);
       
   226             }
       
   227             contentLen = -1;
       
   228         }
       
   229 
       
   230         if (isHeadRequest()) {
       
   231             /* HEAD requests should not set a content length by passing it
       
   232              * through this API, but should instead manually set the required
       
   233              * headers.*/
       
   234             if (contentLen >= 0) {
       
   235                 final Logger logger = server.getLogger();
       
   236                 String msg =
       
   237                     "sendResponseHeaders: being invoked with a content length for a HEAD request";
       
   238                 logger.log (Level.WARNING, msg);
       
   239             }
       
   240             noContentToSend = true;
       
   241             contentLen = 0;
       
   242         } else { /* not a HEAD request */
       
   243             if (contentLen == 0) {
       
   244                 if (http10) {
       
   245                     o.setWrappedStream (new UndefLengthOutputStream (this, ros));
       
   246                     close = true;
       
   247                 } else {
       
   248                     rspHdrs.set ("Transfer-encoding", "chunked");
       
   249                     o.setWrappedStream (new ChunkedOutputStream (this, ros));
       
   250                 }
       
   251             } else {
       
   252                 if (contentLen == -1) {
       
   253                     noContentToSend = true;
       
   254                     contentLen = 0;
       
   255                 }
       
   256                 rspHdrs.set("Content-length", Long.toString(contentLen));
       
   257                 o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));
       
   258             }
       
   259         }
       
   260         write (rspHdrs, tmpout);
       
   261         this.rspContentLen = contentLen;
       
   262         tmpout.flush() ;
       
   263         tmpout = null;
       
   264         sentHeaders = true;
       
   265         if (noContentToSend) {
       
   266             WriteFinishedEvent e = new WriteFinishedEvent (this);
       
   267             server.addEvent (e);
       
   268             closed = true;
       
   269         }
       
   270         server.logReply (rCode, req.requestLine(), null);
       
   271     }
       
   272 
       
   273     void write (Headers map, OutputStream os) throws IOException {
       
   274         Set<Map.Entry<String,List<String>>> entries = map.entrySet();
       
   275         for (Map.Entry<String,List<String>> entry : entries) {
       
   276             String key = entry.getKey();
       
   277             byte[] buf;
       
   278             List<String> values = entry.getValue();
       
   279             for (String val : values) {
       
   280                 int i = key.length();
       
   281                 buf = bytes (key, 2);
       
   282                 buf[i++] = ':';
       
   283                 buf[i++] = ' ';
       
   284                 os.write (buf, 0, i);
       
   285                 buf = bytes (val, 2);
       
   286                 i = val.length();
       
   287                 buf[i++] = '\r';
       
   288                 buf[i++] = '\n';
       
   289                 os.write (buf, 0, i);
       
   290             }
       
   291         }
       
   292         os.write ('\r');
       
   293         os.write ('\n');
       
   294     }
       
   295 
       
   296     private byte[] rspbuf = new byte [128]; // used by bytes()
       
   297 
       
   298     /**
       
   299      * convert string to byte[], using rspbuf
       
   300      * Make sure that at least "extra" bytes are free at end
       
   301      * of rspbuf. Reallocate rspbuf if not big enough.
       
   302      * caller must check return value to see if rspbuf moved
       
   303      */
       
   304     private byte[] bytes (String s, int extra) {
       
   305         int slen = s.length();
       
   306         if (slen+extra > rspbuf.length) {
       
   307             int diff = slen + extra - rspbuf.length;
       
   308             rspbuf = new byte [2* (rspbuf.length + diff)];
       
   309         }
       
   310         char c[] = s.toCharArray();
       
   311         for (int i=0; i<c.length; i++) {
       
   312             rspbuf[i] = (byte)c[i];
       
   313         }
       
   314         return rspbuf;
       
   315     }
       
   316 
       
   317     public InetSocketAddress getRemoteAddress (){
       
   318         Socket s = connection.getChannel().socket();
       
   319         InetAddress ia = s.getInetAddress();
       
   320         int port = s.getPort();
       
   321         return new InetSocketAddress (ia, port);
       
   322     }
       
   323 
       
   324     public InetSocketAddress getLocalAddress (){
       
   325         Socket s = connection.getChannel().socket();
       
   326         InetAddress ia = s.getLocalAddress();
       
   327         int port = s.getLocalPort();
       
   328         return new InetSocketAddress (ia, port);
       
   329     }
       
   330 
       
   331     public String getProtocol (){
       
   332         String reqline = req.requestLine();
       
   333         int index = reqline.lastIndexOf (' ');
       
   334         return reqline.substring (index+1);
       
   335     }
       
   336 
       
   337     public SSLSession getSSLSession () {
       
   338         SSLEngine e = connection.getSSLEngine();
       
   339         if (e == null) {
       
   340             return null;
       
   341         }
       
   342         return e.getSession();
       
   343     }
       
   344 
       
   345     public Object getAttribute (String name) {
       
   346         if (name == null) {
       
   347             throw new NullPointerException ("null name parameter");
       
   348         }
       
   349         if (attributes == null) {
       
   350             attributes = getHttpContext().getAttributes();
       
   351         }
       
   352         return attributes.get (name);
       
   353     }
       
   354 
       
   355     public void setAttribute (String name, Object value) {
       
   356         if (name == null) {
       
   357             throw new NullPointerException ("null name parameter");
       
   358         }
       
   359         if (attributes == null) {
       
   360             attributes = getHttpContext().getAttributes();
       
   361         }
       
   362         attributes.put (name, value);
       
   363     }
       
   364 
       
   365     public void setStreams (InputStream i, OutputStream o) {
       
   366         assert uis != null;
       
   367         if (i != null) {
       
   368             uis = i;
       
   369         }
       
   370         if (o != null) {
       
   371             uos = o;
       
   372         }
       
   373     }
       
   374 
       
   375     /**
       
   376      * PP
       
   377      */
       
   378     HttpConnection getConnection () {
       
   379         return connection;
       
   380     }
       
   381 
       
   382     ServerImpl getServerImpl () {
       
   383         return getHttpContext().getServerImpl();
       
   384     }
       
   385 
       
   386     public HttpPrincipal getPrincipal () {
       
   387         return principal;
       
   388     }
       
   389 
       
   390     void setPrincipal (HttpPrincipal principal) {
       
   391         this.principal = principal;
       
   392     }
       
   393 
       
   394     static ExchangeImpl get (HttpExchange t) {
       
   395         if (t instanceof HttpExchangeImpl) {
       
   396             return ((HttpExchangeImpl)t).getExchangeImpl();
       
   397         } else {
       
   398             assert t instanceof HttpsExchangeImpl;
       
   399             return ((HttpsExchangeImpl)t).getExchangeImpl();
       
   400         }
       
   401     }
       
   402 }
       
   403 
       
   404 /**
       
   405  * An OutputStream which wraps another stream
       
   406  * which is supplied either at creation time, or sometime later.
       
   407  * If a caller/user tries to write to this stream before
       
   408  * the wrapped stream has been provided, then an IOException will
       
   409  * be thrown.
       
   410  */
       
   411 class PlaceholderOutputStream extends java.io.OutputStream {
       
   412 
       
   413     OutputStream wrapped;
       
   414 
       
   415     PlaceholderOutputStream (OutputStream os) {
       
   416         wrapped = os;
       
   417     }
       
   418 
       
   419     void setWrappedStream (OutputStream os) {
       
   420         wrapped = os;
       
   421     }
       
   422 
       
   423     boolean isWrapped () {
       
   424         return wrapped != null;
       
   425     }
       
   426 
       
   427     private void checkWrap () throws IOException {
       
   428         if (wrapped == null) {
       
   429             throw new IOException ("response headers not sent yet");
       
   430         }
       
   431     }
       
   432 
       
   433     public void write(int b) throws IOException {
       
   434         checkWrap();
       
   435         wrapped.write (b);
       
   436     }
       
   437 
       
   438     public void write(byte b[]) throws IOException {
       
   439         checkWrap();
       
   440         wrapped.write (b);
       
   441     }
       
   442 
       
   443     public void write(byte b[], int off, int len) throws IOException {
       
   444         checkWrap();
       
   445         wrapped.write (b, off, len);
       
   446     }
       
   447 
       
   448     public void flush() throws IOException {
       
   449         checkWrap();
       
   450         wrapped.flush();
       
   451     }
       
   452 
       
   453     public void close() throws IOException {
       
   454         checkWrap();
       
   455         wrapped.close();
       
   456     }
       
   457 }