History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/net/httpserver/ExchangeImpl.java (1 of 18352 files). Show all

Unified · Split

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