View Javadoc
1   /*
2    * Copyright (c) 2011-2022, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.http.mock;
31  
32  import com.jcabi.aspects.Immutable;
33  import com.jcabi.http.ImmutableHeader;
34  import com.jcabi.immutable.ArrayMap;
35  import java.io.ByteArrayOutputStream;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.net.URI;
39  import java.nio.charset.StandardCharsets;
40  import java.util.Arrays;
41  import java.util.Collections;
42  import java.util.LinkedList;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.concurrent.ConcurrentHashMap;
46  import java.util.concurrent.ConcurrentMap;
47  import org.glassfish.grizzly.http.Method;
48  import org.glassfish.grizzly.http.server.Request;
49  
50  /**
51   * Mock HTTP query/request.
52   *
53   * @since 0.10
54   */
55  @Immutable
56  final class GrizzlyQuery implements MkQuery {
57  
58      /**
59       * HTTP request method.
60       */
61      private final transient Method mtd;
62  
63      /**
64       * HTTP request content.
65       */
66      @Immutable.Array
67      private final transient byte[] content;
68  
69      /**
70       * HTTP request URI.
71       */
72      private final transient String home;
73  
74      /**
75       * HTTP request headers.
76       */
77      private final transient ArrayMap<String, List<String>> hdrs;
78  
79      /**
80       * Ctor.
81       * @param request Grizzly request
82       * @throws IOException If fails
83       */
84      @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
85      GrizzlyQuery(final Request request) throws IOException {
86          request.setCharacterEncoding(StandardCharsets.UTF_8.toString());
87          this.home = GrizzlyQuery.uri(request);
88          this.mtd = request.getMethod();
89          this.hdrs = GrizzlyQuery.headers(request);
90          this.content = GrizzlyQuery.input(request);
91      }
92  
93      @Override
94      public URI uri() {
95          return URI.create(this.home);
96      }
97  
98      @Override
99      public String method() {
100         return this.mtd.getMethodString();
101     }
102 
103     @Override
104     public Map<String, List<String>> headers() {
105         return Collections.unmodifiableMap(this.hdrs);
106     }
107 
108     @Override
109     public String body() {
110         return new String(this.content, StandardCharsets.UTF_8);
111     }
112 
113     @Override
114     public byte[] binary() {
115         return Arrays.copyOf(this.content, this.content.length);
116     }
117 
118     /**
119      * Fetch URI from the request.
120      * @param request Request
121      * @return URI
122      */
123     private static String uri(final Request request) {
124         final StringBuilder uri = new StringBuilder(request.getRequestURI());
125         final String query = request.getQueryString();
126         if (query != null && !query.isEmpty()) {
127             uri.append('?').append(query);
128         }
129         return uri.toString();
130     }
131 
132     /**
133      * Fetch headers from the request.
134      * @param request Request
135      * @return Headers
136      */
137     private static ArrayMap<String, List<String>> headers(
138         final Request request
139     ) {
140         final ConcurrentMap<String, List<String>> headers =
141             new ConcurrentHashMap<>(0);
142         final Iterable<String> names = request.getHeaderNames();
143         for (final String name : names) {
144             headers.put(
145                 ImmutableHeader.normalize(name),
146                 GrizzlyQuery.headers(request, name)
147             );
148         }
149         return new ArrayMap<>(headers);
150     }
151 
152     /**
153      * Get headers by name.
154      * @param request Grizzly request
155      * @param name Name of header
156      * @return List of values
157      */
158     private static List<String> headers(
159         final Request request, final String name
160     ) {
161         final List<String> list = new LinkedList<>();
162         final Iterable<?> values = request.getHeaders(name);
163         for (final Object value : values) {
164             list.add(value.toString());
165         }
166         return list;
167     }
168 
169     /**
170      * Read req.
171      * @param req Grizzly req
172      * @return Bytes of input
173      * @throws IOException If fails
174      */
175     private static byte[] input(final Request req) throws IOException {
176         // @checkstyle MagicNumber (1 line)
177         final byte[] buffer = new byte[8192];
178         final InputStream input = req.getInputStream();
179         final ByteArrayOutputStream output = new ByteArrayOutputStream();
180         while (true) {
181             final int bytes = input.read(buffer);
182             if (bytes == -1) {
183                 break;
184             }
185             output.write(buffer, 0, bytes);
186         }
187         return output.toByteArray();
188     }
189 }