Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

663
LINES

< > BotCompany Repo | #1009196 // NanoWebSocketServer

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (8664L/56K).

1  
// from https://android.googlesource.com/platform/external/nanohttpd/+/42ff2a9/websocket/src/main/java/fi/iki/elonen/
2  
3  
import java.nio.charset.*;
4  
import java.nio.*;
5  
6  
sbool webSocket_debug;
7  
8  
sclass WebSocketException extends IOException {
9  
    private WebSocketFrame.CloseCode code;
10  
    private String reason;
11  
    public WebSocketException(Exception cause) {
12  
        this(WebSocketFrame.CloseCode.InternalServerError, cause.toString(), cause);
13  
    }
14  
    public WebSocketException(WebSocketFrame.CloseCode code, String reason) {
15  
        this(code, reason, null);
16  
    }
17  
    public WebSocketException(WebSocketFrame.CloseCode code, String reason, Exception cause) {
18  
        super(code + ": " + reason, cause);
19  
        this.code = code;
20  
        this.reason = reason;
21  
    }
22  
    public WebSocketFrame.CloseCode getCode() {
23  
        return code;
24  
    }
25  
    public String getReason() {
26  
        return reason;
27  
    }
28  
}
29  
30  
sclass WebSocketFrame {
31  
    private OpCode opCode;
32  
    private boolean fin;
33  
    private byte[] maskingKey;
34  
    private byte[] payload;
35  
    private transient int _payloadLength;
36  
    private transient String _payloadString;
37  
    private WebSocketFrame(OpCode opCode, boolean fin) {
38  
        setOpCode(opCode);
39  
        setFin(fin);
40  
    }
41  
    public WebSocketFrame(OpCode opCode, boolean fin, byte[] payload, byte[] maskingKey) {
42  
        this(opCode, fin);
43  
        setMaskingKey(maskingKey);
44  
        setBinaryPayload(payload);
45  
    }
46  
    public WebSocketFrame(OpCode opCode, boolean fin, byte[] payload) {
47  
        this(opCode, fin, payload, null);
48  
    }
49  
    public WebSocketFrame(OpCode opCode, boolean fin, String payload, byte[] maskingKey) throws CharacterCodingException {
50  
        this(opCode, fin);
51  
        setMaskingKey(maskingKey);
52  
        setTextPayload(payload);
53  
    }
54  
    public WebSocketFrame(OpCode opCode, boolean fin, String payload) throws CharacterCodingException {
55  
        this(opCode, fin, payload, null);
56  
    }
57  
    public WebSocketFrame(WebSocketFrame clone) {
58  
        setOpCode(clone.getOpCode());
59  
        setFin(clone.isFin());
60  
        setBinaryPayload(clone.getBinaryPayload());
61  
        setMaskingKey(clone.getMaskingKey());
62  
    }
63  
    public WebSocketFrame(OpCode opCode, List<WebSocketFrame> fragments) throws WebSocketException {
64  
        setOpCode(opCode);
65  
        setFin(true);
66  
        long _payloadLength = 0;
67  
        for (WebSocketFrame inter : fragments) {
68  
            _payloadLength += inter.getBinaryPayload().length;
69  
        }
70  
        if (webSocket_debug)
71  
          print("Payload length with " + nFragments(fragments) + ": " + _payloadLength);
72  
        if (_payloadLength < 0 || _payloadLength > Integer.MAX_VALUE) {
73  
            throw new WebSocketException(WebSocketFrame.CloseCode.MessageTooBig, "Max frame length has been exceeded.");
74  
        }
75  
        this._payloadLength = (int) _payloadLength;
76  
        byte[] payload = new byte[this._payloadLength];
77  
        int offset = 0;
78  
        for (WebSocketFrame inter : fragments) {
79  
            System.arraycopy(inter.getBinaryPayload(), 0, payload, offset, inter.getBinaryPayload().length);
80  
            offset += inter.getBinaryPayload().length;
81  
        }
82  
        setBinaryPayload(payload);
83  
    }
84  
    // --------------------------------GETTERS---------------------------------
85  
    public OpCode getOpCode() {
86  
        return opCode;
87  
    }
88  
    public void setOpCode(OpCode opcode) {
89  
        this.opCode = opcode;
90  
    }
91  
    public boolean isFin() {
92  
        return fin;
93  
    }
94  
    public void setFin(boolean fin) {
95  
        this.fin = fin;
96  
    }
97  
    public boolean isMasked() {
98  
        return maskingKey != null && maskingKey.length == 4;
99  
    }
100  
    public byte[] getMaskingKey() {
101  
        return maskingKey;
102  
    }
103  
    public void setMaskingKey(byte[] maskingKey) {
104  
        if (maskingKey != null && maskingKey.length != 4) {
105  
            throw new IllegalArgumentException("MaskingKey " + Arrays.toString(maskingKey) + " hasn't length 4");
106  
        }
107  
        this.maskingKey = maskingKey;
108  
    }
109  
    public void setUnmasked() {
110  
        setMaskingKey(null);
111  
    }
112  
    public byte[] getBinaryPayload() {
113  
        return payload;
114  
    }
115  
    public void setBinaryPayload(byte[] payload) {
116  
        this.payload = payload;
117  
        this._payloadLength = payload.length;
118  
        this._payloadString = null;
119  
    }
120  
    public String getTextPayload() {
121  
        if (_payloadString == null) {
122  
            try {
123  
                _payloadString = binary2Text(getBinaryPayload());
124  
            } catch (CharacterCodingException e) {
125  
                throw new RuntimeException("Undetected CharacterCodingException", e);
126  
            }
127  
        }
128  
        return _payloadString;
129  
    }
130  
    public void setTextPayload(String payload) throws CharacterCodingException {
131  
        this.payload = text2Binary(payload);
132  
        //this._payloadLength = payload.length(); // buggy!
133  
        this._payloadLength = this.payload.length;
134  
        if (webSocket_debug) print("payload length: " + _payloadLength + ", string length: " + l(payload)); // XXX
135  
        this._payloadString = payload;
136  
    }
137  
    // --------------------------------SERIALIZATION---------------------------
138  
    public static WebSocketFrame read(InputStream in) throws IOException {
139  
        byte head = (byte) checkedRead(in.read());
140  
        boolean fin = ((head & 0x80) != 0);
141  
        OpCode opCode = OpCode.find((byte) (head & 0x0F));
142  
        if ((head & 0x70) != 0) {
143  
            throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "The reserved bits (" + Integer.toBinaryString(head & 0x70) + ") must be 0.");
144  
        }
145  
        if (opCode == null) {
146  
            throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Received frame with reserved/unknown opcode " + (head & 0x0F) + ".");
147  
        } else if (opCode.isControlFrame() && !fin) {
148  
            throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Fragmented control frame.");
149  
        }
150  
        WebSocketFrame frame = new WebSocketFrame(opCode, fin);
151  
        frame.readPayloadInfo(in);
152  
        frame.readPayload(in);
153  
        if (frame.getOpCode() == WebSocketFrame.OpCode.Close) {
154  
            return new WebSocketFrame.CloseFrame(frame);
155  
        } else {
156  
            return frame;
157  
        }
158  
    }
159  
    private static int checkedRead(int read) throws IOException {
160  
        if (read < 0) {
161  
            throw new EOFException();
162  
        }
163  
        //System.out.println(Integer.toBinaryString(read) + "/" + read + "/" + Integer.toHexString(read));
164  
        return read;
165  
    }
166  
    private void readPayloadInfo(InputStream in) throws IOException {
167  
        byte b = (byte) checkedRead(in.read());
168  
        boolean masked = ((b & 0x80) != 0);
169  
        _payloadLength = (byte) (0x7F & b);
170  
        if (_payloadLength == 126) {
171  
            // checkedRead must return int for this to work
172  
            _payloadLength = (checkedRead(in.read()) << 8 | checkedRead(in.read())) & 0xFFFF;
173  
            if (_payloadLength < 126) {
174  
                throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Invalid data frame 2byte length. (not using minimal length encoding)");
175  
            }
176  
        } else if (_payloadLength == 127) {
177  
            long _payloadLength = ((long) checkedRead(in.read())) << 56 |
178  
                    ((long) checkedRead(in.read())) << 48 |
179  
                    ((long) checkedRead(in.read())) << 40 |
180  
                    ((long) checkedRead(in.read())) << 32 |
181  
                    checkedRead(in.read()) << 24 | checkedRead(in.read()) << 16 | checkedRead(in.read()) << 8 | checkedRead(in.read());
182  
            if (_payloadLength < 65536) {
183  
                throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Invalid data frame 4byte length. (not using minimal length encoding)");
184  
            }
185  
            if (_payloadLength < 0 || _payloadLength > Integer.MAX_VALUE) {
186  
                throw new WebSocketException(WebSocketFrame.CloseCode.MessageTooBig, "Max frame length has been exceeded.");
187  
            }
188  
            this._payloadLength = (int) _payloadLength;
189  
        }
190  
        if (opCode.isControlFrame()) {
191  
            if (_payloadLength > 125) {
192  
                throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Control frame with payload length > 125 bytes.");
193  
            }
194  
            if (opCode == OpCode.Close && _payloadLength == 1) {
195  
                throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Received close frame with payload len 1.");
196  
            }
197  
        }
198  
        if (masked) {
199  
            maskingKey = new byte[4];
200  
            int read = 0;
201  
            while (read < maskingKey.length) {
202  
                read += checkedRead(in.read(maskingKey, read, maskingKey.length - read));
203  
            }
204  
        }
205  
    }
206  
    private void readPayload(InputStream in) throws IOException {
207  
        payload = new byte[_payloadLength];
208  
        int read = 0;
209  
        while (read < _payloadLength) {
210  
            read += checkedRead(in.read(payload, read, _payloadLength - read));
211  
        }
212  
        if (isMasked()) {
213  
            for (int i = 0; i < payload.length; i++) {
214  
                payload[i] ^= maskingKey[i % 4];
215  
            }
216  
        }
217  
        //Test for Unicode errors
218  
        if (getOpCode() == WebSocketFrame.OpCode.Text) {
219  
            _payloadString = binary2Text(getBinaryPayload());
220  
        }
221  
    }
222  
    public void write(OutputStream out) throws IOException {
223  
        byte header = 0;
224  
        if (fin) {
225  
            header |= 0x80;
226  
        }
227  
        header |= opCode.getValue() & 0x0F;
228  
        out.write(header);
229  
        _payloadLength = getBinaryPayload().length;
230  
        if (_payloadLength <= 125) {
231  
          if (webSocket_debug)
232  
            print("Sending short payload: " + _payloadLength);
233  
          out.write(isMasked() ? 0x80 | (byte) _payloadLength : (byte) _payloadLength);
234  
        } else if (_payloadLength <= 0xFFFF) {
235  
            out.write(isMasked() ? 0xFE : 126);
236  
            out.write(_payloadLength >>> 8);
237  
            out.write(_payloadLength);
238  
        } else {
239  
            out.write(isMasked() ? 0xFF : 127);
240  
            out.write(_payloadLength >>> 56 & 0); //integer only contains 31 bit
241  
            out.write(_payloadLength >>> 48 & 0);
242  
            out.write(_payloadLength >>> 40 & 0);
243  
            out.write(_payloadLength >>> 32 & 0);
244  
            out.write(_payloadLength >>> 24);
245  
            out.write(_payloadLength >>> 16);
246  
            out.write(_payloadLength >>> 8);
247  
            out.write(_payloadLength);
248  
        }
249  
        if (isMasked()) {
250  
            out.write(maskingKey);
251  
            for (int i = 0; i < _payloadLength; i++) {
252  
                out.write(getBinaryPayload()[i] ^ maskingKey[i % 4]);
253  
            }
254  
        } else {
255  
            out.write(getBinaryPayload());
256  
        }
257  
        out.flush();
258  
    }
259  
    // --------------------------------ENCODING--------------------------------
260  
    public static final Charset TEXT_CHARSET = Charset.forName("UTF-8");
261  
    public static String binary2Text(byte[] payload) throws CharacterCodingException {
262  
        ret new S(payload, TEXT_CHARSET);
263  
    }
264  
    public static String binary2Text(byte[] payload, int offset, int length) throws CharacterCodingException {
265  
      ret new S(payload, offset, length, TEXT_CHARSET);
266  
    }
267  
    public static byte[] text2Binary(String payload) throws CharacterCodingException {
268  
      ret payload.getBytes(TEXT_CHARSET);
269  
    }
270  
    @Override
271  
    public String toString() {
272  
        final StringBuilder sb = new StringBuilder("WS[");
273  
        sb.append(getOpCode());
274  
        sb.append(", ").append(isFin() ? "fin" : "inter");
275  
        sb.append(", ").append(isMasked() ? "masked" : "unmasked");
276  
        sb.append(", ").append(payloadToString());
277  
        sb.append(']');
278  
        return sb.toString();
279  
    }
280  
    protected String payloadToString() {
281  
        if (payload == null) return "null";
282  
        else {
283  
            final StringBuilder sb = new StringBuilder();
284  
            sb.append('[').append(payload.length).append("b] ");
285  
            if (getOpCode() == WebSocketFrame.OpCode.Text) {
286  
                String text = getTextPayload();
287  
                if (text.length() > 100)
288  
                    sb.append(text.substring(0, 100)).append("...");
289  
                else
290  
                    sb.append(text);
291  
            } else {
292  
                sb.append("0x");
293  
                for (int i = 0; i < Math.min(payload.length, 50); ++i)
294  
                    sb.append(Integer.toHexString((int) payload[i] & 0xFF));
295  
                if (payload.length > 50)
296  
                    sb.append("...");
297  
            }
298  
            return sb.toString();
299  
        }
300  
    }
301  
    // --------------------------------CONSTANTS-------------------------------
302  
    public static enum OpCode {
303  
        Continuation(0), Text(1), Binary(2), Close(8), Ping(9), Pong(10);
304  
        private final byte code;
305  
        private OpCode(int code) {
306  
            this.code = (byte) code;
307  
        }
308  
        public byte getValue() {
309  
            return code;
310  
        }
311  
        public boolean isControlFrame() {
312  
            return this == Close || this == Ping || this == Pong;
313  
        }
314  
        public static OpCode find(byte value) {
315  
            for (OpCode opcode : values()) {
316  
                if (opcode.getValue() == value) {
317  
                    return opcode;
318  
                }
319  
            }
320  
            return null;
321  
        }
322  
    }
323  
    
324  
    public static enum CloseCode {
325  
        NormalClosure(1000), GoingAway(1001), ProtocolError(1002), UnsupportedData(1003), NoStatusRcvd(1005),
326  
        AbnormalClosure(1006), InvalidFramePayloadData(1007), PolicyViolation(1008), MessageTooBig(1009),
327  
        MandatoryExt(1010), InternalServerError(1011), TLSHandshake(1015);
328  
        private final int code;
329  
        private CloseCode(int code) {
330  
            this.code = code;
331  
        }
332  
        public int getValue() {
333  
            return code;
334  
        }
335  
        public static WebSocketFrame.CloseCode find(int value) {
336  
            for (WebSocketFrame.CloseCode code : values()) {
337  
                if (code.getValue() == value) {
338  
                    return code;
339  
                }
340  
            }
341  
            return null;
342  
        }
343  
    }
344  
    // ------------------------------------------------------------------------
345  
 public static class CloseFrame extends WebSocketFrame {
346  
        private CloseCode _closeCode;
347  
        private String _closeReason;
348  
        private CloseFrame(WebSocketFrame wrap) throws CharacterCodingException {
349  
            super(wrap);
350  
            assert wrap.getOpCode() == OpCode.Close;
351  
            if (wrap.getBinaryPayload().length >= 2) {
352  
                _closeCode = CloseCode.find((wrap.getBinaryPayload()[0] & 0xFF) << 8 |
353  
                        (wrap.getBinaryPayload()[1] & 0xFF));
354  
                _closeReason = binary2Text(getBinaryPayload(), 2, getBinaryPayload().length - 2);
355  
            }
356  
        }
357  
        public CloseFrame(CloseCode code, String closeReason) throws CharacterCodingException {
358  
            super(OpCode.Close, true, generatePayload(code, closeReason));
359  
        }
360  
        private static byte[] generatePayload(CloseCode code, String closeReason) throws CharacterCodingException {
361  
            if (code != null) {
362  
                byte[] reasonBytes = text2Binary(closeReason);
363  
                byte[] payload = new byte[reasonBytes.length + 2];
364  
                payload[0] = (byte) ((code.getValue() >> 8) & 0xFF);
365  
                payload[1] = (byte) ((code.getValue()) & 0xFF);
366  
                System.arraycopy(reasonBytes, 0, payload, 2, reasonBytes.length);
367  
                return payload;
368  
            } else {
369  
                return new byte[0];
370  
            }
371  
        }
372  
        protected String payloadToString() {
373  
            return (_closeCode != null ? _closeCode : "UnknownCloseCode[" + _closeCode + "]") + (_closeReason != null && !_closeReason.isEmpty() ? ": " + _closeReason : "");
374  
        }
375  
        public CloseCode getCloseCode() {
376  
            return _closeCode;
377  
        }
378  
        public String getCloseReason() {
379  
            return _closeReason;
380  
        }
381  
    }
382  
}
383  
384  
sclass WebSocket implements AutoCloseable {
385  
  volatile Object userObject;
386  
  
387  
    protected final InputStream in;
388  
    protected /*final*/ OutputStream out;
389  
    protected WebSocketFrame.OpCode continuousOpCode = null;
390  
    protected List<WebSocketFrame> continuousFrames = new LinkedList<WebSocketFrame>();
391  
    protected State state = State.UNCONNECTED;
392  
    public static enum State {
393  
        UNCONNECTED, CONNECTING, OPEN, CLOSING, CLOSED
394  
    }
395  
    protected final NanoHTTPD.IHTTPSession handshakeRequest;
396  
    //protected final NanoHTTPD.Response handshakeResponse = new NanoHTTPD.Response(NanoHTTPD.Status.SWITCH_PROTOCOL, null, (InputStream) null) {
397  
    protected final NanoHTTPD.Response handshakeResponse = new NanoHTTPD.Response(NanoHTTPD.Status.SWITCH_PROTOCOL, null, (InputStream) null, -1) {
398  
        @Override
399  
        protected void send(OutputStream out) {
400  
            WebSocket.this.out = out;
401  
            state = State.CONNECTING;
402  
            super.send(out);
403  
            state = State.OPEN;
404  
            onOpen();
405  
            readWebsocket();
406  
        }
407  
    };
408  
    public WebSocket(NanoHTTPD.IHTTPSession handshakeRequest) {
409  
        this.handshakeRequest = handshakeRequest;
410  
        this.in = handshakeRequest.getInputStream();
411  
        handshakeResponse.addHeader(NanoWebSocketServer.HEADER_UPGRADE, NanoWebSocketServer.HEADER_UPGRADE_VALUE);
412  
        handshakeResponse.addHeader(NanoWebSocketServer.HEADER_CONNECTION, NanoWebSocketServer.HEADER_CONNECTION_VALUE);
413  
    }
414  
    // --------------------------------IO--------------------------------------
415  
    protected void readWebsocket() {
416  
        try {
417  
            while (state == State.OPEN) {
418  
                handleWebsocketFrame(WebSocketFrame.read(in));
419  
            }
420  
        } catch (CharacterCodingException e) {
421  
            onException(e);
422  
            doClose(WebSocketFrame.CloseCode.InvalidFramePayloadData, e.toString(), false);
423  
        } catch (IOException e) {
424  
            onException(e);
425  
            if (e instanceof WebSocketException) {
426  
                doClose(((WebSocketException) e).getCode(), ((WebSocketException) e).getReason(), false);
427  
            }
428  
        } finally {
429  
            doClose(WebSocketFrame.CloseCode.InternalServerError, "Handler terminated without closing the connection.", false);
430  
        }
431  
    }
432  
    protected void handleWebsocketFrame(WebSocketFrame frame) throws IOException {
433  
        if (frame.getOpCode() == WebSocketFrame.OpCode.Close) {
434  
            handleCloseFrame(frame);
435  
        } else if (frame.getOpCode() == WebSocketFrame.OpCode.Ping) {
436  
            sendFrame(new WebSocketFrame(WebSocketFrame.OpCode.Pong, true, frame.getBinaryPayload()));
437  
        } else if (frame.getOpCode() == WebSocketFrame.OpCode.Pong) {
438  
            onPong(frame);
439  
        } else if (!frame.isFin() || frame.getOpCode() == WebSocketFrame.OpCode.Continuation) {
440  
            handleFrameFragment(frame);
441  
        } else if (continuousOpCode != null) {
442  
            throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Continuous frame sequence not completed.");
443  
        } else if (frame.getOpCode() == WebSocketFrame.OpCode.Text || frame.getOpCode() == WebSocketFrame.OpCode.Binary) {
444  
            onMessage(frame);
445  
        } else {
446  
            throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Non control or continuous frame expected.");
447  
        }
448  
    }
449  
    protected void handleCloseFrame(WebSocketFrame frame) throws IOException {
450  
        WebSocketFrame.CloseCode code = WebSocketFrame.CloseCode.NormalClosure;
451  
        String reason = "";
452  
        if (frame instanceof WebSocketFrame.CloseFrame) {
453  
            code = ((WebSocketFrame.CloseFrame) frame).getCloseCode();
454  
            reason = ((WebSocketFrame.CloseFrame) frame).getCloseReason();
455  
        }
456  
        if (state == State.CLOSING) {
457  
            //Answer for my requested close
458  
            doClose(code, reason, false);
459  
        } else {
460  
            //Answer close request from other endpoint and close self
461  
            State oldState = state;
462  
            state = State.CLOSING;
463  
            if (oldState == State.OPEN) try {
464  
                sendFrame(new WebSocketFrame.CloseFrame(code, reason));
465  
            } catch {}
466  
            doClose(code, reason, true);
467  
        }
468  
    }
469  
    protected void handleFrameFragment(WebSocketFrame frame) throws IOException {
470  
        if (frame.getOpCode() != WebSocketFrame.OpCode.Continuation) {
471  
            //First
472  
            if (continuousOpCode != null) {
473  
                throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Previous continuous frame sequence not completed.");
474  
            }
475  
            continuousOpCode = frame.getOpCode();
476  
            continuousFrames.clear();
477  
            continuousFrames.add(frame);
478  
        } else if (frame.isFin()) {
479  
            //Last
480  
            if (continuousOpCode == null) {
481  
                throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Continuous frame sequence was not started.");
482  
            }
483  
            onMessage(new WebSocketFrame(continuousOpCode, continuousFrames));
484  
            continuousOpCode = null;
485  
            continuousFrames.clear();
486  
        } else if (continuousOpCode == null) {
487  
            //Unexpected
488  
            throw new WebSocketException(WebSocketFrame.CloseCode.ProtocolError, "Continuous frame sequence was not started.");
489  
        } else {
490  
            //Intermediate
491  
            continuousFrames.add(frame);
492  
        }
493  
    }
494  
    public synchronized void sendFrame(WebSocketFrame frame) throws IOException {
495  
        frame.write(out);
496  
    }
497  
    // --------------------------------Close-----------------------------------
498  
    
499  
    // deliberate closing by server, e.g. because of module reload
500  
    public void close() {
501  
      doClose(WebSocketFrame.CloseCode.GoingAway, "Internal closing", false);
502  
    }
503  
    
504  
    protected void doClose(WebSocketFrame.CloseCode code, String reason, boolean initiatedByRemote) {
505  
        if (state == State.CLOSED) {
506  
            return;
507  
        }
508  
        if (in != null) {
509  
            try {
510  
                in.close();
511  
            } catch (IOException e) {
512  
                e.printStackTrace();
513  
            }
514  
        }
515  
        if (out != null) {
516  
            try {
517  
                out.close();
518  
            } catch (IOException e) {
519  
                e.printStackTrace();
520  
            }
521  
        }
522  
        state = State.CLOSED;
523  
        onClose(code, reason, initiatedByRemote);
524  
    }
525  
    // --------------------------------Listener--------------------------------
526  
    protected void onPong(WebSocketFrame pongFrame) { print("WebSocket pong"); }
527  
    swappable void onMessage(WebSocketFrame messageFrame) { print("WebSocket msg: " + messageFrame.getTextPayload()); }
528  
    swappable void onOpen() {}
529  
    protected void onClose(WebSocketFrame.CloseCode code, String reason, boolean initiatedByRemote) { print("WebSocket close"); onClose(); }
530  
    swappable void onClose() {}
531  
    protected void onException(IOException e) {
532  
      printExceptionShort(e); //printStackTrace(e);
533  
    }
534  
    // --------------------------------Public Facade---------------------------
535  
    public void ping(byte[] payload) throws IOException {
536  
        sendFrame(new WebSocketFrame(WebSocketFrame.OpCode.Ping, true, payload));
537  
    }
538  
    public void send(byte[] payload) throws IOException {
539  
        sendFrame(new WebSocketFrame(WebSocketFrame.OpCode.Binary, true, payload));
540  
    }
541  
    public void send(String payload) throws IOException {
542  
        sendFrame(new WebSocketFrame(WebSocketFrame.OpCode.Text, true, payload));
543  
    }
544  
    public void close(WebSocketFrame.CloseCode code, String reason) throws IOException {
545  
        State oldState = state;
546  
        state = State.CLOSING;
547  
        if (oldState == State.OPEN) {
548  
            sendFrame(new WebSocketFrame.CloseFrame(code, reason));
549  
        } else {
550  
            doClose(code, reason, false);
551  
        }
552  
    }
553  
    // --------------------------------Getters---------------------------------
554  
    public NanoHTTPD.IHTTPSession getHandshakeRequest() {
555  
        return handshakeRequest;
556  
    }
557  
    public NanoHTTPD.Response getHandshakeResponse() {
558  
        return handshakeResponse;
559  
    }
560  
    
561  
    // convenience methods
562  
    S getUri() { ret getHandshakeRequest().getUri(); }
563  
    SS getParms() { ret getHandshakeRequest().getParms(); }
564  
    O getUserObject() { ret userObject; }
565  
    void setUserObject(O o) { userObject = o; }
566  
}
567  
568  
sinterface WebSocketFactory {
569  
  WebSocket openWebSocket(NanoHTTPD.IHTTPSession handshake);
570  
}
571  
572  
sclass NanoWebSocketServer extends NanoHTTPD implements WebSocketFactory {
573  
    public static final String HEADER_UPGRADE = "upgrade";
574  
    public static final String HEADER_UPGRADE_VALUE = "websocket";
575  
    public static final String HEADER_CONNECTION = "connection";
576  
    public static final String HEADER_CONNECTION_VALUE = "Upgrade";
577  
    public static final String HEADER_WEBSOCKET_VERSION = "sec-websocket-version";
578  
    public static final String HEADER_WEBSOCKET_VERSION_VALUE = "13";
579  
    public static final String HEADER_WEBSOCKET_KEY = "sec-websocket-key";
580  
    public static final String HEADER_WEBSOCKET_ACCEPT = "sec-websocket-accept";
581  
    public static final String HEADER_WEBSOCKET_PROTOCOL = "sec-websocket-protocol";
582  
    public final static String WEBSOCKET_KEY_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
583  
    
584  
    WebSocketFactory webSocketFactory;
585  
    
586  
    public NanoWebSocketServer(int port) {
587  
        super(port);
588  
        webSocketFactory = null;
589  
    }
590  
    
591  
    public NanoWebSocketServer(String hostname, int port) {
592  
        super(hostname, port);
593  
        webSocketFactory = null;
594  
    }
595  
    
596  
    public NanoWebSocketServer(int port, WebSocketFactory webSocketFactory) {
597  
        super(port);
598  
        this.webSocketFactory = webSocketFactory;
599  
    }
600  
    
601  
    public NanoWebSocketServer(String hostname, int port,WebSocketFactory webSocketFactory) {
602  
        super(hostname, port);
603  
        this.webSocketFactory = webSocketFactory;
604  
    }
605  
    
606  
    @Override
607  
    public Response serve_2(IHTTPSession session) {
608  
        SS headers = session.getHeaders();
609  
        
610  
        if (isWebsocketRequested(session)) {
611  
            if (!HEADER_UPGRADE_VALUE.equalsIgnoreCase(headers.get(HEADER_UPGRADE))
612  
                    || !isWebSocketConnectionHeader(session.getHeaders())) {
613  
                return newFixedLengthResponse(Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Invalid Websocket handshake");
614  
            }
615  
            if (!HEADER_WEBSOCKET_VERSION_VALUE.equalsIgnoreCase(headers.get(HEADER_WEBSOCKET_VERSION))) {
616  
                return newFixedLengthResponse(Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Invalid Websocket-Version " + headers.get(HEADER_WEBSOCKET_VERSION));
617  
            }
618  
            if (!headers.containsKey(HEADER_WEBSOCKET_KEY)) {
619  
                return newFixedLengthResponse(Status.BAD_REQUEST, NanoHTTPD.MIME_PLAINTEXT, "Missing Websocket-Key");
620  
            }
621  
            WebSocket webSocket = openWebSocket(session);
622  
            try {
623  
                webSocket.getHandshakeResponse().addHeader(HEADER_WEBSOCKET_ACCEPT, makeAcceptKey(headers.get(HEADER_WEBSOCKET_KEY)));
624  
            } catch (NoSuchAlgorithmException e) {
625  
                return newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "The SHA-1 Algorithm required for websockets is not available on the server.");
626  
            }
627  
            if (headers.containsKey(HEADER_WEBSOCKET_PROTOCOL)) {
628  
                webSocket.getHandshakeResponse().addHeader(HEADER_WEBSOCKET_PROTOCOL, headers.get(HEADER_WEBSOCKET_PROTOCOL).split(",")[0]);
629  
            }
630  
            return webSocket.getHandshakeResponse();
631  
        } else {
632  
            return super.serve_2(session);
633  
        }
634  
    }
635  
    
636  
    public WebSocket openWebSocket(IHTTPSession handshake) {
637  
        if (webSocketFactory == null) {
638  
            throw new Error("You must either override this method or supply a WebSocketFactory in the constructor");
639  
        }
640  
        return webSocketFactory.openWebSocket(handshake);
641  
    }
642  
    
643  
    protected boolean isWebsocketRequested(IHTTPSession session) {
644  
        Map<String, String> headers = session.getHeaders();
645  
        String upgrade = headers.get(HEADER_UPGRADE);
646  
        boolean isCorrectConnection = isWebSocketConnectionHeader(headers);
647  
        boolean isUpgrade = HEADER_UPGRADE_VALUE.equalsIgnoreCase(upgrade);
648  
        return (isUpgrade && isCorrectConnection);
649  
    }
650  
    
651  
    private boolean isWebSocketConnectionHeader(Map<String, String> headers) {
652  
        String connection = headers.get(HEADER_CONNECTION);
653  
        return (connection != null && connection.toLowerCase().contains(HEADER_CONNECTION_VALUE.toLowerCase()));
654  
    }
655  
    
656  
    public static String makeAcceptKey(String key) throws NoSuchAlgorithmException {
657  
        MessageDigest md = MessageDigest.getInstance("SHA-1");
658  
        String text = key + WEBSOCKET_KEY_MAGIC;
659  
        md.update(text.getBytes(), 0, text.length());
660  
        byte[] sha1hash = md.digest();
661  
        return base64encode(sha1hash);
662  
    }
663  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1009196
Snippet name: NanoWebSocketServer
Eternal ID of this version: #1009196/47
Text MD5: e49cbdba83be65ff71a980e7ff88371e
Transpilation MD5: f90833c5bfe7c6d69f53ed3329ff3995
Author: stefan
Category: javax / networking
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-03 21:27:10
Source code size: 29611 bytes / 663 lines
Pitched / IR pitched: No / No
Views / Downloads: 646 / 1571
Version history: 46 change(s)
Referenced in: [show references]