001: //========================================================================
002: //Copyright 2006 Mort Bay Consulting Pty. Ltd.
003: //------------------------------------------------------------------------
004: //Licensed under the Apache License, Version 2.0 (the "License");
005: //you may not use this file except in compliance with the License.
006: //You may obtain a copy of the License at
007: //http://www.apache.org/licenses/LICENSE-2.0
008: //Unless required by applicable law or agreed to in writing, software
009: //distributed under the License is distributed on an "AS IS" BASIS,
010: //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: //See the License for the specific language governing permissions and
012: //limitations under the License.
013: //========================================================================
014:
015: package org.mortbay.jetty;
016:
017: import java.io.UnsupportedEncodingException;
018:
019: import org.mortbay.util.MultiMap;
020: import org.mortbay.util.StringUtil;
021: import org.mortbay.util.TypeUtil;
022: import org.mortbay.util.URIUtil;
023: import org.mortbay.util.UrlEncoded;
024:
025: /* ------------------------------------------------------------ */
026: /** Http URI.
027: * Parse a HTTP URI from a string or byte array. Given a URI
028: * <code>http://user@host:port/path/info;param?query#fragment</code>
029: * this class will split it into the following undecoded optional elements:<ul>
030: * <li>{@link #getScheme()} - http:</li>
031: * <li>{@link #getAuthority()} - //name@host:port</li>
032: * <li>{@link #getHost()} - host</li>
033: * <li>{@link #getPort()} - port</li>
034: * <li>{@link #getPath()} - /path/info</li>
035: * <li>{@link #getParam()} - param</li>
036: * <li>{@link #getQuery()} - query</li>
037: * <li>{@link #getFragment()} - fragment</li>
038: * </ul>
039: *
040: */
041: public class HttpURI {
042: private static byte[] __empty = {};
043: private final static int START = 0, AUTH_OR_PATH = 1,
044: SCHEME_OR_PATH = 2, AUTH = 4, PORT = 5, PATH = 6,
045: PARAM = 7, QUERY = 8;
046:
047: boolean _partial = false;
048: byte[] _raw = __empty;
049: String _rawString;
050: int _scheme;
051: int _authority;
052: int _host;
053: int _port;
054: int _path;
055: int _param;
056: int _query;
057: int _fragment;
058: int _end;
059:
060: public HttpURI() {
061:
062: }
063:
064: /* ------------------------------------------------------------ */
065: /**
066: * @param parsePartialAuth If True, parse auth without prior scheme, else treat all URIs starting with / as paths
067: */
068: public HttpURI(boolean parsePartialAuth) {
069: _partial = parsePartialAuth;
070: }
071:
072: public HttpURI(String raw) {
073: _rawString = raw;
074: byte[] b = raw.getBytes();
075: parse(b, 0, b.length);
076: }
077:
078: public HttpURI(byte[] raw, int offset, int length) {
079: parse2(raw, offset, length);
080: }
081:
082: public void parse(String raw) {
083: byte[] b = raw.getBytes();
084: parse2(b, 0, b.length);
085: _rawString = raw;
086: }
087:
088: public void parse(byte[] raw, int offset, int length) {
089: _rawString = null;
090: parse2(raw, offset, length);
091: }
092:
093: private void parse2(byte[] raw, int offset, int length) {
094: _raw = raw;
095: int i = offset;
096: int e = offset + length;
097: int state = START;
098: int m = offset;
099: _end = offset + length;
100: _scheme = offset;
101: _authority = offset;
102: _host = offset;
103: _port = offset;
104: _path = offset;
105: _param = _end;
106: _query = _end;
107: _fragment = _end;
108: while (i < e) {
109: char c = (char) (0xff & _raw[i]);
110: int s = i++;
111:
112: switch (state) {
113: case START: {
114: m = s;
115: if (c == '/') {
116: state = AUTH_OR_PATH;
117: } else if (Character.isLetterOrDigit(c)) {
118: state = SCHEME_OR_PATH;
119: } else if (c == ';') {
120: _param = s;
121: state = PARAM;
122: } else if (c == '?') {
123: _param = s;
124: _query = s;
125: state = QUERY;
126: } else if (c == '#') {
127: _param = s;
128: _query = s;
129: _fragment = s;
130: break;
131: } else
132: throw new IllegalArgumentException(new String(_raw,
133: offset, length));
134:
135: continue;
136: }
137:
138: case AUTH_OR_PATH: {
139: if ((_partial || _scheme != _authority) && c == '/') {
140: _host = i;
141: _port = _end;
142: _path = _end;
143: state = AUTH;
144: } else if (c == ';' || c == '?' || c == '#') {
145: i--;
146: state = PATH;
147: } else {
148: _host = m;
149: _port = m;
150: state = PATH;
151: }
152: continue;
153: }
154:
155: case SCHEME_OR_PATH: {
156: // short cut for http and https
157: if (length > 6 && c == 't') {
158: if (_raw[offset + 3] == ':') {
159: s = offset + 3;
160: i = offset + 4;
161: c = ':';
162: } else if (_raw[offset + 4] == ':') {
163: s = offset + 4;
164: i = offset + 5;
165: c = ':';
166: } else if (_raw[offset + 5] == ':') {
167: s = offset + 5;
168: i = offset + 6;
169: c = ':';
170: }
171: }
172:
173: if (c == ':') {
174: m = i++;
175: _authority = m;
176: _path = m;
177: c = (char) (0xff & _raw[i]);
178: if (c == '/')
179: state = AUTH_OR_PATH;
180: else {
181: _host = m;
182: _port = m;
183: state = PATH;
184: }
185: } else if (c == '/') {
186: state = PATH;
187: } else if (c == ';') {
188: _param = s;
189: state = PARAM;
190: } else if (c == '?') {
191: _param = s;
192: _query = s;
193: state = QUERY;
194: } else if (c == '#') {
195: _param = s;
196: _query = s;
197: _fragment = s;
198: break;
199: }
200: continue;
201: }
202:
203: case AUTH: {
204: if (c == '/') {
205: m = s;
206: _path = m;
207: _port = _path;
208: state = PATH;
209: } else if (c == '@') {
210: _host = i;
211: } else if (c == ':') {
212: _port = s;
213: state = PORT;
214: }
215: continue;
216: }
217:
218: case PORT: {
219: if (c == '/') {
220: m = s;
221: _path = m;
222: if (_port <= _authority)
223: _port = _path;
224: state = PATH;
225: }
226: continue;
227: }
228:
229: case PATH: {
230: if (c == ';') {
231: _param = s;
232: state = PARAM;
233: } else if (c == '?') {
234: _param = s;
235: _query = s;
236: state = QUERY;
237: } else if (c == '#') {
238: _param = s;
239: _query = s;
240: _fragment = s;
241: break;
242: }
243: continue;
244: }
245:
246: case PARAM: {
247: if (c == '?') {
248: _query = s;
249: state = QUERY;
250: } else if (c == '#') {
251: _query = s;
252: _fragment = s;
253: break;
254: }
255: continue;
256: }
257:
258: case QUERY: {
259: if (c == '#') {
260: _fragment = s;
261: break;
262: }
263: continue;
264: }
265:
266: }
267: }
268: }
269:
270: public String getScheme() {
271: if (_scheme == _authority)
272: return null;
273: int l = _authority - _scheme;
274: if (l == 5 && _raw[_scheme] == 'h' && _raw[_scheme + 1] == 't'
275: && _raw[_scheme + 2] == 't' && _raw[_scheme + 3] == 'p')
276: return HttpSchemes.HTTP;
277: if (l == 6 && _raw[_scheme] == 'h' && _raw[_scheme + 1] == 't'
278: && _raw[_scheme + 2] == 't' && _raw[_scheme + 3] == 'p'
279: && _raw[_scheme + 4] == 's')
280: return HttpSchemes.HTTPS;
281: return new String(_raw, _scheme, _authority - _scheme - 1);
282: }
283:
284: public String getAuthority() {
285: if (_authority == _path)
286: return null;
287: return new String(_raw, _authority, _path - _authority);
288: }
289:
290: public String getHost() {
291: if (_host == _port)
292: return null;
293: return new String(_raw, _host, _port - _host);
294: }
295:
296: public int getPort() {
297: if (_port == _path)
298: return -1;
299: return TypeUtil
300: .parseInt(_raw, _port + 1, _path - _port - 1, 10);
301: }
302:
303: public String getPath() {
304: if (_path == _param)
305: return null;
306: return StringUtil.toString(_raw, _path, _param - _path,
307: URIUtil.__CHARSET);
308: }
309:
310: public String getDecodedPath() {
311: if (_path == _param)
312: return null;
313: return URIUtil.decodePath(_raw, _path, _param - _path);
314: }
315:
316: public String getPathAndParam() {
317: if (_path == _query)
318: return null;
319: return StringUtil.toString(_raw, _path, _query - _path,
320: URIUtil.__CHARSET);
321: }
322:
323: public String getCompletePath() {
324: if (_path == _end)
325: return null;
326: return StringUtil.toString(_raw, _path, _end - _path,
327: URIUtil.__CHARSET);
328: }
329:
330: public String getParam() {
331: if (_param == _query)
332: return null;
333: return StringUtil.toString(_raw, _param + 1, _query - _param
334: - 1, URIUtil.__CHARSET);
335: }
336:
337: public String getQuery() {
338: if (_query == _fragment)
339: return null;
340: return StringUtil.toString(_raw, _query + 1, _fragment - _query
341: - 1, URIUtil.__CHARSET);
342: }
343:
344: public String getQuery(String encoding) {
345: if (_query == _fragment)
346: return null;
347: return StringUtil.toString(_raw, _query + 1, _fragment - _query
348: - 1, encoding == null ? URIUtil.__CHARSET : encoding);
349: }
350:
351: public String getFragment() {
352: if (_fragment == _end)
353: return null;
354: return new String(_raw, _fragment + 1, _end - _fragment - 1);
355: }
356:
357: public void decodeQueryTo(MultiMap parameters, String encoding)
358: throws UnsupportedEncodingException {
359: if (_query == _fragment)
360: return;
361:
362: if (encoding == null)
363: encoding = URIUtil.__CHARSET;
364:
365: if (StringUtil.isUTF8(encoding))
366: UrlEncoded.decodeUtf8To(_raw, _query + 1, _fragment
367: - _query - 1, parameters);
368: else
369: UrlEncoded.decodeTo(StringUtil.toString(_raw, _query + 1,
370: _fragment - _query - 1, encoding), parameters,
371: encoding);
372: }
373:
374: public void clear() {
375: _scheme = _authority = _host = _port = _path = _param = _query = _fragment = _end = 0;
376: _raw = __empty;
377: _rawString = "";
378: }
379:
380: public String toString() {
381: if (_rawString == null)
382: _rawString = new String(_raw, _scheme, _end - _scheme);
383: return _rawString;
384: }
385:
386: }
|