432939 - Jetty Client ContentResponse should have methods such as getContentType() and getMediaType().

Introduced ContentResponse.getMediaType() and ContentResponse .getEncoding(),
as well as BufferingResponseListener.getMediaType() to make the media
type and the encoding available to applications.
This commit is contained in:
Simone Bordet 2014-05-15 10:58:49 +02:00
parent b603964bb6
commit d75b9177c5
10 changed files with 190 additions and 34 deletions

View File

@ -82,7 +82,7 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
public void onComplete(Result result)
{
HttpRequest request = (HttpRequest)result.getRequest();
ContentResponse response = new HttpContentResponse(result.getResponse(), getContent(), getEncoding());
ContentResponse response = new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding());
if (result.isFailed())
{
Throwable failure = result.getFailure();

View File

@ -88,7 +88,7 @@ public class ContinueProtocolHandler implements ProtocolHandler
// or it does and wants to refuse the request content,
// or we got some other HTTP status code like a redirect.
List<Response.ResponseListener> listeners = exchange.getResponseListeners();
HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getEncoding());
HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getMediaType(), getEncoding());
notifier.forwardSuccess(listeners, contentResponse);
exchange.proceed(new HttpRequestException("Expectation failed", exchange.getRequest()));
break;
@ -108,7 +108,7 @@ public class ContinueProtocolHandler implements ProtocolHandler
HttpExchange exchange = conversation.getExchanges().peekLast();
assert exchange.getResponse() == response;
List<Response.ResponseListener> listeners = exchange.getResponseListeners();
HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getEncoding());
HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getMediaType(), getEncoding());
notifier.forwardFailureComplete(listeners, exchange.getRequest(), exchange.getRequestFailure(), contentResponse, failure);
}

View File

@ -33,12 +33,14 @@ public class HttpContentResponse implements ContentResponse
{
private final Response response;
private final byte[] content;
private final String mediaType;
private final String encoding;
public HttpContentResponse(Response response, byte[] content, String encoding)
public HttpContentResponse(Response response, byte[] content, String mediaType, String encoding)
{
this.response = response;
this.content = content;
this.mediaType = mediaType;
this.encoding = encoding;
}
@ -84,6 +86,18 @@ public class HttpContentResponse implements ContentResponse
return response.abort(cause);
}
@Override
public String getMediaType()
{
return mediaType;
}
@Override
public String getEncoding()
{
return encoding;
}
@Override
public byte[] getContent()
{

View File

@ -118,7 +118,7 @@ public class HttpRedirector
{
resultRef.set(new Result(result.getRequest(),
result.getRequestFailure(),
new HttpContentResponse(result.getResponse(), getContent(), getEncoding()),
new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding()),
result.getResponseFailure()));
latch.countDown();
}

View File

@ -23,6 +23,16 @@ package org.eclipse.jetty.client.api;
*/
public interface ContentResponse extends Response
{
/**
* @return the media type of the content, such as "text/html" or "application/octet-stream"
*/
String getMediaType();
/**
* @return the encoding of the content, such as "UTF-8"
*/
String getEncoding();
/**
* @return the response content
*/

View File

@ -236,7 +236,7 @@ public interface Request
Request agent(String agent);
/**
* @param accepts the content types that are acceptable in the response, such as
* @param accepts the media types that are acceptable in the response, such as
* "text/plain;q=0.5" or "text/html" (corresponds to the {@code Accept} header)
* @return this request object
*/

View File

@ -20,12 +20,9 @@ package org.eclipse.jetty.client.util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;
import java.util.Locale;
import org.eclipse.jetty.client.api.Response;
@ -45,6 +42,7 @@ public abstract class BufferingResponseListener extends Listener.Adapter
{
private final int maxLength;
private volatile ByteBuffer buffer;
private volatile String mediaType;
private volatile String encoding;
/**
@ -62,14 +60,14 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public BufferingResponseListener(int maxLength)
{
this.maxLength=maxLength;
this.maxLength = maxLength;
}
@Override
public void onHeaders(Response response)
{
super.onHeaders(response);
HttpFields headers = response.getHeaders();
long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString());
if (length > maxLength)
@ -77,47 +75,58 @@ public abstract class BufferingResponseListener extends Listener.Adapter
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
return;
}
buffer=BufferUtil.allocate((length > 0)?(int)length:1024);
buffer = BufferUtil.allocate(length > 0 ? (int)length : 1024);
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
if (contentType != null)
{
String media = contentType;
String charset = "charset=";
int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
if (index > 0)
{
media = contentType.substring(0, index);
String encoding = contentType.substring(index + charset.length());
// Sometimes charsets arrive with an ending semicolon
index = encoding.indexOf(';');
if (index > 0)
encoding = encoding.substring(0, index);
int semicolon = encoding.indexOf(';');
if (semicolon > 0)
encoding = encoding.substring(0, semicolon).trim();
this.encoding = encoding;
}
int semicolon = media.indexOf(';');
if (semicolon > 0)
media = media.substring(0, semicolon).trim();
this.mediaType = media;
}
}
@Override
public void onContent(Response response, ByteBuffer content)
{
{
int length = content.remaining();
if (length>BufferUtil.space(buffer))
if (length > BufferUtil.space(buffer))
{
int requiredCapacity = buffer==null?0:buffer.capacity()+length;
if (requiredCapacity>maxLength)
int requiredCapacity = buffer == null ? 0 : buffer.capacity() + length;
if (requiredCapacity > maxLength)
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
int newCapacity = Math.min(Integer.highestOneBit(requiredCapacity) << 1, maxLength);
buffer = BufferUtil.ensureCapacity(buffer,newCapacity);
}
int newCapacity = Math.min(Integer.highestOneBit(requiredCapacity) << 1, maxLength);
buffer = BufferUtil.ensureCapacity(buffer, newCapacity);
}
BufferUtil.append(buffer, content);
}
@Override
public abstract void onComplete(Result result);
public String getMediaType()
{
return mediaType;
}
public String getEncoding()
{
return encoding;
@ -129,14 +138,14 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public byte[] getContent()
{
if (buffer==null)
if (buffer == null)
return new byte[0];
return BufferUtil.toArray(buffer);
}
/**
* @return the content as a string, using the "Content-Type" header to detect the encoding
* or defaulting to UTF-8 if the encoding could not be detected.
* or defaulting to UTF-8 if the encoding could not be detected.
* @see #getContentAsString(String)
*/
public String getContentAsString()
@ -154,7 +163,7 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public String getContentAsString(String encoding)
{
if (buffer==null)
if (buffer == null)
return null;
return BufferUtil.toString(buffer, Charset.forName(encoding));
}
@ -166,18 +175,17 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public String getContentAsString(Charset encoding)
{
if (buffer==null)
if (buffer == null)
return null;
return BufferUtil.toString(buffer, encoding);
}
/* ------------------------------------------------------------ */
/**
* @return Content as InputStream
*/
public InputStream getContentAsInputStream()
{
if (buffer==null)
if (buffer == null)
return new ByteArrayInputStream(new byte[]{});
return new ByteArrayInputStream(buffer.array(), buffer.arrayOffset(), buffer.remaining());
}

View File

@ -70,7 +70,7 @@ public class FutureResponseListener extends BufferingResponseListener implements
@Override
public void onComplete(Result result)
{
response = new HttpContentResponse(result.getResponse(), getContent(), getEncoding());
response = new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding());
failure = result.getFailure();
latch.countDown();
}

View File

@ -0,0 +1,124 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Test;
public class ContentResponseTest extends AbstractHttpClientServerTest
{
public ContentResponseTest(SslContextFactory sslContextFactory)
{
super(sslContextFactory);
}
@Test
public void testResponseWithoutContentType() throws Exception
{
final byte[] content = new byte[1024];
new Random().nextBytes(content);
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.getOutputStream().write(content);
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertArrayEquals(content, response.getContent());
Assert.assertNull(response.getMediaType());
Assert.assertNull(response.getEncoding());
}
@Test
public void testResponseWithMediaType() throws Exception
{
final String content = "The quick brown fox jumped over the lazy dog";
final String mediaType = "text/plain";
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setHeader(HttpHeader.CONTENT_TYPE.asString(), mediaType);
response.getOutputStream().write(content.getBytes("UTF-8"));
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(content, response.getContentAsString());
Assert.assertEquals(mediaType, response.getMediaType());
Assert.assertNull(response.getEncoding());
}
@Test
public void testResponseWithContentType() throws Exception
{
final String content = "The quick brown fox jumped over the lazy dog";
final String mediaType = "text/plain";
final String encoding = "UTF-8";
final String contentType = mediaType + "; charset=" + encoding;
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setHeader(HttpHeader.CONTENT_TYPE.asString(), contentType);
response.getOutputStream().write(content.getBytes("UTF-8"));
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(content, response.getContentAsString());
Assert.assertEquals(mediaType, response.getMediaType());
Assert.assertEquals(encoding, response.getEncoding());
}
}

View File

@ -739,7 +739,7 @@ public class ProxyServletTest
protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
{
byte[] content = temp.remove(request.getRequestURI()).toByteArray();
ContentResponse cached = new HttpContentResponse(proxyResponse, content, null);
ContentResponse cached = new HttpContentResponse(proxyResponse, content, null, null);
cache.put(request.getRequestURI(), cached);
super.onResponseSuccess(request, response, proxyResponse);
}