View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.localserver;
29  
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.io.OutputStream;
33  import java.util.Locale;
34  
35  import org.apache.http.Consts;
36  import org.apache.http.HttpException;
37  import org.apache.http.HttpRequest;
38  import org.apache.http.HttpResponse;
39  import org.apache.http.HttpStatus;
40  import org.apache.http.MethodNotSupportedException;
41  import org.apache.http.entity.AbstractHttpEntity;
42  import org.apache.http.protocol.HttpContext;
43  import org.apache.http.protocol.HttpRequestHandler;
44  
45  /**
46   * A handler that generates random data.
47   */
48  public class RandomHandler implements HttpRequestHandler {
49  
50      /**
51       * Handles a request by generating random data.
52       * The length of the response can be specified in the request URI
53       * as a number after the last /. For example /random/whatever/20
54       * will generate 20 random bytes in the printable ASCII range.
55       * If the request URI ends with /, a random number of random bytes
56       * is generated, but at least one.
57       *
58       * @param request   the request
59       * @param response  the response
60       * @param context   the context
61       *
62       * @throws HttpException    in case of a problem
63       * @throws IOException      in case of an IO problem
64       */
65      @Override
66      public void handle(final HttpRequest request,
67                         final HttpResponse response,
68                         final HttpContext context)
69          throws HttpException, IOException {
70  
71          final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
72          if (!"GET".equals(method) && !"HEAD".equals(method)) {
73              throw new MethodNotSupportedException
74                  (method + " not supported by " + getClass().getName());
75          }
76  
77          final String uri = request.getRequestLine().getUri();
78          final int  slash = uri.lastIndexOf('/');
79          int length = -1;
80          if (slash < uri.length()-1) {
81              try {
82                  // no more than Integer, 2 GB ought to be enough for anybody
83                  length = Integer.parseInt(uri.substring(slash+1));
84  
85                  if (length < 0) {
86                      response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
87                      response.setReasonPhrase("LENGTH " + length);
88                  }
89              } catch (final NumberFormatException nfx) {
90                  response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
91                  response.setReasonPhrase(nfx.toString());
92              }
93          } else {
94              // random length, but make sure at least something is sent
95              length = 1 + (int)(Math.random() * 79.0);
96          }
97  
98          if (length >= 0) {
99  
100             response.setStatusCode(HttpStatus.SC_OK);
101 
102             if (!"HEAD".equals(method)) {
103                 final RandomEntity entity = new RandomEntity(length);
104                 entity.setContentType("text/plain; charset=US-ASCII");
105                 response.setEntity(entity);
106             } else {
107                 response.setHeader("Content-Type",
108                                    "text/plain; charset=US-ASCII");
109                 response.setHeader("Content-Length",
110                                    String.valueOf(length));
111             }
112         }
113 
114     } // handle
115 
116 
117     /**
118      * An entity that generates random data.
119      * This is an outgoing entity, it supports {@link #writeTo writeTo}
120      * but not {@link #getContent getContent}.
121      */
122     public static class RandomEntity extends AbstractHttpEntity {
123 
124         /** The range from which to generate random data. */
125         private final static byte[] RANGE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
126                 .getBytes(Consts.ASCII);
127 
128         /** The length of the random data to generate. */
129         protected final long length;
130 
131 
132         /**
133          * Creates a new entity generating the given amount of data.
134          *
135          * @param len   the number of random bytes to generate,
136          *              0 to maxint
137          */
138         public RandomEntity(final long len) {
139             length = len;
140         }
141 
142         /**
143          * Tells that this entity is not streaming.
144          *
145          * @return      false
146          */
147         @Override
148         public final boolean isStreaming() {
149             return false;
150         }
151 
152         /**
153          * Tells that this entity is repeatable, in a way.
154          * Repetitions will generate different random data,
155          * unless perchance the same random data is generated twice.
156          *
157          * @return      {@code true}
158          */
159         @Override
160         public boolean isRepeatable() {
161             return true;
162         }
163 
164         /**
165          * Obtains the size of the random data.
166          *
167          * @return      the number of random bytes to generate
168          */
169         @Override
170         public long getContentLength() {
171             return length;
172         }
173 
174 
175         /**
176          * Not supported.
177          * This method throws an exception.
178          *
179          * @return      never anything
180          */
181         @Override
182         public InputStream getContent() {
183             throw new UnsupportedOperationException();
184         }
185 
186 
187         /**
188          * Generates the random content.
189          *
190          * @param out   where to write the content to
191          */
192         @Override
193         public void writeTo(final OutputStream out) throws IOException {
194 
195             final int blocksize = 2048;
196             int       remaining = (int) length; // range checked in constructor
197             final byte[]         data = new byte[Math.min(remaining, blocksize)];
198 
199             while (remaining > 0) {
200                 final int end = Math.min(remaining, data.length);
201 
202                 double value = 0.0;
203                 for (int i = 0; i < end; i++) {
204                     // we get 5 random characters out of one random value
205                     if (i%5 == 0) {
206                         value = Math.random();
207                     }
208                     value = value * RANGE.length;
209                     final int d = (int) value;
210                     value = value - d;
211                     data[i] = RANGE[d];
212                 }
213                 out.write(data, 0, end);
214                 out.flush();
215 
216                 remaining = remaining - end;
217             }
218             out.close();
219 
220         } // writeTo
221 
222     } // class RandomEntity
223 
224 
225 } // class RandomHandler