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