View | Details | Raw Unified | Return to bug 51181
Collapse All | Expand All

(-)test/org/apache/catalina/websocket/TestWebSocket.java (-7 / +21 lines)
Lines 31-36 Link Here
31
31
32
import static org.junit.Assert.assertEquals;
32
import static org.junit.Assert.assertEquals;
33
import static org.junit.Assert.assertTrue;
33
import static org.junit.Assert.assertTrue;
34
35
import org.junit.After;
36
import org.junit.Before;
34
import org.junit.Test;
37
import org.junit.Test;
35
38
36
import org.apache.catalina.startup.Tomcat;
39
import org.apache.catalina.startup.Tomcat;
Lines 44-55 Link Here
44
47
45
    private static final String CRLF = "\r\n";
48
    private static final String CRLF = "\r\n";
46
49
50
    private Socket socket;
47
    private OutputStream os;
51
    private OutputStream os;
48
    private InputStream is;
52
    private InputStream is;
49
    private boolean isContinuation = false;
53
    private boolean isContinuation = false;
50
54
51
    @Test
55
    @Override
52
    public void testSimple() throws Exception {
56
    @Before
57
    public void setUp() throws Exception {
58
        super.setUp();
59
53
        Tomcat tomcat = getTomcatInstance();
60
        Tomcat tomcat = getTomcatInstance();
54
        File appDir = new File(getBuildDirectory(), "webapps/examples");
61
        File appDir = new File(getBuildDirectory(), "webapps/examples");
55
        tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
62
        tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
Lines 59-65 Link Here
59
        // Open the socket
66
        // Open the socket
60
        final String encoding = "ISO-8859-1";
67
        final String encoding = "ISO-8859-1";
61
        SocketAddress addr = new InetSocketAddress("localhost", getPort());
68
        SocketAddress addr = new InetSocketAddress("localhost", getPort());
62
        Socket socket = new Socket();
69
        socket = new Socket();
63
        socket.setSoTimeout(10000);
70
        socket.setSoTimeout(10000);
64
        socket.connect(addr, 10000);
71
        socket.connect(addr, 10000);
65
        os = socket.getOutputStream();
72
        os = socket.getOutputStream();
Lines 87-101 Link Here
87
        while (!responseHeaderLine.equals("")) {
94
        while (!responseHeaderLine.equals("")) {
88
            responseHeaderLine = reader.readLine();
95
            responseHeaderLine = reader.readLine();
89
        }
96
        }
97
    }
90
98
91
        // Now we can do WebSocket
99
    @Override
100
    @After
101
    public void tearDown() throws Exception {
102
        super.tearDown();
103
        // Finished with the socket
104
        socket.close();
105
    }
106
107
    @Test
108
    public void testFragmentation() throws IOException {
92
        sendMessage("foo", false);
109
        sendMessage("foo", false);
93
        sendMessage("foo", true);
110
        sendMessage("foo", true);
94
111
95
        assertEquals("foofoo",readMessage());
112
        assertEquals("foofoo",readMessage());
96
97
        // Finished with the socket
98
        socket.close();
99
    }
113
    }
100
114
101
    private void sendMessage(String message, boolean finalFragment)
115
    private void sendMessage(String message, boolean finalFragment)
(-)java/org/apache/catalina/websocket/LocalStrings.properties (-1 / +1 lines)
Lines 17-23 Link Here
17
frame.invalidUtf8=A sequence of bytes was received that did not represent valid UTF-8
17
frame.invalidUtf8=A sequence of bytes was received that did not represent valid UTF-8
18
frame.notMasked=The client frame was not masked but all client frames must be masked
18
frame.notMasked=The client frame was not masked but all client frames must be masked
19
19
20
is.notContinutation=A frame with the OpCode [{0}] was recieved when a continuation frame was expected
20
is.notContinuation=A frame with the OpCode [{0}] was received when a continuation frame was expected
21
is.unknownOpCode=A frame with the unrecognized OpCode [{0}] was received
21
is.unknownOpCode=A frame with the unrecognized OpCode [{0}] was received
22
22
23
message.bufferTooSmall=The buffer is not big enough to contain the message currently being processed
23
message.bufferTooSmall=The buffer is not big enough to contain the message currently being processed
(-)java/org/apache/catalina/websocket/WsInputStream.java (-2 / +3 lines)
Lines 17-22 Link Here
17
package org.apache.catalina.websocket;
17
package org.apache.catalina.websocket;
18
18
19
import java.io.IOException;
19
import java.io.IOException;
20
import java.io.InputStream;
20
21
21
import org.apache.coyote.http11.upgrade.UpgradeProcessor;
22
import org.apache.coyote.http11.upgrade.UpgradeProcessor;
22
import org.apache.tomcat.util.res.StringManager;
23
import org.apache.tomcat.util.res.StringManager;
Lines 27-33 Link Here
27
 * makes the number of bytes declared in the payload length available for
28
 * makes the number of bytes declared in the payload length available for
28
 * reading even if more bytes are available from the socket.
29
 * reading even if more bytes are available from the socket.
29
 */
30
 */
30
public class WsInputStream extends java.io.InputStream {
31
public class WsInputStream extends InputStream {
31
32
32
    private static final StringManager sm =
33
    private static final StringManager sm =
33
            StringManager.getManager(Constants.Package);
34
            StringManager.getManager(Constants.Package);
Lines 147-153 Link Here
147
                nextFrame(true);
148
                nextFrame(true);
148
            }
149
            }
149
            if (frame.getOpCode() != Constants.OPCODE_CONTINUATION) {
150
            if (frame.getOpCode() != Constants.OPCODE_CONTINUATION) {
150
                error = sm.getString("is.notContinutation",
151
                error = sm.getString("is.notContinuation",
151
                        Byte.valueOf(frame.getOpCode()));
152
                        Byte.valueOf(frame.getOpCode()));
152
                throw new IOException(error);
153
                throw new IOException(error);
153
            }
154
            }
(-)java/org/apache/catalina/websocket/StreamInbound.java (-5 / +7 lines)
Lines 28-33 Link Here
28
import org.apache.coyote.http11.upgrade.UpgradeProcessor;
28
import org.apache.coyote.http11.upgrade.UpgradeProcessor;
29
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
29
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
30
30
31
import static org.apache.catalina.websocket.Constants.*;
32
31
/**
33
/**
32
 * Base implementation of the class used to process WebSocket connections based
34
 * Base implementation of the class used to process WebSocket connections based
33
 * on streams. Applications should extend this class to provide application
35
 * on streams. Applications should extend this class to provide application
Lines 72-78 Link Here
72
            try {
74
            try {
73
                // TODO User defined extensions may define values for rsv
75
                // TODO User defined extensions may define values for rsv
74
                if (frame.getRsv() > 0) {
76
                if (frame.getRsv() > 0) {
75
                    getWsOutbound().close(1002, null);
77
                    getWsOutbound().close(STATUS_PROTOCOL_ERROR, null);
76
                    return SocketState.CLOSED;
78
                    return SocketState.CLOSED;
77
                }
79
                }
78
80
Lines 93-113 Link Here
93
                    // NO-OP
95
                    // NO-OP
94
                } else {
96
                } else {
95
                    // Unknown OpCode
97
                    // Unknown OpCode
96
                    getWsOutbound().close(1002, null);
98
                    getWsOutbound().close(STATUS_PROTOCOL_ERROR, null);
97
                    return SocketState.CLOSED;
99
                    return SocketState.CLOSED;
98
                }
100
                }
99
            } catch (MalformedInputException mie) {
101
            } catch (MalformedInputException mie) {
100
                // Invalid UTF-8
102
                // Invalid UTF-8
101
                getWsOutbound().close(1007, null);
103
                getWsOutbound().close(STATUS_BAD_DATA, null);
102
                return SocketState.CLOSED;
104
                return SocketState.CLOSED;
103
            } catch (UnmappableCharacterException uce) {
105
            } catch (UnmappableCharacterException uce) {
104
                // Invalid UTF-8
106
                // Invalid UTF-8
105
                getWsOutbound().close(1007, null);
107
                getWsOutbound().close(STATUS_BAD_DATA, null);
106
                return SocketState.CLOSED;
108
                return SocketState.CLOSED;
107
            } catch (IOException ioe) {
109
            } catch (IOException ioe) {
108
                // Given something must have gone to reach this point, this might
110
                // Given something must have gone to reach this point, this might
109
                // not work but try it anyway.
111
                // not work but try it anyway.
110
                getWsOutbound().close(1002, null);
112
                getWsOutbound().close(STATUS_PROTOCOL_ERROR, null);
111
                return SocketState.CLOSED;
113
                return SocketState.CLOSED;
112
            }
114
            }
113
            frame = wsIs.nextFrame(false);
115
            frame = wsIs.nextFrame(false);
(-)java/org/apache/catalina/websocket/Constants.java (+83 lines)
Lines 29-32 Link Here
29
    public static final byte OPCODE_CLOSE = 0x08;
29
    public static final byte OPCODE_CLOSE = 0x08;
30
    public static final byte OPCODE_PING = 0x09;
30
    public static final byte OPCODE_PING = 0x09;
31
    public static final byte OPCODE_PONG = 0x0A;
31
    public static final byte OPCODE_PONG = 0x0A;
32
33
    // Status Codes
34
35
    /*
36
     * 1000 indicates a normal closure, meaning whatever purpose the
37
     * connection was established for has been fulfilled.
38
     */
39
    public static final int STATUS_CLOSE_NORMAL = 1000;
40
41
    /*
42
     * 1001 indicates that an endpoint is "going away", such as a server
43
     * going down, or a browser having navigated away from a page.
44
     */
45
    public static final int STATUS_SHUTDOWN = 1001;
46
47
    /*
48
     * 1002 indicates that an endpoint is terminating the connection due
49
     * to a protocol error.
50
     */
51
    public static final int STATUS_PROTOCOL_ERROR = 1002;
52
53
    /*
54
     * 1003 indicates that an endpoint is terminating the connection
55
     * because it has received a type of data it cannot accept (e.g. an
56
     * endpoint that understands only text data MAY send this if it
57
     * receives a binary message).
58
     */
59
    public static final int STATUS_UNEXPECTED_DATA_TYPE = 1003;
60
61
    // 1004 is reserved. The specific meaning might be defined in the future.
62
63
    /*
64
     * 1005 is a reserved value and MUST NOT be set as a status code in a
65
     * Close control frame by an endpoint.  It is designated for use in
66
     * applications expecting a status code to indicate that no status
67
     * code was actually present.
68
     */
69
    public static final int STATUS_CODE_MISSING = 1005;
70
71
    /*
72
     * 1006 is a reserved value and MUST NOT be set as a status code in a
73
     * Close control frame by an endpoint.  It is designated for use in
74
     * applications expecting a status code to indicate that the
75
     * connection was closed abnormally, e.g. without sending or
76
     * receiving a Close control frame.
77
     */
78
    public static final int STATUS_CLOSED_UNEXPECTEDLY = 1006;
79
80
    /*
81
     * 1007 indicates that an endpoint is terminating the connection
82
     * because it has received data within a message that was not
83
     * consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
84
     * data within a text message).
85
     */
86
    public static final int STATUS_BAD_DATA = 1007;
87
88
    /*
89
     * 1008 indicates that an endpoint is terminating the connection
90
     * because it has received a message that violates its policy.  This
91
     * is a generic status code that can be returned when there is no
92
     * other more suitable status code (e.g. 1003 or 1009), or if there
93
     * is a need to hide specific details about the policy.
94
     */
95
    public static final int STATUS_POLICY_VIOLATION = 1008;
96
97
    /*
98
     * 1009 indicates that an endpoint is terminating the connection
99
     * because it has received a message which is too big for it to
100
     * process.
101
     */
102
    public static final int STATUS_MESSAGE_TOO_LARGE = 1009;
103
104
    /*
105
     * 1010 indicates that an endpoint (client) is terminating the
106
     * connection because it has expected the server to negotiate one or
107
     * more extension, but the server didn't return them in the response
108
     * message of the WebSocket handshake.  The list of extensions which
109
     * are needed SHOULD appear in the /reason/ part of the Close frame.
110
     * Note that this status code is not used by the server, because it
111
     * can fail the WebSocket handshake instead.
112
     */
113
    public static final int STATUS_REQUIRED_EXTENSION = 1010;
114
32
}
115
}
(-)java/org/apache/catalina/websocket/WsOutbound.java (-6 / +8 lines)
Lines 26-31 Link Here
26
import org.apache.tomcat.util.buf.B2CConverter;
26
import org.apache.tomcat.util.buf.B2CConverter;
27
import org.apache.tomcat.util.res.StringManager;
27
import org.apache.tomcat.util.res.StringManager;
28
28
29
import static org.apache.catalina.websocket.Constants.*;
30
29
/**
31
/**
30
 * Provides the means to write WebSocket messages to the client.
32
 * Provides the means to write WebSocket messages to the client.
31
 */
33
 */
Lines 88-94 Link Here
88
     * message started. If the buffer for textual data is full, the buffer will
90
     * message started. If the buffer for textual data is full, the buffer will
89
     * be flushed and a new textual continuation fragment started.
91
     * be flushed and a new textual continuation fragment started.
90
     *
92
     *
91
     * @param b The character to send to the client.
93
     * @param c The character to send to the client.
92
     *
94
     *
93
     * @throws IOException  If a flush is required and an error occurs writing
95
     * @throws IOException  If a flush is required and an error occurs writing
94
     *                      the WebSocket frame to the client
96
     *                      the WebSocket frame to the client
Lines 141-147 Link Here
141
     * a WebSocket text message as a single frame with the provided buffer as
143
     * a WebSocket text message as a single frame with the provided buffer as
142
     * the payload of the message.
144
     * the payload of the message.
143
     *
145
     *
144
     * @param msgBb The buffer containing the payload
146
     * @param msgCb The buffer containing the payload
145
     *
147
     *
146
     * @throws IOException  If an error occurs writing to the client
148
     * @throws IOException  If an error occurs writing to the client
147
     */
149
     */
Lines 209-215 Link Here
209
                close(status, frame.getPayLoad());
211
                close(status, frame.getPayLoad());
210
            } else {
212
            } else {
211
                // Invalid close code
213
                // Invalid close code
212
                close(1002, null);
214
                close(STATUS_PROTOCOL_ERROR, null);
213
            }
215
            }
214
        } else {
216
        } else {
215
            // No status
217
            // No status
Lines 220-228 Link Here
220
222
221
    private boolean validateCloseStatus(int status) {
223
    private boolean validateCloseStatus(int status) {
222
224
223
        if (status == 1000 || status == 1001 || status == 1002 ||
225
        if (status == STATUS_CLOSE_NORMAL || status == STATUS_SHUTDOWN || status == STATUS_PROTOCOL_ERROR ||
224
                status == 1003 || status == 1007 || status == 1008 ||
226
                status == STATUS_UNEXPECTED_DATA_TYPE || status == STATUS_BAD_DATA || status == STATUS_POLICY_VIOLATION ||
225
                status == 1009 || status == 1010 || status == 1011 ||
227
                status == STATUS_MESSAGE_TOO_LARGE || status == STATUS_REQUIRED_EXTENSION || status == 1011 ||
226
                (status > 2999 && status < 5000)) {
228
                (status > 2999 && status < 5000)) {
227
            // Other 1xxx reserved / not permitted
229
            // Other 1xxx reserved / not permitted
228
            // 2xxx reserved
230
            // 2xxx reserved

Return to bug 51181