Skip to content

Commit b2424c1

Browse files
author
Femi Omojola
committed
Switched to source/target 1.5 in the build.xml (fixes Android deployment errors: see http://code.google.com/p/android/issues/detail?id=22970).
Added the SSLSocketChannel file, and modified the WebSocketImpl to support its use. Added an example that shows how it would be used. Rebuild the distribution output.
1 parent 8ef67b4 commit b2424c1

6 files changed

Lines changed: 404 additions & 10 deletions

File tree

build.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
<target name="compile">
44
<mkdir dir="build/classes" />
55
<mkdir dir="build/examples" />
6-
<javac includeantruntime="false" debug="on" srcdir="src" destdir="build/classes" />
7-
<javac includeantruntime="false" srcdir="example" classpath="build/classes" destdir="build/examples" />
6+
<javac includeantruntime="false" debug="on" srcdir="src" destdir="build/classes" target="1.5" source="1.5" />
7+
<javac includeantruntime="false" srcdir="example" classpath="build/classes" destdir="build/examples" target="1.5" source="1.5" />
88
</target>
99

1010
<target name="jar" depends="compile">

dist/WebSocket.jar

12.9 KB
Binary file not shown.

example/SSLServer.java

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package test;
2+
3+
import java.util.List;
4+
5+
import java.io.File;
6+
import java.io.FileInputStream;
7+
import java.io.FileOutputStream;
8+
import java.io.IOException;
9+
10+
import java.security.KeyStore;
11+
import java.security.KeyPair;
12+
import java.security.KeyPairGenerator;
13+
import java.security.PrivateKey;
14+
import java.security.PublicKey;
15+
import java.security.SecureRandom;
16+
import java.security.GeneralSecurityException;
17+
import java.security.NoSuchProviderException;
18+
import java.security.InvalidKeyException;
19+
import java.security.Security;
20+
import java.security.SignatureException;
21+
22+
import javax.net.ssl.TrustManagerFactory;
23+
import javax.net.ssl.SSLContext;
24+
import javax.net.ssl.SSLEngine;
25+
import javax.net.ssl.KeyManagerFactory;
26+
27+
import java.nio.channels.SocketChannel;
28+
29+
import java.net.InetAddress;
30+
import java.net.InetSocketAddress;
31+
32+
import org.java_websocket.WebSocket;
33+
import org.java_websocket.WebSocketServer;
34+
import org.java_websocket.handshake.ClientHandshake;
35+
import org.java_websocket.WebSocketAdapter;
36+
import org.java_websocket.WebSocketImpl;
37+
import org.java_websocket.SSLSocketChannel;
38+
import org.java_websocket.drafts.Draft;
39+
40+
/*
41+
* Create the appropriate websocket server.
42+
*/
43+
public class SSLServer implements WebSocketServer.WebSocketServerFactory
44+
{
45+
private static final String STORETYPE = "JKS";
46+
private static final String KEYSTORE = "keystore.jks";
47+
private static final String STOREPASSWORD = "storepassword";
48+
private static final String KEYPASSWORD = "keypassword";
49+
50+
public static void main(String[] args) throws Exception
51+
{
52+
new SSLServer();
53+
}
54+
55+
private SSLContext sslContext;
56+
57+
void loadFromFile() throws Exception
58+
{
59+
// load up the key store
60+
KeyStore ks = KeyStore.getInstance(STORETYPE);
61+
File kf = new File(KEYSTORE);
62+
ks.load(new FileInputStream(kf), STOREPASSWORD.toCharArray());
63+
64+
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
65+
kmf.init(ks, KEYPASSWORD.toCharArray());
66+
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
67+
tmf.init(ks);
68+
69+
sslContext = SSLContext.getInstance("TLS");
70+
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
71+
}
72+
73+
/*
74+
* Keystore with certificate created like so (in JKS format):
75+
*
76+
keytool -genkey -validity 3650 -keystore "keystore.jks" -storepass "storepassword" -keypass "keypassword" -alias "default" -dname "CN=127.0.0.1, OU=MyOrgUnit, O=MyOrg, L=MyCity, S=MyRegion, C=MyCountry"
77+
*/
78+
SSLServer() throws Exception
79+
{
80+
sslContext = null;
81+
loadFromFile();
82+
83+
// create the web socket server
84+
WebSocketSource wsgateway = new WebSocketSource(8001, InetAddress.getByName("127.0.0.1"));
85+
wsgateway.setWebSocketFactory(this);
86+
wsgateway.start();
87+
}
88+
89+
@Override
90+
public WebSocketImpl createWebSocket( WebSocketAdapter a, Draft d, SocketChannel c ) {
91+
if(sslContext != null) try{
92+
SSLEngine e = sslContext.createSSLEngine();
93+
e.setUseClientMode(false);
94+
return new WebSocketImpl( a, d, new SSLSocketChannel(c, e));
95+
}catch(Exception e1){}
96+
return new WebSocketImpl( a, d, c );
97+
}
98+
99+
@Override
100+
public WebSocketImpl createWebSocket( WebSocketAdapter a, List<Draft> d, SocketChannel c ) {
101+
if(sslContext != null) try{
102+
SSLEngine e = sslContext.createSSLEngine();
103+
e.setUseClientMode(false);
104+
return new WebSocketImpl( a, d, new SSLSocketChannel(c, e)); }catch(Exception e1){}
105+
return new WebSocketImpl( a, d, c );
106+
}
107+
108+
class WebSocketSource extends WebSocketServer
109+
{
110+
private WebSocket handle;
111+
WebSocketSource(int port, InetAddress addr)
112+
{
113+
super(new InetSocketAddress(addr, port));
114+
handle = null;
115+
}
116+
117+
@Override
118+
public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3)
119+
{
120+
System.err.println("---------------------------->Closed");
121+
if(arg0 == handle) handle = null;
122+
}
123+
124+
@Override
125+
public void onError(WebSocket arg0, Exception arg1) {
126+
// TODO Auto-generated method stub
127+
}
128+
129+
@Override
130+
public void onMessage(WebSocket arg0, String arg1)
131+
{
132+
if(arg0 != handle){
133+
arg0.close(org.java_websocket.framing.CloseFrame.NORMAL);
134+
return;
135+
}
136+
137+
System.out.println("--------->["+arg1+"]");
138+
}
139+
140+
@Override
141+
public void onOpen(WebSocket arg0, ClientHandshake arg1)
142+
{
143+
// nothing to see just yet
144+
if(handle == null){
145+
handle = arg0;
146+
}else if(handle != arg0){
147+
arg0.close(org.java_websocket.framing.CloseFrame.NORMAL);
148+
}
149+
}
150+
151+
void done()
152+
{
153+
if(handle != null) handle.close(org.java_websocket.framing.CloseFrame.NORMAL);
154+
}
155+
}
156+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package org.java_websocket;
2+
3+
import java.net.*;
4+
import java.nio.*;
5+
import java.nio.channels.*;
6+
import javax.net.ssl.*;
7+
import java.io.*;
8+
9+
/**
10+
* Implements the relevant portions of the SocketChannel interface with the SSLEngine wrapper.
11+
*/
12+
public class SSLSocketChannel
13+
{
14+
private ByteBuffer clientIn, clientOut, cTOs, sTOc, wbuf;
15+
private SocketChannel sc;
16+
private SSLEngineResult res;
17+
private SSLEngine sslEngine;
18+
private int SSL;
19+
20+
public SSLSocketChannel(SocketChannel sc, SSLEngine sslEngine) throws IOException
21+
{
22+
this.sc = sc;
23+
this.sslEngine = sslEngine;
24+
SSL = 1;
25+
try {
26+
sslEngine.setEnableSessionCreation(true);
27+
SSLSession session = sslEngine.getSession();
28+
createBuffers(session);
29+
// wrap
30+
clientOut.clear();
31+
sc.write(wrap(clientOut));
32+
while (res.getHandshakeStatus() !=
33+
SSLEngineResult.HandshakeStatus.FINISHED) {
34+
if (res.getHandshakeStatus() ==
35+
SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
36+
// unwrap
37+
sTOc.clear();
38+
while (sc.read(sTOc) < 1)
39+
Thread.sleep(20);
40+
sTOc.flip();
41+
unwrap(sTOc);
42+
if (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED) {
43+
clientOut.clear();
44+
sc.write(wrap(clientOut));
45+
}
46+
} else if (res.getHandshakeStatus() ==
47+
SSLEngineResult.HandshakeStatus.NEED_WRAP) {
48+
// wrap
49+
clientOut.clear();
50+
sc.write(wrap(clientOut));
51+
} else {Thread.sleep(1000);}
52+
}
53+
clientIn.clear();
54+
clientIn.flip();
55+
SSL = 4;
56+
} catch (Exception e) {
57+
e.printStackTrace(System.out);
58+
SSL = 0;
59+
}
60+
}
61+
62+
private synchronized ByteBuffer wrap(ByteBuffer b) throws SSLException {
63+
cTOs.clear();
64+
res = sslEngine.wrap(b, cTOs);
65+
cTOs.flip();
66+
return cTOs;
67+
}
68+
69+
private synchronized ByteBuffer unwrap(ByteBuffer b) throws SSLException {
70+
clientIn.clear();
71+
int pos;
72+
while (b.hasRemaining()) {
73+
res = sslEngine.unwrap(b, clientIn);
74+
if (res.getHandshakeStatus() ==
75+
SSLEngineResult.HandshakeStatus.NEED_TASK) {
76+
// Task
77+
Runnable task;
78+
while ((task=sslEngine.getDelegatedTask()) != null)
79+
{
80+
task.run();
81+
}
82+
} else if (res.getHandshakeStatus() ==
83+
SSLEngineResult.HandshakeStatus.FINISHED) {
84+
return clientIn;
85+
} else if (res.getStatus() ==
86+
SSLEngineResult.Status.BUFFER_UNDERFLOW) {
87+
return clientIn;
88+
}
89+
}
90+
return clientIn;
91+
}
92+
93+
private void createBuffers(SSLSession session) {
94+
95+
int appBufferMax = session.getApplicationBufferSize();
96+
int netBufferMax = session.getPacketBufferSize();
97+
98+
clientIn = ByteBuffer.allocate(65536);
99+
clientOut = ByteBuffer.allocate(appBufferMax);
100+
wbuf = ByteBuffer.allocate(65536);
101+
102+
cTOs = ByteBuffer.allocate(netBufferMax);
103+
sTOc = ByteBuffer.allocate(netBufferMax);
104+
105+
}
106+
107+
public int write(ByteBuffer src) throws IOException {
108+
if (SSL == 4) {
109+
return sc.write(wrap(src));
110+
}
111+
return sc.write(src);
112+
}
113+
114+
public int read(ByteBuffer dst) throws IOException {
115+
int amount = 0, limit;
116+
if (SSL == 4) {
117+
// test if there was a buffer overflow in dst
118+
if (clientIn.hasRemaining()) {
119+
limit = Math.min(clientIn.remaining(), dst.remaining());
120+
for (int i = 0; i < limit; i++) {
121+
dst.put(clientIn.get());
122+
amount++;
123+
}
124+
return amount;
125+
}
126+
// test if some bytes left from last read (e.g. BUFFER_UNDERFLOW)
127+
if (sTOc.hasRemaining()) {
128+
unwrap(sTOc);
129+
clientIn.flip();
130+
limit = Math.min(clientIn.limit(), dst.remaining());
131+
for (int i = 0; i < limit; i++) {
132+
dst.put(clientIn.get());
133+
amount++;
134+
}
135+
if (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW) {
136+
sTOc.clear();
137+
sTOc.flip();
138+
return amount;
139+
}
140+
}
141+
if (!sTOc.hasRemaining())
142+
sTOc.clear();
143+
else
144+
sTOc.compact();
145+
146+
if (sc.read(sTOc) == -1) {
147+
sTOc.clear();
148+
sTOc.flip();
149+
return -1;
150+
}
151+
sTOc.flip();
152+
unwrap(sTOc);
153+
// write in dst
154+
clientIn.flip();
155+
limit = Math.min(clientIn.limit(), dst.remaining());
156+
for (int i = 0; i < limit; i++) {
157+
dst.put(clientIn.get());
158+
amount++;
159+
}
160+
return amount;
161+
}
162+
return sc.read(dst);
163+
}
164+
165+
public boolean isConnected() {
166+
return sc.isConnected();
167+
}
168+
169+
public void close() throws IOException {
170+
if (SSL == 4) {
171+
sslEngine.closeOutbound();
172+
sslEngine.getSession().invalidate();
173+
clientOut.clear();
174+
sc.write(wrap(clientOut));
175+
sc.close();
176+
} else
177+
sc.close();
178+
}
179+
180+
public SelectableChannel configureBlocking(boolean b) throws IOException {
181+
return sc.configureBlocking(b);
182+
}
183+
184+
public boolean connect(SocketAddress remote) throws IOException {
185+
return sc.connect(remote);
186+
}
187+
188+
public boolean finishConnect() throws IOException {
189+
return sc.finishConnect();
190+
}
191+
192+
public Socket socket() {
193+
return sc.socket();
194+
}
195+
196+
public boolean isInboundDone() {
197+
return sslEngine.isInboundDone();
198+
}
199+
}

src/org/java_websocket/WebSocket.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public enum Role {
1515
CLIENT, SERVER
1616
}
1717

18-
public static int RCVBUF = 256;
18+
public static int RCVBUF = 16384;
1919

2020
public static/*final*/boolean DEBUG = false; // must be final in the future in order to take advantage of VM optimization
2121

0 commit comments

Comments
 (0)