001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.mock;
018
019import static java.util.Collections.*;
020import static org.apache.juneau.common.utils.IOUtils.*;
021import static org.apache.juneau.common.utils.ThrowableUtils.*;
022import static org.apache.juneau.common.utils.Utils.*;
023import static org.apache.juneau.http.HttpHeaders.*;
024
025import java.io.*;
026import java.security.*;
027import java.util.*;
028
029import org.apache.http.*;
030import org.apache.juneau.common.utils.*;
031import org.apache.juneau.internal.*;
032import org.apache.juneau.rest.util.*;
033import org.apache.juneau.urlencoding.*;
034
035import jakarta.servlet.*;
036import jakarta.servlet.http.*;
037
038/**
039 * A mutable implementation of {@link HttpServletRequest} for mocking purposes.
040 *
041 * <h5 class='section'>See Also:</h5><ul>
042 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestMockBasics">juneau-rest-mock Basics</a>
043 * </ul>
044 */
045public class MockServletRequest implements HttpServletRequest {
046
047   private String method = "GET";
048   private Map<String,String[]> queryDataMap = map();
049   private Map<String,String[]> formDataMap;
050   private Map<String,String[]> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
051   private Map<String,Object> attributeMap = map();
052   private String characterEncoding = "UTF-8";
053   private byte[] content = {};
054   private String protocol = "HTTP/1.1";
055   private String scheme = "http";
056   private String serverName = "localhost";
057   private int serverPort = 8080;
058   private String remoteAddr = "";
059   private String remoteHost = "";
060   private Locale locale = Locale.ENGLISH;
061   private int remotePort;
062   private String localName;
063   private String localAddr;
064   private int localPort;
065   private Map<String,RequestDispatcher> requestDispatcher = map();
066   private ServletContext servletContext;
067   private DispatcherType dispatcherType;
068   private String authType;
069   private Cookie[] cookies;
070   private String pathInfo;
071   private String pathTranslated;
072   private String contextPath = "";
073   private String queryString;
074   private String remoteUser;
075   private Principal userPrincipal;
076   private String requestedSessionId;
077   private String requestURI;
078   private String servletPath = "";
079   private HttpSession httpSession = MockHttpSession.create();
080   private String uri = "";
081   private Set<String> roles = set();
082
083   /**
084    * Creates a new servlet request.
085    *
086    * Initialized with the following:
087    * <ul>
088    *    <li><c>"Accept: text/json5"</c>
089    *    <li><c>"Content-Type: text/json"</c>
090    * </ul>
091    *
092    * @return A new request.
093    */
094   public static MockServletRequest create() {
095      MockServletRequest r = new MockServletRequest();
096      return r;
097   }
098
099   /**
100    * Creates a new servlet request with the specified method name and request path.
101    *
102    * Initialized with the following:
103    * <ul>
104    *    <li><c>"Accept: text/json5"</c>
105    *    <li><c>"Content-Type: text/json"</c>
106    * </ul>
107    *
108    * @param method The HTTP method  name.
109    * @param uri The request path.
110    * @param pathArgs Optional path arguments.
111    *
112    * @return A new request.
113    */
114   public static MockServletRequest create(String method, String uri, Object...pathArgs) {
115      return create()
116         .method(method)
117         .uri(StringUtils.format(uri, pathArgs));
118   }
119
120   /**
121    * Fluent setter.
122    *
123    * @param uri The URI of the request.
124    * @return This object.
125    */
126   public MockServletRequest uri(String uri) {
127      uri = emptyIfNull(uri);
128      this.uri = uri;
129
130      if (uri.indexOf('?') != -1) {
131         String qs = uri.substring(uri.indexOf('?') + 1);
132         if (qs.indexOf('#') != -1)
133            qs = qs.substring(0, qs.indexOf('#'));
134         queryString = qs;
135         queryDataMap.putAll(RestUtils.parseQuery(qs));
136      }
137
138      return this;
139   }
140
141   /**
142    * Adds the specified roles on this request.
143    *
144    * @param roles The roles to add to this request (e.g. <js>"ROLE_ADMIN"</js>).
145    * @return This object.
146    */
147   public MockServletRequest roles(String...roles) {
148      this.roles = set(roles);
149      return this;
150   }
151
152   /**
153    * Adds the specified parent path variables to this servlet request.
154    *
155    * <p>
156    * See {@link MockRestClient.Builder#pathVars(Map)} for an example.
157    *
158    * @param pathVars The
159    * @return This object.
160    * @see MockRestClient.Builder#pathVars(Map)
161    */
162   public MockServletRequest pathVars(Map<String,String> pathVars) {
163      if (pathVars != null)
164         this.attributeMap.put("juneau.pathVars", new TreeMap<>(pathVars));
165      return this;
166   }
167
168   /**
169    * Add resolved path variables to this client.
170    *
171    * <p>
172    * Identical to {@link #pathVars(Map)} but allows you to specify as a list of key/value pairs.
173    *
174    * @param pairs The key/value pairs.  Must be an even number of parameters.
175    * @return This object.
176    */
177   public MockServletRequest pathVars(String...pairs) {
178      return pathVars(CollectionUtils.mapBuilder(String.class,String.class).addPairs((Object[])pairs).build());
179   }
180
181   /**
182    * Adds the specified role on this request.
183    *
184    * @param role The role to add to this request (e.g. <js>"ROLE_ADMIN"</js>).
185    * @return This object.
186    */
187   public MockServletRequest role(String role) {
188      this.roles = set(role);
189      return this;
190   }
191
192   /**
193    * Fluent setter.
194    *
195    * @param value The method name for this request.
196    * @return This object.
197    */
198   public MockServletRequest method(String value) {
199      this.method = value;
200      return this;
201   }
202
203   /**
204    * Fluent setter.
205    *
206    * @param value The character encoding.
207    * @return This object.
208    */
209   public MockServletRequest characterEncoding(String value) {
210      this.characterEncoding = value;
211      return this;
212   }
213
214   /**
215    * Fluent setter.
216    *
217    * @param value The protocol.
218    * @return This object.
219    */
220   public MockServletRequest protocol(String value) {
221      this.protocol = value;
222      return this;
223   }
224
225   /**
226    * Fluent setter.
227    *
228    * @param value The scheme.
229    * @return This object.
230    */
231   public MockServletRequest scheme(String value) {
232      this.scheme = value;
233      return this;
234   }
235
236   /**
237    * Fluent setter.
238    *
239    * @param value The server name.
240    * @return This object.
241    */
242   public MockServletRequest serverName(String value) {
243      this.serverName = value;
244      return this;
245   }
246
247   /**
248    * Fluent setter.
249    *
250    * @param value The server port.
251    * @return This object.
252    */
253   public MockServletRequest serverPort(int value) {
254      this.serverPort = value;
255      return this;
256   }
257
258   /**
259    * Fluent setter.
260    *
261    * @param value The remote address.
262    * @return This object.
263    */
264   public MockServletRequest remoteAddr(String value) {
265      this.remoteAddr = value;
266      return this;
267   }
268
269   /**
270    * Fluent setter.
271    *
272    * @param value The remote port.
273    * @return This object.
274    */
275   public MockServletRequest remoteHost(String value) {
276      this.remoteHost = value;
277      return this;
278   }
279
280   /**
281    * Fluent setter.
282    *
283    * @param value The locale.
284    * @return This object.
285    */
286   public MockServletRequest locale(Locale value) {
287      this.locale = value;
288      return this;
289   }
290
291   /**
292    * Fluent setter.
293    *
294    * @param value The remote port.
295    * @return This object.
296    */
297   public MockServletRequest remotePort(int value) {
298      this.remotePort = value;
299      return this;
300   }
301
302   /**
303    * Fluent setter.
304    *
305    * @param value The local name.
306    * @return This object.
307    */
308   public MockServletRequest localName(String value) {
309      this.localName = value;
310      return this;
311   }
312
313   /**
314    * Fluent setter.
315    *
316    * @param value The local address.
317    * @return This object.
318    */
319   public MockServletRequest localAddr(String value) {
320      this.localAddr = value;
321      return this;
322   }
323
324   /**
325    * Fluent setter.
326    *
327    * @param value The local port.
328    * @return This object.
329    */
330   public MockServletRequest localPort(int value) {
331      this.localPort = value;
332      return this;
333   }
334
335   /**
336    * Fluent setter.
337    *
338    * @param name The path to resolve.
339    * @param value The request dispatcher.
340    * @return This object.
341    */
342   public MockServletRequest requestDispatcher(String name, RequestDispatcher value) {
343      this.requestDispatcher.put(name, value);
344      return this;
345   }
346
347   /**
348    * Fluent setter.
349    *
350    * @param value The servlet context.
351    * @return This object.
352    */
353   public MockServletRequest servletContext(ServletContext value) {
354      this.servletContext = value;
355      return this;
356   }
357
358   /**
359    * Fluent setter.
360    *
361    * @param value The dispatcher type.
362    * @return This object.
363    */
364   public MockServletRequest dispatcherType(DispatcherType value) {
365      this.dispatcherType = value;
366      return this;
367   }
368
369   /**
370    * Fluent setter.
371    *
372    * @param value The auth type.
373    * @return This object.
374    */
375   public MockServletRequest authType(String value) {
376      this.authType = value;
377      return this;
378   }
379
380   /**
381    * Fluent setter.
382    *
383    * @param value The cookies.
384    * @return This object.
385    */
386   public MockServletRequest cookies(Cookie[] value) {
387      this.cookies = value;
388      return this;
389   }
390
391   /**
392    * Fluent setter.
393    *
394    * @param value The path info.
395    * @return This object.
396    */
397   public MockServletRequest pathInfo(String value) {
398      this.pathInfo = value;
399      return this;
400   }
401
402   /**
403    * Fluent setter.
404    *
405    * @param value The path translated.
406    * @return This object.
407    */
408   public MockServletRequest pathTranslated(String value) {
409      this.pathTranslated = value;
410      return this;
411   }
412
413   /**
414    * Fluent setter.
415    *
416    * @param value The context path.
417    * @return This object.
418    */
419   public MockServletRequest contextPath(String value) {
420      this.contextPath = value;
421      return this;
422   }
423
424   /**
425    * Fluent setter.
426    *
427    * @param value The query string.
428    * @return This object.
429    */
430   public MockServletRequest queryString(String value) {
431      this.queryString = value;
432      return this;
433   }
434
435   /**
436    * Fluent setter.
437    *
438    * @param value The remote user.
439    * @return This object.
440    */
441   public MockServletRequest remoteUser(String value) {
442      this.remoteUser = value;
443      return this;
444   }
445
446   /**
447    * Fluent setter.
448    *
449    * @param value The user principal.
450    * @return This object.
451    */
452   public MockServletRequest userPrincipal(Principal value) {
453      this.userPrincipal = value;
454      return this;
455   }
456
457   /**
458    * Fluent setter.
459    *
460    * @param value The requested session ID.
461    * @return This object.
462    */
463   public MockServletRequest requestedSessionId(String value) {
464      this.requestedSessionId = value;
465      return this;
466   }
467
468   /**
469    * Fluent setter.
470    *
471    * @param value The request URI.
472    * @return This object.
473    */
474   public MockServletRequest requestURI(String value) {
475      this.requestURI = value;
476      return this;
477   }
478
479   /**
480    * Fluent setter.
481    *
482    * @param value The servlet path.
483    * @return This object.
484    */
485   public MockServletRequest servletPath(String value) {
486      this.servletPath = value;
487      return this;
488   }
489
490   /**
491    * Fluent setter.
492    *
493    * @param value The HTTP session.
494    * @return This object.
495    */
496   public MockServletRequest httpSession(HttpSession value) {
497      this.httpSession = value;
498      return this;
499   }
500
501   @Override /* HttpServletRequest */
502   public Object getAttribute(String name) {
503      return attributeMap.get(name);
504   }
505
506   @Override /* HttpServletRequest */
507   public Enumeration<String> getAttributeNames() {
508      return Collections.enumeration(attributeMap.keySet());
509   }
510
511   @Override /* HttpServletRequest */
512   public String getCharacterEncoding() {
513      return characterEncoding;
514   }
515
516   @Override /* HttpServletRequest */
517   public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException {
518      this.characterEncoding = characterEncoding;
519   }
520
521   @Override /* HttpServletRequest */
522   public int getContentLength() {
523      return content == null ? 0 : content.length;
524   }
525
526   @Override /* HttpServletRequest */
527   public long getContentLengthLong() {
528      return content == null ? 0 : content.length;
529   }
530
531   @Override /* HttpServletRequest */
532   public String getContentType() {
533      return getHeader("Content-Type");
534   }
535
536   @Override /* HttpServletRequest */
537   public ServletInputStream getInputStream() throws IOException {
538      if (formDataMap != null)
539         content = UrlEncodingSerializer.DEFAULT.toString(formDataMap).getBytes();
540      return new BoundedServletInputStream(new ByteArrayInputStream(content), Integer.MAX_VALUE);
541   }
542
543   @Override /* HttpServletRequest */
544   public String getParameter(String name) {
545      String[] s = getParameterMap().get(name);
546      return s == null || s.length == 0 ? null : s[0];
547   }
548
549   @Override /* HttpServletRequest */
550   public Enumeration<String> getParameterNames() {
551      return enumeration(CollectionUtils.listFrom(getParameterMap().keySet()));
552   }
553
554   @Override /* HttpServletRequest */
555   public String[] getParameterValues(String name) {
556      return getParameterMap().get(name);
557   }
558
559   @Override /* HttpServletRequest */
560   public Map<String,String[]> getParameterMap() {
561      if ("POST".equalsIgnoreCase(method)) {
562         if (formDataMap == null)
563            formDataMap = RestUtils.parseQuery(read(content));
564         return formDataMap;
565      }
566      return queryDataMap;
567   }
568
569   @Override /* HttpServletRequest */
570   public String getProtocol() {
571      return protocol;
572   }
573
574   @Override /* HttpServletRequest */
575   public String getScheme() {
576      return scheme;
577   }
578
579   @Override /* HttpServletRequest */
580   public String getServerName() {
581      return serverName;
582   }
583
584   @Override /* HttpServletRequest */
585   public int getServerPort() {
586      return serverPort;
587   }
588
589   @Override /* HttpServletRequest */
590   public BufferedReader getReader() throws IOException {
591      return new BufferedReader(new InputStreamReader(getInputStream(), characterEncoding));
592   }
593
594   @Override /* HttpServletRequest */
595   public String getRemoteAddr() {
596      return remoteAddr;
597   }
598
599   @Override /* HttpServletRequest */
600   public String getRemoteHost() {
601      return remoteHost;
602   }
603
604   @Override /* HttpServletRequest */
605   public void setAttribute(String name, Object o) {
606      this.attributeMap.put(name, o);
607   }
608
609   @Override /* HttpServletRequest */
610   public void removeAttribute(String name) {
611      this.attributeMap.remove(name);
612   }
613
614   @Override /* HttpServletRequest */
615   public Locale getLocale() {
616      return locale;
617   }
618
619   @Override /* HttpServletRequest */
620   public Enumeration<Locale> getLocales() {
621      return Collections.enumeration(alist(locale));
622   }
623
624   @Override /* HttpServletRequest */
625   public boolean isSecure() {
626      return false;
627   }
628
629   @Override /* HttpServletRequest */
630   public RequestDispatcher getRequestDispatcher(String path) {
631      return requestDispatcher.get(path);
632   }
633
634   @Override /* HttpServletRequest */
635   public int getRemotePort() {
636      return remotePort;
637   }
638
639   @Override /* HttpServletRequest */
640   public String getLocalName() {
641      return localName;
642   }
643
644   @Override /* HttpServletRequest */
645   public String getLocalAddr() {
646      return localAddr;
647   }
648
649   @Override /* HttpServletRequest */
650   public int getLocalPort() {
651      return localPort;
652   }
653
654   @Override /* HttpServletRequest */
655   public ServletContext getServletContext() {
656      return servletContext;
657   }
658
659   @Override /* HttpServletRequest */
660   public AsyncContext startAsync() throws IllegalStateException {
661      return null;
662   }
663
664   @Override /* HttpServletRequest */
665   public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
666      return null;
667   }
668
669   @Override /* HttpServletRequest */
670   public boolean isAsyncStarted() {
671      return false;
672   }
673
674   @Override /* HttpServletRequest */
675   public boolean isAsyncSupported() {
676      return false;
677   }
678
679   @Override /* HttpServletRequest */
680   public AsyncContext getAsyncContext() {
681      return null;
682   }
683
684   @Override /* HttpServletRequest */
685   public DispatcherType getDispatcherType() {
686      return dispatcherType;
687   }
688
689   @Override /* HttpServletRequest */
690   public String getAuthType() {
691      return authType;
692   }
693
694   @Override /* HttpServletRequest */
695   public Cookie[] getCookies() {
696      return cookies;
697   }
698
699   @Override /* HttpServletRequest */
700   public long getDateHeader(String name) {
701      String s = getHeader(name);
702      return s == null ? 0 : date(s).asZonedDateTime().get().toInstant().toEpochMilli();
703   }
704
705   @Override /* HttpServletRequest */
706   public String getHeader(String name) {
707      String[] s = headerMap.get(name);
708      return s == null || s.length == 0 ? null : s[0];
709   }
710
711   @Override /* HttpServletRequest */
712   public Enumeration<String> getHeaders(String name) {
713      String[] s = headerMap.get(name);
714      return Collections.enumeration(alist(s == null ? new String[0] : s));
715   }
716
717   @Override /* HttpServletRequest */
718   public Enumeration<String> getHeaderNames() {
719      return Collections.enumeration(headerMap.keySet());
720   }
721
722   @Override /* HttpServletRequest */
723   public int getIntHeader(String name) {
724      String s = getHeader(name);
725      return s == null || s.isEmpty() ? 0 : Integer.parseInt(s);
726   }
727
728   @Override /* HttpServletRequest */
729   public String getMethod() {
730      return method;
731   }
732
733   @Override /* HttpServletRequest */
734   public String getPathInfo() {
735      if (pathInfo == null) {
736         pathInfo = getRequestURI();
737         if (isNotEmpty(contextPath))
738            pathInfo = pathInfo.substring(contextPath.length());
739         if (isNotEmpty(servletPath))
740            pathInfo = pathInfo.substring(servletPath.length());
741      }
742      return nullIfEmpty(StringUtils.urlDecode(pathInfo));
743   }
744
745   @Override /* HttpServletRequest */
746   public String getPathTranslated() {
747      if (pathTranslated == null)
748         pathTranslated = "/mock-path" + getPathInfo();
749      return pathTranslated;
750   }
751
752   @Override /* HttpServletRequest */
753   public String getContextPath() {
754      return contextPath;
755   }
756
757   @Override /* HttpServletRequest */
758   public String getQueryString() {
759      if (queryString == null) {
760         if (queryDataMap.isEmpty())
761            queryString = "";
762         else {
763            StringBuilder sb = new StringBuilder();
764            queryDataMap.forEach((k,v) -> {
765               if (v == null)
766                  sb.append(sb.length() == 0 ? "" : "&").append(StringUtils.urlEncode(k));
767               else for (String v2 : v)
768                  sb.append(sb.length() == 0 ? "" : "&").append(StringUtils.urlEncode(k)).append('=').append(StringUtils.urlEncode(v2));
769            });
770            queryString = sb.toString();
771         }
772      }
773      return isEmpty(queryString) ? null : queryString;
774   }
775
776   @Override /* HttpServletRequest */
777   public String getRemoteUser() {
778      return remoteUser;
779   }
780
781   @Override /* HttpServletRequest */
782   public boolean isUserInRole(String role) {
783      return roles.contains(role);
784   }
785
786   @Override /* HttpServletRequest */
787   public Principal getUserPrincipal() {
788      return userPrincipal;
789   }
790
791   @Override /* HttpServletRequest */
792   public String getRequestedSessionId() {
793      return requestedSessionId;
794   }
795
796   @Override /* HttpServletRequest */
797   public String getRequestURI() {
798      if (requestURI == null) {
799         requestURI = uri;
800         requestURI = requestURI.replaceAll("^\\w+\\:\\/\\/[^\\/]+", "").replaceAll("\\?.*$", "");
801      }
802      return requestURI;
803   }
804
805   @Override /* HttpServletRequest */
806   public StringBuffer getRequestURL() {
807      return new StringBuffer(uri.replaceAll("\\?.*$", ""));
808   }
809
810   @Override /* HttpServletRequest */
811   public String getServletPath() {
812      return servletPath;
813   }
814
815   @Override /* HttpServletRequest */
816   public HttpSession getSession(boolean create) {
817      return httpSession;
818   }
819
820   @Override /* HttpServletRequest */
821   public HttpSession getSession() {
822      return httpSession;
823   }
824
825   @Override /* HttpServletRequest */
826   public String changeSessionId() {
827      return null;
828   }
829
830   @Override /* HttpServletRequest */
831   public boolean isRequestedSessionIdValid() {
832      return false;
833   }
834
835   @Override /* HttpServletRequest */
836   public boolean isRequestedSessionIdFromCookie() {
837      return false;
838   }
839
840   @Override /* HttpServletRequest */
841   public boolean isRequestedSessionIdFromURL() {
842      return false;
843   }
844
845   @Override /* HttpServletRequest */
846   public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
847      return false;
848   }
849
850   @Override /* HttpServletRequest */
851   public void login(String username, String password) throws ServletException {
852   }
853
854   @Override /* HttpServletRequest */
855   public void logout() throws ServletException {
856   }
857
858   @Override /* HttpServletRequest */
859   public Collection<Part> getParts() throws IOException, ServletException {
860      return null;
861   }
862
863   @Override /* HttpServletRequest */
864   public Part getPart(String name) throws IOException, ServletException {
865      return null;
866   }
867
868   @Override /* HttpServletRequest */
869   public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
870      return null;
871   }
872
873   //=================================================================================================================
874   // Convenience methods
875   //=================================================================================================================
876
877   /**
878    * Fluent setter.
879    *
880    * @param name Header name.
881    * @param value
882    *    Header value.
883    *    <br>The value is converted to a simple string using {@link Object#toString()}.
884    * @return This object.
885    */
886   public MockServletRequest header(String name, Object value) {
887      if (value != null) {
888         String[] v1 = (value instanceof String[]) ? (String[])value : new String[]{value.toString()};
889         String[] v2 = headerMap.get(name);
890         String[] v3 = ArrayUtils.combine(v2, v1);
891         headerMap.put(name, v3);
892      }
893      return this;
894   }
895
896   /**
897    * Fluent setter.
898    *
899    * @param name Request attribute name.
900    * @param value Request attribute value.
901    * @return This object.
902    */
903   public MockServletRequest attribute(String name, Object value) {
904      this.attributeMap.put(name, value);
905      return this;
906   }
907
908   /**
909    * Fluent setter.
910    *
911    * @param value
912    *    The body of the request.
913    *    <br>Can be any of the following data types:
914    *    <ul>
915    *       <li><code><jk>byte</jk>[]</code>
916    *       <li>{@link Reader}
917    *       <li>{@link InputStream}
918    *       <li>{@link CharSequence}
919    *    </ul>
920    *    Any other types are converted to a string using the <c>toString()</c> method.
921    * @return This object.
922    */
923   public MockServletRequest content(Object value) {
924      try {
925         if (value instanceof byte[])
926            this.content = (byte[])value;
927         else if (value instanceof Reader)
928            this.content = readBytes((Reader)value);
929         else if (value instanceof InputStream)
930            this.content = readBytes((InputStream)value);
931         else if (value instanceof CharSequence)
932            this.content = ((CharSequence)value).toString().getBytes();
933         else if (value != null)
934            this.content = value.toString().getBytes();
935      } catch (IOException e) {
936         throw asRuntimeException(e);
937      }
938      return this;
939   }
940
941   //=================================================================================================================
942   // Convenience methods - headers
943   //=================================================================================================================
944
945   /**
946    * Enabled debug mode on this request.
947    *
948    * <p>
949    * Causes information about the request execution to be sent to STDERR.
950    *
951    * @param value The enable flag value.
952    * @return This object.
953    */
954   protected MockServletRequest debug(boolean value) {
955      if (value)
956         header("Debug", "true");
957      return this;
958   }
959
960   /**
961    * Enabled debug mode on this request.
962    *
963    * <p>
964    * Prevents errors from being logged on the server side if no-trace per-request is enabled.
965    *
966    * @param value The enable flag value.
967    * @return This object.
968    */
969   public MockServletRequest noTrace(boolean value) {
970      if (value)
971         header("No-Trace", "true");
972      return this;
973   }
974
975   /**
976    * If the specified request is a {@link MockRestRequest}, applies any of the override values to this servlet request.
977    *
978    * @param req The request to copy overrides from.
979    * @return This object.
980    */
981   public MockServletRequest applyOverrides(HttpRequest req) {
982
983      if (req instanceof MockRestRequest) {
984         MockRestRequest mreq = (MockRestRequest)req;
985         mreq.getAttributeMap().forEach(this::attribute);
986         mreq.getRequestDispatcherMap().forEach(this::requestDispatcher);
987         if (mreq.getCharacterEncoding() != null)
988            characterEncoding(mreq.getCharacterEncoding());
989         if (mreq.getProtocol() != null)
990            protocol(mreq.getProtocol());
991         if (mreq.getScheme() != null)
992            scheme(mreq.getScheme());
993         if (mreq.getServerName() != null)
994            serverName(mreq.getServerName());
995         if (mreq.getRemoteAddr() != null)
996            remoteAddr(mreq.getRemoteAddr());
997         if (mreq.getRemoteHost() != null)
998            remoteHost(mreq.getRemoteHost());
999         if (mreq.getLocalName() != null)
1000            localName(mreq.getLocalName());
1001         if (mreq.getLocalAddr() != null)
1002            localAddr(mreq.getLocalAddr());
1003         if (mreq.getPathInfo() != null)
1004            pathInfo(mreq.getPathInfo());
1005         if (mreq.getPathTranslated() != null)
1006            pathTranslated(mreq.getPathTranslated());
1007         if (mreq.getContextPath() != null)
1008            contextPath(mreq.getContextPath());
1009         if (mreq.getQueryString() != null)
1010            queryString(mreq.getQueryString());
1011         if (mreq.getRemoteUser() != null)
1012            remoteUser(mreq.getRemoteUser());
1013         if (mreq.getRequestedSessionId() != null)
1014            requestedSessionId(mreq.getRequestedSessionId());
1015         if (mreq.getRequestURI() != null)
1016            requestURI(mreq.getRequestURI());
1017         if (mreq.getServletPath() != null)
1018            servletPath(mreq.getServletPath());
1019         if (mreq.getAuthType() != null)
1020            authType(mreq.getAuthType());
1021         if (mreq.getServerPort() != null)
1022            serverPort(mreq.getServerPort());
1023         if (mreq.getRemotePort() != null)
1024            remotePort(mreq.getRemotePort());
1025         if (mreq.getLocalPort() != null)
1026            localPort(mreq.getLocalPort());
1027         if (mreq.getLocale() != null)
1028            locale(mreq.getLocale());
1029         if (mreq.getServletContext() != null)
1030            servletContext(mreq.getServletContext());
1031         if (mreq.getDispatcherType() != null)
1032            dispatcherType(mreq.getDispatcherType());
1033         if (mreq.getCookies() != null)
1034            cookies(mreq.getCookies());
1035         if (mreq.getUserPrincipal() != null)
1036            userPrincipal(mreq.getUserPrincipal());
1037         if (mreq.getHttpSession() != null)
1038            httpSession(mreq.getHttpSession());
1039         if (mreq.getRoles() != null)
1040            roles(mreq.getRoles());
1041      }
1042
1043      return this;
1044   }
1045
1046   @Override
1047   public String getRequestId() {
1048      return null;
1049   }
1050
1051   @Override
1052   public String getProtocolRequestId() {
1053      return null;
1054   }
1055
1056   @Override
1057   public ServletConnection getServletConnection() {
1058      return null;
1059   }
1060}