Skip to content

Commit

Permalink
Merge pull request Azure#258 from WindowsAzure/redirect_filter
Browse files Browse the repository at this point in the history
Implementation of redirect filter
  • Loading branch information
Chris Tavares committed Aug 11, 2012
2 parents cf41286 + 538cf03 commit b5e41f8
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright 2011 Microsoft Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.microsoft.windowsazure.services.media.implementation;

import java.net.URI;
import java.net.URISyntaxException;

import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.filter.ClientFilter;

public class RedirectFilter extends ClientFilter {
private final ResourceLocationManager locationManager;

public RedirectFilter(ResourceLocationManager locationManager) {
this.locationManager = locationManager;
}

@Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
if (request == null) {
throw new IllegalArgumentException("Request should not be null");
}

URI originalURI = request.getURI();
request.setURI(locationManager.getRedirectedURI(originalURI));

ClientResponse response = getNext().handle(request);
while (response.getClientResponseStatus() == ClientResponse.Status.MOVED_PERMANENTLY) {
try {
locationManager.setRedirectedURI(response.getHeaders().getFirst("Location"));
}
catch (NullPointerException e) {
throw new ClientHandlerException("HTTP Redirect did not include Location header");
}
catch (URISyntaxException e) {
throw new ClientHandlerException("HTTP Redirect location is not a valid URI");
}

request.setURI(locationManager.getRedirectedURI(originalURI));
response = getNext().handle(request);
}
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright 2011 Microsoft Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.microsoft.windowsazure.services.media.implementation;

import java.net.URI;
import java.net.URISyntaxException;

import javax.ws.rs.core.UriBuilder;

public class ResourceLocationManager {
private URI baseURI;

public ResourceLocationManager(String baseUri) throws URISyntaxException {
this.baseURI = new URI(baseUri);
}

public URI getBaseURI() {
return baseURI;
}

public URI getRedirectedURI(URI originalURI) {
return UriBuilder.fromUri(baseURI).path(originalURI.getPath()).build();
}

public void setRedirectedURI(String newURI) throws URISyntaxException {
baseURI = new URI(newURI);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.microsoft.windowsazure.services.media.implementation;

import static org.junit.Assert.*;

import javax.ws.rs.core.MultivaluedMap;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.filter.ClientFilter;
import com.sun.jersey.core.header.InBoundHeaders;

public class RedirectionFilterTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

private final String originalBaseURI = "https://base.somewhere.example/API/";
private final String redirectedBaseURI = "http://redirected.somewhere.example/Stuff/";

@Test
public void whenInvokedAndNotRedirected_shouldAddBaseURIToRequest() throws Exception {
RequestRecordingFilter sink = new RequestRecordingFilter();
Client c = Client.create();
c.addFilter(sink);
c.addFilter(new RedirectFilter(new ResourceLocationManager(originalBaseURI)));

c.resource("Files").get(ClientResponse.class);

assertEquals(originalBaseURI + "Files", sink.request.getURI().toString());
}

@Test
public void whenInvokedAndRedirected_shouldHaveRedirectedURIInRequest() throws Exception {
RequestRecordingFilter sink = new RequestRecordingFilter();
Client c = Client.create();
c.addFilter(sink);
c.addFilter(new RedirectingTestFilter(originalBaseURI, redirectedBaseURI));

c.addFilter(new RedirectFilter(new ResourceLocationManager(originalBaseURI)));

ClientResponse response = c.resource("Things").get(ClientResponse.class);

assertEquals(200, response.getStatus());
assertEquals(redirectedBaseURI + "Things", sink.request.getURI().toString());
}

@Test
public void whenRedirectedMultipleTimes_requestEndsUpAtFinalRediret() throws Exception {
RequestRecordingFilter sink = new RequestRecordingFilter();
Client c = Client.create();
c.addFilter(sink);
c.addFilter(new RedirectingTestFilter("https://a.example/API/", "https://b.example/API/"));
c.addFilter(new RedirectingTestFilter("https://b.example/API/", "https://c.example/API/"));
c.addFilter(new RedirectingTestFilter("https://c.example/API/", "https://final.example/Code/"));

c.addFilter(new RedirectFilter(new ResourceLocationManager("https://a.example/API/")));

ClientResponse response = c.resource("Stuff").get(ClientResponse.class);

assertEquals(200, response.getStatus());

assertEquals("https://final.example/Code/Stuff", sink.request.getURI().toString());
}

@Test
public void whenRedirectingToNull_shouldGetClientException() throws Exception {
RequestRecordingFilter sink = new RequestRecordingFilter();
Client c = Client.create();
c.addFilter(sink);
c.addFilter(new RedirectingTestFilter(originalBaseURI, null));
c.addFilter(new RedirectFilter(new ResourceLocationManager(originalBaseURI)));

thrown.expect(ClientHandlerException.class);
c.resource("Something").get(ClientResponse.class);
}

@Test
public void whenRedirectingToBadURI_shouldGetClientException() throws Exception {
RequestRecordingFilter sink = new RequestRecordingFilter();
Client c = Client.create();
c.addFilter(sink);
c.addFilter(new RedirectingTestFilter(originalBaseURI, "no way this is valid!"));
c.addFilter(new RedirectFilter(new ResourceLocationManager(originalBaseURI)));

thrown.expect(ClientHandlerException.class);
c.resource("Something").get(ClientResponse.class);
}

// Test support classes

//
// Filter that acts as a "sink" so the request doesn't go out over
// the wire. Also holds onto the request object that went through
// the pipeline so that it can be asserted against in the test.
//
private class RequestRecordingFilter extends ClientFilter {
public ClientRequest request;

@Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
this.request = request;

ClientResponse response = Mockito.mock(ClientResponse.class);
Mockito.when(response.getStatus()).thenReturn(200);
return response;
}
}

//
// Filter that will 301-redirect requests depending on which URI
// the request goes to.
//

private class RedirectingTestFilter extends ClientFilter {
private final String uriToRedirect;
private final String uriRedirectedTo;

public RedirectingTestFilter(String uriToRedirect, String uriRedirectedTo) {
this.uriToRedirect = uriToRedirect;
this.uriRedirectedTo = uriRedirectedTo;
}

@Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {

if (request.getURI().toString().startsWith(uriToRedirect)) {
ClientResponse response = Mockito.mock(ClientResponse.class);
Mockito.when(response.getClientResponseStatus()).thenReturn(ClientResponse.Status.MOVED_PERMANENTLY);
MultivaluedMap<String, String> headers = new InBoundHeaders();
headers.add("location", uriRedirectedTo);
Mockito.when(response.getHeaders()).thenReturn(headers);
return response;
}
else {
return getNext().handle(request);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.microsoft.windowsazure.services.media.implementation;

import static org.junit.Assert.*;

import java.net.URI;

import org.junit.Test;

public class ResourceLocationManagerTest {

@Test
public void testCanCreateWithBaseUri() throws Exception {
String baseUri = "https://base.uri.example";
ResourceLocationManager m = new ResourceLocationManager(baseUri);

assertEquals(baseUri, m.getBaseURI().toString());
}

@Test
public void testWhenCallingGetRedirectedURI_shouldReturnURIWithBaseURIPreprended() throws Exception {
String baseURI = "http://base.uri.example/path/";
ResourceLocationManager m = new ResourceLocationManager(baseURI);

URI originalURI = new URI("Assets");

URI redirectedURI = m.getRedirectedURI(originalURI);

assertEquals(baseURI + "Assets", redirectedURI.toString());
}

@Test
public void settingBaseURIAfterRedirecting_shouldReturnURIWithNewBaseURI() throws Exception {
String baseURI = "http://base.uri.example/path/";
String redirectedBaseURI = "http://other.uri.example/API/";
ResourceLocationManager m = new ResourceLocationManager(baseURI);

URI targetURI = new URI("Assets");

URI originalURI = m.getRedirectedURI(targetURI);
m.setRedirectedURI(redirectedBaseURI);
URI redirectedURI = m.getRedirectedURI(targetURI);

assertEquals(baseURI + "Assets", originalURI.toString());
assertEquals(redirectedBaseURI + "Assets", redirectedURI.toString());
}
}

0 comments on commit b5e41f8

Please sign in to comment.