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/SSLStreams.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/net/httpserver/SSLStreams.java+662−0
@@ -0,0 +1,662 @@
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
26package sun.net.httpserver;
27
28import java.net.*;
29import java.nio.*;
30import java.io.*;
31import java.nio.channels.*;
32import java.util.*;
33import java.util.concurrent.*;
34import java.util.concurrent.locks.*;
35import javax.net.ssl.*;
36import javax.net.ssl.SSLEngineResult.*;
37import com.sun.net.httpserver.*;
38import com.sun.net.httpserver.spi.*;
39
40/**
41 * given a non-blocking SocketChannel, it produces
42 * (blocking) streams which encrypt/decrypt the SSL content
43 * and handle the SSL handshaking automatically.
44 */
45
46class SSLStreams {
47
48 SSLContext sslctx;
49 SocketChannel chan;
50 TimeSource time;
51 ServerImpl server;
52 SSLEngine engine;
53 EngineWrapper wrapper;
54 OutputStream os;
55 InputStream is;
56
57 /* held by thread doing the hand-shake on this connection */
58 Lock handshaking = new ReentrantLock();
59
60 SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException {
61 this.server = server;
62 this.time= (TimeSource)server;
63 this.sslctx= sslctx;
64 this.chan= chan;
65 InetSocketAddress addr =
66 (InetSocketAddress)chan.socket().getRemoteSocketAddress();
67 engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort());
68 engine.setUseClientMode (false);
69 HttpsConfigurator cfg = server.getHttpsConfigurator();
70 configureEngine (cfg, addr);
71 wrapper = new EngineWrapper (chan, engine);
72 }
73
74 private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){
75 if (cfg != null) {
76 Parameters params = new Parameters (cfg, addr);
77//BEGIN_TIGER_EXCLUDE
78 cfg.configure (params);
79 SSLParameters sslParams = params.getSSLParameters();
80 if (sslParams != null) {
81 engine.setSSLParameters (sslParams);
82 } else
83//END_TIGER_EXCLUDE
84 {
85 /* tiger compatibility */
86 if (params.getCipherSuites() != null) {
87 try {
88 engine.setEnabledCipherSuites (
89 params.getCipherSuites()
90 );
91 } catch (IllegalArgumentException e) { /* LOG */}
92 }
93 engine.setNeedClientAuth (params.getNeedClientAuth());
94 engine.setWantClientAuth (params.getWantClientAuth());
95 if (params.getProtocols() != null) {
96 try {
97 engine.setEnabledProtocols (
98 params.getProtocols()
99 );
100 } catch (IllegalArgumentException e) { /* LOG */}
101 }
102 }
103 }
104 }
105
106 class Parameters extends HttpsParameters {
107 InetSocketAddress addr;
108 HttpsConfigurator cfg;
109
110 Parameters (HttpsConfigurator cfg, InetSocketAddress addr) {
111 this.addr = addr;
112 this.cfg = cfg;
113 }
114 public InetSocketAddress getClientAddress () {
115 return addr;
116 }
117 public HttpsConfigurator getHttpsConfigurator() {
118 return cfg;
119 }
120//BEGIN_TIGER_EXCLUDE
121 SSLParameters params;
122 public void setSSLParameters (SSLParameters p) {
123 params = p;
124 }
125 SSLParameters getSSLParameters () {
126 return params;
127 }
128//END_TIGER_EXCLUDE
129 }
130
131 /**
132 * cleanup resources allocated inside this object
133 */
134 void close () throws IOException {
135 wrapper.close();
136 }
137
138 /**
139 * return the SSL InputStream
140 */
141 InputStream getInputStream () throws IOException {
142 if (is == null) {
143 is = new InputStream();
144 }
145 return is;
146 }
147
148 /**
149 * return the SSL OutputStream
150 */
151 OutputStream getOutputStream () throws IOException {
152 if (os == null) {
153 os = new OutputStream();
154 }
155 return os;
156 }
157
158 SSLEngine getSSLEngine () {
159 return engine;
160 }
161
162 /**
163 * request the engine to repeat the handshake on this session
164 * the handshake must be driven by reads/writes on the streams
165 * Normally, not necessary to call this.
166 */
167 void beginHandshake() throws SSLException {
168 engine.beginHandshake();
169 }
170
171 class WrapperResult {
172 SSLEngineResult result;
173
174 /* if passed in buffer was not big enough then the
175 * a reallocated buffer is returned here
176 */
177 ByteBuffer buf;
178 }
179
180 int app_buf_size;
181 int packet_buf_size;
182
183 enum BufType {
184 PACKET, APPLICATION
185 };
186
187 private ByteBuffer allocate (BufType type) {
188 return allocate (type, -1);
189 }
190
191 private ByteBuffer allocate (BufType type, int len) {
192 assert engine != null;
193 synchronized (this) {
194 int size;
195 if (type == BufType.PACKET) {
196 if (packet_buf_size == 0) {
197 SSLSession sess = engine.getSession();
198 packet_buf_size = sess.getPacketBufferSize();
199 }
200 if (len > packet_buf_size) {
201 packet_buf_size = len;
202 }
203 size = packet_buf_size;
204 } else {
205 if (app_buf_size == 0) {
206 SSLSession sess = engine.getSession();
207 app_buf_size = sess.getApplicationBufferSize();
208 }
209 if (len > app_buf_size) {
210 app_buf_size = len;
211 }
212 size = app_buf_size;
213 }
214 return ByteBuffer.allocate (size);
215 }
216 }
217
218 /* reallocates the buffer by :-
219 * 1. creating a new buffer double the size of the old one
220 * 2. putting the contents of the old buffer into the new one
221 * 3. set xx_buf_size to the new size if it was smaller than new size
222 *
223 * flip is set to true if the old buffer needs to be flipped
224 * before it is copied.
225 */
226 private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
227 synchronized (this) {
228 int nsize = 2 * b.capacity();
229 ByteBuffer n = allocate (type, nsize);
230 if (flip) {
231 b.flip();
232 }
233 n.put(b);
234 b = n;
235 }
236 return b;
237 }
238 /**
239 * This is a thin wrapper over SSLEngine and the SocketChannel,
240 * which guarantees the ordering of wraps/unwraps with respect to the underlying
241 * channel read/writes. It handles the UNDER/OVERFLOW status codes
242 * It does not handle the handshaking status codes, or the CLOSED status code
243 * though once the engine is closed, any attempt to read/write to it
244 * will get an exception. The overall result is returned.
245 * It functions synchronously/blocking
246 */
247 class EngineWrapper {
248
249 SocketChannel chan;
250 SSLEngine engine;
251 Object wrapLock, unwrapLock;
252 ByteBuffer unwrap_src, wrap_dst;
253 boolean closed = false;
254 int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
255
256 EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException {
257 this.chan = chan;
258 this.engine = engine;
259 wrapLock = new Object();
260 unwrapLock = new Object();
261 unwrap_src = allocate(BufType.PACKET);
262 wrap_dst = allocate(BufType.PACKET);
263 }
264
265 void close () throws IOException {
266 }
267
268 /* try to wrap and send the data in src. Handles OVERFLOW.
269 * Might block if there is an outbound blockage or if another
270 * thread is calling wrap(). Also, might not send any data
271 * if an unwrap is needed.
272 */
273 WrapperResult wrapAndSend(ByteBuffer src) throws IOException {
274 return wrapAndSendX(src, false);
275 }
276
277 WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException {
278 if (closed && !ignoreClose) {
279 throw new IOException ("Engine is closed");
280 }
281 Status status;
282 WrapperResult r = new WrapperResult();
283 synchronized (wrapLock) {
284 wrap_dst.clear();
285 do {
286 r.result = engine.wrap (src, wrap_dst);
287 status = r.result.getStatus();
288 if (status == Status.BUFFER_OVERFLOW) {
289 wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
290 }
291 } while (status == Status.BUFFER_OVERFLOW);
292 if (status == Status.CLOSED && !ignoreClose) {
293 closed = true;
294 return r;
295 }
296 if (r.result.bytesProduced() > 0) {
297 wrap_dst.flip();
298 int l = wrap_dst.remaining();
299 assert l == r.result.bytesProduced();
300 while (l>0) {
301 l -= chan.write (wrap_dst);
302 }
303 }
304 }
305 return r;
306 }
307
308 /* block until a complete message is available and return it
309 * in dst, together with the Result. dst may have been re-allocated
310 * so caller should check the returned value in Result
311 * If handshaking is in progress then, possibly no data is returned
312 */
313 WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
314 Status status = Status.OK;
315 WrapperResult r = new WrapperResult();
316 r.buf = dst;
317 if (closed) {
318 throw new IOException ("Engine is closed");
319 }
320 boolean needData;
321 if (u_remaining > 0) {
322 unwrap_src.compact();
323 unwrap_src.flip();
324 needData = false;
325 } else {
326 unwrap_src.clear();
327 needData = true;
328 }
329 synchronized (unwrapLock) {
330 int x;
331 do {
332 if (needData) {
333 do {
334 x = chan.read (unwrap_src);
335 } while (x == 0);
336 if (x == -1) {
337 throw new IOException ("connection closed for reading");
338 }
339 unwrap_src.flip();
340 }
341 r.result = engine.unwrap (unwrap_src, r.buf);
342 status = r.result.getStatus();
343 if (status == Status.BUFFER_UNDERFLOW) {
344 if (unwrap_src.limit() == unwrap_src.capacity()) {
345 /* buffer not big enough */
346 unwrap_src = realloc (
347 unwrap_src, false, BufType.PACKET
348 );
349 } else {
350 /* Buffer not full, just need to read more
351 * data off the channel. Reset pointers
352 * for reading off SocketChannel
353 */
354 unwrap_src.position (unwrap_src.limit());
355 unwrap_src.limit (unwrap_src.capacity());
356 }
357 needData = true;
358 } else if (status == Status.BUFFER_OVERFLOW) {
359 r.buf = realloc (r.buf, true, BufType.APPLICATION);
360 needData = false;
361 } else if (status == Status.CLOSED) {
362 closed = true;
363 r.buf.flip();
364 return r;
365 }
366 } while (status != Status.OK);
367 }
368 u_remaining = unwrap_src.remaining();
369 return r;
370 }
371 }
372
373 /**
374 * send the data in the given ByteBuffer. If a handshake is needed
375 * then this is handled within this method. When this call returns,
376 * all of the given user data has been sent and any handshake has been
377 * completed. Caller should check if engine has been closed.
378 */
379 public WrapperResult sendData (ByteBuffer src) throws IOException {
380 WrapperResult r=null;
381 while (src.remaining() > 0) {
382 r = wrapper.wrapAndSend(src);
383 Status status = r.result.getStatus();
384 if (status == Status.CLOSED) {
385 doClosure ();
386 return r;
387 }
388 HandshakeStatus hs_status = r.result.getHandshakeStatus();
389 if (hs_status != HandshakeStatus.FINISHED &&
390 hs_status != HandshakeStatus.NOT_HANDSHAKING)
391 {
392 doHandshake(hs_status);
393 }
394 }
395 return r;
396 }
397
398 /**
399 * read data thru the engine into the given ByteBuffer. If the
400 * given buffer was not large enough, a new one is allocated
401 * and returned. This call handles handshaking automatically.
402 * Caller should check if engine has been closed.
403 */
404 public WrapperResult recvData (ByteBuffer dst) throws IOException {
405 /* we wait until some user data arrives */
406 WrapperResult r = null;
407 assert dst.position() == 0;
408 while (dst.position() == 0) {
409 r = wrapper.recvAndUnwrap (dst);
410 dst = (r.buf != dst) ? r.buf: dst;
411 Status status = r.result.getStatus();
412 if (status == Status.CLOSED) {
413 doClosure ();
414 return r;
415 }
416
417 HandshakeStatus hs_status = r.result.getHandshakeStatus();
418 if (hs_status != HandshakeStatus.FINISHED &&
419 hs_status != HandshakeStatus.NOT_HANDSHAKING)
420 {
421 doHandshake (hs_status);
422 }
423 }
424 dst.flip();
425 return r;
426 }
427
428 /* we've received a close notify. Need to call wrap to send
429 * the response
430 */
431 void doClosure () throws IOException {
432 try {
433 handshaking.lock();
434 ByteBuffer tmp = allocate(BufType.APPLICATION);
435 WrapperResult r;
436 do {
437 tmp.clear();
438 tmp.flip ();
439 r = wrapper.wrapAndSendX (tmp, true);
440 } while (r.result.getStatus() != Status.CLOSED);
441 } finally {
442 handshaking.unlock();
443 }
444 }
445
446 /* do the (complete) handshake after acquiring the handshake lock.
447 * If two threads call this at the same time, then we depend
448 * on the wrapper methods being idempotent. eg. if wrapAndSend()
449 * is called with no data to send then there must be no problem
450 */
451 void doHandshake (HandshakeStatus hs_status) throws IOException {
452 try {
453 handshaking.lock();
454 ByteBuffer tmp = allocate(BufType.APPLICATION);
455 while (hs_status != HandshakeStatus.FINISHED &&
456 hs_status != HandshakeStatus.NOT_HANDSHAKING)
457 {
458 WrapperResult r = null;
459 switch (hs_status) {
460 case NEED_TASK:
461 Runnable task;
462 while ((task = engine.getDelegatedTask()) != null) {
463 /* run in current thread, because we are already
464 * running an external Executor
465 */
466 task.run();
467 }
468 /* fall thru - call wrap again */
469 case NEED_WRAP:
470 tmp.clear();
471 tmp.flip();
472 r = wrapper.wrapAndSend(tmp);
473 break;
474
475 case NEED_UNWRAP:
476 tmp.clear();
477 r = wrapper.recvAndUnwrap (tmp);
478 if (r.buf != tmp) {
479 tmp = r.buf;
480 }
481 assert tmp.position() == 0;
482 break;
483 }
484 hs_status = r.result.getHandshakeStatus();
485 }
486 } finally {
487 handshaking.unlock();
488 }
489 }
490
491 /**
492 * represents an SSL input stream. Multiple https requests can
493 * be sent over one stream. closing this stream causes an SSL close
494 * input.
495 */
496 class InputStream extends java.io.InputStream {
497
498 ByteBuffer bbuf;
499 boolean closed = false;
500
501 /* this stream eof */
502 boolean eof = false;
503
504 boolean needData = true;
505
506 InputStream () {
507 bbuf = allocate (BufType.APPLICATION);
508 }
509
510 public int read (byte[] buf, int off, int len) throws IOException {
511 if (closed) {
512 throw new IOException ("SSL stream is closed");
513 }
514 if (eof) {
515 return 0;
516 }
517 int available=0;
518 if (!needData) {
519 available = bbuf.remaining();
520 needData = (available==0);
521 }
522 if (needData) {
523 bbuf.clear();
524 WrapperResult r = recvData (bbuf);
525 bbuf = r.buf== bbuf? bbuf: r.buf;
526 if ((available=bbuf.remaining()) == 0) {
527 eof = true;
528 return 0;
529 } else {
530 needData = false;
531 }
532 }
533 /* copy as much as possible from buf into users buf */
534 if (len > available) {
535 len = available;
536 }
537 bbuf.get (buf, off, len);
538 return len;
539 }
540
541 public int available () throws IOException {
542 return bbuf.remaining();
543 }
544
545 public boolean markSupported () {
546 return false; /* not possible with SSLEngine */
547 }
548
549 public void reset () throws IOException {
550 throw new IOException ("mark/reset not supported");
551 }
552
553 public long skip (long s) throws IOException {
554 int n = (int)s;
555 if (closed) {
556 throw new IOException ("SSL stream is closed");
557 }
558 if (eof) {
559 return 0;
560 }
561 int ret = n;
562 while (n > 0) {
563 if (bbuf.remaining() >= n) {
564 bbuf.position (bbuf.position()+n);
565 return ret;
566 } else {
567 n -= bbuf.remaining();
568 bbuf.clear();
569 WrapperResult r = recvData (bbuf);
570 bbuf = r.buf==bbuf? bbuf: r.buf;
571 }
572 }
573 return ret; /* not reached */
574 }
575
576 /**
577 * close the SSL connection. All data must have been consumed
578 * before this is called. Otherwise an exception will be thrown.
579 * [Note. May need to revisit this. not quite the normal close() symantics
580 */
581 public void close () throws IOException {
582 eof = true;
583 engine.closeInbound ();
584 }
585
586 public int read (byte[] buf) throws IOException {
587 return read (buf, 0, buf.length);
588 }
589
590 byte single[] = new byte [1];
591
592 public int read () throws IOException {
593 int n = read (single, 0, 1);
594 if (n == 0) {
595 return -1;
596 } else {
597 return single[0] & 0xFF;
598 }
599 }
600 }
601
602 /**
603 * represents an SSL output stream. plain text data written to this stream
604 * is encrypted by the stream. Multiple HTTPS responses can be sent on
605 * one stream. closing this stream initiates an SSL closure
606 */
607 class OutputStream extends java.io.OutputStream {
608 ByteBuffer buf;
609 boolean closed = false;
610 byte single[] = new byte[1];
611
612 OutputStream() {
613 buf = allocate(BufType.APPLICATION);
614 }
615
616 public void write(int b) throws IOException {
617 single[0] = (byte)b;
618 write (single, 0, 1);
619 }
620
621 public void write(byte b[]) throws IOException {
622 write (b, 0, b.length);
623 }
624 public void write(byte b[], int off, int len) throws IOException {
625 if (closed) {
626 throw new IOException ("output stream is closed");
627 }
628 while (len > 0) {
629 int l = len > buf.capacity() ? buf.capacity() : len;
630 buf.clear();
631 buf.put (b, off, l);
632 len -= l;
633 off += l;
634 buf.flip();
635 WrapperResult r = sendData (buf);
636 if (r.result.getStatus() == Status.CLOSED) {
637 closed = true;
638 if (len > 0) {
639 throw new IOException ("output stream is closed");
640 }
641 }
642 }
643 }
644
645 public void flush() throws IOException {
646 /* no-op */
647 }
648
649 public void close() throws IOException {
650 WrapperResult r=null;
651 engine.closeOutbound();
652 closed = true;
653 HandshakeStatus stat = HandshakeStatus.NEED_WRAP;
654 buf.clear();
655 while (stat == HandshakeStatus.NEED_WRAP) {
656 r = wrapper.wrapAndSend (buf);
657 stat = r.result.getHandshakeStatus();
658 }
659 assert r.result.getStatus() == Status.CLOSED;
660 }
661 }
662}