View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.client.util;
20  
21  import java.io.UnsupportedEncodingException;
22  import java.nio.ByteBuffer;
23  import java.nio.charset.Charset;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.charset.UnsupportedCharsetException;
26  import java.util.Locale;
27  
28  import org.eclipse.jetty.client.api.Response;
29  import org.eclipse.jetty.client.api.Result;
30  import org.eclipse.jetty.http.HttpFields;
31  import org.eclipse.jetty.http.HttpHeader;
32  
33  /**
34   * <p>Implementation of {@link Response.Listener} that buffers the content up to a maximum length
35   * specified to the constructors.</p>
36   * <p>The content may be retrieved from {@link #onSuccess(Response)} or {@link #onComplete(Result)}
37   * via {@link #getContent()} or {@link #getContentAsString()}.</p>
38   */
39  public abstract class BufferingResponseListener extends Response.Listener.Empty
40  {
41      private final int maxLength;
42      private volatile byte[] buffer = new byte[0];
43      private volatile String encoding;
44  
45      /**
46       * Creates an instance with a default maximum length of 2 MiB.
47       */
48      public BufferingResponseListener()
49      {
50          this(2 * 1024 * 1024);
51      }
52  
53      /**
54       * Creates an instance with the given maximum length
55       *
56       * @param maxLength the maximum length of the content
57       */
58      public BufferingResponseListener(int maxLength)
59      {
60          this.maxLength = maxLength;
61      }
62  
63      @Override
64      public void onHeaders(Response response)
65      {
66          HttpFields headers = response.getHeaders();
67          long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString());
68          if (length > maxLength)
69          {
70              response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
71          }
72          else
73          {
74              String contentType = headers.get(HttpHeader.CONTENT_TYPE);
75              if (contentType != null)
76              {
77                  String charset = "charset=";
78                  int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
79                  if (index > 0)
80                  {
81                      String encoding = contentType.substring(index + charset.length());
82                      // Sometimes charsets arrive with an ending semicolon
83                      index = encoding.indexOf(';');
84                      if (index > 0)
85                          encoding = encoding.substring(0, index);
86                      this.encoding = encoding;
87                  }
88              }
89          }
90      }
91  
92      @Override
93      public void onContent(Response response, ByteBuffer content)
94      {
95          long newLength = buffer.length + content.remaining();
96          if (newLength > maxLength)
97          {
98              response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
99          }
100         else
101         {
102             byte[] newBuffer = new byte[(int)newLength];
103             System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
104             content.get(newBuffer, buffer.length, content.remaining());
105             buffer = newBuffer;
106         }
107     }
108 
109     @Override
110     public abstract void onComplete(Result result);
111 
112     public String getEncoding()
113     {
114         return encoding;
115     }
116 
117     /**
118      * @return the content as bytes
119      * @see #getContentAsString()
120      */
121     public byte[] getContent()
122     {
123         return buffer;
124     }
125 
126     /**
127      * @return the content as a string, using the "Content-Type" header to detect the encoding
128      *         or defaulting to UTF-8 if the encoding could not be detected.
129      * @see #getContentAsString(String)
130      */
131     public String getContentAsString()
132     {
133         String encoding = this.encoding;
134         if (encoding == null)
135             return getContentAsString(StandardCharsets.UTF_8);
136         return getContentAsString(encoding);
137     }
138 
139     /**
140      * @param encoding the encoding of the content bytes
141      * @return the content as a string, with the specified encoding
142      * @see #getContentAsString()
143      */
144     public String getContentAsString(String encoding)
145     {
146         try
147         {
148             return new String(getContent(), encoding);
149         }
150         catch (UnsupportedEncodingException x)
151         {
152             throw new UnsupportedCharsetException(encoding);
153         }
154     }
155 
156     /**
157      * @param encoding the encoding of the content bytes
158      * @return the content as a string, with the specified encoding
159      * @see #getContentAsString()
160      */
161     public String getContentAsString(Charset encoding)
162     {
163         return new String(getContent(), encoding);
164     }
165 }