xref: /unit/src/java/nginx/unit/Request.java (revision 1157:7ae152bda303)
1 package nginx.unit;
2 
3 import java.io.BufferedReader;
4 import java.io.ByteArrayOutputStream;
5 import java.io.File;
6 import java.io.InputStreamReader;
7 import java.io.IOException;
8 import java.io.UnsupportedEncodingException;
9 
10 import java.lang.IllegalArgumentException;
11 import java.lang.IllegalStateException;
12 import java.lang.Object;
13 import java.lang.String;
14 import java.lang.StringBuffer;
15 
16 import java.net.URI;
17 import java.net.URISyntaxException;
18 
19 import java.nio.ByteBuffer;
20 import java.nio.charset.Charset;
21 import java.nio.charset.StandardCharsets;
22 
23 import java.text.ParseException;
24 import java.text.SimpleDateFormat;
25 
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.Enumeration;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.Set;
36 
37 import java.security.Principal;
38 
39 import javax.servlet.AsyncContext;
40 import javax.servlet.DispatcherType;
41 import javax.servlet.MultipartConfigElement;
42 import javax.servlet.RequestDispatcher;
43 import javax.servlet.ServletContext;
44 import javax.servlet.ServletException;
45 import javax.servlet.ServletInputStream;
46 import javax.servlet.ServletRequest;
47 import javax.servlet.ServletRequestAttributeEvent;
48 import javax.servlet.ServletRequestAttributeListener;
49 import javax.servlet.ServletResponse;
50 import javax.servlet.SessionTrackingMode;
51 import javax.servlet.SessionCookieConfig;
52 import javax.servlet.http.Cookie;
53 import javax.servlet.http.HttpSession;
54 import javax.servlet.http.HttpServletRequest;
55 import javax.servlet.http.HttpServletResponse;
56 import javax.servlet.http.HttpUpgradeHandler;
57 import javax.servlet.http.Part;
58 
59 import org.eclipse.jetty.util.IO;
60 import org.eclipse.jetty.util.MultiMap;
61 import org.eclipse.jetty.util.UrlEncoded;
62 import org.eclipse.jetty.util.StringUtil;
63 
64 import org.eclipse.jetty.server.CookieCutter;
65 import org.eclipse.jetty.http.MultiPartFormInputStream;
66 import org.eclipse.jetty.http.HttpFields;
67 import org.eclipse.jetty.http.MimeTypes;
68 
69 import nginx.unit.websocket.WsSession;
70 import nginx.unit.websocket.WsIOException;
71 
72 public class Request implements HttpServletRequest, DynamicPathRequest
73 {
74     private final Context context;
75     private final long req_info_ptr;
76     private final long req_ptr;
77 
78     protected String authType = null;
79 
80     protected boolean cookiesParsed = false;
81 
82     protected CookieCutter cookies = null;
83 
84     private final Map<String, Object> attributes = new HashMap<>();
85 
86     private MultiMap<String> parameters = null;
87 
88     private final String context_path;
89     private String filter_path = null;
90     private String servlet_path = null;
91     private String path_info = null;
92     private String request_uri = null;
93     private String query_string = null;
94     private boolean query_string_valid = false;
95 
96     private DispatcherType dispatcher_type = DispatcherType.REQUEST;
97 
98     private String characterEncoding = null;
99 
100     /**
101      * The only date format permitted when generating HTTP headers.
102      */
103     public static final String RFC1123_DATE =
104             "EEE, dd MMM yyyy HH:mm:ss zzz";
105 
106     private static final SimpleDateFormat formats[] = {
107         new SimpleDateFormat(RFC1123_DATE, Locale.US),
108         new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
109         new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
110     };
111 
112     private InputStream inputStream = null;
113     private BufferedReader reader = null;
114 
115     private boolean request_session_id_parsed = false;
116     private String request_session_id = null;
117     private boolean request_session_id_from_cookie = false;
118     private boolean request_session_id_from_url = false;
119     private Session session = null;
120 
121     private WsSession wsSession = null;
122     private boolean skip_close_ws = false;
123 
124     private final ServletRequestAttributeListener attr_listener;
125 
126     public static final String BARE = "nginx.unit.request.bare";
127 
128     private MultiPartFormInputStream multi_parts;
129     private MultipartConfigElement multipart_config;
130 
Request(Context ctx, long req_info, long req)131     public Request(Context ctx, long req_info, long req) {
132         context = ctx;
133         req_info_ptr = req_info;
134         req_ptr = req;
135 
136         attr_listener = context.getRequestAttributeListener();
137         context_path = context.getContextPath();
138     }
139 
140     @Override
authenticate(HttpServletResponse response)141     public boolean authenticate(HttpServletResponse response)
142         throws IOException, ServletException
143     {
144         log("authenticate");
145 
146         if (response.isCommitted()) {
147             throw new IllegalStateException();
148         }
149 
150         return false;
151     }
152 
153     @Override
getAuthType()154     public String getAuthType()
155     {
156         log("getAuthType");
157 
158         return authType;
159     }
160 
161     @Override
getContextPath()162     public String getContextPath()
163     {
164         trace("getContextPath: " + context_path);
165 
166         return context_path;
167     }
168 
169     @Override
getCookies()170     public Cookie[] getCookies()
171     {
172         trace("getCookies");
173 
174         if (!cookiesParsed) {
175             parseCookies();
176         }
177 
178         //Javadoc for Request.getCookies() stipulates null for no cookies
179         if (cookies == null || cookies.getCookies().length == 0) {
180             return null;
181         }
182 
183         return cookies.getCookies();
184     }
185 
parseCookies()186     protected void parseCookies()
187     {
188         cookiesParsed = true;
189 
190         cookies = new CookieCutter();
191 
192         Enumeration<String> cookie_headers = getHeaders("Cookie");
193 
194         while (cookie_headers.hasMoreElements()) {
195             cookies.addCookieField(cookie_headers.nextElement());
196         }
197     }
198 
199     @Override
getDateHeader(String name)200     public long getDateHeader(String name)
201     {
202         trace("getDateHeader: " + name);
203 
204         String value = getHeader(name);
205         if (value == null) {
206             return -1L;
207         }
208 
209         long res = parseDate(value);
210         if (res == -1L) {
211             throw new IllegalArgumentException(value);
212         }
213 
214         return res;
215     }
216 
parseDate(String value)217     protected long parseDate(String value)
218     {
219         Date date = null;
220         for (int i = 0; (date == null) && (i < formats.length); i++) {
221             try {
222                 date = formats[i].parse(value);
223             } catch (ParseException e) {
224                 // Ignore
225             }
226         }
227         if (date == null) {
228             return -1L;
229         }
230         return date.getTime();
231     }
232 
233     @Override
getHeader(String name)234     public String getHeader(String name)
235     {
236         String res = getHeader(req_ptr, name, name.length());
237 
238         trace("getHeader: " + name + " = '" + res + "'");
239 
240         return res;
241     }
242 
getHeader(long req_ptr, String name, int name_len)243     private static native String getHeader(long req_ptr, String name, int name_len);
244 
245 
246     @Override
getHeaderNames()247     public Enumeration<String> getHeaderNames()
248     {
249         trace("getHeaderNames");
250 
251         return getHeaderNames(req_ptr);
252     }
253 
getHeaderNames(long req_ptr)254     private static native Enumeration<String> getHeaderNames(long req_ptr);
255 
256 
257     @Override
getHeaders(String name)258     public Enumeration<String> getHeaders(String name)
259     {
260         trace("getHeaders: " + name);
261 
262         return getHeaders(req_ptr, name, name.length());
263     }
264 
getHeaders(long req_ptr, String name, int name_len)265     private static native Enumeration<String> getHeaders(long req_ptr, String name, int name_len);
266 
267 
268     @Override
getIntHeader(String name)269     public int getIntHeader(String name)
270     {
271         trace("getIntHeader: " + name);
272 
273         return getIntHeader(req_ptr, name, name.length());
274     }
275 
getIntHeader(long req_ptr, String name, int name_len)276     private static native int getIntHeader(long req_ptr, String name, int name_len);
277 
278 
279     @Override
getMethod()280     public String getMethod()
281     {
282         trace("getMethod");
283 
284         return getMethod(req_ptr);
285     }
286 
getMethod(long req_ptr)287     private static native String getMethod(long req_ptr);
288 
289 
290     @Override
getPart(String name)291     public Part getPart(String name) throws IOException, ServletException
292     {
293         trace("getPart: " + name);
294 
295         if (multi_parts == null) {
296             parseMultiParts();
297         }
298 
299         return multi_parts.getPart(name);
300     }
301 
302     @Override
getParts()303     public Collection<Part> getParts() throws IOException, ServletException
304     {
305         trace("getParts");
306 
307         if (multi_parts == null) {
308             parseMultiParts();
309         }
310 
311         return multi_parts.getParts();
312     }
313 
checkMultiPart(String content_type)314     private boolean checkMultiPart(String content_type)
315     {
316         return content_type != null
317                && MimeTypes.Type.MULTIPART_FORM_DATA.is(HttpFields.valueParameters(content_type, null));
318     }
319 
parseMultiParts()320     private void parseMultiParts() throws IOException, ServletException, IllegalStateException
321     {
322         String content_type = getContentType();
323 
324         if (!checkMultiPart(content_type)) {
325             throw new ServletException("Content-Type != multipart/form-data");
326         }
327 
328         if (multipart_config == null) {
329             throw new IllegalStateException("No multipart config for servlet");
330         }
331 
332         parseMultiParts(content_type);
333     }
334 
parseMultiParts(String content_type)335     private void parseMultiParts(String content_type) throws IOException
336     {
337         File tmpDir = (File) context.getAttribute(ServletContext.TEMPDIR);
338 
339         multi_parts = new MultiPartFormInputStream(getInputStream(),
340             content_type, multipart_config, tmpDir);
341     }
342 
setMultipartConfig(MultipartConfigElement mce)343     public void setMultipartConfig(MultipartConfigElement mce)
344     {
345         multipart_config = mce;
346     }
347 
getMultipartConfig()348     public MultipartConfigElement getMultipartConfig()
349     {
350         return multipart_config;
351     }
352 
353     @Override
getPathInfo()354     public String getPathInfo()
355     {
356         trace("getPathInfo: " + path_info);
357 
358         return path_info;
359     }
360 
361     @Override
getPathTranslated()362     public String getPathTranslated()
363     {
364         trace("getPathTranslated");
365 
366         if (path_info == null) {
367             return null;
368         }
369 
370         return context.getRealPath(path_info);
371     }
372 
373     @Override
getQueryString()374     public String getQueryString()
375     {
376         if (!query_string_valid) {
377             query_string = getQueryString(req_ptr);
378             query_string_valid = true;
379         }
380 
381         trace("getQueryString: " + query_string);
382 
383         return query_string;
384     }
385 
getQueryString(long req_ptr)386     private static native String getQueryString(long req_ptr);
387 
388     @Override
setQueryString(String query)389     public void setQueryString(String query)
390     {
391         trace("setQueryString: " + query);
392 
393         query_string = query;
394         query_string_valid = true;
395     }
396 
397     @Override
getRemoteUser()398     public String getRemoteUser()
399     {
400         log("getRemoteUser");
401 
402         /* TODO */
403         return null;
404     }
405 
406     @Override
getRequestedSessionId()407     public String getRequestedSessionId()
408     {
409         trace("getRequestedSessionId");
410 
411         if (!request_session_id_parsed) {
412             parseRequestSessionId();
413         }
414 
415         return request_session_id;
416     }
417 
parseRequestSessionId()418     private void parseRequestSessionId()
419     {
420         request_session_id_parsed = true;
421 
422         Cookie[] cookies = getCookies();
423         if (cookies == null) {
424             return;
425         }
426 
427         if (context.getEffectiveSessionTrackingModes().contains(
428             SessionTrackingMode.COOKIE))
429         {
430             final String name = context.getSessionCookieConfig().getName();
431 
432             for (Cookie c : cookies) {
433                 if (c.getName().equals(name)) {
434                     request_session_id = c.getValue();
435                     request_session_id_from_cookie = true;
436 
437                     return;
438                 }
439             }
440         }
441     }
442 
443     @Override
getRequestURI()444     public String getRequestURI()
445     {
446         if (request_uri == null) {
447             request_uri = getRequestURI(req_ptr);
448         }
449 
450         trace("getRequestURI: " + request_uri);
451 
452         return request_uri;
453     }
454 
getRequestURI(long req_ptr)455     private static native String getRequestURI(long req_ptr);
456 
457 
458     @Override
setRequestURI(String uri)459     public void setRequestURI(String uri)
460     {
461         trace("setRequestURI: " + uri);
462 
463         request_uri = uri;
464     }
465 
466 
467     @Override
getRequestURL()468     public StringBuffer getRequestURL()
469     {
470         String host = getHeader("Host");
471         String uri = getRequestURI();
472         StringBuffer res = new StringBuffer("http://" + host + uri);
473 
474         trace("getRequestURL: " + res);
475 
476         return res;
477     }
478 
479     @Override
getServletPath()480     public String getServletPath()
481     {
482         trace("getServletPath: " + servlet_path);
483 
484         return servlet_path;
485     }
486 
487     @Override
setServletPath(String servlet_path, String path_info)488     public void setServletPath(String servlet_path, String path_info)
489     {
490         trace("setServletPath: " + servlet_path);
491 
492         this.filter_path = servlet_path;
493         this.servlet_path = servlet_path;
494         this.path_info = path_info;
495     }
496 
497     @Override
setServletPath(String filter_path, String servlet_path, String path_info)498     public void setServletPath(String filter_path, String servlet_path, String path_info)
499     {
500         trace("setServletPath: " + filter_path + ", " + servlet_path);
501 
502         this.filter_path = filter_path;
503         this.servlet_path = servlet_path;
504         this.path_info = path_info;
505     }
506 
507     @Override
getFilterPath()508     public String getFilterPath()
509     {
510         return filter_path;
511     }
512 
513     @Override
getSession()514     public HttpSession getSession()
515     {
516         return getSession(true);
517     }
518 
519     @Override
getSession(boolean create)520     public HttpSession getSession(boolean create)
521     {
522         if (session != null) {
523             if (context.isSessionIdValid(session.getId())) {
524                 trace("getSession(" + create + "): " + session.getId());
525 
526                 return session;
527             }
528 
529             session = null;
530         }
531 
532         if (!request_session_id_parsed) {
533             parseRequestSessionId();
534 
535             session = context.getSession(request_session_id);
536         }
537 
538         if (session != null || !create) {
539             trace("getSession(" + create + "): " + (session != null ? session.getId() : "null"));
540 
541             return session;
542         }
543 
544         session = context.createSession();
545 
546         if (context.getEffectiveSessionTrackingModes().contains(
547             SessionTrackingMode.COOKIE))
548         {
549             setSessionIdCookie();
550         }
551 
552         trace("getSession(" + create + "): " + session.getId());
553 
554         return session;
555     }
556 
setSessionIdCookie()557     private void setSessionIdCookie()
558     {
559         SessionCookieConfig config = context.getSessionCookieConfig();
560 
561         Cookie c = new Cookie(config.getName(), session.getId());
562 
563         c.setComment(config.getComment());
564         if (!StringUtil.isBlank(config.getDomain())) {
565             c.setDomain(config.getDomain());
566         }
567 
568         c.setHttpOnly(config.isHttpOnly());
569         if (!StringUtil.isBlank(config.getPath())) {
570             c.setPath(config.getPath());
571         }
572 
573         c.setMaxAge(config.getMaxAge());
574 
575         getResponse(req_info_ptr).addSessionIdCookie(c);
576     }
577 
578     @Override
getUserPrincipal()579     public Principal getUserPrincipal()
580     {
581         log("getUserPrincipal");
582 
583         return null;
584     }
585 
586     @Override
isRequestedSessionIdFromCookie()587     public boolean isRequestedSessionIdFromCookie()
588     {
589         trace("isRequestedSessionIdFromCookie");
590 
591         if (!request_session_id_parsed) {
592             parseRequestSessionId();
593         }
594 
595         return request_session_id_from_cookie;
596     }
597 
598     @Override
599     @Deprecated
isRequestedSessionIdFromUrl()600     public boolean isRequestedSessionIdFromUrl()
601     {
602         trace("isRequestedSessionIdFromUrl");
603 
604         if (!request_session_id_parsed) {
605             parseRequestSessionId();
606         }
607 
608         return request_session_id_from_url;
609     }
610 
611     @Override
isRequestedSessionIdFromURL()612     public boolean isRequestedSessionIdFromURL()
613     {
614         trace("isRequestedSessionIdFromURL");
615 
616         if (!request_session_id_parsed) {
617             parseRequestSessionId();
618         }
619 
620         return request_session_id_from_url;
621     }
622 
623     @Override
isRequestedSessionIdValid()624     public boolean isRequestedSessionIdValid()
625     {
626         trace("isRequestedSessionIdValid");
627 
628         if (!request_session_id_parsed) {
629             parseRequestSessionId();
630         }
631 
632         return context.isSessionIdValid(request_session_id);
633     }
634 
635     @Override
isUserInRole(String role)636     public boolean isUserInRole(String role)
637     {
638         log("isUserInRole: " + role);
639 
640         return false;
641     }
642 
643     @Override
login(String username, String password)644     public void login(String username, String password) throws ServletException
645     {
646         log("login: " + username + "," + password);
647     }
648 
649     @Override
logout()650     public void logout() throws ServletException
651     {
652         log("logout");
653     }
654 
655 
656     @Override
getAsyncContext()657     public AsyncContext getAsyncContext()
658     {
659         log("getAsyncContext");
660 
661         return null;
662     }
663 
664     @Override
getAttribute(String name)665     public Object getAttribute(String name)
666     {
667         if (BARE.equals(name)) {
668             return this;
669         }
670 
671         Object o = attributes.get(name);
672 
673         trace("getAttribute: " + name + " = " + o);
674 
675         return o;
676     }
677 
678     @Override
getAttributeNames()679     public Enumeration<String> getAttributeNames()
680     {
681         trace("getAttributeNames");
682 
683         Set<String> names = attributes.keySet();
684         return Collections.enumeration(names);
685     }
686 
687     @Override
getCharacterEncoding()688     public String getCharacterEncoding()
689     {
690         trace("getCharacterEncoding");
691 
692         if (characterEncoding != null) {
693             return characterEncoding;
694         }
695 
696         getContentType();
697 
698         return characterEncoding;
699     }
700 
701     @Override
getContentLength()702     public int getContentLength()
703     {
704         trace("getContentLength");
705 
706         return (int) getContentLength(req_ptr);
707     }
708 
getContentLength(long req_ptr)709     private static native long getContentLength(long req_ptr);
710 
711     @Override
getContentLengthLong()712     public long getContentLengthLong()
713     {
714         trace("getContentLengthLong");
715 
716         return getContentLength(req_ptr);
717     }
718 
719     @Override
getContentType()720     public String getContentType()
721     {
722         trace("getContentType");
723 
724         String content_type = getContentType(req_ptr);
725 
726         if (characterEncoding == null && content_type != null) {
727             MimeTypes.Type mime = MimeTypes.CACHE.get(content_type);
728             String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(content_type) : mime.getCharset().toString();
729             if (charset != null) {
730                 characterEncoding = charset;
731             }
732         }
733 
734         return content_type;
735     }
736 
getContentType(long req_ptr)737     private static native String getContentType(long req_ptr);
738 
739 
740     @Override
getDispatcherType()741     public DispatcherType getDispatcherType()
742     {
743         trace("getDispatcherType: " + dispatcher_type);
744 
745         return dispatcher_type;
746     }
747 
748     @Override
setDispatcherType(DispatcherType type)749     public void setDispatcherType(DispatcherType type)
750     {
751         trace("setDispatcherType: " + type);
752 
753         dispatcher_type = type;
754     }
755 
756     @Override
getInputStream()757     public ServletInputStream getInputStream() throws IOException
758     {
759         trace("getInputStream");
760 
761         if (reader != null) {
762             throw new IllegalStateException("getInputStream: getReader() already used");
763         }
764 
765         if (inputStream == null) {
766             inputStream = new InputStream(req_info_ptr);
767         }
768 
769         return inputStream;
770     }
771 
772     @Override
getLocalAddr()773     public String getLocalAddr()
774     {
775         trace("getLocalAddr");
776 
777         return getLocalAddr(req_ptr);
778     }
779 
getLocalAddr(long req_ptr)780     private static native String getLocalAddr(long req_ptr);
781 
782 
783     @Override
getLocale()784     public Locale getLocale()
785     {
786         log("getLocale");
787 
788         return Locale.getDefault();
789     }
790 
791     @Override
getLocales()792     public Enumeration<Locale> getLocales()
793     {
794         log("getLocales");
795 
796         return Collections.emptyEnumeration();
797     }
798 
799     @Override
getLocalName()800     public String getLocalName()
801     {
802         trace("getLocalName");
803 
804         return getLocalName(req_ptr);
805     }
806 
getLocalName(long req_ptr)807     private static native String getLocalName(long req_ptr);
808 
809 
810     @Override
getLocalPort()811     public int getLocalPort()
812     {
813         trace("getLocalPort");
814 
815         return getLocalPort(req_ptr);
816     }
817 
getLocalPort(long req_ptr)818     private static native int getLocalPort(long req_ptr);
819 
820 
getParameters()821     public MultiMap<String> getParameters()
822     {
823         if (parameters != null) {
824             return parameters;
825         }
826 
827         parameters = new MultiMap<>();
828 
829         String query = getQueryString();
830 
831         if (query != null) {
832             UrlEncoded.decodeUtf8To(query, parameters);
833         }
834 
835         int content_length = getContentLength();
836 
837         if (content_length == 0 || !getMethod().equals("POST")) {
838             return parameters;
839         }
840 
841         String content_type = getContentType();
842 
843         try {
844             if (content_type.startsWith("application/x-www-form-urlencoded")) {
845                 UrlEncoded.decodeUtf8To(new InputStream(req_info_ptr),
846                     parameters, content_length, -1);
847             } else if (checkMultiPart(content_type) && multipart_config != null) {
848                 if (multi_parts == null) {
849                     parseMultiParts(content_type);
850                 }
851 
852                 if (multi_parts != null) {
853                     Collection<Part> parts = multi_parts.getParts();
854 
855                     String _charset_ = null;
856                     Part charset_part = multi_parts.getPart("_charset_");
857                     if (charset_part != null) {
858                         try (java.io.InputStream is = charset_part.getInputStream())
859                         {
860                             ByteArrayOutputStream os = new ByteArrayOutputStream();
861                             IO.copy(is, os);
862                             _charset_ = new String(os.toByteArray(),StandardCharsets.UTF_8);
863                         }
864                     }
865 
866                     /*
867                     Select Charset to use for this part. (NOTE: charset behavior is for the part value only and not the part header/field names)
868                         1. Use the part specific charset as provided in that part's Content-Type header; else
869                         2. Use the overall default charset. Determined by:
870                             a. if part name _charset_ exists, use that part's value.
871                             b. if the request.getCharacterEncoding() returns a value, use that.
872                                 (note, this can be either from the charset field on the request Content-Type
873                                 header, or from a manual call to request.setCharacterEncoding())
874                             c. use utf-8.
875                      */
876                     Charset def_charset;
877                     if (_charset_ != null) {
878                         def_charset = Charset.forName(_charset_);
879                     } else if (getCharacterEncoding() != null) {
880                         def_charset = Charset.forName(getCharacterEncoding());
881                     } else {
882                         def_charset = StandardCharsets.UTF_8;
883                     }
884 
885                     ByteArrayOutputStream os = null;
886                     for (Part p : parts) {
887                         if (p.getSubmittedFileName() != null) {
888                             continue;
889                         }
890 
891                         // Servlet Spec 3.0 pg 23, parts without filename must be put into params.
892                         String charset = null;
893                         if (p.getContentType() != null) {
894                             charset = MimeTypes.getCharsetFromContentType(p.getContentType());
895                         }
896 
897                         try (java.io.InputStream is = p.getInputStream())
898                         {
899                             if (os == null) {
900                                 os = new ByteArrayOutputStream();
901                             }
902                             IO.copy(is, os);
903 
904                             String content = new String(os.toByteArray(), charset == null ? def_charset : Charset.forName(charset));
905                             parameters.add(p.getName(), content);
906                         }
907                         os.reset();
908                     }
909                 }
910             }
911         } catch (IOException e) {
912             log("Unhandled IOException: " + e);
913         }
914 
915         return parameters;
916     }
917 
setParameters(MultiMap<String> p)918     public void setParameters(MultiMap<String> p)
919     {
920         parameters = p;
921     }
922 
923     @Override
getParameter(String name)924     public String getParameter(String name)
925     {
926         trace("getParameter: " + name);
927 
928         return getParameters().getValue(name, 0);
929     }
930 
931     @Override
getParameterMap()932     public Map<String,String[]> getParameterMap()
933     {
934         trace("getParameterMap");
935 
936         return Collections.unmodifiableMap(getParameters().toStringArrayMap());
937     }
938 
939     @Override
getParameterNames()940     public Enumeration<String> getParameterNames()
941     {
942         trace("getParameterNames");
943 
944         return Collections.enumeration(getParameters().keySet());
945     }
946 
947     @Override
getParameterValues(String name)948     public String[] getParameterValues(String name)
949     {
950         trace("getParameterValues: " + name);
951 
952         List<String> vals = getParameters().getValues(name);
953         if (vals == null)
954             return null;
955         return vals.toArray(new String[vals.size()]);
956     }
957 
958     @Override
getProtocol()959     public String getProtocol()
960     {
961         trace("getProtocol");
962 
963         return getProtocol(req_ptr);
964     }
965 
getProtocol(long req_ptr)966     private static native String getProtocol(long req_ptr);
967 
968     @Override
getReader()969     public BufferedReader getReader() throws IOException
970     {
971         trace("getReader");
972 
973         if (inputStream != null) {
974             throw new IllegalStateException("getReader: getInputStream() already used");
975         }
976 
977         if (reader == null) {
978             reader = new BufferedReader(new InputStreamReader(new InputStream(req_info_ptr)));
979         }
980 
981         return reader;
982     }
983 
984     @Override
985     @Deprecated
getRealPath(String path)986     public String getRealPath(String path)
987     {
988         trace("getRealPath: " + path);
989 
990         return context.getRealPath(path);
991     }
992 
993     @Override
getRemoteAddr()994     public String getRemoteAddr()
995     {
996         String res = getRemoteAddr(req_ptr);
997 
998         trace("getRemoteAddr: " + res);
999 
1000         return res;
1001     }
1002 
getRemoteAddr(long req_ptr)1003     private static native String getRemoteAddr(long req_ptr);
1004 
1005 
1006     @Override
getRemoteHost()1007     public String getRemoteHost()
1008     {
1009         String res = getRemoteHost(req_ptr);
1010 
1011         trace("getRemoteHost: " + res);
1012 
1013         return res;
1014     }
1015 
getRemoteHost(long req_ptr)1016     private static native String getRemoteHost(long req_ptr);
1017 
1018 
1019     @Override
getRemotePort()1020     public int getRemotePort()
1021     {
1022         int res = getRemotePort(req_ptr);
1023 
1024         trace("getRemotePort: " + res);
1025 
1026         return res;
1027     }
1028 
getRemotePort(long req_ptr)1029     private static native int getRemotePort(long req_ptr);
1030 
1031 
1032     @Override
getRequestDispatcher(String path)1033     public RequestDispatcher getRequestDispatcher(String path)
1034     {
1035         trace("getRequestDispatcher: " + path);
1036 
1037         if (path.startsWith("/")) {
1038             return context.getRequestDispatcher(path);
1039         }
1040 
1041         try {
1042             URI uri = new URI(getRequestURI());
1043             uri = uri.resolve(path);
1044 
1045             return context.getRequestDispatcher(uri);
1046         } catch (URISyntaxException e) {
1047             log("getRequestDispatcher: failed to create dispatcher: " + e);
1048         }
1049 
1050         return null;
1051     }
1052 
1053 
1054     @Override
getScheme()1055     public String getScheme()
1056     {
1057         trace("getScheme");
1058 
1059         return getScheme(req_ptr);
1060     }
1061 
getScheme(long req_ptr)1062     private static native String getScheme(long req_ptr);
1063 
1064 
1065     @Override
getServerName()1066     public String getServerName()
1067     {
1068         String res = getServerName(req_ptr);
1069 
1070         trace("getServerName: " + res);
1071 
1072         return res;
1073     }
1074 
getServerName(long req_ptr)1075     private static native String getServerName(long req_ptr);
1076 
1077 
1078     @Override
getServerPort()1079     public int getServerPort()
1080     {
1081         int res = getServerPort(req_ptr);
1082 
1083         trace("getServerPort: " + res);
1084 
1085         return res;
1086     }
1087 
getServerPort(long req_ptr)1088     private static native int getServerPort(long req_ptr);
1089 
1090     @Override
getServletContext()1091     public ServletContext getServletContext()
1092     {
1093         trace("getServletContext");
1094 
1095         return context;
1096     }
1097 
1098     @Override
isAsyncStarted()1099     public boolean isAsyncStarted()
1100     {
1101         log("isAsyncStarted");
1102 
1103         return false;
1104     }
1105 
1106     @Override
isAsyncSupported()1107     public boolean isAsyncSupported()
1108     {
1109         log("isAsyncSupported");
1110 
1111         return false;
1112     }
1113 
1114     @Override
isSecure()1115     public boolean isSecure()
1116     {
1117         trace("isSecure");
1118 
1119         return isSecure(req_ptr);
1120     }
1121 
isSecure(long req_ptr)1122     private static native boolean isSecure(long req_ptr);
1123 
1124     @Override
removeAttribute(String name)1125     public void removeAttribute(String name)
1126     {
1127         trace("removeAttribute: " + name);
1128 
1129         Object prev = attributes.remove(name);
1130 
1131         if (attr_listener == null || prev == null) {
1132             return;
1133         }
1134 
1135         attr_listener.attributeRemoved(
1136             new ServletRequestAttributeEvent(context, this, name, prev));
1137     }
1138 
1139     @Override
setAttribute(String name, Object o)1140     public void setAttribute(String name, Object o)
1141     {
1142         trace("setAttribute: " + name + ", " + o);
1143 
1144         Object prev;
1145 
1146         if (o != null) {
1147             prev = attributes.put(name, o);
1148         } else {
1149             prev = attributes.remove(name);
1150         }
1151 
1152         if (attr_listener == null) {
1153             return;
1154         }
1155 
1156         if (prev == null) {
1157             if (o == null) {
1158                 return;
1159             }
1160 
1161             attr_listener.attributeAdded(new ServletRequestAttributeEvent(
1162                 context, this, name, o));
1163         } else {
1164             if (o != null) {
1165                 attr_listener.attributeReplaced(
1166                     new ServletRequestAttributeEvent(context, this, name, prev));
1167             } else {
1168                 attr_listener.attributeRemoved(
1169                     new ServletRequestAttributeEvent(context, this, name, prev));
1170             }
1171         }
1172     }
1173 
setAttribute_(String name, Object o)1174     public void setAttribute_(String name, Object o)
1175     {
1176         trace("setAttribute_: " + name + ", " + o);
1177 
1178         if (o != null) {
1179             attributes.put(name, o);
1180         } else {
1181             attributes.remove(name);
1182         }
1183     }
1184 
1185     @Override
setCharacterEncoding(String env)1186     public void setCharacterEncoding(String env) throws UnsupportedEncodingException
1187     {
1188         trace("setCharacterEncoding: " + env);
1189 
1190         characterEncoding = env;
1191     }
1192 
1193     @Override
startAsync()1194     public AsyncContext startAsync() throws IllegalStateException
1195     {
1196         log("startAsync");
1197 
1198         return null;
1199     }
1200 
1201     @Override
startAsync(ServletRequest servletRequest, ServletResponse servletResponse)1202     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
1203     {
1204         log("startAsync(Req, resp)");
1205 
1206         return null;
1207     }
1208 
1209     @Override
upgrade( Class<T> httpUpgradeHandlerClass)1210     public <T extends HttpUpgradeHandler> T upgrade(
1211             Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException
1212     {
1213         trace("upgrade: " + httpUpgradeHandlerClass.getName());
1214 
1215         T handler;
1216 
1217         try {
1218             handler = httpUpgradeHandlerClass.getConstructor().newInstance();
1219         } catch (Exception e) {
1220             throw new ServletException(e);
1221         }
1222 
1223         upgrade(req_info_ptr);
1224 
1225         return handler;
1226     }
1227 
upgrade(long req_info_ptr)1228     private static native void upgrade(long req_info_ptr);
1229 
isUpgrade()1230     public boolean isUpgrade()
1231     {
1232         return isUpgrade(req_info_ptr);
1233     }
1234 
isUpgrade(long req_info_ptr)1235     private static native boolean isUpgrade(long req_info_ptr);
1236 
1237     @Override
changeSessionId()1238     public String changeSessionId()
1239     {
1240         trace("changeSessionId");
1241 
1242         getSession(false);
1243 
1244         if (session == null) {
1245             return null;
1246         }
1247 
1248         context.changeSessionId(session);
1249 
1250         if (context.getEffectiveSessionTrackingModes().contains(
1251             SessionTrackingMode.COOKIE))
1252         {
1253             setSessionIdCookie();
1254         }
1255 
1256         return session.getId();
1257     }
1258 
log(String msg)1259     private void log(String msg)
1260     {
1261         msg = "Request." + msg;
1262         log(req_info_ptr, msg, msg.length());
1263     }
1264 
log(long req_info_ptr, String msg, int msg_len)1265     public static native void log(long req_info_ptr, String msg, int msg_len);
1266 
1267 
trace(String msg)1268     private void trace(String msg)
1269     {
1270         msg = "Request." + msg;
1271         trace(req_info_ptr, msg, msg.length());
1272     }
1273 
trace(long req_info_ptr, String msg, int msg_len)1274     public static native void trace(long req_info_ptr, String msg, int msg_len);
1275 
getResponse(long req_info_ptr)1276     private static native Response getResponse(long req_info_ptr);
1277 
1278 
setWsSession(WsSession s)1279     public void setWsSession(WsSession s)
1280     {
1281         wsSession = s;
1282     }
1283 
processWsFrame(ByteBuffer buf, byte opCode, boolean last)1284     private void processWsFrame(ByteBuffer buf, byte opCode, boolean last)
1285         throws IOException
1286     {
1287         trace("processWsFrame: " + opCode + ", [" + buf.position() + ", " + buf.limit() + "]");
1288         try {
1289             wsSession.processFrame(buf, opCode, last);
1290         } catch (WsIOException e) {
1291             wsSession.onClose(e.getCloseReason());
1292         }
1293     }
1294 
closeWsSession()1295     private void closeWsSession()
1296     {
1297         trace("closeWsSession");
1298         skip_close_ws = true;
1299 
1300         wsSession.onClose();
1301     }
1302 
sendWsFrame(ByteBuffer payload, byte opCode, boolean last, long timeoutExpiry)1303     public void sendWsFrame(ByteBuffer payload, byte opCode, boolean last,
1304         long timeoutExpiry) throws IOException
1305     {
1306         trace("sendWsFrame: " + opCode + ", [" + payload.position() +
1307               ", " + payload.limit() + "]");
1308 
1309         if (payload.isDirect()) {
1310             sendWsFrame(req_info_ptr, payload, payload.position(),
1311                         payload.limit() - payload.position(), opCode, last);
1312         } else {
1313             sendWsFrame(req_info_ptr, payload.array(), payload.position(),
1314                         payload.limit() - payload.position(), opCode, last);
1315         }
1316     }
1317 
sendWsFrame(long req_info_ptr, ByteBuffer buf, int pos, int len, byte opCode, boolean last)1318     private static native void sendWsFrame(long req_info_ptr,
1319         ByteBuffer buf, int pos, int len, byte opCode, boolean last);
1320 
sendWsFrame(long req_info_ptr, byte[] arr, int pos, int len, byte opCode, boolean last)1321     private static native void sendWsFrame(long req_info_ptr,
1322         byte[] arr, int pos, int len, byte opCode, boolean last);
1323 
1324 
closeWs()1325     public void closeWs()
1326     {
1327         if (skip_close_ws) {
1328             return;
1329         }
1330 
1331         trace("closeWs");
1332 
1333         closeWs(req_info_ptr);
1334     }
1335 
closeWs(long req_info_ptr)1336     private static native void closeWs(long req_info_ptr);
1337 }
1338 
1339