]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/Request.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / server / Request.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.server;
20
21 import java.io.BufferedReader;
22 import java.io.ByteArrayOutputStream;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.UnsupportedEncodingException;
28 import java.net.InetAddress;
29 import java.net.InetSocketAddress;
30 import java.nio.charset.Charset;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.charset.UnsupportedCharsetException;
33 import java.security.Principal;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Enumeration;
38 import java.util.EventListener;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import javax.servlet.AsyncContext;
44 import javax.servlet.AsyncListener;
45 import javax.servlet.DispatcherType;
46 import javax.servlet.MultipartConfigElement;
47 import javax.servlet.RequestDispatcher;
48 import javax.servlet.ServletContext;
49 import javax.servlet.ServletException;
50 import javax.servlet.ServletInputStream;
51 import javax.servlet.ServletRequest;
52 import javax.servlet.ServletRequestAttributeEvent;
53 import javax.servlet.ServletRequestAttributeListener;
54 import javax.servlet.ServletRequestEvent;
55 import javax.servlet.ServletRequestListener;
56 import javax.servlet.ServletResponse;
57 import javax.servlet.http.Cookie;
58 import javax.servlet.http.HttpServletRequest;
59 import javax.servlet.http.HttpServletResponse;
60 import javax.servlet.http.HttpSession;
61 import javax.servlet.http.HttpUpgradeHandler;
62 import javax.servlet.http.Part;
63
64 import org.eclipse.jetty.http.HttpCookie;
65 import org.eclipse.jetty.http.HttpFields;
66 import org.eclipse.jetty.http.HttpHeader;
67 import org.eclipse.jetty.http.HttpMethod;
68 import org.eclipse.jetty.http.HttpStatus;
69 import org.eclipse.jetty.http.HttpURI;
70 import org.eclipse.jetty.http.HttpVersion;
71 import org.eclipse.jetty.http.MimeTypes;
72 import org.eclipse.jetty.server.handler.ContextHandler;
73 import org.eclipse.jetty.server.handler.ContextHandler.Context;
74 import org.eclipse.jetty.server.session.AbstractSession;
75 import org.eclipse.jetty.util.Attributes;
76 import org.eclipse.jetty.util.AttributesMap;
77 import org.eclipse.jetty.util.IO;
78 import org.eclipse.jetty.util.MultiException;
79 import org.eclipse.jetty.util.MultiMap;
80 import org.eclipse.jetty.util.MultiPartInputStreamParser;
81 import org.eclipse.jetty.util.StringUtil;
82 import org.eclipse.jetty.util.URIUtil;
83 import org.eclipse.jetty.util.UrlEncoded;
84 import org.eclipse.jetty.util.log.Log;
85 import org.eclipse.jetty.util.log.Logger;
86
87 /* ------------------------------------------------------------ */
88 /**
89  * Jetty Request.
90  * <p>
91  * Implements {@link javax.servlet.http.HttpServletRequest} from the <code>javax.servlet.http</code> package.
92  * </p>
93  * <p>
94  * The standard interface of mostly getters, is extended with setters so that the request is mutable by the handlers that it is passed to. This allows the
95  * request object to be as lightweight as possible and not actually implement any significant behavior. For example
96  * <ul>
97  *
98  * <li>The {@link Request#getContextPath()} method will return null, until the request has been passed to a {@link ContextHandler} which matches the
99  * {@link Request#getPathInfo()} with a context path and calls {@link Request#setContextPath(String)} as a result.</li>
100  *
101  * <li>the HTTP session methods will all return null sessions until such time as a request has been passed to a
102  * {@link org.eclipse.jetty.server.session.SessionHandler} which checks for session cookies and enables the ability to create new sessions.</li>
103  *
104  * <li>The {@link Request#getServletPath()} method will return null until the request has been passed to a <code>org.eclipse.jetty.servlet.ServletHandler</code>
105  * and the pathInfo matched against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li>
106  * </ul>
107  *
108  * A request instance is created for each connection accepted by the server and recycled for each HTTP request received via that connection.
109  * An effort is made to avoid reparsing headers and cookies that are likely to be the same for requests from the same connection.
110  *
111  * <p>
112  * The form content that a request can process is limited to protect from Denial of Service attacks. The size in bytes is limited by
113  * {@link ContextHandler#getMaxFormContentSize()} or if there is no context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server}
114  * attribute. The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no context then the
115  * "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute.
116  *
117  *
118  */
119 public class Request implements HttpServletRequest
120 {
121     public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.jetty.multipartConfig";
122     public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.jetty.multiPartInputStream";
123     public static final String __MULTIPART_CONTEXT = "org.eclipse.jetty.multiPartContext";
124
125     private static final Logger LOG = Log.getLogger(Request.class);
126     private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
127     private static final int __NONE = 0, _STREAM = 1, __READER = 2;
128
129     private final HttpChannel<?> _channel;
130     private final HttpFields _fields=new HttpFields();
131     private final List<ServletRequestAttributeListener>  _requestAttributeListeners=new ArrayList<>();
132     private final HttpInput<?> _input;
133     
134     public static class MultiPartCleanerListener implements ServletRequestListener
135     {
136         @Override
137         public void requestDestroyed(ServletRequestEvent sre)
138         {
139             //Clean up any tmp files created by MultiPartInputStream
140             MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
141             if (mpis != null)
142             {
143                 ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
144
145                 //Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
146                 if (context == sre.getServletContext())
147                 {
148                     try
149                     {
150                         mpis.deleteParts();
151                     }
152                     catch (MultiException e)
153                     {
154                         sre.getServletContext().log("Errors deleting multipart tmp files", e);
155                     }
156                 }
157             }
158         }
159
160         @Override
161         public void requestInitialized(ServletRequestEvent sre)
162         {
163             //nothing to do, multipart config set up by ServletHolder.handle()
164         }
165         
166     }
167     
168     
169
170     private boolean _secure;
171     private boolean _asyncSupported = true;
172     private boolean _newContext;
173     private boolean _cookiesExtracted = false;
174     private boolean _handled = false;
175     private boolean _paramsExtracted;
176     private boolean _requestedSessionIdFromCookie = false;
177     private volatile Attributes _attributes;
178     private Authentication _authentication;
179     private String _characterEncoding;
180     private ContextHandler.Context _context;
181     private String _contextPath;
182     private CookieCutter _cookies;
183     private DispatcherType _dispatcherType;
184     private int _inputState = __NONE;
185     private HttpMethod _httpMethod;
186     private String _httpMethodString;
187     private MultiMap<String> _queryParameters;
188     private MultiMap<String> _contentParameters;
189     private MultiMap<String> _parameters;
190     private String _pathInfo;
191     private int _port;
192     private HttpVersion _httpVersion = HttpVersion.HTTP_1_1;
193     private String _queryEncoding;
194     private String _queryString;
195     private BufferedReader _reader;
196     private String _readerEncoding;
197     private InetSocketAddress _remote;
198     private String _requestedSessionId;
199     private String _requestURI;
200     private Map<Object, HttpSession> _savedNewSessions;
201     private String _scheme = URIUtil.HTTP;
202     private UserIdentity.Scope _scope;
203     private String _serverName;
204     private String _servletPath;
205     private HttpSession _session;
206     private SessionManager _sessionManager;
207     private long _timeStamp;
208     private HttpURI _uri;
209     private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
210     private AsyncContextState _async;
211     
212     /* ------------------------------------------------------------ */
213     public Request(HttpChannel<?> channel, HttpInput<?> input)
214     {
215         _channel = channel;
216         _input = input;
217     }
218
219     /* ------------------------------------------------------------ */
220     public HttpFields getHttpFields()
221     {
222         return _fields;
223     }
224
225     /* ------------------------------------------------------------ */
226     public HttpInput<?> getHttpInput()
227     {
228         return _input;
229     }
230
231     /* ------------------------------------------------------------ */
232     public void addEventListener(final EventListener listener)
233     {
234         if (listener instanceof ServletRequestAttributeListener)
235             _requestAttributeListeners.add((ServletRequestAttributeListener)listener);
236         if (listener instanceof AsyncListener)
237             throw new IllegalArgumentException(listener.getClass().toString());
238     }
239
240     public void extractParameters()
241     {
242         if (_paramsExtracted)
243             return;
244
245         _paramsExtracted = true;
246
247         // Extract query string parameters; these may be replaced by a forward()
248         // and may have already been extracted by mergeQueryParameters().
249         if (_queryParameters == null)
250             _queryParameters = extractQueryParameters();
251
252         // Extract content parameters; these cannot be replaced by a forward()
253         // once extracted and may have already been extracted by getParts() or
254         // by a processing happening after a form-based authentication.
255         if (_contentParameters == null)
256             _contentParameters = extractContentParameters();
257
258         _parameters = restoreParameters();
259     }
260
261     private MultiMap<String> extractQueryParameters()
262     {
263         MultiMap<String> result = new MultiMap<>();
264         if (_uri != null && _uri.hasQuery())
265         {
266             if (_queryEncoding == null)
267             {
268                 _uri.decodeQueryTo(result);
269             }
270             else
271             {
272                 try
273                 {
274                     _uri.decodeQueryTo(result, _queryEncoding);
275                 }
276                 catch (UnsupportedEncodingException e)
277                 {
278                     if (LOG.isDebugEnabled())
279                         LOG.warn(e);
280                     else
281                         LOG.warn(e.toString());
282                 }
283             }
284         }
285         return result;
286     }
287
288     private MultiMap<String> extractContentParameters()
289     {
290         MultiMap<String> result = new MultiMap<>();
291
292         String contentType = getContentType();
293         if (contentType != null && !contentType.isEmpty())
294         {
295             contentType = HttpFields.valueParameters(contentType, null);
296             int contentLength = getContentLength();
297             if (contentLength != 0)
298             {
299                 if (MimeTypes.Type.FORM_ENCODED.is(contentType) && _inputState == __NONE &&
300                         (HttpMethod.POST.is(getMethod()) || HttpMethod.PUT.is(getMethod())))
301                 {
302                     extractFormParameters(result);
303                 }
304                 else if (contentType.startsWith("multipart/form-data") &&
305                         getAttribute(__MULTIPART_CONFIG_ELEMENT) != null &&
306                         _multiPartInputStream == null)
307                 {
308                     extractMultipartParameters(result);
309                 }
310             }
311         }
312
313         return result;
314     }
315
316     public void extractFormParameters(MultiMap<String> params)
317     {
318         try
319         {
320             int maxFormContentSize = -1;
321             int maxFormKeys = -1;
322
323             if (_context != null)
324             {
325                 maxFormContentSize = _context.getContextHandler().getMaxFormContentSize();
326                 maxFormKeys = _context.getContextHandler().getMaxFormKeys();
327             }
328
329             if (maxFormContentSize < 0)
330             {
331                 Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
332                 if (obj == null)
333                     maxFormContentSize = 200000;
334                 else if (obj instanceof Number)
335                 {
336                     Number size = (Number)obj;
337                     maxFormContentSize = size.intValue();
338                 }
339                 else if (obj instanceof String)
340                 {
341                     maxFormContentSize = Integer.valueOf((String)obj);
342                 }
343             }
344
345             if (maxFormKeys < 0)
346             {
347                 Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
348                 if (obj == null)
349                     maxFormKeys = 1000;
350                 else if (obj instanceof Number)
351                 {
352                     Number keys = (Number)obj;
353                     maxFormKeys = keys.intValue();
354                 }
355                 else if (obj instanceof String)
356                 {
357                     maxFormKeys = Integer.valueOf((String)obj);
358                 }
359             }
360
361             int contentLength = getContentLength();
362             if (contentLength > maxFormContentSize && maxFormContentSize > 0)
363             {
364                 throw new IllegalStateException("Form too large: " + contentLength + " > " + maxFormContentSize);
365             }
366             InputStream in = getInputStream();
367             if (_input.isAsync())
368                 throw new IllegalStateException("Cannot extract parameters with async IO");
369
370             UrlEncoded.decodeTo(in,params,getCharacterEncoding(),contentLength<0?maxFormContentSize:-1,maxFormKeys);
371         }
372         catch (IOException e)
373         {
374             if (LOG.isDebugEnabled())
375                 LOG.warn(e);
376             else
377                 LOG.warn(e.toString());
378         }
379     }
380
381     private void extractMultipartParameters(MultiMap<String> result)
382     {
383         try
384         {
385             getParts(result);
386         }
387         catch (IOException | ServletException e)
388         {
389             if (LOG.isDebugEnabled())
390                 LOG.warn(e);
391             else
392                 LOG.warn(e.toString());
393         }
394     }
395
396     /* ------------------------------------------------------------ */
397     @Override
398     public AsyncContext getAsyncContext()
399     {
400         HttpChannelState state = getHttpChannelState();
401         if (_async==null || state.isInitial() && !state.isAsync())
402             throw new IllegalStateException(state.getStatusString());
403         
404         return _async;
405     }
406
407     /* ------------------------------------------------------------ */
408     public HttpChannelState getHttpChannelState()
409     {
410         return _channel.getState();
411     }
412
413     /* ------------------------------------------------------------ */
414     /**
415      * Get Request Attribute.
416      * <p>Also supports jetty specific attributes to gain access to Jetty APIs:
417      * <dl>
418      * <dt>org.eclipse.jetty.server.Server</dt><dd>The Jetty Server instance</dd>
419      * <dt>org.eclipse.jetty.server.HttpChannel</dt><dd>The HttpChannel for this request</dd>
420      * <dt>org.eclipse.jetty.server.HttpConnection</dt><dd>The HttpConnection or null if another transport is used</dd>
421      * </dl>
422      * While these attributes may look like security problems, they are exposing nothing that is not already
423      * available via reflection from a Request instance.
424      * </p>
425      * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
426      */
427     @Override
428     public Object getAttribute(String name)
429     {
430         if (name.startsWith("org.eclipse.jetty"))
431         {
432             if ("org.eclipse.jetty.server.Server".equals(name))
433                 return _channel.getServer();
434             if ("org.eclipse.jetty.server.HttpChannel".equals(name))
435                 return _channel;
436             if ("org.eclipse.jetty.server.HttpConnection".equals(name) &&
437                 _channel.getHttpTransport() instanceof HttpConnection)
438                 return _channel.getHttpTransport();
439         }
440         return (_attributes == null)?null:_attributes.getAttribute(name);
441     }
442
443     /* ------------------------------------------------------------ */
444     /*
445      * @see javax.servlet.ServletRequest#getAttributeNames()
446      */
447     @Override
448     public Enumeration<String> getAttributeNames()
449     {
450         if (_attributes == null)
451             return Collections.enumeration(Collections.<String>emptyList());
452
453         return AttributesMap.getAttributeNamesCopy(_attributes);
454     }
455
456     /* ------------------------------------------------------------ */
457     /*
458      */
459     public Attributes getAttributes()
460     {
461         if (_attributes == null)
462             _attributes = new AttributesMap();
463         return _attributes;
464     }
465
466     /* ------------------------------------------------------------ */
467     /**
468      * Get the authentication.
469      *
470      * @return the authentication
471      */
472     public Authentication getAuthentication()
473     {
474         return _authentication;
475     }
476
477     /* ------------------------------------------------------------ */
478     /*
479      * @see javax.servlet.http.HttpServletRequest#getAuthType()
480      */
481     @Override
482     public String getAuthType()
483     {
484         if (_authentication instanceof Authentication.Deferred)
485             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
486
487         if (_authentication instanceof Authentication.User)
488             return ((Authentication.User)_authentication).getAuthMethod();
489         return null;
490     }
491
492     /* ------------------------------------------------------------ */
493     /*
494      * @see javax.servlet.ServletRequest#getCharacterEncoding()
495      */
496     @Override
497     public String getCharacterEncoding()
498     {
499         return _characterEncoding;
500     }
501
502     /* ------------------------------------------------------------ */
503     /**
504      * @return Returns the connection.
505      */
506     public HttpChannel<?> getHttpChannel()
507     {
508         return _channel;
509     }
510
511     /* ------------------------------------------------------------ */
512     /*
513      * @see javax.servlet.ServletRequest#getContentLength()
514      */
515     @Override
516     public int getContentLength()
517     {
518         return (int)_fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
519     }
520
521     /* ------------------------------------------------------------ */
522     /*
523      * @see javax.servlet.ServletRequest.getContentLengthLong()
524      */
525     @Override
526     public long getContentLengthLong()
527     {
528         return _fields.getLongField(HttpHeader.CONTENT_LENGTH.toString());
529     }
530
531     /* ------------------------------------------------------------ */
532     public long getContentRead()
533     {
534         return _input.getContentRead();
535     }
536     
537     /* ------------------------------------------------------------ */
538     /*
539      * @see javax.servlet.ServletRequest#getContentType()
540      */
541     @Override
542     public String getContentType()
543     {
544         return _fields.getStringField(HttpHeader.CONTENT_TYPE);
545     }
546
547     /* ------------------------------------------------------------ */
548     /**
549      * @return The current {@link Context context} used for this request, or <code>null</code> if {@link #setContext} has not yet been called.
550      */
551     public Context getContext()
552     {
553         return _context;
554     }
555
556     /* ------------------------------------------------------------ */
557     /*
558      * @see javax.servlet.http.HttpServletRequest#getContextPath()
559      */
560     @Override
561     public String getContextPath()
562     {
563         return _contextPath;
564     }
565
566     /* ------------------------------------------------------------ */
567     /*
568      * @see javax.servlet.http.HttpServletRequest#getCookies()
569      */
570     @Override
571     public Cookie[] getCookies()
572     {
573         if (_cookiesExtracted)
574         {
575             if (_cookies == null || _cookies.getCookies().length == 0)
576                 return null;
577             
578             return _cookies.getCookies();
579         }
580
581         _cookiesExtracted = true;
582
583         Enumeration<?> enm = _fields.getValues(HttpHeader.COOKIE.toString());
584
585         // Handle no cookies
586         if (enm != null)
587         {
588             if (_cookies == null)
589                 _cookies = new CookieCutter();
590
591             while (enm.hasMoreElements())
592             {
593                 String c = (String)enm.nextElement();
594                 _cookies.addCookieField(c);
595             }
596         }
597
598         //Javadoc for Request.getCookies() stipulates null for no cookies
599         if (_cookies == null || _cookies.getCookies().length == 0)
600             return null;
601         
602         return _cookies.getCookies();
603     }
604
605     /* ------------------------------------------------------------ */
606     /*
607      * @see javax.servlet.http.HttpServletRequest#getDateHeader(java.lang.String)
608      */
609     @Override
610     public long getDateHeader(String name)
611     {
612         return _fields.getDateField(name);
613     }
614
615     /* ------------------------------------------------------------ */
616     @Override
617     public DispatcherType getDispatcherType()
618     {
619         return _dispatcherType;
620     }
621
622     /* ------------------------------------------------------------ */
623     /*
624      * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)
625      */
626     @Override
627     public String getHeader(String name)
628     {
629         return _fields.getStringField(name);
630     }
631
632     /* ------------------------------------------------------------ */
633     /*
634      * @see javax.servlet.http.HttpServletRequest#getHeaderNames()
635      */
636     @Override
637     public Enumeration<String> getHeaderNames()
638     {
639         return _fields.getFieldNames();
640     }
641
642     /* ------------------------------------------------------------ */
643     /*
644      * @see javax.servlet.http.HttpServletRequest#getHeaders(java.lang.String)
645      */
646     @Override
647     public Enumeration<String> getHeaders(String name)
648     {
649         Enumeration<String> e = _fields.getValues(name);
650         if (e == null)
651             return Collections.enumeration(Collections.<String>emptyList());
652         return e;
653     }
654
655     /* ------------------------------------------------------------ */
656     /**
657      * @return Returns the inputState.
658      */
659     public int getInputState()
660     {
661         return _inputState;
662     }
663
664     /* ------------------------------------------------------------ */
665     /*
666      * @see javax.servlet.ServletRequest#getInputStream()
667      */
668     @Override
669     public ServletInputStream getInputStream() throws IOException
670     {
671         if (_inputState != __NONE && _inputState != _STREAM)
672             throw new IllegalStateException("READER");
673         _inputState = _STREAM;
674
675         if (_channel.isExpecting100Continue())
676             _channel.continue100(_input.available());
677
678         return _input;
679     }
680
681     /* ------------------------------------------------------------ */
682     /*
683      * @see javax.servlet.http.HttpServletRequest#getIntHeader(java.lang.String)
684      */
685     @Override
686     public int getIntHeader(String name)
687     {
688         return (int)_fields.getLongField(name);
689     }
690
691
692     /* ------------------------------------------------------------ */
693     /*
694      * @see javax.servlet.ServletRequest#getLocale()
695      */
696     @Override
697     public Locale getLocale()
698     {
699         Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
700
701         // handle no locale
702         if (enm == null || !enm.hasMoreElements())
703             return Locale.getDefault();
704
705         // sort the list in quality order
706         List<?> acceptLanguage = HttpFields.qualityList(enm);
707         if (acceptLanguage.size() == 0)
708             return Locale.getDefault();
709
710         int size = acceptLanguage.size();
711
712         if (size > 0)
713         {
714             String language = (String)acceptLanguage.get(0);
715             language = HttpFields.valueParameters(language,null);
716             String country = "";
717             int dash = language.indexOf('-');
718             if (dash > -1)
719             {
720                 country = language.substring(dash + 1).trim();
721                 language = language.substring(0,dash).trim();
722             }
723             return new Locale(language,country);
724         }
725
726         return Locale.getDefault();
727     }
728
729     /* ------------------------------------------------------------ */
730     /*
731      * @see javax.servlet.ServletRequest#getLocales()
732      */
733     @Override
734     public Enumeration<Locale> getLocales()
735     {
736
737         Enumeration<String> enm = _fields.getValues(HttpHeader.ACCEPT_LANGUAGE.toString(),HttpFields.__separators);
738
739         // handle no locale
740         if (enm == null || !enm.hasMoreElements())
741             return Collections.enumeration(__defaultLocale);
742
743         // sort the list in quality order
744         List<String> acceptLanguage = HttpFields.qualityList(enm);
745
746         if (acceptLanguage.size() == 0)
747             return Collections.enumeration(__defaultLocale);
748
749         List<Locale> langs = new ArrayList<>();
750
751         // convert to locals
752         for (String language : acceptLanguage)
753         {
754             language = HttpFields.valueParameters(language, null);
755             String country = "";
756             int dash = language.indexOf('-');
757             if (dash > -1)
758             {
759                 country = language.substring(dash + 1).trim();
760                 language = language.substring(0, dash).trim();
761             }
762             langs.add(new Locale(language, country));
763         }
764
765         if (langs.size() == 0)
766             return Collections.enumeration(__defaultLocale);
767
768         return Collections.enumeration(langs);
769     }
770
771     /* ------------------------------------------------------------ */
772     /*
773      * @see javax.servlet.ServletRequest#getLocalAddr()
774      */
775     @Override
776     public String getLocalAddr()
777     {
778         InetSocketAddress local=_channel.getLocalAddress();
779         if (local==null)
780             return "";
781         InetAddress address = local.getAddress();
782         if (address==null)
783             return local.getHostString();
784         return address.getHostAddress();
785     }
786
787     /* ------------------------------------------------------------ */
788     /*
789      * @see javax.servlet.ServletRequest#getLocalName()
790      */
791     @Override
792     public String getLocalName()
793     {
794         InetSocketAddress local=_channel.getLocalAddress();
795         return local.getHostString();
796     }
797
798     /* ------------------------------------------------------------ */
799     /*
800      * @see javax.servlet.ServletRequest#getLocalPort()
801      */
802     @Override
803     public int getLocalPort()
804     {
805         InetSocketAddress local=_channel.getLocalAddress();
806         return local.getPort();
807     }
808
809     /* ------------------------------------------------------------ */
810     /*
811      * @see javax.servlet.http.HttpServletRequest#getMethod()
812      */
813     @Override
814     public String getMethod()
815     {
816         return _httpMethodString;
817     }
818
819     /* ------------------------------------------------------------ */
820     /*
821      * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
822      */
823     @Override
824     public String getParameter(String name)
825     {
826         if (!_paramsExtracted)
827             extractParameters();
828         if (_parameters == null)
829             _parameters = restoreParameters();
830         return _parameters.getValue(name,0);
831     }
832
833     /* ------------------------------------------------------------ */
834     /*
835      * @see javax.servlet.ServletRequest#getParameterMap()
836      */
837     @Override
838     public Map<String, String[]> getParameterMap()
839     {
840         if (!_paramsExtracted)
841             extractParameters();
842         if (_parameters == null)
843             _parameters = restoreParameters();
844         return Collections.unmodifiableMap(_parameters.toStringArrayMap());
845     }
846
847     /* ------------------------------------------------------------ */
848     /*
849      * @see javax.servlet.ServletRequest#getParameterNames()
850      */
851     @Override
852     public Enumeration<String> getParameterNames()
853     {
854         if (!_paramsExtracted)
855             extractParameters();
856         if (_parameters == null)
857             _parameters = restoreParameters();
858         return Collections.enumeration(_parameters.keySet());
859     }
860
861     /* ------------------------------------------------------------ */
862     /*
863      * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
864      */
865     @Override
866     public String[] getParameterValues(String name)
867     {
868         if (!_paramsExtracted)
869             extractParameters();
870         if (_parameters == null)
871             _parameters = restoreParameters();
872         List<String> vals = _parameters.getValues(name);
873         if (vals == null)
874             return null;
875         return vals.toArray(new String[vals.size()]);
876     }
877
878     private MultiMap<String> restoreParameters()
879     {
880         MultiMap<String> result = new MultiMap<>();
881         if (_queryParameters == null)
882             _queryParameters = extractQueryParameters();
883         result.addAllValues(_queryParameters);
884         result.addAllValues(_contentParameters);
885         return result;
886     }
887
888     public MultiMap<String> getQueryParameters()
889     {
890         return _queryParameters;
891     }
892
893     public void setQueryParameters(MultiMap<String> queryParameters)
894     {
895         _queryParameters = queryParameters;
896     }
897
898     public void setContentParameters(MultiMap<String> contentParameters)
899     {
900         _contentParameters = contentParameters;
901     }
902
903     public void resetParameters()
904     {
905         _parameters = null;
906     }
907
908     /* ------------------------------------------------------------ */
909     /*
910      * @see javax.servlet.http.HttpServletRequest#getPathInfo()
911      */
912     @Override
913     public String getPathInfo()
914     {
915         return _pathInfo;
916     }
917
918     /* ------------------------------------------------------------ */
919     /*
920      * @see javax.servlet.http.HttpServletRequest#getPathTranslated()
921      */
922     @Override
923     public String getPathTranslated()
924     {
925         if (_pathInfo == null || _context == null)
926             return null;
927         return _context.getRealPath(_pathInfo);
928     }
929
930     /* ------------------------------------------------------------ */
931     /*
932      * @see javax.servlet.ServletRequest#getProtocol()
933      */
934     @Override
935     public String getProtocol()
936     {
937         return _httpVersion.toString();
938     }
939
940     /* ------------------------------------------------------------ */
941     /*
942      * @see javax.servlet.ServletRequest#getProtocol()
943      */
944     public HttpVersion getHttpVersion()
945     {
946         return _httpVersion;
947     }
948
949     /* ------------------------------------------------------------ */
950     public String getQueryEncoding()
951     {
952         return _queryEncoding;
953     }
954
955     /* ------------------------------------------------------------ */
956     /*
957      * @see javax.servlet.http.HttpServletRequest#getQueryString()
958      */
959     @Override
960     public String getQueryString()
961     {
962         if (_queryString == null && _uri != null)
963         {
964             if (_queryEncoding == null)
965                 _queryString = _uri.getQuery();
966             else
967                 _queryString = _uri.getQuery(_queryEncoding);
968         }
969         return _queryString;
970     }
971
972     /* ------------------------------------------------------------ */
973     /*
974      * @see javax.servlet.ServletRequest#getReader()
975      */
976     @Override
977     public BufferedReader getReader() throws IOException
978     {
979         if (_inputState != __NONE && _inputState != __READER)
980             throw new IllegalStateException("STREAMED");
981
982         if (_inputState == __READER)
983             return _reader;
984
985         String encoding = getCharacterEncoding();
986         if (encoding == null)
987             encoding = StringUtil.__ISO_8859_1;
988
989         if (_reader == null || !encoding.equalsIgnoreCase(_readerEncoding))
990         {
991             final ServletInputStream in = getInputStream();
992             _readerEncoding = encoding;
993             _reader = new BufferedReader(new InputStreamReader(in,encoding))
994             {
995                 @Override
996                 public void close() throws IOException
997                 {
998                     in.close();
999                 }
1000             };
1001         }
1002         _inputState = __READER;
1003         return _reader;
1004     }
1005
1006     /* ------------------------------------------------------------ */
1007     /*
1008      * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)
1009      */
1010     @Override
1011     public String getRealPath(String path)
1012     {
1013         if (_context == null)
1014             return null;
1015         return _context.getRealPath(path);
1016     }
1017
1018     /* ------------------------------------------------------------ */
1019     /**
1020      * Access the underlying Remote {@link InetSocketAddress} for this request.
1021      * 
1022      * @return the remote {@link InetSocketAddress} for this request, or null if the request has no remote (see {@link ServletRequest#getRemoteAddr()} for
1023      *         conditions that result in no remote address)
1024      */
1025     public InetSocketAddress getRemoteInetSocketAddress()
1026     {
1027         InetSocketAddress remote = _remote;
1028         if (remote == null)
1029             remote = _channel.getRemoteAddress();
1030
1031         return remote;
1032     }
1033
1034     /* ------------------------------------------------------------ */
1035     /*
1036      * @see javax.servlet.ServletRequest#getRemoteAddr()
1037      */
1038     @Override
1039     public String getRemoteAddr()
1040     {
1041         InetSocketAddress remote=_remote;
1042         if (remote==null)
1043             remote=_channel.getRemoteAddress();
1044         
1045         if (remote==null)
1046             return "";
1047         
1048         InetAddress address = remote.getAddress();
1049         if (address==null)
1050             return remote.getHostString();
1051         
1052         return address.getHostAddress();
1053     }
1054
1055     /* ------------------------------------------------------------ */
1056     /*
1057      * @see javax.servlet.ServletRequest#getRemoteHost()
1058      */
1059     @Override
1060     public String getRemoteHost()
1061     {
1062         InetSocketAddress remote=_remote;
1063         if (remote==null)
1064             remote=_channel.getRemoteAddress();
1065         return remote==null?"":remote.getHostString();
1066     }
1067
1068     /* ------------------------------------------------------------ */
1069     /*
1070      * @see javax.servlet.ServletRequest#getRemotePort()
1071      */
1072     @Override
1073     public int getRemotePort()
1074     {
1075         InetSocketAddress remote=_remote;
1076         if (remote==null)
1077             remote=_channel.getRemoteAddress();
1078         return remote==null?0:remote.getPort();
1079     }
1080
1081     /* ------------------------------------------------------------ */
1082     /*
1083      * @see javax.servlet.http.HttpServletRequest#getRemoteUser()
1084      */
1085     @Override
1086     public String getRemoteUser()
1087     {
1088         Principal p = getUserPrincipal();
1089         if (p == null)
1090             return null;
1091         return p.getName();
1092     }
1093
1094     /* ------------------------------------------------------------ */
1095     /*
1096      * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String)
1097      */
1098     @Override
1099     public RequestDispatcher getRequestDispatcher(String path)
1100     {
1101         if (path == null || _context == null)
1102             return null;
1103
1104         // handle relative path
1105         if (!path.startsWith("/"))
1106         {
1107             String relTo = URIUtil.addPaths(_servletPath,_pathInfo);
1108             int slash = relTo.lastIndexOf("/");
1109             if (slash > 1)
1110                 relTo = relTo.substring(0,slash + 1);
1111             else
1112                 relTo = "/";
1113             path = URIUtil.addPaths(relTo,path);
1114         }
1115
1116         return _context.getRequestDispatcher(path);
1117     }
1118
1119     /* ------------------------------------------------------------ */
1120     /*
1121      * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()
1122      */
1123     @Override
1124     public String getRequestedSessionId()
1125     {
1126         return _requestedSessionId;
1127     }
1128
1129     /* ------------------------------------------------------------ */
1130     /*
1131      * @see javax.servlet.http.HttpServletRequest#getRequestURI()
1132      */
1133     @Override
1134     public String getRequestURI()
1135     {
1136         if (_requestURI == null && _uri != null)
1137             _requestURI = _uri.getPathAndParam();
1138         return _requestURI;
1139     }
1140
1141     /* ------------------------------------------------------------ */
1142     /*
1143      * @see javax.servlet.http.HttpServletRequest#getRequestURL()
1144      */
1145     @Override
1146     public StringBuffer getRequestURL()
1147     {
1148         final StringBuffer url = new StringBuffer(128);
1149         URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
1150         url.append(getRequestURI());
1151         return url;
1152     }
1153
1154     /* ------------------------------------------------------------ */
1155     public Response getResponse()
1156     {
1157         return _channel.getResponse();
1158     }
1159
1160     /* ------------------------------------------------------------ */
1161     /**
1162      * Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and, but it does not include a
1163      * path.
1164      * <p>
1165      * Because this method returns a <code>StringBuffer</code>, not a string, you can modify the URL easily, for example, to append path and query parameters.
1166      *
1167      * This method is useful for creating redirect messages and for reporting errors.
1168      *
1169      * @return "scheme://host:port"
1170      */
1171     public StringBuilder getRootURL()
1172     {
1173         StringBuilder url = new StringBuilder(128);
1174         URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
1175         return url;
1176     }
1177
1178     /* ------------------------------------------------------------ */
1179     /*
1180      * @see javax.servlet.ServletRequest#getScheme()
1181      */
1182     @Override
1183     public String getScheme()
1184     {
1185         return _scheme;
1186     }
1187
1188     /* ------------------------------------------------------------ */
1189     /*
1190      * @see javax.servlet.ServletRequest#getServerName()
1191      */
1192     @Override
1193     public String getServerName()
1194     {
1195         // Return already determined host
1196         if (_serverName != null)
1197             return _serverName;
1198
1199         if (_uri == null)
1200             throw new IllegalStateException("No uri");
1201
1202         // Return host from absolute URI
1203         _serverName = _uri.getHost();
1204         if (_serverName != null)
1205         {
1206             _port = _uri.getPort();
1207             return _serverName;
1208         }
1209
1210         // Return host from header field
1211         String hostPort = _fields.getStringField(HttpHeader.HOST);
1212         
1213         _port=0;
1214         if (hostPort != null)
1215         {
1216             int len=hostPort.length();
1217             loop: for (int i = len; i-- > 0;)
1218             {
1219                 char c2 = (char)(0xff & hostPort.charAt(i));
1220                 switch (c2)
1221                 {
1222                     case ']':
1223                         break loop;
1224
1225                     case ':':
1226                         try
1227                         {
1228                             len=i;
1229                             _port = StringUtil.toInt(hostPort.substring(i+1));
1230                         }
1231                         catch (NumberFormatException e)
1232                         {
1233                             LOG.warn(e);
1234                             _serverName=hostPort;
1235                             _port=0;
1236                             return _serverName;
1237                         }
1238                         break loop;
1239                 }
1240             }
1241             if (hostPort.charAt(0)=='[')
1242             {
1243                 if (hostPort.charAt(len-1)!=']') 
1244                 {
1245                     LOG.warn("Bad IPv6 "+hostPort);
1246                     _serverName=hostPort;
1247                     _port=0;
1248                     return _serverName;
1249                 }
1250                 _serverName = hostPort.substring(1,len-1);
1251             }
1252             else if (len==hostPort.length())
1253                 _serverName=hostPort;
1254             else
1255                 _serverName = hostPort.substring(0,len);
1256
1257             return _serverName;
1258         }
1259
1260         // Return host from connection
1261         if (_channel != null)
1262         {
1263             _serverName = getLocalName();
1264             _port = getLocalPort();
1265             if (_serverName != null && !StringUtil.ALL_INTERFACES.equals(_serverName))
1266                 return _serverName;
1267         }
1268
1269         // Return the local host
1270         try
1271         {
1272             _serverName = InetAddress.getLocalHost().getHostAddress();
1273         }
1274         catch (java.net.UnknownHostException e)
1275         {
1276             LOG.ignore(e);
1277         }
1278         return _serverName;
1279     }
1280
1281     /* ------------------------------------------------------------ */
1282     /*
1283      * @see javax.servlet.ServletRequest#getServerPort()
1284      */
1285     @Override
1286     public int getServerPort()
1287     {
1288         if (_port <= 0)
1289         {
1290             if (_serverName == null)
1291                 getServerName();
1292
1293             if (_port <= 0)
1294             {
1295                 if (_serverName != null && _uri != null)
1296                     _port = _uri.getPort();
1297                 else
1298                 {
1299                     InetSocketAddress local = _channel.getLocalAddress();
1300                     _port = local == null?0:local.getPort();
1301                 }
1302             }
1303         }
1304
1305         if (_port <= 0)
1306         {
1307             if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
1308                 return 443;
1309             return 80;
1310         }
1311         return _port;
1312     }
1313
1314     /* ------------------------------------------------------------ */
1315     @Override
1316     public ServletContext getServletContext()
1317     {
1318         return _context;
1319     }
1320
1321     /* ------------------------------------------------------------ */
1322     /*
1323      */
1324     public String getServletName()
1325     {
1326         if (_scope != null)
1327             return _scope.getName();
1328         return null;
1329     }
1330
1331     /* ------------------------------------------------------------ */
1332     /*
1333      * @see javax.servlet.http.HttpServletRequest#getServletPath()
1334      */
1335     @Override
1336     public String getServletPath()
1337     {
1338         if (_servletPath == null)
1339             _servletPath = "";
1340         return _servletPath;
1341     }
1342
1343     /* ------------------------------------------------------------ */
1344     public ServletResponse getServletResponse()
1345     {
1346         return _channel.getResponse();
1347     }
1348
1349     /* ------------------------------------------------------------ */
1350     /*
1351      * Add @override when 3.1 api is available
1352      */
1353     public String changeSessionId()
1354     {
1355         HttpSession session = getSession(false);
1356         if (session == null)
1357             throw new IllegalStateException("No session");
1358
1359         if (session instanceof AbstractSession)
1360         {
1361             AbstractSession abstractSession =  ((AbstractSession)session);
1362             abstractSession.renewId(this);
1363             if (getRemoteUser() != null)
1364                 abstractSession.setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
1365             if (abstractSession.isIdChanged())
1366                 _channel.getResponse().addCookie(_sessionManager.getSessionCookie(abstractSession, getContextPath(), isSecure()));
1367         }
1368
1369         return session.getId();
1370     }
1371
1372     /* ------------------------------------------------------------ */
1373     /*
1374      * @see javax.servlet.http.HttpServletRequest#getSession()
1375      */
1376     @Override
1377     public HttpSession getSession()
1378     {
1379         return getSession(true);
1380     }
1381
1382     /* ------------------------------------------------------------ */
1383     /*
1384      * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
1385      */
1386     @Override
1387     public HttpSession getSession(boolean create)
1388     {
1389         if (_session != null)
1390         {
1391             if (_sessionManager != null && !_sessionManager.isValid(_session))
1392                 _session = null;
1393             else
1394                 return _session;
1395         }
1396
1397         if (!create)
1398             return null;
1399         
1400         if (getResponse().isCommitted())
1401             throw new IllegalStateException("Response is committed");
1402
1403         if (_sessionManager == null)
1404             throw new IllegalStateException("No SessionManager");
1405
1406         _session = _sessionManager.newHttpSession(this);
1407         HttpCookie cookie = _sessionManager.getSessionCookie(_session,getContextPath(),isSecure());
1408         if (cookie != null)
1409             _channel.getResponse().addCookie(cookie);
1410
1411         return _session;
1412     }
1413
1414     /* ------------------------------------------------------------ */
1415     /**
1416      * @return Returns the sessionManager.
1417      */
1418     public SessionManager getSessionManager()
1419     {
1420         return _sessionManager;
1421     }
1422
1423     /* ------------------------------------------------------------ */
1424     /**
1425      * Get Request TimeStamp
1426      *
1427      * @return The time that the request was received.
1428      */
1429     public long getTimeStamp()
1430     {
1431         return _timeStamp;
1432     }
1433
1434     /* ------------------------------------------------------------ */
1435     /**
1436      * @return Returns the uri.
1437      */
1438     public HttpURI getUri()
1439     {
1440         return _uri;
1441     }
1442
1443     /* ------------------------------------------------------------ */
1444     public UserIdentity getUserIdentity()
1445     {
1446         if (_authentication instanceof Authentication.Deferred)
1447             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1448
1449         if (_authentication instanceof Authentication.User)
1450             return ((Authentication.User)_authentication).getUserIdentity();
1451         return null;
1452     }
1453
1454     /* ------------------------------------------------------------ */
1455     /**
1456      * @return The resolved user Identity, which may be null if the {@link Authentication} is not {@link Authentication.User} (eg.
1457      *         {@link Authentication.Deferred}).
1458      */
1459     public UserIdentity getResolvedUserIdentity()
1460     {
1461         if (_authentication instanceof Authentication.User)
1462             return ((Authentication.User)_authentication).getUserIdentity();
1463         return null;
1464     }
1465
1466     /* ------------------------------------------------------------ */
1467     public UserIdentity.Scope getUserIdentityScope()
1468     {
1469         return _scope;
1470     }
1471
1472     /* ------------------------------------------------------------ */
1473     /*
1474      * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
1475      */
1476     @Override
1477     public Principal getUserPrincipal()
1478     {
1479         if (_authentication instanceof Authentication.Deferred)
1480             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1481
1482         if (_authentication instanceof Authentication.User)
1483         {
1484             UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
1485             return user.getUserPrincipal();
1486         }
1487         
1488         return null;
1489     }
1490
1491
1492     /* ------------------------------------------------------------ */
1493     public boolean isHandled()
1494     {
1495         return _handled;
1496     }
1497
1498     @Override
1499     public boolean isAsyncStarted()
1500     {
1501        return getHttpChannelState().isAsyncStarted();
1502     }
1503
1504
1505     /* ------------------------------------------------------------ */
1506     @Override
1507     public boolean isAsyncSupported()
1508     {
1509         return _asyncSupported;
1510     }
1511
1512     /* ------------------------------------------------------------ */
1513     /*
1514      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie()
1515      */
1516     @Override
1517     public boolean isRequestedSessionIdFromCookie()
1518     {
1519         return _requestedSessionId != null && _requestedSessionIdFromCookie;
1520     }
1521
1522     /* ------------------------------------------------------------ */
1523     /*
1524      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl()
1525      */
1526     @Override
1527     public boolean isRequestedSessionIdFromUrl()
1528     {
1529         return _requestedSessionId != null && !_requestedSessionIdFromCookie;
1530     }
1531
1532     /* ------------------------------------------------------------ */
1533     /*
1534      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL()
1535      */
1536     @Override
1537     public boolean isRequestedSessionIdFromURL()
1538     {
1539         return _requestedSessionId != null && !_requestedSessionIdFromCookie;
1540     }
1541
1542     /* ------------------------------------------------------------ */
1543     /*
1544      * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
1545      */
1546     @Override
1547     public boolean isRequestedSessionIdValid()
1548     {
1549         if (_requestedSessionId == null)
1550             return false;
1551
1552         HttpSession session = getSession(false);
1553         return (session != null && _sessionManager.getSessionIdManager().getClusterId(_requestedSessionId).equals(_sessionManager.getClusterId(session)));
1554     }
1555
1556     /* ------------------------------------------------------------ */
1557     /*
1558      * @see javax.servlet.ServletRequest#isSecure()
1559      */
1560     @Override
1561     public boolean isSecure()
1562     {
1563         return _secure;
1564     }
1565
1566     /* ------------------------------------------------------------ */
1567     public void setSecure(boolean secure)
1568     {
1569         _secure=secure;
1570     }
1571
1572     /* ------------------------------------------------------------ */
1573     /*
1574      * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String)
1575      */
1576     @Override
1577     public boolean isUserInRole(String role)
1578     {
1579         if (_authentication instanceof Authentication.Deferred)
1580             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
1581
1582         if (_authentication instanceof Authentication.User)
1583             return ((Authentication.User)_authentication).isUserInRole(_scope,role);
1584         return false;
1585     }
1586
1587     /* ------------------------------------------------------------ */
1588     public HttpSession recoverNewSession(Object key)
1589     {
1590         if (_savedNewSessions == null)
1591             return null;
1592         return _savedNewSessions.get(key);
1593     }
1594
1595     /* ------------------------------------------------------------ */
1596     protected void recycle()
1597     {
1598         if (_context != null)
1599             throw new IllegalStateException("Request in context!");
1600         
1601         if (_inputState == __READER)
1602         {
1603             try
1604             {
1605                 int r = _reader.read();
1606                 while (r != -1)
1607                     r = _reader.read();
1608             }
1609             catch (Exception e)
1610             {
1611                 LOG.ignore(e);
1612                 _reader = null;
1613             }
1614         }
1615
1616         _dispatcherType=null;
1617         setAuthentication(Authentication.NOT_CHECKED);
1618         getHttpChannelState().recycle();
1619         if (_async!=null)
1620             _async.reset();
1621         _async=null;
1622         _asyncSupported = true;
1623         _handled = false;
1624         if (_attributes != null)
1625             _attributes.clearAttributes();
1626         _characterEncoding = null;
1627         _contextPath = null;
1628         if (_cookies != null)
1629             _cookies.reset();
1630         _cookiesExtracted = false;
1631         _context = null;
1632         _newContext=false;
1633         _serverName = null;
1634         _httpMethod=null;
1635         _httpMethodString = null;
1636         _pathInfo = null;
1637         _port = 0;
1638         _httpVersion = HttpVersion.HTTP_1_1;
1639         _queryEncoding = null;
1640         _queryString = null;
1641         _requestedSessionId = null;
1642         _requestedSessionIdFromCookie = false;
1643         _secure=false;
1644         _session = null;
1645         _sessionManager = null;
1646         _requestURI = null;
1647         _scope = null;
1648         _scheme = URIUtil.HTTP;
1649         _servletPath = null;
1650         _timeStamp = 0;
1651         _uri = null;
1652         _queryParameters = null;
1653         _contentParameters = null;
1654         _parameters = null;
1655         _paramsExtracted = false;
1656         _inputState = __NONE;
1657
1658         if (_savedNewSessions != null)
1659             _savedNewSessions.clear();
1660         _savedNewSessions=null;
1661         _multiPartInputStream = null;
1662         _remote=null;
1663         _fields.clear();
1664         _input.recycle();
1665     }
1666
1667     /* ------------------------------------------------------------ */
1668     /*
1669      * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String)
1670      */
1671     @Override
1672     public void removeAttribute(String name)
1673     {
1674         Object old_value = _attributes == null?null:_attributes.getAttribute(name);
1675
1676         if (_attributes != null)
1677             _attributes.removeAttribute(name);
1678
1679         if (old_value != null && !_requestAttributeListeners.isEmpty())
1680         {
1681             final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value);
1682             for (ServletRequestAttributeListener listener : _requestAttributeListeners)
1683                 listener.attributeRemoved(event);
1684         }
1685     }
1686
1687     /* ------------------------------------------------------------ */
1688     public void removeEventListener(final EventListener listener)
1689     {
1690         _requestAttributeListeners.remove(listener);
1691     }
1692
1693     /* ------------------------------------------------------------ */
1694     public void saveNewSession(Object key, HttpSession session)
1695     {
1696         if (_savedNewSessions == null)
1697             _savedNewSessions = new HashMap<>();
1698         _savedNewSessions.put(key,session);
1699     }
1700
1701     /* ------------------------------------------------------------ */
1702     public void setAsyncSupported(boolean supported)
1703     {
1704         _asyncSupported = supported;
1705     }
1706
1707     /* ------------------------------------------------------------ */
1708     /*
1709      * Set a request attribute. if the attribute name is "org.eclipse.jetty.server.server.Request.queryEncoding" then the value is also passed in a call to
1710      * {@link #setQueryEncoding}.
1711      *
1712      * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)
1713      */
1714     @Override
1715     public void setAttribute(String name, Object value)
1716     {
1717         Object old_value = _attributes == null?null:_attributes.getAttribute(name);
1718
1719         if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
1720             setQueryEncoding(value == null?null:value.toString());
1721         else if ("org.eclipse.jetty.server.sendContent".equals(name))
1722             LOG.warn("Deprecated: org.eclipse.jetty.server.sendContent");
1723         
1724         if (_attributes == null)
1725             _attributes = new AttributesMap();
1726         _attributes.setAttribute(name,value);
1727
1728         if (!_requestAttributeListeners.isEmpty())
1729         {
1730             final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value == null?value:old_value);
1731             for (ServletRequestAttributeListener l : _requestAttributeListeners)
1732             {
1733                 if (old_value == null)
1734                     l.attributeAdded(event);
1735                 else if (value == null)
1736                     l.attributeRemoved(event);
1737                 else
1738                     l.attributeReplaced(event);
1739             }
1740         }
1741     }
1742
1743     /* ------------------------------------------------------------ */
1744     /*
1745      */
1746     public void setAttributes(Attributes attributes)
1747     {
1748         _attributes = attributes;
1749     }
1750
1751     /* ------------------------------------------------------------ */
1752
1753     /* ------------------------------------------------------------ */
1754     /**
1755      * Set the authentication.
1756      *
1757      * @param authentication
1758      *            the authentication to set
1759      */
1760     public void setAuthentication(Authentication authentication)
1761     {
1762         _authentication = authentication;
1763     }
1764
1765     /* ------------------------------------------------------------ */
1766     /*
1767      * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
1768      */
1769     @Override
1770     public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
1771     {
1772         if (_inputState != __NONE)
1773             return;
1774
1775         _characterEncoding = encoding;
1776
1777         // check encoding is supported
1778         if (!StringUtil.isUTF8(encoding))
1779         {
1780             try
1781             {
1782                 Charset.forName(encoding);
1783             }
1784             catch (UnsupportedCharsetException e)
1785             {
1786                 throw new UnsupportedEncodingException(e.getMessage());
1787             }
1788         }
1789     }
1790
1791     /* ------------------------------------------------------------ */
1792     /*
1793      * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
1794      */
1795     public void setCharacterEncodingUnchecked(String encoding)
1796     {
1797         _characterEncoding = encoding;
1798     }
1799
1800     /* ------------------------------------------------------------ */
1801     /*
1802      * @see javax.servlet.ServletRequest#getContentType()
1803      */
1804     public void setContentType(String contentType)
1805     {
1806         _fields.put(HttpHeader.CONTENT_TYPE,contentType);
1807
1808     }
1809
1810     /* ------------------------------------------------------------ */
1811     /**
1812      * Set request context
1813      *
1814      * @param context
1815      *            context object
1816      */
1817     public void setContext(Context context)
1818     {
1819         _newContext = _context != context;
1820         _context = context;
1821     }
1822
1823     /* ------------------------------------------------------------ */
1824     /**
1825      * @return True if this is the first call of {@link #takeNewContext()} since the last
1826      *         {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call.
1827      */
1828     public boolean takeNewContext()
1829     {
1830         boolean nc = _newContext;
1831         _newContext = false;
1832         return nc;
1833     }
1834
1835     /* ------------------------------------------------------------ */
1836     /**
1837      * Sets the "context path" for this request
1838      *
1839      * @see HttpServletRequest#getContextPath()
1840      */
1841     public void setContextPath(String contextPath)
1842     {
1843         _contextPath = contextPath;
1844     }
1845
1846     /* ------------------------------------------------------------ */
1847     /**
1848      * @param cookies
1849      *            The cookies to set.
1850      */
1851     public void setCookies(Cookie[] cookies)
1852     {
1853         if (_cookies == null)
1854             _cookies = new CookieCutter();
1855         _cookies.setCookies(cookies);
1856     }
1857
1858     /* ------------------------------------------------------------ */
1859     public void setDispatcherType(DispatcherType type)
1860     {
1861         _dispatcherType = type;
1862     }
1863
1864     /* ------------------------------------------------------------ */
1865     public void setHandled(boolean h)
1866     {
1867         _handled = h;
1868         Response r=getResponse();
1869         if (_handled && r.getStatus()==0)
1870             r.setStatus(200);
1871     }
1872
1873     /* ------------------------------------------------------------ */
1874     /**
1875      * @param method
1876      *            The method to set.
1877      */
1878     public void setMethod(HttpMethod httpMethod, String method)
1879     {
1880         _httpMethod=httpMethod;
1881         _httpMethodString = method;
1882     }
1883
1884     /* ------------------------------------------------------------ */
1885     public boolean isHead()
1886     {
1887         return HttpMethod.HEAD==_httpMethod;
1888     }
1889
1890     /* ------------------------------------------------------------ */
1891     /**
1892      * @param pathInfo
1893      *            The pathInfo to set.
1894      */
1895     public void setPathInfo(String pathInfo)
1896     {
1897         _pathInfo = pathInfo;
1898     }
1899
1900     /* ------------------------------------------------------------ */
1901     /**
1902      * @param version
1903      *            The protocol to set.
1904      */
1905     public void setHttpVersion(HttpVersion version)
1906     {
1907         _httpVersion = version;
1908     }
1909
1910     /* ------------------------------------------------------------ */
1911     /**
1912      * Set the character encoding used for the query string. This call will effect the return of getQueryString and getParamaters. It must be called before any
1913      * getParameter methods.
1914      *
1915      * The request attribute "org.eclipse.jetty.server.server.Request.queryEncoding" may be set as an alternate method of calling setQueryEncoding.
1916      *
1917      * @param queryEncoding
1918      */
1919     public void setQueryEncoding(String queryEncoding)
1920     {
1921         _queryEncoding = queryEncoding;
1922         _queryString = null;
1923     }
1924
1925     /* ------------------------------------------------------------ */
1926     /**
1927      * @param queryString
1928      *            The queryString to set.
1929      */
1930     public void setQueryString(String queryString)
1931     {
1932         _queryString = queryString;
1933         _queryEncoding = null; //assume utf-8
1934     }
1935
1936     /* ------------------------------------------------------------ */
1937     /**
1938      * @param addr
1939      *            The address to set.
1940      */
1941     public void setRemoteAddr(InetSocketAddress addr)
1942     {
1943         _remote = addr;
1944     }
1945     
1946     /* ------------------------------------------------------------ */
1947     /**
1948      * @param requestedSessionId
1949      *            The requestedSessionId to set.
1950      */
1951     public void setRequestedSessionId(String requestedSessionId)
1952     {
1953         _requestedSessionId = requestedSessionId;
1954     }
1955
1956     /* ------------------------------------------------------------ */
1957     /**
1958      * @param requestedSessionIdCookie
1959      *            The requestedSessionIdCookie to set.
1960      */
1961     public void setRequestedSessionIdFromCookie(boolean requestedSessionIdCookie)
1962     {
1963         _requestedSessionIdFromCookie = requestedSessionIdCookie;
1964     }
1965
1966     /* ------------------------------------------------------------ */
1967     /**
1968      * @param requestURI
1969      *            The requestURI to set.
1970      */
1971     public void setRequestURI(String requestURI)
1972     {
1973         _requestURI = requestURI;
1974     }
1975
1976     /* ------------------------------------------------------------ */
1977     /**
1978      * @param scheme
1979      *            The scheme to set.
1980      */
1981     public void setScheme(String scheme)
1982     {
1983         _scheme = scheme;
1984     }
1985
1986     /* ------------------------------------------------------------ */
1987     /**
1988      * @param host
1989      *            The host to set.
1990      */
1991     public void setServerName(String host)
1992     {
1993         _serverName = host;
1994     }
1995
1996     /* ------------------------------------------------------------ */
1997     /**
1998      * @param port
1999      *            The port to set.
2000      */
2001     public void setServerPort(int port)
2002     {
2003         _port = port;
2004     }
2005
2006     /* ------------------------------------------------------------ */
2007     /**
2008      * @param servletPath
2009      *            The servletPath to set.
2010      */
2011     public void setServletPath(String servletPath)
2012     {
2013         _servletPath = servletPath;
2014     }
2015
2016     /* ------------------------------------------------------------ */
2017     /**
2018      * @param session
2019      *            The session to set.
2020      */
2021     public void setSession(HttpSession session)
2022     {
2023         _session = session;
2024     }
2025
2026     /* ------------------------------------------------------------ */
2027     /**
2028      * @param sessionManager
2029      *            The sessionManager to set.
2030      */
2031     public void setSessionManager(SessionManager sessionManager)
2032     {
2033         _sessionManager = sessionManager;
2034     }
2035
2036     /* ------------------------------------------------------------ */
2037     public void setTimeStamp(long ts)
2038     {
2039         _timeStamp = ts;
2040     }
2041
2042     /* ------------------------------------------------------------ */
2043     /**
2044      * @param uri
2045      *            The uri to set.
2046      */
2047     public void setUri(HttpURI uri)
2048     {
2049         _uri = uri;
2050     }
2051
2052     /* ------------------------------------------------------------ */
2053     public void setUserIdentityScope(UserIdentity.Scope scope)
2054     {
2055         _scope = scope;
2056     }
2057
2058     /* ------------------------------------------------------------ */
2059     @Override
2060     public AsyncContext startAsync() throws IllegalStateException
2061     {
2062         if (!_asyncSupported)
2063             throw new IllegalStateException("!asyncSupported");
2064         HttpChannelState state = getHttpChannelState();
2065         if (_async==null)
2066             _async=new AsyncContextState(state);
2067         AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,this,getResponse());
2068         state.startAsync(event);
2069         return _async;
2070     }
2071
2072     /* ------------------------------------------------------------ */
2073     @Override
2074     public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
2075     {
2076         if (!_asyncSupported)
2077             throw new IllegalStateException("!asyncSupported");
2078         HttpChannelState state = getHttpChannelState();
2079         if (_async==null)
2080             _async=new AsyncContextState(state);
2081         AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse);
2082         event.setDispatchContext(getServletContext());
2083         event.setDispatchPath(URIUtil.addPaths(getServletPath(),getPathInfo()));
2084         state.startAsync(event);
2085         return _async;
2086     }
2087
2088     /* ------------------------------------------------------------ */
2089     @Override
2090     public String toString()
2091     {
2092         return (_handled?"[":"(") + getMethod() + " " + _uri + (_handled?"]@":")@") + hashCode() + " " + super.toString();
2093     }
2094
2095     /* ------------------------------------------------------------ */
2096     @Override
2097     public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
2098     {
2099         if (_authentication instanceof Authentication.Deferred)
2100         {
2101             setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
2102             return !(_authentication instanceof Authentication.ResponseSent);
2103         }
2104         response.sendError(HttpStatus.UNAUTHORIZED_401);
2105         return false;
2106     }
2107
2108     /* ------------------------------------------------------------ */
2109     @Override
2110     public Part getPart(String name) throws IOException, ServletException
2111     {
2112         getParts();
2113
2114         return _multiPartInputStream.getPart(name);
2115     }
2116
2117     /* ------------------------------------------------------------ */
2118     @Override
2119     public Collection<Part> getParts() throws IOException, ServletException
2120     {
2121         if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
2122             throw new ServletException("Content-Type != multipart/form-data");
2123         return getParts(null);
2124     }
2125
2126     private Collection<Part> getParts(MultiMap<String> params) throws IOException, ServletException
2127     {
2128         if (_multiPartInputStream == null)
2129             _multiPartInputStream = (MultiPartInputStreamParser)getAttribute(__MULTIPART_INPUT_STREAM);
2130
2131         if (_multiPartInputStream == null)
2132         {
2133             MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
2134             
2135             if (config == null)
2136                 throw new IllegalStateException("No multipart config for servlet");
2137             
2138             _multiPartInputStream = new MultiPartInputStreamParser(getInputStream(),
2139                                                              getContentType(), config, 
2140                                                              (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
2141             
2142             setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
2143             setAttribute(__MULTIPART_CONTEXT, _context);
2144             Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
2145             ByteArrayOutputStream os = null;
2146             for (Part p:parts)
2147             {
2148                 MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
2149                 if (mp.getContentDispositionFilename() == null)
2150                 {
2151                     // Servlet Spec 3.0 pg 23, parts without filename must be put into params.
2152                     String charset = null;
2153                     if (mp.getContentType() != null)
2154                         charset = MimeTypes.getCharsetFromContentType(mp.getContentType());
2155
2156                     try (InputStream is = mp.getInputStream())
2157                     {
2158                         if (os == null)
2159                             os = new ByteArrayOutputStream();
2160                         IO.copy(is, os);
2161                         String content=new String(os.toByteArray(),charset==null?StandardCharsets.UTF_8:Charset.forName(charset));
2162                         if (_contentParameters == null)
2163                             _contentParameters = params == null ? new MultiMap<String>() : params;
2164                         _contentParameters.add(mp.getName(), content);
2165                     }
2166                     os.reset();
2167                 }
2168             }
2169         }
2170
2171         return _multiPartInputStream.getParts();
2172     }
2173
2174     /* ------------------------------------------------------------ */
2175     @Override
2176     public void login(String username, String password) throws ServletException
2177     {
2178         if (_authentication instanceof Authentication.Deferred)
2179         {
2180             _authentication=((Authentication.Deferred)_authentication).login(username,password,this);
2181             if (_authentication == null)
2182                 throw new Authentication.Failed("Authentication failed for username '"+username+"'");
2183         }
2184         else
2185         {
2186             throw new Authentication.Failed("Authenticated failed for username '"+username+"'. Already authenticated as "+_authentication);
2187         }
2188     }
2189
2190     /* ------------------------------------------------------------ */
2191     @Override
2192     public void logout() throws ServletException
2193     {
2194         if (_authentication instanceof Authentication.User)
2195             ((Authentication.User)_authentication).logout();
2196         _authentication=Authentication.UNAUTHENTICATED;
2197     }
2198
2199     public void mergeQueryParameters(String newQuery, boolean updateQueryString)
2200     {
2201         MultiMap<String> newQueryParams = new MultiMap<>();
2202         // Have to assume ENCODING because we can't know otherwise.
2203         UrlEncoded.decodeTo(newQuery, newQueryParams, UrlEncoded.ENCODING, -1);
2204
2205         MultiMap<String> oldQueryParams = _queryParameters;
2206         if (oldQueryParams == null && _queryString != null)
2207         {
2208             oldQueryParams = new MultiMap<>();
2209             UrlEncoded.decodeTo(_queryString, oldQueryParams, getQueryEncoding(), -1);
2210         }
2211
2212         MultiMap<String> mergedQueryParams = newQueryParams;
2213         if (oldQueryParams != null)
2214         {
2215             // Parameters values are accumulated.
2216             mergedQueryParams = new MultiMap<>(newQueryParams);
2217             mergedQueryParams.addAllValues(oldQueryParams);
2218         }
2219
2220         setQueryParameters(mergedQueryParams);
2221         resetParameters();
2222
2223         if (updateQueryString)
2224         {
2225             // Build the new merged query string, parameters in the
2226             // new query string hide parameters in the old query string.
2227             StringBuilder mergedQuery = new StringBuilder(newQuery);
2228             for (Map.Entry<String, List<String>> entry : mergedQueryParams.entrySet())
2229             {
2230                 if (newQueryParams.containsKey(entry.getKey()))
2231                     continue;
2232                 for (String value : entry.getValue())
2233                     mergedQuery.append("&").append(entry.getKey()).append("=").append(value);
2234             }
2235
2236             setQueryString(mergedQuery.toString());
2237         }
2238     }
2239
2240     /**
2241      * @see javax.servlet.http.HttpServletRequest#upgrade(java.lang.Class)
2242      */
2243     @Override
2244     public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException
2245     {
2246         if (getContext() == null)
2247             throw new ServletException ("Unable to instantiate "+handlerClass);
2248
2249         try
2250         {
2251             //Instantiate an instance and inject it
2252             T h = getContext().createInstance(handlerClass);
2253             
2254             //TODO handle the rest of the upgrade process
2255             
2256             return h;
2257         }
2258         catch (Exception e)
2259         {
2260             if (e instanceof ServletException)
2261                 throw (ServletException)e;
2262             throw new ServletException(e);
2263         }
2264     }
2265 }