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