httpparser.java
来自「很棒的web服务器源代码」· Java 代码 · 共 527 行
JAVA
527 行
// HttpParser.java// $Id: HttpParser.java,v 1.15 2003/07/01 14:13:43 ylafon Exp $$// (c) COPYRIGHT MIT and INRIA, 1996.// Please first read the full copyright statement in file COPYRIGHT.htmlpackage org.w3c.www.http;import java.util.Date;/** * A private class to help with the parsing. * Contains only some static method, helping to parse various byte * buffers into Java object (Yes, I am still and again trying to reduce * memory consumption). * <p>I don't know wether this sucks or not. One hand I am sparing a tremedous * amount of Strings creation, on the other end I am recoding a number of * parsers that are available on String instances. */public class HttpParser { private static final boolean debug = false; /** * Emit an error. * @param mth The method trigerring the error. * @param msg An associated message. * @exception HttpInvalidValueException To indicate the error to caller. */ protected static void error(String mth, String msg) throws HttpInvalidValueException { throw new HttpInvalidValueException(mth+": "+msg); } /** * Compare two byte arrays. * I am not comfident about how the equality of byte arrays is performed * by other means, sorry. * @param b1 The first byte array. * @param o1 The offset of the bytes to compare. * @param l1 The number of bytes to compare. * @param b2 What to compare against. * @param o2 The offset of the bytes to compare. * @param l2 The length of the bytes to compare. * @return An integer, <strong><0</strong> if b1 is less than b2, * <strong>0</strong> if equals, <strong>>0</strong>otherwise. */ public static final int compare (byte b1[], int o1, int l1 , byte b2[], int o2, int l2) { while ((o1 < l1) && (o2 < l2)) { int cmp = (((int) b1[o1]) &0xff) - (((int) b2[o2]) &0xff) ; if ( cmp != 0 ) return cmp; o1++; o2++; } return ((o1 == l1) && (o2 == l2)) ? 0 : l2-l1; } /** * Compare two byte arrays. * Short-cut version of the above version. * @param b1 The first byte array. * @param o1 The offset of the bytes to compare. * @param l1 The number of bytes to compare. * @param b2 What to compare against. * @return An integer, <strong><0</strong> if b1 is less than b2, * <strong>0</strong> if equals, <strong>>0</strong>otherwise. */ public static final int compare(byte b1[], int o1, int l1, byte b2[]) { return compare(b1, o1, l1, b2, 0, b2.length); } /** * Parse an integer, and return an updated pointer. */ public static final int parseInt(byte buf[], int radix, ParseState ps) { // Skip spaces if needed int off = -1; if ( ps.isSkipable ) ps.start = off = skipSpaces(buf, ps); else ps.start = off = ps.ioff; // Parse the integer from byte[] straight (without creating Strings) int len = (ps.bufend > 0) ? ps.bufend : buf.length; int ret = 0 ; int oldret = 0 ; boolean neg = false ; if (buf[off] == (byte) '-') { neg = true; off++; } while (off < len) { int digit = ((int) buf[off]) & 0xff; if ((digit >= (byte) '0') && (digit <= (byte) '9')) { ret = ret * radix + (digit - (byte) '0'); } else if (radix >= 10) { if ((digit >= 'A') && (digit <= 'Z')) { if ((digit - 'A') + 10 < radix) ret = ret * radix + (digit - 'A' + 10); else break; } else if ((digit >= 'a') && (digit <= 'z')) { if ((digit - 'a') + 10 < radix ) ret = ret * radix + digit - 'a' + 10; else break ; } else { break ; } } else { break; } if (ret < oldret) { error("parseInt", "Integer overflow: "+ new String(buf, 0, ps.start, len)); } else { oldret = ret; } off++; } if (ret < oldret) { error("parseInt", "Integer overflow: "+ new String(buf, 0, ps.start, len)); } // Return, after updating the parsing state: ps.ooff = off; ps.end = off; if (ps.ooff == ps.ioff ) // We didn't get any number, err error("parseInt", "No number available."); return neg ? -ret : ret; } public static final int parseInt(byte buf[], ParseState ps) { return parseInt(buf, 10, ps); } /** * Parse an integer, and return an updated pointer. */ public static final long parseLong(byte buf[], int radix, ParseState ps) { // Skip spaces if needed int off = -1; if ( ps.isSkipable ) ps.start = off = skipSpaces(buf, ps); else ps.start = off = ps.ioff; // Parse the integer from byte[] straight (without creating Strings) int len = (ps.bufend > 0) ? ps.bufend : buf.length; long ret = 0 ; long oldret = 0 ; boolean neg = false ; if (buf[off] == (byte) '-') { neg = true; off++; } while (off < len) { int digit = ((int) buf[off]) & 0xff; if ((digit >= (byte) '0') && (digit <= (byte) '9')) { ret = ret * radix + (digit - (byte) '0'); } else if (radix >= 10) { if ((digit >= 'A') && (digit <= 'Z')) { if ((digit - 'A') + 10 < radix) ret = ret * radix + (digit - 'A' + 10); else break; } else if ((digit >= 'a') && (digit <= 'z')) { if ((digit - 'a') + 10 < radix ) ret = ret * radix + digit - 'a' + 10; else break ; } else { break ; } } else { break; } if (ret < oldret) { error("parseLong", "Long overflow: "+ new String(buf, 0, ps.start, len)); } else { oldret = ret; } off++; } if (ret < oldret) { error("parseLong", "Long overflow: "+ new String(buf, 0, ps.start, len)); } // Return, after updating the parsing state: ps.ooff = off; ps.end = off; if (ps.ooff == ps.ioff ) // We didn't get any number, err error("parseLong", "No number available."); return neg ? -ret : ret; } public static final long parseLong(byte buf[], ParseState ps) { return parseLong(buf, 10, ps); } public static boolean unquote(byte buf[], ParseState ps) { int off = -1; int len = -1; if (ps.isSkipable) off = skipSpaces(buf, ps); else off = ps.ioff; len = (ps.bufend > 0) ? ps.bufend : buf.length; if ((off < len) && (buf[off] == (byte) '"') ) { ps.start = ps.ioff = ++off; while(off < len) { if (buf[off] == (byte) '"') { ps.end = ps.bufend = off; return true; } else { off++; } } } else { ps.start = off; ps.end = len; } return false; } /** * Skip leading LWS, <em>not</em> including CR LF. * Update the input offset, <em>after</em> any leading space. * @param buf The buffer to be parsed. * @param ptr The buffer pointer to be updated on return. * @return The potentially advanced buffer input offset. */ public static final int skipSpaces(byte buf[], ParseState ps) { int len = (ps.bufend > 0) ? ps.bufend : buf.length; int off = ps.ioff; while (off < len) { if ((buf[off] != (byte) ' ') && (buf[off] != (byte) '\t') && (buf[off] != (byte) ps.separator)) { ps.ioff = off; return off; } off++; } return off; } /** * Parse list of items, taking care of quotes and optional LWS. * The output offset points to the <em>next</em> element of the list. * @eturn The starting location (i.e. <code>ps.start</code> value), or * <strong>-1</strong> if no item available (end of list). */ public static final int nextItem(byte buf[], ParseState ps) { // Skip leading spaces, if needed: int off = -1; int len = -1; if ( ps.isSkipable ) ps.start = off = skipSpaces(buf, ps) ; else ps.start = off = ps.ioff ; len = (ps.bufend > 0) ? ps.bufend : buf.length; if ( debug ) System.out.println("parsing: ["+new String(buf, 0, off, len-off)+ "]"); // Parse ! if ( off >= len ) return -1; // Setup for parsing, and parse ps.start = off; loop: while (off < len) { if ( buf[off] == (byte) '"' ) { // A quoted item, read as one chunk off++; while (off < len ) { if (buf[off] == (byte) '\\') { off += 2; } else if (buf[off] == (byte) '"') { off++; continue loop; } else { off++; } } if ( off == len ) error("nextItem", "Un-terminated quoted item."); } else if ((buf[off] == ps.separator) || (ps.spaceIsSep && ((buf[off] == ' ') || (buf[off] == '\t')))) { break loop; } off++; } ps.end = off; // Item start is set, we are right at the end of item if ( ps.isSkipable ) { ps.ioff = off ; ps.ooff = skipSpaces(buf, ps); } // Check for either the end of the list, or the separator: if (ps.ooff < ps.bufend) { if (buf[ps.ooff] == (byte) ps.separator) ps.ooff++; } if ( debug ) System.out.println("nextItem = ["+new String(buf, 0, ps.start, ps.end-ps.start)+"]"); return (ps.end > ps.start) ? ps.start : -1; } /** * Parse the name of a month. * Monthes are parsed as their three letters format. * @return An integer between <strong>0</strong> and <strong>11</strong>. */ private static byte monthes[][] = { { (byte) 'J', (byte) 'a', (byte) 'n' }, { (byte) 'F', (byte) 'e', (byte) 'b' }, { (byte) 'M', (byte) 'a', (byte) 'r' }, { (byte) 'A', (byte) 'p', (byte) 'r' }, { (byte) 'M', (byte) 'a', (byte) 'y' }, { (byte) 'J', (byte) 'u', (byte) 'n' }, { (byte) 'J', (byte) 'u', (byte) 'l' }, { (byte) 'A', (byte) 'u', (byte) 'g' }, { (byte) 'S', (byte) 'e', (byte) 'p' }, { (byte) 'O', (byte) 'c', (byte) 't' }, { (byte) 'N', (byte) 'o', (byte) 'v' }, { (byte) 'D', (byte) 'e', (byte) 'c' } } ; private final static byte lowerCase(int x) { if ((x >= 'A') && (x <= 'Z')) x = (x - 'A' + 'a'); return (byte) (x & 0xff); } public static int parseMonth(byte buf[], ParseState ps) { int off = -1; if ( ps.isSkipable ) off = ps.start = skipSpaces(buf, ps); else off = ps.start = ps.ioff; int len = (ps.bufend > 0) ? ps.bufend : buf.length; if ( len < 3 ) { error("parseMonth", "Invalid month name (too short)."); // NOT REACHED return -1; } // Compare to get the month: for (int i = 0 ; i < monthes.length ; i++) { int mo = off; byte m[] = monthes[i]; boolean ok = true; month_loop: for (int j = 0 ; j < m.length ; j++, mo++) { if (lowerCase(m[j]) != lowerCase(buf[mo])) { ok = false; break month_loop; } } if ( ok ) { if (mo-off == m.length) { // Skip remaining chars of month off += 3; while (off < len) { byte l = lowerCase(buf[off++]); if ((l < 'a') || (l > 'z')) break; } ps.ooff = ps.end = off; } return i; } } error("parseMonth", "Invalid month name (unknown)."); // NOT REACHED return -1; } /** * Parse a delta-second value. * @return A long giving the date at which to retry as a number of * milliseconds since Java epoch. */ public static long parseDeltaSecond(byte buf[], ParseState ps) { return parseInt(buf, ps); } /** * Parse the given byte array as an HTTP compliant date. * @param buf The byte buffer to parse. * @param sp The current parsing state. * @return A long giving the date as a number of milliseconds since epoch. */ public static long parseDate(byte buf[], ParseState ps) { int d = -1; int m = -1; int y = -1; int hh = -1; int mm = -1; int ss = -1; // My prefered argument as to why HTTP is broken ParseState it = new ParseState(); it.ioff = ps.ioff; it.bufend = ((ps.bufend > -1) ? ps.bufend : buf.length); // Skip the day name: if ( nextItem(buf, ps) < 0 ) error("parseDate", "Invalid date format (no day)"); ps.prepare(); int off = skipSpaces(buf, ps); // First fork: if ((buf[off] >= (byte) '0') && (buf[off] <= (byte) '9')) { // rfc 1123, or rfc 1036 d = parseInt(buf, ps); ps.prepare(); if (buf[ps.ioff] == (byte) ' ') { // rfc 1123 m = parseMonth(buf, ps); ps.prepare(); if ((y = parseInt(buf, ps) - 1900) < 0) y += 1900; ps.prepare(); ps.separator = (byte) ':'; hh = parseInt(buf, ps); ps.prepare(); mm = parseInt(buf, ps); ps.prepare(); ss = parseInt(buf, ps); } else { // rfc 1036 ps.separator = (byte) '-'; m = parseMonth(buf, ps); ps.prepare(); y = parseInt(buf, ps); ps.prepare(); ps.separator = (byte) ':'; hh = parseInt(buf, ps); ps.prepare(); mm = parseInt(buf, ps); ps.prepare(); ss = parseInt(buf, ps); } } else { m = parseMonth(buf, ps); ps.prepare(); d = parseInt(buf, ps); ps.prepare(); ps.separator = (byte) ':'; hh = parseInt(buf, ps); ps.prepare(); mm = parseInt(buf, ps); ps.prepare(); ss = parseInt(buf, ps); ps.prepare(); ps.separator = (byte) ' '; y = parseInt(buf, ps) - 1900; } return Date.UTC(y, m, d, hh, mm, ss); } /** * Parse a date as either a delta-second value, or a date. * In case of delta seconds, we use the current time (except if one * is provided), to compute the date. * @return A date encoded as the number of millisconds since Java epoch. */ public static long parseDateOrDeltaSeconds(byte buf[] , ParseState ps , long relto) { int off = -1; if ( ps.isSkipable ) off = ps.start = skipSpaces(buf, ps); else off = ps.ioff; int len = (ps.bufend >= 0) ? ps.bufend : buf.length; // If all digits, delta secs, otherwise date: for (int i = off ; i < len ; i++) { if ((buf[i] > '9') || (buf[i] < '0')) return parseDate(buf, ps); } // Delta seconds: long secs = (long) parseInt(buf, ps); return ((relto >= 0) ? relto + (secs * 1000) : System.currentTimeMillis() + (secs*1000)); } public static long parseDateOrDeltaSeconds(byte buf[] , ParseState ps) { return parseDateOrDeltaSeconds(buf, ps, (long) -1); } public static double parseQuality(byte buf[], ParseState ps) { // Skip spaces if needed int off = -1; if ( ps.isSkipable ) ps.start = off = skipSpaces(buf, ps); else ps.start = off = ps.ioff; // Parse the integer from byte[] straight (without creating Strings) int len = (ps.bufend > 0) ? ps.bufend : buf.length; String str = new String(buf, 0, off, len-off); try { return Double.valueOf(str).doubleValue(); } catch (Exception ex) { error("parseQuality", "Invalid floating point number."); } // Not reached: return 1.0; }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?