]> WPIA git - gigi.git/blobdiff - lib/jetty/org/eclipse/jetty/http/HttpURI.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / http / HttpURI.java
diff --git a/lib/jetty/org/eclipse/jetty/http/HttpURI.java b/lib/jetty/org/eclipse/jetty/http/HttpURI.java
new file mode 100644 (file)
index 0000000..afe925e
--- /dev/null
@@ -0,0 +1,784 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.http;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.UrlEncoded;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+
+
+/* ------------------------------------------------------------ */
+/** Http URI.
+ * Parse a HTTP URI from a string or byte array.  Given a URI
+ * <code>http://user@host:port/path/info;param?query#fragment</code>
+ * this class will split it into the following undecoded optional elements:<ul>
+ * <li>{@link #getScheme()} - http:</li>
+ * <li>{@link #getAuthority()} - //name@host:port</li>
+ * <li>{@link #getHost()} - host</li>
+ * <li>{@link #getPort()} - port</li>
+ * <li>{@link #getPath()} - /path/info</li>
+ * <li>{@link #getParam()} - param</li>
+ * <li>{@link #getQuery()} - query</li>
+ * <li>{@link #getFragment()} - fragment</li>
+ * </ul>
+ *
+ */
+public class HttpURI
+{
+    private static final byte[] __empty={};
+    private final static int
+    START=0,
+    AUTH_OR_PATH=1,
+    SCHEME_OR_PATH=2,
+    AUTH=4,
+    IPV6=5,
+    PORT=6,
+    PATH=7,
+    PARAM=8,
+    QUERY=9,
+    ASTERISK=10;
+
+    final Charset _charset;
+    boolean _partial=false;
+    byte[] _raw=__empty;
+    String _rawString;
+    int _scheme;
+    int _authority;
+    int _host;
+    int _port;
+    int _portValue;
+    int _path;
+    int _param;
+    int _query;
+    int _fragment;
+    int _end;
+    boolean _encoded=false;
+
+    public HttpURI()
+    {
+        _charset = URIUtil.__CHARSET;
+    }
+
+    public HttpURI(Charset charset)
+    {
+        _charset = charset;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param parsePartialAuth If True, parse auth without prior scheme, else treat all URIs starting with / as paths
+     */
+    public HttpURI(boolean parsePartialAuth)
+    {
+        _partial=parsePartialAuth;
+        _charset = URIUtil.__CHARSET;
+    }
+
+    public HttpURI(String raw)
+    {
+        _rawString=raw;
+        byte[] b = raw.getBytes(StandardCharsets.UTF_8);
+        parse(b,0,b.length);
+        _charset = URIUtil.__CHARSET;
+    }
+
+    public HttpURI(byte[] raw,int offset, int length)
+    {
+        parse2(raw,offset,length);
+        _charset = URIUtil.__CHARSET;
+    }
+
+    public HttpURI(URI uri)
+    {
+        parse(uri.toASCIIString());
+        _charset = URIUtil.__CHARSET;
+    }
+
+    public void parse(String raw)
+    {
+        byte[] b = StringUtil.getUtf8Bytes(raw);
+        parse2(b,0,b.length);
+        _rawString=raw;
+    }
+
+    public void parseConnect(String raw)
+    {
+        byte[] b = StringUtil.getBytes(raw);
+        parseConnect(b,0,b.length);
+        _rawString=raw;
+    }
+
+    public void parse(byte[] raw,int offset, int length)
+    {
+        _rawString=null;
+        parse2(raw,offset,length);
+    }
+
+
+    public void parseConnect(byte[] raw,int offset, int length)
+    {
+        _rawString=null;
+        _encoded=false;
+        _raw=raw;
+        int i=offset;
+        int e=offset+length;
+        int state=AUTH;
+        _end=offset+length;
+        _scheme=offset;
+        _authority=offset;
+        _host=offset;
+        _port=_end;
+        _portValue=-1;
+        _path=_end;
+        _param=_end;
+        _query=_end;
+        _fragment=_end;
+
+        loop: while (i<e)
+        {
+            char c=(char)(0xff&_raw[i]);
+            int s=i++;
+
+            switch (state)
+            {
+                case AUTH:
+                {
+                    switch (c)
+                    {
+                        case ':':
+                        {
+                            _port = s;
+                            break loop;
+                        }
+                        case '[':
+                        {
+                            state = IPV6;
+                            break;
+                        }
+                    }
+                    continue;
+                }
+
+                case IPV6:
+                {
+                    switch (c)
+                    {
+                        case '/':
+                        {
+                            throw new IllegalArgumentException("No closing ']' for " + new String(_raw,offset,length,_charset));
+                        }
+                        case ']':
+                        {
+                            state = AUTH;
+                            break;
+                        }
+                    }
+
+                    continue;
+                }
+            }
+        }
+
+        if (_port<_path)
+            _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
+        else
+            throw new IllegalArgumentException("No port");
+        _path=offset;
+    }
+
+
+    private void parse2(byte[] raw,int offset, int length)
+    {
+        _encoded=false;
+        _raw=raw;
+        int i=offset;
+        int e=offset+length;
+        int state=START;
+        int m=offset;
+        _end=offset+length;
+        _scheme=offset;
+        _authority=offset;
+        _host=offset;
+        _port=offset;
+        _portValue=-1;
+        _path=offset;
+        _param=_end;
+        _query=_end;
+        _fragment=_end;
+        while (i<e)
+        {
+            char c=(char)(0xff&_raw[i]);
+            int s=i++;
+
+            state: switch (state)
+            {
+                case START:
+                {
+                    m=s;
+                    switch(c)
+                    {
+                        case '/':
+                            state=AUTH_OR_PATH;
+                            break;
+                        case ';':
+                            _param=s;
+                            state=PARAM;
+                            break;
+                        case '?':
+                            _param=s;
+                            _query=s;
+                            state=QUERY;
+                            break;
+                        case '#':
+                            _param=s;
+                            _query=s;
+                            _fragment=s;
+                            break;
+                        case '*':
+                            _path=s;
+                            state=ASTERISK;
+                            break;
+
+                        default:
+                            state=SCHEME_OR_PATH;
+                    }
+
+                    continue;
+                }
+
+                case AUTH_OR_PATH:
+                {
+                    if ((_partial||_scheme!=_authority) && c=='/')
+                    {
+                        _host=i;
+                        _port=_end;
+                        _path=_end;
+                        state=AUTH;
+                    }
+                    else if (c==';' || c=='?' || c=='#')
+                    {
+                        i--;
+                        state=PATH;
+                    }
+                    else
+                    {
+                        _host=m;
+                        _port=m;
+                        state=PATH;
+                    }
+                    continue;
+                }
+
+                case SCHEME_OR_PATH:
+                {
+                    // short cut for http and https
+                    if (length>6 && c=='t')
+                    {
+                        if (_raw[offset+3]==':')
+                        {
+                            s=offset+3;
+                            i=offset+4;
+                            c=':';
+                        }
+                        else if (_raw[offset+4]==':')
+                        {
+                            s=offset+4;
+                            i=offset+5;
+                            c=':';
+                        }
+                        else if (_raw[offset+5]==':')
+                        {
+                            s=offset+5;
+                            i=offset+6;
+                            c=':';
+                        }
+                    }
+
+                    switch (c)
+                    {
+                        case ':':
+                        {
+                            m = i++;
+                            _authority = m;
+                            _path = m;
+                            c = (char)(0xff & _raw[i]);
+                            if (c == '/')
+                                state = AUTH_OR_PATH;
+                            else
+                            {
+                                _host = m;
+                                _port = m;
+                                state = PATH;
+                            }
+                            break;
+                        }
+
+                        case '/':
+                        {
+                            state = PATH;
+                            break;
+                        }
+
+                        case ';':
+                        {
+                            _param = s;
+                            state = PARAM;
+                            break;
+                        }
+
+                        case '?':
+                        {
+                            _param = s;
+                            _query = s;
+                            state = QUERY;
+                            break;
+                        }
+
+                        case '#':
+                        {
+                            _param = s;
+                            _query = s;
+                            _fragment = s;
+                            break;
+                        }
+                    }
+                    continue;
+                }
+
+                case AUTH:
+                {
+                    switch (c)
+                    {
+
+                        case '/':
+                        {
+                            m = s;
+                            _path = m;
+                            _port = _path;
+                            state = PATH;
+                            break;
+                        }
+                        case '@':
+                        {
+                            _host = i;
+                            break;
+                        }
+                        case ':':
+                        {
+                            _port = s;
+                            state = PORT;
+                            break;
+                        }
+                        case '[':
+                        {
+                            state = IPV6;
+                            break;
+                        }
+                    }
+                    continue;
+                }
+
+                case IPV6:
+                {
+                    switch (c)
+                    {
+                        case '/':
+                        {
+                            throw new IllegalArgumentException("No closing ']' for " + new String(_raw,offset,length,_charset));
+                        }
+                        case ']':
+                        {
+                            state = AUTH;
+                            break;
+                        }
+                    }
+
+                    continue;
+                }
+
+                case PORT:
+                {
+                    if (c=='/')
+                    {
+                        m=s;
+                        _path=m;
+                        if (_port<=_authority)
+                            _port=_path;
+                        state=PATH;
+                    }
+                    continue;
+                }
+
+                case PATH:
+                {
+                    switch (c)
+                    {
+                        case ';':
+                        {
+                            _param = s;
+                            state = PARAM;
+                            break;
+                        }
+                        case '?':
+                        {
+                            _param = s;
+                            _query = s;
+                            state = QUERY;
+                            break;
+                        }
+                        case '#':
+                        {
+                            _param = s;
+                            _query = s;
+                            _fragment = s;
+                            break state;
+                        }
+                        case '%':
+                        {
+                            _encoded=true;
+                        }
+                    }
+                    continue;
+                }
+
+                case PARAM:
+                {
+                    switch (c)
+                    {
+                        case '?':
+                        {
+                            _query = s;
+                            state = QUERY;
+                            break;
+                        }
+                        case '#':
+                        {
+                            _query = s;
+                            _fragment = s;
+                            break state;
+                        }
+                    }
+                    continue;
+                }
+
+                case QUERY:
+                {
+                    if (c=='#')
+                    {
+                        _fragment=s;
+                        break state;
+                    }
+                    continue;
+                }
+
+                case ASTERISK:
+                {
+                    throw new IllegalArgumentException("only '*'");
+                }
+            }
+        }
+
+        if (_port<_path)
+            _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
+    }
+
+    public String getScheme()
+    {
+        if (_scheme==_authority)
+            return null;
+        int l=_authority-_scheme;
+        if (l==5 &&
+                _raw[_scheme]=='h' &&
+                _raw[_scheme+1]=='t' &&
+                _raw[_scheme+2]=='t' &&
+                _raw[_scheme+3]=='p' )
+            return HttpScheme.HTTP.asString();
+        if (l==6 &&
+                _raw[_scheme]=='h' &&
+                _raw[_scheme+1]=='t' &&
+                _raw[_scheme+2]=='t' &&
+                _raw[_scheme+3]=='p' &&
+                _raw[_scheme+4]=='s' )
+            return HttpScheme.HTTPS.asString();
+
+        return new String(_raw,_scheme,_authority-_scheme-1,_charset);
+    }
+
+    public String getAuthority()
+    {
+        if (_authority==_path)
+            return null;
+        return new String(_raw,_authority,_path-_authority,_charset);
+    }
+
+    public String getHost()
+    {
+        if (_host==_port)
+            return null;
+        if (_raw[_host]=='[')
+            return new String(_raw,_host+1,_port-_host-2,_charset);
+        return new String(_raw,_host,_port-_host,_charset);
+    }
+
+    public int getPort()
+    {
+        return _portValue;
+    }
+
+    public String getPath()
+    {
+        if (_path==_param)
+            return null;
+        return new String(_raw,_path,_param-_path,_charset);
+    }
+
+    public String getDecodedPath()
+    {
+        if (_path==_param)
+            return null;
+
+        Utf8StringBuilder utf8b=null;
+
+        for (int i=_path;i<_param;i++)
+        {
+            byte b = _raw[i];
+
+            if (b=='%')
+            {
+                if (utf8b==null)
+                {
+                    utf8b=new Utf8StringBuilder();
+                    utf8b.append(_raw,_path,i-_path);
+                }
+                
+                if ((i+2)>=_param)
+                    throw new IllegalArgumentException("Bad % encoding: "+this);
+                if (_raw[i+1]=='u')
+                {
+                    if ((i+5)>=_param)
+                        throw new IllegalArgumentException("Bad %u encoding: "+this);
+                    try
+                    {
+                        String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
+                        utf8b.getStringBuilder().append(unicode);
+                        i+=5;
+                    }
+                    catch(Exception e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                }
+                else
+                {
+                    b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
+                    utf8b.append(b);
+                    i+=2;
+                }
+                continue;
+            }
+            else if (utf8b!=null)
+            {
+                utf8b.append(b);
+            }
+        }
+
+        if (utf8b==null)
+            return StringUtil.toUTF8String(_raw, _path, _param-_path);
+        return utf8b.toString();
+    }
+
+    public String getDecodedPath(String encoding)
+    {
+        return getDecodedPath(Charset.forName(encoding));
+    }
+
+    public String getDecodedPath(Charset encoding)
+    {
+        if (_path==_param)
+            return null;
+
+        int length = _param-_path;
+        byte[] bytes=null;
+        int n=0;
+
+        for (int i=_path;i<_param;i++)
+        {
+            byte b = _raw[i];
+
+            if (b=='%')
+            {
+                if (bytes==null)
+                {
+                    bytes=new byte[length];
+                    System.arraycopy(_raw,_path,bytes,0,n);
+                }
+                
+                if ((i+2)>=_param)
+                    throw new IllegalArgumentException("Bad % encoding: "+this);
+                if (_raw[i+1]=='u')
+                {
+                    if ((i+5)>=_param)
+                        throw new IllegalArgumentException("Bad %u encoding: "+this);
+
+                    try
+                    {
+                        String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
+                        byte[] encoded = unicode.getBytes(encoding);
+                        System.arraycopy(encoded,0,bytes,n,encoded.length);
+                        n+=encoded.length;
+                        i+=5;
+                    }
+                    catch(Exception e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                }
+                else
+                {
+                    b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
+                    bytes[n++]=b;
+                    i+=2;
+                }
+                continue;
+            }
+            else if (bytes==null)
+            {
+                n++;
+                continue;
+            }
+
+            bytes[n++]=b;
+        }
+
+
+        if (bytes==null)
+            return new String(_raw,_path,_param-_path,encoding);
+
+        return new String(bytes,0,n,encoding);
+    }
+
+    public String getPathAndParam()
+    {
+        if (_path==_query)
+            return null;
+        return new String(_raw,_path,_query-_path,_charset);
+    }
+
+    public String getCompletePath()
+    {
+        if (_path==_end)
+            return null;
+        return new String(_raw,_path,_end-_path,_charset);
+    }
+
+    public String getParam()
+    {
+        if (_param==_query)
+            return null;
+        return new String(_raw,_param+1,_query-_param-1,_charset);
+    }
+
+    public String getQuery()
+    {
+        if (_query==_fragment)
+            return null;
+        return new String(_raw,_query+1,_fragment-_query-1,_charset);
+    }
+
+    public String getQuery(String encoding)
+    {
+        if (_query==_fragment)
+            return null;
+        return StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding);
+    }
+
+    public boolean hasQuery()
+    {
+        return (_fragment>_query);
+    }
+
+    public String getFragment()
+    {
+        if (_fragment==_end)
+            return null;
+        return new String(_raw,_fragment+1,_end-_fragment-1,_charset);
+    }
+
+    public void decodeQueryTo(MultiMap<String> parameters)
+    {
+        if (_query==_fragment)
+            return;
+        if (_charset.equals(StandardCharsets.UTF_8))
+            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+        else
+            UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,_charset),parameters,_charset,-1);
+    }
+
+    public void decodeQueryTo(MultiMap<String> parameters, String encoding) throws UnsupportedEncodingException
+    {
+        if (_query==_fragment)
+            return;
+
+        if (encoding==null || StringUtil.isUTF8(encoding))
+            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+        else
+            UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
+    }
+
+    public void decodeQueryTo(MultiMap<String> parameters, Charset encoding) throws UnsupportedEncodingException
+    {
+        if (_query==_fragment)
+            return;
+
+        if (encoding==null || StandardCharsets.UTF_8.equals(encoding))
+            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+        else
+            UrlEncoded.decodeTo(new String(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding,-1);
+    }
+
+    public void clear()
+    {
+        _scheme=_authority=_host=_port=_path=_param=_query=_fragment=_end=0;
+        _raw=__empty;
+        _rawString="";
+        _encoded=false;
+    }
+
+    @Override
+    public String toString()
+    {
+        if (_rawString==null)
+            _rawString=new String(_raw,_scheme,_end-_scheme,_charset);
+        return _rawString;
+    }
+
+    public void writeTo(Utf8StringBuilder buf)
+    {
+        buf.append(_raw,_scheme,_end-_scheme);
+    }
+
+}