1 /*
   2  * Copyright (c) 2005, 2008, 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.util.*;
  29 import java.nio.*;
  30 import java.net.*;
  31 import java.io.*;
  32 import java.nio.channels.*;
  33 import com.sun.net.httpserver.*;
  34 import com.sun.net.httpserver.spi.*;
  35 
  36 /**
  37  */
  38 class Request {
  39 
  40     final static int BUF_LEN = 2048;
  41     final static byte CR = 13;
  42     final static byte LF = 10;
  43 
  44     private String startLine;
  45     private SocketChannel chan;
  46     private InputStream is;
  47     private OutputStream os;
  48 
  49     Request (InputStream rawInputStream, OutputStream rawout) throws IOException {
  50         this.chan = chan;
  51         is = rawInputStream;
  52         os = rawout;
  53         do {
  54             startLine = readLine();
  55             if (startLine == null) {
  56                 return;
  57             }
  58             /* skip blank lines */
  59         } while (startLine == null ? false : startLine.equals (""));
  60     }
  61 
  62 
  63     char[] buf = new char [BUF_LEN];
  64     int pos;
  65     StringBuffer lineBuf;
  66 
  67     public InputStream inputStream () {
  68         return is;
  69     }
  70 
  71     public OutputStream outputStream () {
  72         return os;
  73     }
  74 
  75     /**
  76      * read a line from the stream returning as a String.
  77      * Not used for reading headers.
  78      */
  79 
  80     public String readLine () throws IOException {
  81         boolean gotCR = false, gotLF = false;
  82         pos = 0; lineBuf = new StringBuffer();
  83         while (!gotLF) {
  84             int c = is.read();
  85             if (c == -1) {
  86                 return null;
  87             }
  88             if (gotCR) {
  89                 if (c == LF) {
  90                     gotLF = true;
  91                 } else {
  92                     gotCR = false;
  93                     consume (CR);
  94                     consume (c);
  95                 }
  96             } else {
  97                 if (c == CR) {
  98                     gotCR = true;
  99                 } else {
 100                     consume (c);
 101                 }
 102             }
 103         }
 104         lineBuf.append (buf, 0, pos);
 105         return new String (lineBuf);
 106     }
 107 
 108     private void consume (int c) {
 109         if (pos == BUF_LEN) {
 110             lineBuf.append (buf);
 111             pos = 0;
 112         }
 113         buf[pos++] = (char)c;
 114     }
 115 
 116     /**
 117      * returns the request line (first line of a request)
 118      */
 119     public String requestLine () {
 120         return startLine;
 121     }
 122 
 123     Headers hdrs = null;
 124 
 125     Headers headers () throws IOException {
 126         if (hdrs != null) {
 127             return hdrs;
 128         }
 129         hdrs = new Headers();
 130 
 131         char s[] = new char[10];
 132         int len = 0;
 133 
 134         int firstc = is.read();
 135 
 136         // check for empty headers
 137         if (firstc == CR || firstc == LF) {
 138             int c = is.read();
 139             if (c == CR || c == LF) {
 140                 return hdrs;
 141             }
 142             s[0] = (char)firstc;
 143             len = 1;
 144             firstc = c;
 145         }
 146 
 147         while (firstc != LF && firstc != CR && firstc >= 0) {
 148             int keyend = -1;
 149             int c;
 150             boolean inKey = firstc > ' ';
 151             s[len++] = (char) firstc;
 152     parseloop:{
 153                 while ((c = is.read()) >= 0) {
 154                     switch (c) {
 155                       case ':':
 156                         if (inKey && len > 0)
 157                             keyend = len;
 158                         inKey = false;
 159                         break;
 160                       case '\t':
 161                         c = ' ';
 162                       case ' ':
 163                         inKey = false;
 164                         break;
 165                       case CR:
 166                       case LF:
 167                         firstc = is.read();
 168                         if (c == CR && firstc == LF) {
 169                             firstc = is.read();
 170                             if (firstc == CR)
 171                                 firstc = is.read();
 172                         }
 173                         if (firstc == LF || firstc == CR || firstc > ' ')
 174                             break parseloop;
 175                         /* continuation */
 176                         c = ' ';
 177                         break;
 178                     }
 179                     if (len >= s.length) {
 180                         char ns[] = new char[s.length * 2];
 181                         System.arraycopy(s, 0, ns, 0, len);
 182                         s = ns;
 183                     }
 184                     s[len++] = (char) c;
 185                 }
 186                 firstc = -1;
 187             }
 188             while (len > 0 && s[len - 1] <= ' ')
 189                 len--;
 190             String k;
 191             if (keyend <= 0) {
 192                 k = null;
 193                 keyend = 0;
 194             } else {
 195                 k = String.copyValueOf(s, 0, keyend);
 196                 if (keyend < len && s[keyend] == ':')
 197                     keyend++;
 198                 while (keyend < len && s[keyend] <= ' ')
 199                     keyend++;
 200             }
 201             String v;
 202             if (keyend >= len)
 203                 v = new String();
 204             else
 205                 v = String.copyValueOf(s, keyend, len - keyend);
 206             hdrs.add (k,v);
 207             len = 0;
 208         }
 209         return hdrs;
 210     }
 211 
 212     /**
 213      * Implements blocking reading semantics on top of a non-blocking channel
 214      */
 215 
 216     static class ReadStream extends InputStream {
 217         SocketChannel channel;
 218         ByteBuffer chanbuf;
 219         byte[] one;
 220         private boolean closed = false, eof = false;
 221         ByteBuffer markBuf; /* reads may be satisifed from this buffer */
 222         boolean marked;
 223         boolean reset;
 224         int readlimit;
 225         static long readTimeout;
 226         ServerImpl server;
 227         final static int BUFSIZE = 8 * 1024;
 228 
 229         public ReadStream (ServerImpl server, SocketChannel chan) throws IOException {
 230             this.channel = chan;
 231             this.server = server;
 232             chanbuf = ByteBuffer.allocate (BUFSIZE);
 233             chanbuf.clear();
 234             one = new byte[1];
 235             closed = marked = reset = false;
 236         }
 237 
 238         public synchronized int read (byte[] b) throws IOException {
 239             return read (b, 0, b.length);
 240         }
 241 
 242         public synchronized int read () throws IOException {
 243             int result = read (one, 0, 1);
 244             if (result == 1) {
 245                 return one[0] & 0xFF;
 246             } else {
 247                 return -1;
 248             }
 249         }
 250 
 251         public synchronized int read (byte[] b, int off, int srclen) throws IOException {
 252 
 253             int canreturn, willreturn;
 254 
 255             if (closed)
 256                 throw new IOException ("Stream closed");
 257 
 258             if (eof) {
 259                 return -1;
 260             }
 261 
 262             assert channel.isBlocking();
 263 
 264             if (off < 0 || srclen < 0|| srclen > (b.length-off)) {
 265                 throw new IndexOutOfBoundsException ();
 266             }
 267 
 268             if (reset) { /* satisfy from markBuf */
 269                 canreturn = markBuf.remaining ();
 270                 willreturn = canreturn>srclen ? srclen : canreturn;
 271                 markBuf.get(b, off, willreturn);
 272                 if (canreturn == willreturn) {
 273                     reset = false;
 274                 }
 275             } else { /* satisfy from channel */
 276                 chanbuf.clear ();
 277                 if (srclen <  BUFSIZE) {
 278                     chanbuf.limit (srclen);
 279                 }
 280                 do {
 281                     willreturn = channel.read (chanbuf);
 282                 } while (willreturn == 0);
 283                 if (willreturn == -1) {
 284                     eof = true;
 285                     return -1;
 286                 }
 287                 chanbuf.flip ();
 288                 chanbuf.get(b, off, willreturn);
 289 
 290                 if (marked) { /* copy into markBuf */
 291                     try {
 292                         markBuf.put (b, off, willreturn);
 293                     } catch (BufferOverflowException e) {
 294                         marked = false;
 295                     }
 296                 }
 297             }
 298             return willreturn;
 299         }
 300 
 301         public boolean markSupported () {
 302             return true;
 303         }
 304 
 305         /* Does not query the OS socket */
 306         public synchronized int available () throws IOException {
 307             if (closed)
 308                 throw new IOException ("Stream is closed");
 309 
 310             if (eof)
 311                 return -1;
 312 
 313             if (reset)
 314                 return markBuf.remaining();
 315 
 316             return chanbuf.remaining();
 317         }
 318 
 319         public void close () throws IOException {
 320             if (closed) {
 321                 return;
 322             }
 323             channel.close ();
 324             closed = true;
 325         }
 326 
 327         public synchronized void mark (int readlimit) {
 328             if (closed)
 329                 return;
 330             this.readlimit = readlimit;
 331             markBuf = ByteBuffer.allocate (readlimit);
 332             marked = true;
 333             reset = false;
 334         }
 335 
 336         public synchronized void reset () throws IOException {
 337             if (closed )
 338                 return;
 339             if (!marked)
 340                 throw new IOException ("Stream not marked");
 341             marked = false;
 342             reset = true;
 343             markBuf.flip ();
 344         }
 345     }
 346 
 347     static class WriteStream extends java.io.OutputStream {
 348         SocketChannel channel;
 349         ByteBuffer buf;
 350         SelectionKey key;
 351         boolean closed;
 352         byte[] one;
 353         ServerImpl server;
 354 
 355         public WriteStream (ServerImpl server, SocketChannel channel) throws IOException {
 356             this.channel = channel;
 357             this.server = server;
 358             assert channel.isBlocking();
 359             closed = false;
 360             one = new byte [1];
 361             buf = ByteBuffer.allocate (4096);
 362         }
 363 
 364         public synchronized void write (int b) throws IOException {
 365             one[0] = (byte)b;
 366             write (one, 0, 1);
 367         }
 368 
 369         public synchronized void write (byte[] b) throws IOException {
 370             write (b, 0, b.length);
 371         }
 372 
 373         public synchronized void write (byte[] b, int off, int len) throws IOException {
 374             int l = len;
 375             if (closed)
 376                 throw new IOException ("stream is closed");
 377 
 378             int cap = buf.capacity();
 379             if (cap < len) {
 380                 int diff = len - cap;
 381                 buf = ByteBuffer.allocate (2*(cap+diff));
 382             }
 383             buf.clear();
 384             buf.put (b, off, len);
 385             buf.flip ();
 386             int n;
 387             while ((n = channel.write (buf)) < l) {
 388                 l -= n;
 389                 if (l == 0)
 390                     return;
 391             }
 392         }
 393 
 394         public void close () throws IOException {
 395             if (closed)
 396                 return;
 397             //server.logStackTrace ("Request.OS.close: isOpen="+channel.isOpen());
 398             channel.close ();
 399             closed = true;
 400         }
 401     }
 402 }