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

(-)java/org/apache/tomcat/websocket/Constants.java (+15 lines)
Lines 80-90 Link Here
80
    public static final String ORIGIN_HEADER_NAME = "Origin";
80
    public static final String ORIGIN_HEADER_NAME = "Origin";
81
    public static final String CONNECTION_HEADER_NAME = "Connection";
81
    public static final String CONNECTION_HEADER_NAME = "Connection";
82
    public static final String CONNECTION_HEADER_VALUE = "upgrade";
82
    public static final String CONNECTION_HEADER_VALUE = "upgrade";
83
    public static final String LOCATION_HEADER_NAME = "Location";
83
    public static final String WS_VERSION_HEADER_NAME = "Sec-WebSocket-Version";
84
    public static final String WS_VERSION_HEADER_NAME = "Sec-WebSocket-Version";
84
    public static final String WS_VERSION_HEADER_VALUE = "13";
85
    public static final String WS_VERSION_HEADER_VALUE = "13";
85
    public static final String WS_KEY_HEADER_NAME = "Sec-WebSocket-Key";
86
    public static final String WS_KEY_HEADER_NAME = "Sec-WebSocket-Key";
86
    public static final String WS_PROTOCOL_HEADER_NAME = "Sec-WebSocket-Protocol";
87
    public static final String WS_PROTOCOL_HEADER_NAME = "Sec-WebSocket-Protocol";
87
    public static final String WS_EXTENSIONS_HEADER_NAME = "Sec-WebSocket-Extensions";
88
    public static final String WS_EXTENSIONS_HEADER_NAME = "Sec-WebSocket-Extensions";
89
    
90
    /// HTTP redirection status codes
91
    public static final int MULTIPLE_CHOICES = 300;
92
    public static final int MOVED_PERMANENTLY = 301;
93
    public static final int FOUND = 302;
94
    public static final int SEE_OTHER = 303;
95
    public static final int USE_PROXY = 305;
96
    public static final int TEMPORARY_REDIRECT = 307;
88
97
89
    // Configuration for Origin header in client
98
    // Configuration for Origin header in client
90
    static final String DEFAULT_ORIGIN_HEADER_VALUE =
99
    static final String DEFAULT_ORIGIN_HEADER_VALUE =
Lines 115-120 Link Here
115
    // Configuration for stream behavior
124
    // Configuration for stream behavior
116
    static final boolean STREAMS_DROP_EMPTY_MESSAGES =
125
    static final boolean STREAMS_DROP_EMPTY_MESSAGES =
117
            Boolean.getBoolean("org.apache.tomcat.websocket.STREAMS_DROP_EMPTY_MESSAGES");
126
            Boolean.getBoolean("org.apache.tomcat.websocket.STREAMS_DROP_EMPTY_MESSAGES");
127
    
128
    static final boolean REDIRECT_ENABLED = Boolean.getBoolean("org.apache.tomcat.websocket.REDIRECT_ENABLED");
129
    
130
    //RFC 2616 recommends a maximum of 5 redirections
131
    static final int MAX_REDIRECTIONS =
132
    		Integer.getInteger("org.apache.tomcat.websocket.MAX_REDIRECTIONS",5).intValue();
118
133
119
    public static final boolean STRICT_SPEC_COMPLIANCE =
134
    public static final boolean STRICT_SPEC_COMPLIANCE =
120
            Boolean.getBoolean(
135
            Boolean.getBoolean(
(-)java/org/apache/tomcat/websocket/LocalStrings.properties (+3 lines)
Lines 136-138 Link Here
136
wsWebSocketContainer.proxyConnectFail=Failed to connect to the configured Proxy [{0}]. The HTTP response code was [{1}]
136
wsWebSocketContainer.proxyConnectFail=Failed to connect to the configured Proxy [{0}]. The HTTP response code was [{1}]
137
wsWebSocketContainer.sessionCloseFail=Session with ID [{0}] did not close cleanly
137
wsWebSocketContainer.sessionCloseFail=Session with ID [{0}] did not close cleanly
138
wsWebSocketContainer.sslEngineFail=Unable to create SSLEngine to support SSL/TLS connections
138
wsWebSocketContainer.sslEngineFail=Unable to create SSLEngine to support SSL/TLS connections
139
wsWebSocketContainer.redirectDisabled=Failed to handle HTTP response code [{0}]. HTTP redirection is disabled
140
wsWebSocketContainer.missingLocationHeader=Failed to handle HTTP response code [{0}]. Missing Location header in response
141
wsWebSocketContainer.redirectThreshold=Cyclic Location header [{0}] detected / reached max number of redirects [{1}] of max [{2}]
(-)java/org/apache/tomcat/websocket/WsWebSocketContainer.java (-3 / +64 lines)
Lines 26-31 Link Here
26
import java.net.ProxySelector;
26
import java.net.ProxySelector;
27
import java.net.SocketAddress;
27
import java.net.SocketAddress;
28
import java.net.URI;
28
import java.net.URI;
29
import java.net.URISyntaxException;
29
import java.nio.ByteBuffer;
30
import java.nio.ByteBuffer;
30
import java.nio.channels.AsynchronousChannelGroup;
31
import java.nio.channels.AsynchronousChannelGroup;
31
import java.nio.channels.AsynchronousSocketChannel;
32
import java.nio.channels.AsynchronousSocketChannel;
Lines 98-103 Link Here
98
    private volatile long defaultMaxSessionIdleTimeout = 0;
99
    private volatile long defaultMaxSessionIdleTimeout = 0;
99
    private int backgroundProcessCount = 0;
100
    private int backgroundProcessCount = 0;
100
    private int processPeriod = Constants.DEFAULT_PROCESS_PERIOD;
101
    private int processPeriod = Constants.DEFAULT_PROCESS_PERIOD;
102
    private final Set<URI> redirectSet = new HashSet<>(Constants.MAX_REDIRECTIONS);
101
103
102
    private InstanceManager instanceManager;
104
    private InstanceManager instanceManager;
103
105
Lines 340-347 Link Here
340
            writeRequest(channel, request, timeout);
342
            writeRequest(channel, request, timeout);
341
343
342
            HttpResponse httpResponse = processResponse(response, channel, timeout);
344
            HttpResponse httpResponse = processResponse(response, channel, timeout);
343
            // TODO: Handle redirects
345
                      
344
            if (httpResponse.status != 101) {
346
            if (httpResponse.status != 101) {            	
347
                if(isRedirectStatus(httpResponse.status)){
348
                    if (!Constants.REDIRECT_ENABLED) {
349
            	    throw new DeploymentException(sm.getString("wsWebSocketContainer.redirectDisabled",
350
            	            Integer.toString(httpResponse.status)));
351
            	}
352
353
            	List<String> locationHeader = httpResponse.getHandshakeResponse().getHeaders()
354
            	        .get(Constants.LOCATION_HEADER_NAME);
355
356
            	if (locationHeader == null || locationHeader.isEmpty() || locationHeader.get(0) == null
357
            	        || locationHeader.get(0).isEmpty()) {
358
            	    throw new DeploymentException(sm.getString("wsWebSocketContainer.missingLocationHeader",
359
            	            Integer.toString(httpResponse.status)));
360
            	}
361
362
            	URI redirectLocation = URI.create(locationHeader.get(0)).normalize();
363
364
            	if (!redirectLocation.isAbsolute()) {
365
            	    redirectLocation = path.resolve(redirectLocation);
366
            	}
367
368
            	String redirectScheme = redirectLocation.getScheme().toLowerCase();
369
370
            	if (redirectScheme.startsWith("http")) {
371
            	    redirectLocation = new URI(redirectScheme.replace("http", "ws"),
372
            		        redirectLocation.getUserInfo(), redirectLocation.getHost(),
373
            		        redirectLocation.getPort(), redirectLocation.getPath(),
374
            		        redirectLocation.getQuery(), redirectLocation.getFragment());     
375
            	}
376
377
            	if (!redirectSet.add(redirectLocation) || redirectSet.size() > Constants.MAX_REDIRECTIONS) {
378
            	    throw new DeploymentException(sm.getString("wsWebSocketContainer.redirectThreshold",
379
            	            redirectLocation, Integer.toString(redirectSet.size()),
380
            	            Integer.toString(Constants.MAX_REDIRECTIONS)));
381
            	}
382
383
            	return connectToServer(endpoint, clientEndpointConfiguration, redirectLocation);
384
            		
385
            	}
345
                throw new DeploymentException(sm.getString("wsWebSocketContainer.invalidStatus",
386
                throw new DeploymentException(sm.getString("wsWebSocketContainer.invalidStatus",
346
                        Integer.toString(httpResponse.status)));
387
                        Integer.toString(httpResponse.status)));
347
            }
388
            }
Lines 390-396 Link Here
390
431
391
            success = true;
432
            success = true;
392
        } catch (ExecutionException | InterruptedException | SSLException |
433
        } catch (ExecutionException | InterruptedException | SSLException |
393
                EOFException | TimeoutException e) {
434
                EOFException | TimeoutException | URISyntaxException e) {
394
            throw new DeploymentException(
435
            throw new DeploymentException(
395
                    sm.getString("wsWebSocketContainer.httpRequestFailed"), e);
436
                    sm.getString("wsWebSocketContainer.httpRequestFailed"), e);
396
        } finally {
437
        } finally {
Lines 446-453 Link Here
446
            toWrite -= thisWrite.intValue();
487
            toWrite -= thisWrite.intValue();
447
        }
488
        }
448
    }
489
    }
490
    
491
    
492
    private static boolean isRedirectStatus(int httpResponseCode) {
449
493
494
   	boolean isRedirect = false;
450
495
496
   	switch (httpResponseCode) {
497
   	case Constants.MULTIPLE_CHOICES:
498
   	case Constants.MOVED_PERMANENTLY:
499
   	case Constants.FOUND:
500
   	case Constants.SEE_OTHER:
501
   	case Constants.USE_PROXY:
502
   	case Constants.TEMPORARY_REDIRECT:
503
   	    isRedirect = true;
504
   	    break;
505
   	default:
506
   	    break;
507
   	}
508
509
   	return isRedirect;
510
       }
511
451
    private static ByteBuffer createProxyRequest(String host, int port) {
512
    private static ByteBuffer createProxyRequest(String host, int port) {
452
        StringBuilder request = new StringBuilder();
513
        StringBuilder request = new StringBuilder();
453
        request.append("CONNECT ");
514
        request.append("CONNECT ");
(-)test/org/apache/tomcat/websocket/TestWebSocketFrameClient.java (-6 / +4 lines)
Lines 82-87 Link Here
82
    @Test
82
    @Test
83
    public void testConnectToRootEndpoint() throws Exception {
83
    public void testConnectToRootEndpoint() throws Exception {
84
84
85
        System.setProperty("org.apache.tomcat.websocket.REDIRECT_ENABLED", "true");
86
	
85
        Tomcat tomcat = getTomcatInstance();
87
        Tomcat tomcat = getTomcatInstance();
86
        // No file system docBase required
88
        // No file system docBase required
87
        Context ctx = tomcat.addContext("", null);
89
        Context ctx = tomcat.addContext("", null);
Lines 94-107 Link Here
94
        ctx2.addServletMappingDecoded("/", "default");
96
        ctx2.addServletMappingDecoded("/", "default");
95
97
96
        tomcat.start();
98
        tomcat.start();
97
99
        
98
        echoTester("");
100
        echoTester("");
99
        echoTester("/");
101
        echoTester("/");
100
        // FIXME: The ws client doesn't handle any response other than the upgrade,
102
        echoTester("/foo");
101
        // which may or may not be allowed. In that case, the server will return
102
        // a redirect to the root of the webapp to avoid possible broken relative
103
        // paths.
104
        // echoTester("/foo");
105
        echoTester("/foo/");
103
        echoTester("/foo/");
106
    }
104
    }
107
105

Return to bug 57767