001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.mock2;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.io.*;
018import java.security.*;
019import java.util.*;
020
021import javax.servlet.*;
022import javax.servlet.http.*;
023
024import org.apache.juneau.internal.*;
025import org.apache.juneau.rest.*;
026import org.apache.juneau.rest.util.*;
027import org.apache.juneau.rest.util.RestUtils;
028import org.apache.juneau.urlencoding.*;
029import org.apache.juneau.utils.*;
030
031/**
032 * An implementation of {@link HttpServletRequest} for mocking purposes.
033 *
034 * <ul class='seealso'>
035 *    <li class='link'>{@doc juneau-rest-mock.MockRest}
036 * </ul>
037 */
038public class MockServletRequest implements HttpServletRequest, MockHttpRequest {
039
040   private String method = "GET";
041   private Map<String,String[]> queryDataMap = new LinkedHashMap<>();
042   private Map<String,String[]> formDataMap;
043   private Map<String,String[]> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
044   private Map<String,Object> attributeMap = new LinkedHashMap<>();
045   private String characterEncoding = "UTF-8";
046   private byte[] body = new byte[0];
047   private String protocol = "HTTP/1.1";
048   private String scheme = "http";
049   private String serverName = "localhost";
050   private int serverPort = 8080;
051   private String remoteAddr = "";
052   private String remoteHost = "";
053   private Locale locale = Locale.ENGLISH;
054   private String realPath;
055   private int remotePort;
056   private String localName;
057   private String localAddr;
058   private int localPort;
059   private RequestDispatcher requestDispatcher;
060   private ServletContext servletContext;
061   private DispatcherType dispatcherType;
062   private String authType;
063   private Cookie[] cookies;
064   private String pathInfo;
065   private String pathTranslated;
066   private String contextPath = "";
067   private String queryString;
068   private String remoteUser;
069   private Principal userPrincipal;
070   private String requestedSessionId;
071   private String requestURI;
072   private String servletPath = "";
073   private HttpSession httpSession = MockHttpSession.create();
074   private RestContext restContext;
075   private String uri = "";
076   private boolean debug = false;
077   private Set<String> roles = new LinkedHashSet<>();
078
079   /**
080    * Creates a new servlet request.
081    *
082    * Initialized with the following:
083    * <ul>
084    *    <li><c>"Accept: text/json+simple"</c>
085    *    <li><c>"Content-Type: text/json"</c>
086    * </ul>
087    *
088    * @return A new request.
089    */
090   public static MockServletRequest create() {
091      MockServletRequest r = new MockServletRequest();
092      return r;
093   }
094
095   /**
096    * Creates a new servlet request with the specified method name and request path.
097    *
098    * Initialized with the following:
099    * <ul>
100    *    <li><c>"Accept: text/json+simple"</c>
101    *    <li><c>"Content-Type: text/json"</c>
102    * </ul>
103    *
104    * @param method The HTTP method  name.
105    * @param path The request path.
106    * @param pathArgs Optional path arguments.
107    *
108    * @return A new request.
109    */
110   public static MockServletRequest create(String method, String path, Object...pathArgs) {
111      return create()
112         .method(method)
113         .uri(StringUtils.format(path, pathArgs));
114   }
115
116   /**
117    * Specifies the <c>Accept</c> header value.
118    *
119    * @param value The <c>Accept</c> header value.
120    * @return This object (for method chaining).
121    */
122   public MockServletRequest accept(String value) {
123      return header("Accept", value);
124   }
125
126   /**
127    * Specifies the <c>Content-Type</c> header value.
128    *
129    * @param value The <c>Content-Type</c> header value.
130    * @return This object (for method chaining).
131    */
132   public MockServletRequest contentType(String value) {
133      return header("Content-Type", value);
134   }
135
136   /**
137    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"application/json"</js>.
138    *
139    * @return This object (for method chaining).
140    */
141   public MockServletRequest json() {
142      return accept("application/json").contentType("application/json");
143   }
144
145   /**
146    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"application/json+simple"</js>.
147    *
148    * @return This object (for method chaining).
149    */
150   public MockServletRequest simpleJson() {
151      return accept("application/json+simple").contentType("application/json+simple");
152   }
153
154   /**
155    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/xml"</js>.
156    *
157    * @return This object (for method chaining).
158    */
159   public MockServletRequest xml() {
160      return accept("text/xml").contentType("text/xml");
161   }
162
163   /**
164    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/html"</js>.
165    *
166    * @return This object (for method chaining).
167    */
168   public MockServletRequest html() {
169      return accept("text/html").contentType("text/html");
170   }
171
172   /**
173    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/plain"</js>.
174    *
175    * @return This object (for method chaining).
176    */
177   public MockServletRequest plainText() {
178      return accept("text/plain").contentType("text/plain");
179   }
180
181   /**
182    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"octal/msgpack"</js>.
183    *
184    * @return This object (for method chaining).
185    */
186   public MockServletRequest msgpack() {
187      return accept("octal/msgpack").contentType("octal/msgpack");
188   }
189
190   /**
191    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/uon"</js>.
192    *
193    * @return This object (for method chaining).
194    */
195   public MockServletRequest uon() {
196      return accept("text/uon").contentType("text/uon");
197   }
198
199   /**
200    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"application/x-www-form-urlencoded"</js>.
201    *
202    * @return This object (for method chaining).
203    */
204   public MockServletRequest urlEnc() {
205      return accept("application/x-www-form-urlencoded").contentType("application/x-www-form-urlencoded");
206   }
207
208   /**
209    * Convenience method for setting <c>Accept</c> and <c>Content-Type</c> headers to <js>"text/yaml"</js>.
210    *
211    * @return This object (for method chaining).
212    */
213   public MockServletRequest yaml() {
214      return accept("text/yaml").contentType("text/yaml");
215   }
216
217   /**
218    * Fluent setter.
219    *
220    * @param uri The URI of the request.
221    * @return This object (for method chaining).
222    */
223   @Override /* MockHttpRequest */
224   public MockServletRequest uri(String uri) {
225      uri = emptyIfNull(uri);
226      this.uri = uri;
227
228      if (uri.indexOf('?') != -1) {
229         String qs = uri.substring(uri.indexOf('?') + 1);
230         if (qs.indexOf('#') != -1)
231            qs = qs.substring(0, qs.indexOf('#'));
232         queryDataMap.putAll(RestUtils.parseQuery(qs));
233      }
234
235      return this;
236   }
237
238   /**
239    * Fluent setter.
240    *
241    * @param restContext The rest context.
242    * @return This object (for method chaining).
243    */
244   public MockServletRequest restContext(RestContext restContext) {
245      this.restContext = restContext;
246      return this;
247   }
248
249   /**
250    * Adds the specified roles on this request.
251    *
252    * @param roles The roles to add to this request.
253    * @return This object (for method chaining).
254    */
255   public MockServletRequest roles(String...roles) {
256      this.roles.addAll(Arrays.asList(roles));
257      return this;
258   }
259
260   /**
261    * Executes this request and returns the response object.
262    *
263    * @return The response object.
264    * @throws IOException Stream exception occurred.
265    * @throws ServletException Servlet exception occurred.
266    */
267   @Override /* MockHttpRequest */
268   public MockServletResponse execute() throws ServletException, IOException {
269      MockServletResponse res = MockServletResponse.create();
270      restContext.getCallHandler().service(this, res);
271
272      // If the status isn't set, something's broken.
273      if (res.getStatus() == 0)
274         throw new RuntimeException("Response status was 0.");
275
276      if (debug)
277         log(this, res);
278
279      return res;
280   }
281
282   private void log(MockServletRequest req, MockServletResponse res) {
283      StringBuilder sb = new StringBuilder();
284      sb.append("\n=== HTTP Call =================================================================");
285
286      sb.append("\n=== REQUEST ===");
287      sb.append("\n---request headers---");
288      for (Map.Entry<String,String[]> h : req.getHeaders().entrySet())
289         for (String h2 : h.getValue())
290            sb.append("\n").append(h.getKey()).append(": ").append(h2);
291      sb.append("\n---request entity---");
292      sb.append(body == null ? "NONE" : new String(body));
293      sb.append("\n=== RESPONSE ===");
294      sb.append("\nStatus: ").append(res.getStatus());
295      sb.append("\n---response headers---");
296      for (Map.Entry<String,String[]> h : res.getHeaders().entrySet())
297         for (String h2 : h.getValue())
298            sb.append("\n").append(h.getKey()).append(": ").append(h2);
299      sb.append("\n---response content---\n");
300      sb.append(res.getBodyAsString());
301      sb.append("\n=== END ========================================================================");
302
303      System.err.println(sb);  // NOT DEBUG
304   }
305
306   /**
307    * Fluent setter.
308    *
309    * @param value The method name for this request.
310    * @return This object (for method chaining).
311    */
312   @Override /* MockHttpRequest */
313   public MockServletRequest method(String value) {
314      this.method = value;
315      return this;
316   }
317
318   /**
319    * Fluent setter.
320    *
321    * @param value The character encoding.
322    * @return This object (for method chaining).
323    */
324   public MockServletRequest characterEncoding(String value) {
325      this.characterEncoding = value;
326      return this;
327   }
328
329   /**
330    * Fluent setter.
331    *
332    * @param value The protocol.
333    * @return This object (for method chaining).
334    */
335   public MockServletRequest protocol(String value) {
336      this.protocol = value;
337      return this;
338   }
339
340   /**
341    * Fluent setter.
342    *
343    * @param value The scheme.
344    * @return This object (for method chaining).
345    */
346   public MockServletRequest scheme(String value) {
347      this.scheme = value;
348      return this;
349   }
350
351   /**
352    * Fluent setter.
353    *
354    * @param value The server name.
355    * @return This object (for method chaining).
356    */
357   public MockServletRequest serverName(String value) {
358      this.serverName = value;
359      return this;
360   }
361
362   /**
363    * Fluent setter.
364    *
365    * @param value The server port.
366    * @return This object (for method chaining).
367    */
368   public MockServletRequest serverPort(int value) {
369      this.serverPort = value;
370      return this;
371   }
372
373   /**
374    * Fluent setter.
375    *
376    * @param value The remote address.
377    * @return This object (for method chaining).
378    */
379   public MockServletRequest remoteAddr(String value) {
380      this.remoteAddr = value;
381      return this;
382   }
383
384   /**
385    * Fluent setter.
386    *
387    * @param value The remote port.
388    * @return This object (for method chaining).
389    */
390   public MockServletRequest remoteHost(String value) {
391      this.remoteHost = value;
392      return this;
393   }
394
395   /**
396    * Fluent setter.
397    *
398    * @param value The locale.
399    * @return This object (for method chaining).
400    */
401   public MockServletRequest locale(Locale value) {
402      this.locale = value;
403      return this;
404   }
405
406   /**
407    * Fluent setter.
408    *
409    * @param value The real path.
410    * @return This object (for method chaining).
411    */
412   public MockServletRequest realPath(String value) {
413      this.realPath = value;
414      return this;
415   }
416
417   /**
418    * Fluent setter.
419    *
420    * @param value The remote port.
421    * @return This object (for method chaining).
422    */
423   public MockServletRequest remotePort(int value) {
424      this.remotePort = value;
425      return this;
426   }
427
428   /**
429    * Fluent setter.
430    *
431    * @param value The local name.
432    * @return This object (for method chaining).
433    */
434   public MockServletRequest localName(String value) {
435      this.localName = value;
436      return this;
437   }
438
439   /**
440    * Fluent setter.
441    *
442    * @param value The local address.
443    * @return This object (for method chaining).
444    */
445   public MockServletRequest localAddr(String value) {
446      this.localAddr = value;
447      return this;
448   }
449
450   /**
451    * Fluent setter.
452    *
453    * @param value The local port.
454    * @return This object (for method chaining).
455    */
456   public MockServletRequest localPort(int value) {
457      this.localPort = value;
458      return this;
459   }
460
461   /**
462    * Fluent setter.
463    *
464    * @param value The request dispatcher.
465    * @return This object (for method chaining).
466    */
467   public MockServletRequest requestDispatcher(RequestDispatcher value) {
468      this.requestDispatcher = value;
469      return this;
470   }
471
472   /**
473    * Fluent setter.
474    *
475    * @param value The servlet context.
476    * @return This object (for method chaining).
477    */
478   public MockServletRequest servletContext(ServletContext value) {
479      this.servletContext = value;
480      return this;
481   }
482
483   /**
484    * Fluent setter.
485    *
486    * @param value The dispatcher type.
487    * @return This object (for method chaining).
488    */
489   public MockServletRequest dispatcherType(DispatcherType value) {
490      this.dispatcherType = value;
491      return this;
492   }
493
494   /**
495    * Fluent setter.
496    *
497    * @param value The auth type.
498    * @return This object (for method chaining).
499    */
500   public MockServletRequest authType(String value) {
501      this.authType = value;
502      return this;
503   }
504
505   /**
506    * Fluent setter.
507    *
508    * @param value The cookies.
509    * @return This object (for method chaining).
510    */
511   public MockServletRequest cookies(Cookie[] value) {
512      this.cookies = value;
513      return this;
514   }
515
516   /**
517    * Fluent setter.
518    *
519    * @param value The path info.
520    * @return This object (for method chaining).
521    */
522   public MockServletRequest pathInfo(String value) {
523      this.pathInfo = value;
524      return this;
525   }
526
527   /**
528    * Fluent setter.
529    *
530    * @param value The path translated.
531    * @return This object (for method chaining).
532    */
533   public MockServletRequest pathTranslated(String value) {
534      this.pathTranslated = value;
535      return this;
536   }
537
538   /**
539    * Fluent setter.
540    *
541    * @param value The context path.
542    * @return This object (for method chaining).
543    */
544   public MockServletRequest contextPath(String value) {
545      this.contextPath = value;
546      return this;
547   }
548
549   /**
550    * Fluent setter.
551    *
552    * @param value The query string.
553    * @return This object (for method chaining).
554    */
555   public MockServletRequest queryString(String value) {
556      this.queryString = value;
557      return this;
558   }
559
560   /**
561    * Fluent setter.
562    *
563    * @param value The remote user.
564    * @return This object (for method chaining).
565    */
566   public MockServletRequest remoteUser(String value) {
567      this.remoteUser = value;
568      return this;
569   }
570
571   /**
572    * Fluent setter.
573    *
574    * @param value The user principal.
575    * @return This object (for method chaining).
576    */
577   public MockServletRequest userPrincipal(Principal value) {
578      this.userPrincipal = value;
579      return this;
580   }
581
582   /**
583    * Fluent setter.
584    *
585    * @param value The requested session ID.
586    * @return This object (for method chaining).
587    */
588   public MockServletRequest requestedSessionId(String value) {
589      this.requestedSessionId = value;
590      return this;
591   }
592
593   /**
594    * Fluent setter.
595    *
596    * @param value The request URI.
597    * @return This object (for method chaining).
598    */
599   public MockServletRequest requestURI(String value) {
600      this.requestURI = value;
601      return this;
602   }
603
604   /**
605    * Fluent setter.
606    *
607    * @param value The servlet path.
608    * @return This object (for method chaining).
609    */
610   public MockServletRequest servletPath(String value) {
611      this.servletPath = value;
612      return this;
613   }
614
615   /**
616    * Fluent setter.
617    *
618    * @param value The HTTP session.
619    * @return This object (for method chaining).
620    */
621   public MockServletRequest httpSession(HttpSession value) {
622      this.httpSession = value;
623      return this;
624   }
625
626   @Override /* HttpServletRequest */
627   public Object getAttribute(String name) {
628      return attributeMap.get(name);
629   }
630
631   @Override /* HttpServletRequest */
632   public Enumeration<String> getAttributeNames() {
633      return Collections.enumeration(attributeMap.keySet());
634   }
635
636   @Override /* HttpServletRequest */
637   public String getCharacterEncoding() {
638      return characterEncoding;
639   }
640
641   @Override /* HttpServletRequest */
642   public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException {
643      this.characterEncoding = characterEncoding;
644   }
645
646   @Override /* HttpServletRequest */
647   public int getContentLength() {
648      return body == null ? 0 : body.length;
649   }
650
651   @Override /* HttpServletRequest */
652   public long getContentLengthLong() {
653      return body == null ? 0 : body.length;
654   }
655
656   @Override /* HttpServletRequest */
657   public String getContentType() {
658      return getHeader("Content-Type");
659   }
660
661   @Override /* HttpServletRequest */
662   public ServletInputStream getInputStream() throws IOException {
663      if (formDataMap != null)
664         body = UrlEncodingSerializer.DEFAULT.toString(formDataMap).getBytes();
665      return new BoundedServletInputStream(new ByteArrayInputStream(body), Integer.MAX_VALUE);
666   }
667
668   @Override /* HttpServletRequest */
669   public String getParameter(String name) {
670      String[] s = getParameterMap().get(name);
671      return s == null || s.length == 0 ? null : s[0];
672   }
673
674   @Override /* HttpServletRequest */
675   public Enumeration<String> getParameterNames() {
676      return Collections.enumeration(new ArrayList<>(getParameterMap().keySet()));
677   }
678
679   @Override /* HttpServletRequest */
680   public String[] getParameterValues(String name) {
681      return getParameterMap().get(name);
682   }
683
684   @Override /* HttpServletRequest */
685   public Map<String,String[]> getParameterMap() {
686      if ("POST".equalsIgnoreCase(method)) {
687         if (formDataMap == null) {
688            try {
689               formDataMap = RestUtils.parseQuery(IOUtils.read(body));
690            } catch (IOException e) {
691               e.printStackTrace();
692            }
693         }
694         return formDataMap;
695      }
696      return queryDataMap;
697   }
698
699   @Override /* HttpServletRequest */
700   public String getProtocol() {
701      return protocol;
702   }
703
704   @Override /* HttpServletRequest */
705   public String getScheme() {
706      return scheme;
707   }
708
709   @Override /* HttpServletRequest */
710   public String getServerName() {
711      return serverName;
712   }
713
714   @Override /* HttpServletRequest */
715   public int getServerPort() {
716      return serverPort;
717   }
718
719   @Override /* HttpServletRequest */
720   public BufferedReader getReader() throws IOException {
721      return new BufferedReader(new InputStreamReader(getInputStream(), characterEncoding));
722   }
723
724   @Override /* HttpServletRequest */
725   public String getRemoteAddr() {
726      return remoteAddr;
727   }
728
729   @Override /* HttpServletRequest */
730   public String getRemoteHost() {
731      return remoteHost;
732   }
733
734   @Override /* HttpServletRequest */
735   public void setAttribute(String name, Object o) {
736      this.attributeMap.put(name, o);
737   }
738
739   @Override /* HttpServletRequest */
740   public void removeAttribute(String name) {
741      this.attributeMap.remove(name);
742   }
743
744   @Override /* HttpServletRequest */
745   public Locale getLocale() {
746      return locale;
747   }
748
749   @Override /* HttpServletRequest */
750   public Enumeration<Locale> getLocales() {
751      return Collections.enumeration(Arrays.asList(locale));
752   }
753
754   @Override /* HttpServletRequest */
755   public boolean isSecure() {
756      return false;
757   }
758
759   @Override /* HttpServletRequest */
760   public RequestDispatcher getRequestDispatcher(String path) {
761      return requestDispatcher;
762   }
763
764   @Override /* HttpServletRequest */
765   public String getRealPath(String path) {
766      return realPath;
767   }
768
769   @Override /* HttpServletRequest */
770   public int getRemotePort() {
771      return remotePort;
772   }
773
774   @Override /* HttpServletRequest */
775   public String getLocalName() {
776      return localName;
777   }
778
779   @Override /* HttpServletRequest */
780   public String getLocalAddr() {
781      return localAddr;
782   }
783
784   @Override /* HttpServletRequest */
785   public int getLocalPort() {
786      return localPort;
787   }
788
789   @Override /* HttpServletRequest */
790   public ServletContext getServletContext() {
791      return servletContext;
792   }
793
794   @Override /* HttpServletRequest */
795   public AsyncContext startAsync() throws IllegalStateException {
796      return null;
797   }
798
799   @Override /* HttpServletRequest */
800   public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
801      return null;
802   }
803
804   @Override /* HttpServletRequest */
805   public boolean isAsyncStarted() {
806      return false;
807   }
808
809   @Override /* HttpServletRequest */
810   public boolean isAsyncSupported() {
811      return false;
812   }
813
814   @Override /* HttpServletRequest */
815   public AsyncContext getAsyncContext() {
816      return null;
817   }
818
819   @Override /* HttpServletRequest */
820   public DispatcherType getDispatcherType() {
821      return dispatcherType;
822   }
823
824   @Override /* HttpServletRequest */
825   public String getAuthType() {
826      return authType;
827   }
828
829   @Override /* HttpServletRequest */
830   public Cookie[] getCookies() {
831      return cookies;
832   }
833
834   @Override /* HttpServletRequest */
835   public long getDateHeader(String name) {
836      String s = getHeader(name);
837      return s == null ? 0 : org.apache.juneau.http.Date.forString(s).asDate().getTime();
838   }
839
840   @Override /* HttpServletRequest */
841   public String getHeader(String name) {
842      String[] s = headerMap.get(name);
843      return s == null || s.length == 0 ? null : s[0];
844   }
845
846   @Override /* HttpServletRequest */
847   public Enumeration<String> getHeaders(String name) {
848      String[] s = headerMap.get(name);
849      return Collections.enumeration(Arrays.asList(s == null ? new String[0] : s));
850   }
851
852   /**
853    * Returns the headers defined on this request.
854    *
855    * @return The headers defined on this request.  Never <jk>null</jk>.
856    */
857   public Map<String,String[]> getHeaders() {
858      return headerMap;
859   }
860
861   @Override /* HttpServletRequest */
862   public Enumeration<String> getHeaderNames() {
863      return Collections.enumeration(headerMap.keySet());
864   }
865
866   @Override /* HttpServletRequest */
867   public int getIntHeader(String name) {
868      String s = getHeader(name);
869      return s == null || s.isEmpty() ? 0 : Integer.parseInt(s);
870   }
871
872   @Override /* HttpServletRequest */
873   public String getMethod() {
874      return method;
875   }
876
877   @Override /* HttpServletRequest */
878   public String getPathInfo() {
879      if (pathInfo == null) {
880         pathInfo = getRequestURI();
881         if (isNotEmpty(contextPath))
882            pathInfo = pathInfo.substring(contextPath.length());
883         if (isNotEmpty(servletPath))
884            pathInfo = pathInfo.substring(servletPath.length());
885      }
886      return nullIfEmpty(urlDecode(pathInfo));
887   }
888
889   @Override /* HttpServletRequest */
890   public String getPathTranslated() {
891      if (pathTranslated == null)
892         pathTranslated = "/mock-path" + getPathInfo();
893      return pathTranslated;
894   }
895
896   @Override /* HttpServletRequest */
897   public String getContextPath() {
898      return contextPath;
899   }
900
901   @Override /* HttpServletRequest */
902   public String getQueryString() {
903      if (queryString == null) {
904         if (queryDataMap.isEmpty())
905            queryString = "";
906         else {
907            StringBuilder sb = new StringBuilder();
908            for (Map.Entry<String,String[]> e : queryDataMap.entrySet())
909               if (e.getValue() == null)
910                  sb.append(sb.length() == 0 ? "" : "&").append(urlEncode(e.getKey()));
911               else for (String v : e.getValue())
912                  sb.append(sb.length() == 0 ? "" : "&").append(urlEncode(e.getKey())).append('=').append(urlEncode(v));
913            queryString = sb.toString();
914         }
915      }
916      return isEmpty(queryString) ? null : queryString;
917   }
918
919   @Override /* HttpServletRequest */
920   public String getRemoteUser() {
921      return remoteUser;
922   }
923
924   @Override /* HttpServletRequest */
925   public boolean isUserInRole(String role) {
926      return roles.contains(role);
927   }
928
929   @Override /* HttpServletRequest */
930   public Principal getUserPrincipal() {
931      return userPrincipal;
932   }
933
934   @Override /* HttpServletRequest */
935   public String getRequestedSessionId() {
936      return requestedSessionId;
937   }
938
939   @Override /* HttpServletRequest */
940   public String getRequestURI() {
941      if (requestURI == null) {
942         requestURI = uri;
943         requestURI = requestURI.replaceAll("^\\w+\\:\\/\\/[^\\/]+", "").replaceAll("\\?.*$", "");
944      }
945      return requestURI;
946   }
947
948   @Override /* HttpServletRequest */
949   public StringBuffer getRequestURL() {
950      return new StringBuffer(uri.replaceAll("\\?.*$", ""));
951   }
952
953   @Override /* HttpServletRequest */
954   public String getServletPath() {
955      return servletPath;
956   }
957
958   @Override /* HttpServletRequest */
959   public HttpSession getSession(boolean create) {
960      return httpSession;
961   }
962
963   @Override /* HttpServletRequest */
964   public HttpSession getSession() {
965      return httpSession;
966   }
967
968   @Override /* HttpServletRequest */
969   public String changeSessionId() {
970      return null;
971   }
972
973   @Override /* HttpServletRequest */
974   public boolean isRequestedSessionIdValid() {
975      return false;
976   }
977
978   @Override /* HttpServletRequest */
979   public boolean isRequestedSessionIdFromCookie() {
980      return false;
981   }
982
983   @Override /* HttpServletRequest */
984   public boolean isRequestedSessionIdFromURL() {
985      return false;
986   }
987
988   @Override /* HttpServletRequest */
989   public boolean isRequestedSessionIdFromUrl() {
990      return false;
991   }
992
993   @Override /* HttpServletRequest */
994   public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
995      return false;
996   }
997
998   @Override /* HttpServletRequest */
999   public void login(String username, String password) throws ServletException {
1000   }
1001
1002   @Override /* HttpServletRequest */
1003   public void logout() throws ServletException {
1004   }
1005
1006   @Override /* HttpServletRequest */
1007   public Collection<Part> getParts() throws IOException, ServletException {
1008      return null;
1009   }
1010
1011   @Override /* HttpServletRequest */
1012   public Part getPart(String name) throws IOException, ServletException {
1013      return null;
1014   }
1015
1016   @Override /* HttpServletRequest */
1017   public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
1018      return null;
1019   }
1020
1021   //=================================================================================================================
1022   // Convenience methods
1023   //=================================================================================================================
1024
1025   /**
1026    * Fluent setter.
1027    *
1028    * @param headers Headers to add to this request.
1029    * @return This object (for method chaining).
1030    */
1031   public MockServletRequest headers(Map<String,Object> headers) {
1032      if (headers != null)
1033         for (Map.Entry<String,Object> e : headers.entrySet())
1034            header(e.getKey(), e.getValue());
1035      return this;
1036   }
1037
1038   /**
1039    * Fluent setter.
1040    *
1041    * @param name Header name.
1042    * @param value
1043    *    Header value.
1044    *    <br>The value is converted to a simple string using {@link Object#toString()}.
1045    * @return This object (for method chaining).
1046    */
1047   @Override /* MockHttpRequest */
1048   public MockServletRequest header(String name, Object value) {
1049      if (value == null)
1050         headerMap.remove(name);
1051      else
1052         headerMap.put(name, new String[] {stringify(value)});
1053      return this;
1054   }
1055
1056   /**
1057    * Fluent setter.
1058    *
1059    * @param name Request attribute name.
1060    * @param value Request attribute value.
1061    * @return This object (for method chaining).
1062    */
1063   public MockServletRequest attribute(String name, Object value) {
1064      this.attributeMap.put(name, value);
1065      return this;
1066   }
1067
1068   /**
1069    * Fluent setter.
1070    *
1071    * @param value
1072    *    The body of the request.
1073    *    <br>Can be any of the following data types:
1074    *    <ul>
1075    *       <li><code><jk>byte</jk>[]</code>
1076    *       <li>{@link Reader}
1077    *       <li>{@link InputStream}
1078    *       <li>{@link CharSequence}
1079    *    </ul>
1080    *    Any other types are converted to a string using the <c>toString()</c> method.
1081    * @return This object (for method chaining).
1082    */
1083   @Override /* MockHttpRequest */
1084   public MockServletRequest body(Object value) {
1085      try {
1086         if (value instanceof byte[])
1087            this.body = (byte[])value;
1088         else if (value instanceof Reader)
1089            this.body = IOUtils.read((Reader)value).getBytes();
1090         else if (value instanceof InputStream)
1091            this.body = IOUtils.readBytes((InputStream)value, 1024);
1092         else if (value instanceof CharSequence)
1093            this.body = ((CharSequence)value).toString().getBytes();
1094         else if (value != null)
1095            this.body = value.toString().getBytes();
1096      } catch (IOException e) {
1097         throw new RuntimeException(e);
1098      }
1099      return this;
1100   }
1101
1102   /**
1103    * Adds a form data entry to this request.
1104    *
1105    * @param key The form data key.
1106    * @param value The form data value.
1107    *    <br>The value is converted to a simple string using {@link Object#toString()}.
1108    * @return This object (for method chaining).
1109    */
1110   public MockServletRequest formData(String key, Object value) {
1111      if (formDataMap == null)
1112         formDataMap = new LinkedHashMap<>();
1113      String s = stringify(value);
1114      String[] existing = formDataMap.get(key);
1115      if (existing == null)
1116         existing = new String[]{s};
1117      else
1118         existing = new AList<>().appendAll(Arrays.asList(existing)).append(s).toArray(new String[0]);
1119      formDataMap.put(key, existing);
1120      return this;
1121   }
1122
1123   /**
1124    * Adds a query data entry to this request.
1125    *
1126    * @param key The query key.
1127    * @param value The query value.
1128    *    <br>The value is converted to a simple string using {@link Object#toString()}.
1129    * @return This object (for method chaining).
1130    */
1131   public MockServletRequest query(String key, Object value) {
1132      String s = stringify(value);
1133      String[] existing = queryDataMap.get(key);
1134      if (existing == null)
1135         existing = new String[]{s};
1136      else
1137         existing = new AList<>().appendAll(Arrays.asList(existing)).append(s).toArray(new String[0]);
1138      queryDataMap.put(key, existing);
1139      queryString = null;
1140      return this;
1141   }
1142
1143   //=================================================================================================================
1144   // Convenience methods - headers
1145   //=================================================================================================================
1146
1147   /**
1148    * Specifies the <c>Accept</c> header value on the request.
1149    *
1150    * @param value The new value.
1151    * @return This object (for method chaining).
1152    */
1153   public MockServletRequest accept(Object value) {
1154      return header("Accept", value);
1155   }
1156
1157   /**
1158    * Specifies the <c>Accept-Charset</c> header value on the request.
1159    *
1160    * @param value The new value.
1161    * @return This object (for method chaining).
1162    */
1163   public MockServletRequest acceptCharset(Object value) {
1164      return header("Accept-Charset", value);
1165   }
1166
1167   /**
1168    * Specifies the <c>Accept-Encoding</c> header value on the request.
1169    *
1170    * @param value The new value.
1171    * @return This object (for method chaining).
1172    */
1173   public MockServletRequest acceptEncoding(Object value) {
1174      return header("Accept-Encoding", value);
1175   }
1176
1177   /**
1178    * Specifies the <c>Accept-Language</c> header value on the request.
1179    *
1180    * @param value The new value.
1181    * @return This object (for method chaining).
1182    */
1183   public MockServletRequest acceptLanguage(Object value) {
1184      return header("Accept-Language", value);
1185   }
1186
1187   /**
1188    * Specifies the <c>Authorization</c> header value on the request.
1189    *
1190    * @param value The new value for the header.
1191    * @return This object (for method chaining).
1192    */
1193   public MockServletRequest authorization(Object value) {
1194      return header("Authorization", value);
1195   }
1196
1197   /**
1198    * Specifies the <c>Cache-Control</c> header value on the request.
1199    *
1200    * @param value The new value for the header.
1201    * @return This object (for method chaining).
1202    */
1203   public MockServletRequest cacheControl(Object value) {
1204      return header("Cache-Control", value);
1205   }
1206
1207   /**
1208    * Specifies the <c>X-Client-Version</c> header value on the request.
1209    *
1210    * @param value The new value.
1211    * @return This object (for method chaining).
1212    */
1213   public MockServletRequest clientVersion(Object value) {
1214      return header("X-Client-Version", value);
1215   }
1216
1217   /**
1218    * Specifies the <c>Connection</c> header value on the request.
1219    *
1220    * @param value The new value for the header.
1221    * @return This object (for method chaining).
1222    */
1223   public MockServletRequest connection(Object value) {
1224      return header("Connection", value);
1225   }
1226
1227   /**
1228    * Specifies the <c>Content-Encoding</c> header value on the request.
1229    *
1230    * @param value The new value.
1231    * @return This object (for method chaining).
1232    */
1233   public MockServletRequest contentEncoding(Object value) {
1234      return header("Content-Encoding", value);
1235   }
1236
1237   /**
1238    * Specifies the <c>Content-Length</c> header value on the request.
1239    *
1240    * @param value The new value for the header.
1241    * @return This object (for method chaining).
1242    */
1243   public MockServletRequest contentLength(Object value) {
1244      return header("Content-Length", value);
1245   }
1246
1247   /**
1248    * Specifies the <c>Content-Type</c> header value on the request.
1249    *
1250    * @param value The new value.
1251    * @return This object (for method chaining).
1252    */
1253   public MockServletRequest contentType(Object value) {
1254      return header("Content-Type", value);
1255   }
1256
1257   /**
1258    * Specifies the <c>Date</c> header value on the request.
1259    *
1260    * @param value The new value for the header.
1261    * @return This object (for method chaining).
1262    */
1263   public MockServletRequest date(Object value) {
1264      return header("Date", value);
1265   }
1266
1267   /**
1268    * Specifies the <c>Expect</c> header value on the request.
1269    *
1270    * @param value The new value for the header.
1271    * @return This object (for method chaining).
1272    */
1273   public MockServletRequest expect(Object value) {
1274      return header("Expect", value);
1275   }
1276
1277   /**
1278    * Specifies the <c>From</c> header value on the request.
1279    *
1280    * @param value The new value for the header.
1281    * @return This object (for method chaining).
1282    */
1283   public MockServletRequest from(Object value) {
1284      return header("From", value);
1285   }
1286
1287   /**
1288    * Specifies the <c>Host</c> header value on the request.
1289    *
1290    * @param value The new value for the header.
1291    * @return This object (for method chaining).
1292    */
1293   public MockServletRequest host(Object value) {
1294      return header("Host", value);
1295   }
1296
1297   /**
1298    * Specifies the <c>If-Match</c> header value on the request.
1299    *
1300    * @param value The new value for the header.
1301    * @return This object (for method chaining).
1302    */
1303   public MockServletRequest ifMatch(Object value) {
1304      return header("If-Match", value);
1305   }
1306
1307   /**
1308    * Specifies the <c>If-Modified-Since</c> header value on the request.
1309    *
1310    * @param value The new value for the header.
1311    * @return This object (for method chaining).
1312    */
1313   public MockServletRequest ifModifiedSince(Object value) {
1314      return header("If-Modified-Since", value);
1315   }
1316
1317   /**
1318    * Specifies the <c>If-None-Match</c> header value on the request.
1319    *
1320    * @param value The new value for the header.
1321    * @return This object (for method chaining).
1322    */
1323   public MockServletRequest ifNoneMatch(Object value) {
1324      return header("If-None-Match", value);
1325   }
1326
1327   /**
1328    * Specifies the <c>If-Range</c> header value on the request.
1329    *
1330    * @param value The new value for the header.
1331    * @return This object (for method chaining).
1332    */
1333   public MockServletRequest ifRange(Object value) {
1334      return header("If-Range", value);
1335   }
1336
1337   /**
1338    * Specifies the <c>If-Unmodified-Since</c> header value on the request.
1339    *
1340    * @param value The new value for the header.
1341    * @return This object (for method chaining).
1342    */
1343   public MockServletRequest ifUnmodifiedSince(Object value) {
1344      return header("If-Unmodified-Since", value);
1345   }
1346
1347   /**
1348    * Specifies the <c>Max-Forwards</c> header value on the request.
1349    *
1350    * @param value The new value for the header.
1351    * @return This object (for method chaining).
1352    */
1353   public MockServletRequest maxForwards(Object value) {
1354      return header("Max-Forwards", value);
1355   }
1356
1357   /**
1358    * Specifies the <c>Pragma</c> header value on the request.
1359    *
1360    * @param value The new value for the header.
1361    * @return This object (for method chaining).
1362    */
1363   public MockServletRequest pragma(Object value) {
1364      return header("Pragma", value);
1365   }
1366
1367   /**
1368    * Specifies the <c>Proxy-Authorization</c> header value on the request.
1369    *
1370    * @param value The new value for the header.
1371    * @return This object (for method chaining).
1372    */
1373   public MockServletRequest proxyAuthorization(Object value) {
1374      return header("Proxy-Authorization", value);
1375   }
1376
1377   /**
1378    * Specifies the <c>Range</c> header value on the request.
1379    *
1380    * @param value The new value for the header.
1381    * @return This object (for method chaining).
1382    */
1383   public MockServletRequest range(Object value) {
1384      return header("Range", value);
1385   }
1386
1387   /**
1388    * Specifies the <c>Referer</c> header value on the request.
1389    *
1390    * @param value The new value for the header.
1391    * @return This object (for method chaining).
1392    */
1393   public MockServletRequest referer(Object value) {
1394      return header("Referer", value);
1395   }
1396
1397   /**
1398    * Specifies the <c>TE</c> header value on the request.
1399    *
1400    * @param value The new value for the header.
1401    * @return This object (for method chaining).
1402    */
1403   public MockServletRequest te(Object value) {
1404      return header("TE", value);
1405   }
1406
1407   /**
1408    * Specifies the <c>Upgrade</c> header value on the request.
1409    *
1410    * @param value The new value for the header.
1411    * @return This object (for method chaining).
1412    */
1413   public MockServletRequest upgrade(Object value) {
1414      return header("Upgrade", value);
1415   }
1416
1417   /**
1418    * Specifies the <c>User-Agent</c> header value on the request.
1419    *
1420    * @param value The new value for the header.
1421    * @return This object (for method chaining).
1422    */
1423   public MockServletRequest userAgent(Object value) {
1424      return header("User-Agent", value);
1425   }
1426
1427   /**
1428    * Specifies the <c>Warning</c> header value on the request.
1429    *
1430    * @param value The new value for the header.
1431    * @return This object (for method chaining).
1432    */
1433   public MockServletRequest warning(Object value) {
1434      return header("Warning", value);
1435   }
1436
1437   /**
1438    * Enabled debug mode on this request.
1439    *
1440    * <p>
1441    * Causes information about the request execution to be sent to STDERR.
1442    *
1443    * @return This object (for method chaining).
1444    */
1445   public MockServletRequest debug() {
1446      return debug(true);
1447   }
1448
1449   /**
1450    * Enabled debug mode on this request.
1451    *
1452    * <p>
1453    * Causes information about the request execution to be sent to STDERR.
1454    *
1455    * @param value The enable flag value.
1456    * @return This object (for method chaining).
1457    */
1458   public MockServletRequest debug(boolean value) {
1459      this.debug = value;
1460      header("X-Debug", value ? true : null);
1461      return this;
1462   }
1463
1464   /**
1465    * Enabled no-trace on this request.
1466    *
1467    * <p>
1468    * Prevents errors from being logged on the server side if no-trace per-request is enabled.
1469    *
1470    * @return This object (for method chaining).
1471    */
1472   public MockServletRequest noTrace() {
1473      return noTrace(true);
1474   }
1475
1476   /**
1477    * Enabled debug mode on this request.
1478    *
1479    * <p>
1480    * Prevents errors from being logged on the server side if no-trace per-request is enabled.
1481    *
1482    * @param value The enable flag value.
1483    * @return This object (for method chaining).
1484    */
1485   public MockServletRequest noTrace(boolean value) {
1486      header("X-NoTrace", true);
1487      return this;
1488   }
1489}