1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineParser.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,502 @@ 1.4 +/* 1.5 + * ==================================================================== 1.6 + * Licensed to the Apache Software Foundation (ASF) under one 1.7 + * or more contributor license agreements. See the NOTICE file 1.8 + * distributed with this work for additional information 1.9 + * regarding copyright ownership. The ASF licenses this file 1.10 + * to you under the Apache License, Version 2.0 (the 1.11 + * "License"); you may not use this file except in compliance 1.12 + * with the License. You may obtain a copy of the License at 1.13 + * 1.14 + * http://www.apache.org/licenses/LICENSE-2.0 1.15 + * 1.16 + * Unless required by applicable law or agreed to in writing, 1.17 + * software distributed under the License is distributed on an 1.18 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1.19 + * KIND, either express or implied. See the License for the 1.20 + * specific language governing permissions and limitations 1.21 + * under the License. 1.22 + * ==================================================================== 1.23 + * 1.24 + * This software consists of voluntary contributions made by many 1.25 + * individuals on behalf of the Apache Software Foundation. For more 1.26 + * information on the Apache Software Foundation, please see 1.27 + * <http://www.apache.org/>. 1.28 + * 1.29 + */ 1.30 + 1.31 +package ch.boye.httpclientandroidlib.message; 1.32 + 1.33 +import ch.boye.httpclientandroidlib.HttpVersion; 1.34 +import ch.boye.httpclientandroidlib.ProtocolVersion; 1.35 +import ch.boye.httpclientandroidlib.ParseException; 1.36 +import ch.boye.httpclientandroidlib.RequestLine; 1.37 +import ch.boye.httpclientandroidlib.StatusLine; 1.38 +import ch.boye.httpclientandroidlib.Header; 1.39 +import ch.boye.httpclientandroidlib.protocol.HTTP; 1.40 +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; 1.41 + 1.42 +/** 1.43 + * Basic parser for lines in the head section of an HTTP message. 1.44 + * There are individual methods for parsing a request line, a 1.45 + * status line, or a header line. 1.46 + * The lines to parse are passed in memory, the parser does not depend 1.47 + * on any specific IO mechanism. 1.48 + * Instances of this class are stateless and thread-safe. 1.49 + * Derived classes MUST maintain these properties. 1.50 + * 1.51 + * <p> 1.52 + * Note: This class was created by refactoring parsing code located in 1.53 + * various other classes. The author tags from those other classes have 1.54 + * been replicated here, although the association with the parsing code 1.55 + * taken from there has not been traced. 1.56 + * </p> 1.57 + * 1.58 + * @since 4.0 1.59 + */ 1.60 +public class BasicLineParser implements LineParser { 1.61 + 1.62 + /** 1.63 + * A default instance of this class, for use as default or fallback. 1.64 + * Note that {@link BasicLineParser} is not a singleton, there can 1.65 + * be many instances of the class itself and of derived classes. 1.66 + * The instance here provides non-customized, default behavior. 1.67 + */ 1.68 + public final static BasicLineParser DEFAULT = new BasicLineParser(); 1.69 + 1.70 + 1.71 + /** 1.72 + * A version of the protocol to parse. 1.73 + * The version is typically not relevant, but the protocol name. 1.74 + */ 1.75 + protected final ProtocolVersion protocol; 1.76 + 1.77 + 1.78 + /** 1.79 + * Creates a new line parser for the given HTTP-like protocol. 1.80 + * 1.81 + * @param proto a version of the protocol to parse, or 1.82 + * <code>null</code> for HTTP. The actual version 1.83 + * is not relevant, only the protocol name. 1.84 + */ 1.85 + public BasicLineParser(ProtocolVersion proto) { 1.86 + if (proto == null) { 1.87 + proto = HttpVersion.HTTP_1_1; 1.88 + } 1.89 + this.protocol = proto; 1.90 + } 1.91 + 1.92 + 1.93 + /** 1.94 + * Creates a new line parser for HTTP. 1.95 + */ 1.96 + public BasicLineParser() { 1.97 + this(null); 1.98 + } 1.99 + 1.100 + 1.101 + public final static 1.102 + ProtocolVersion parseProtocolVersion(String value, 1.103 + LineParser parser) 1.104 + throws ParseException { 1.105 + 1.106 + if (value == null) { 1.107 + throw new IllegalArgumentException 1.108 + ("Value to parse may not be null."); 1.109 + } 1.110 + 1.111 + if (parser == null) 1.112 + parser = BasicLineParser.DEFAULT; 1.113 + 1.114 + CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 1.115 + buffer.append(value); 1.116 + ParserCursor cursor = new ParserCursor(0, value.length()); 1.117 + return parser.parseProtocolVersion(buffer, cursor); 1.118 + } 1.119 + 1.120 + 1.121 + // non-javadoc, see interface LineParser 1.122 + public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer, 1.123 + final ParserCursor cursor) 1.124 + throws ParseException { 1.125 + 1.126 + if (buffer == null) { 1.127 + throw new IllegalArgumentException("Char array buffer may not be null"); 1.128 + } 1.129 + if (cursor == null) { 1.130 + throw new IllegalArgumentException("Parser cursor may not be null"); 1.131 + } 1.132 + 1.133 + final String protoname = this.protocol.getProtocol(); 1.134 + final int protolength = protoname.length(); 1.135 + 1.136 + int indexFrom = cursor.getPos(); 1.137 + int indexTo = cursor.getUpperBound(); 1.138 + 1.139 + skipWhitespace(buffer, cursor); 1.140 + 1.141 + int i = cursor.getPos(); 1.142 + 1.143 + // long enough for "HTTP/1.1"? 1.144 + if (i + protolength + 4 > indexTo) { 1.145 + throw new ParseException 1.146 + ("Not a valid protocol version: " + 1.147 + buffer.substring(indexFrom, indexTo)); 1.148 + } 1.149 + 1.150 + // check the protocol name and slash 1.151 + boolean ok = true; 1.152 + for (int j=0; ok && (j<protolength); j++) { 1.153 + ok = (buffer.charAt(i+j) == protoname.charAt(j)); 1.154 + } 1.155 + if (ok) { 1.156 + ok = (buffer.charAt(i+protolength) == '/'); 1.157 + } 1.158 + if (!ok) { 1.159 + throw new ParseException 1.160 + ("Not a valid protocol version: " + 1.161 + buffer.substring(indexFrom, indexTo)); 1.162 + } 1.163 + 1.164 + i += protolength+1; 1.165 + 1.166 + int period = buffer.indexOf('.', i, indexTo); 1.167 + if (period == -1) { 1.168 + throw new ParseException 1.169 + ("Invalid protocol version number: " + 1.170 + buffer.substring(indexFrom, indexTo)); 1.171 + } 1.172 + int major; 1.173 + try { 1.174 + major = Integer.parseInt(buffer.substringTrimmed(i, period)); 1.175 + } catch (NumberFormatException e) { 1.176 + throw new ParseException 1.177 + ("Invalid protocol major version number: " + 1.178 + buffer.substring(indexFrom, indexTo)); 1.179 + } 1.180 + i = period + 1; 1.181 + 1.182 + int blank = buffer.indexOf(' ', i, indexTo); 1.183 + if (blank == -1) { 1.184 + blank = indexTo; 1.185 + } 1.186 + int minor; 1.187 + try { 1.188 + minor = Integer.parseInt(buffer.substringTrimmed(i, blank)); 1.189 + } catch (NumberFormatException e) { 1.190 + throw new ParseException( 1.191 + "Invalid protocol minor version number: " + 1.192 + buffer.substring(indexFrom, indexTo)); 1.193 + } 1.194 + 1.195 + cursor.updatePos(blank); 1.196 + 1.197 + return createProtocolVersion(major, minor); 1.198 + 1.199 + } // parseProtocolVersion 1.200 + 1.201 + 1.202 + /** 1.203 + * Creates a protocol version. 1.204 + * Called from {@link #parseProtocolVersion}. 1.205 + * 1.206 + * @param major the major version number, for example 1 in HTTP/1.0 1.207 + * @param minor the minor version number, for example 0 in HTTP/1.0 1.208 + * 1.209 + * @return the protocol version 1.210 + */ 1.211 + protected ProtocolVersion createProtocolVersion(int major, int minor) { 1.212 + return protocol.forVersion(major, minor); 1.213 + } 1.214 + 1.215 + 1.216 + 1.217 + // non-javadoc, see interface LineParser 1.218 + public boolean hasProtocolVersion(final CharArrayBuffer buffer, 1.219 + final ParserCursor cursor) { 1.220 + 1.221 + if (buffer == null) { 1.222 + throw new IllegalArgumentException("Char array buffer may not be null"); 1.223 + } 1.224 + if (cursor == null) { 1.225 + throw new IllegalArgumentException("Parser cursor may not be null"); 1.226 + } 1.227 + int index = cursor.getPos(); 1.228 + 1.229 + final String protoname = this.protocol.getProtocol(); 1.230 + final int protolength = protoname.length(); 1.231 + 1.232 + if (buffer.length() < protolength+4) 1.233 + return false; // not long enough for "HTTP/1.1" 1.234 + 1.235 + if (index < 0) { 1.236 + // end of line, no tolerance for trailing whitespace 1.237 + // this works only for single-digit major and minor version 1.238 + index = buffer.length() -4 -protolength; 1.239 + } else if (index == 0) { 1.240 + // beginning of line, tolerate leading whitespace 1.241 + while ((index < buffer.length()) && 1.242 + HTTP.isWhitespace(buffer.charAt(index))) { 1.243 + index++; 1.244 + } 1.245 + } // else within line, don't tolerate whitespace 1.246 + 1.247 + 1.248 + if (index + protolength + 4 > buffer.length()) 1.249 + return false; 1.250 + 1.251 + 1.252 + // just check protocol name and slash, no need to analyse the version 1.253 + boolean ok = true; 1.254 + for (int j=0; ok && (j<protolength); j++) { 1.255 + ok = (buffer.charAt(index+j) == protoname.charAt(j)); 1.256 + } 1.257 + if (ok) { 1.258 + ok = (buffer.charAt(index+protolength) == '/'); 1.259 + } 1.260 + 1.261 + return ok; 1.262 + } 1.263 + 1.264 + 1.265 + 1.266 + public final static 1.267 + RequestLine parseRequestLine(final String value, 1.268 + LineParser parser) 1.269 + throws ParseException { 1.270 + 1.271 + if (value == null) { 1.272 + throw new IllegalArgumentException 1.273 + ("Value to parse may not be null."); 1.274 + } 1.275 + 1.276 + if (parser == null) 1.277 + parser = BasicLineParser.DEFAULT; 1.278 + 1.279 + CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 1.280 + buffer.append(value); 1.281 + ParserCursor cursor = new ParserCursor(0, value.length()); 1.282 + return parser.parseRequestLine(buffer, cursor); 1.283 + } 1.284 + 1.285 + 1.286 + /** 1.287 + * Parses a request line. 1.288 + * 1.289 + * @param buffer a buffer holding the line to parse 1.290 + * 1.291 + * @return the parsed request line 1.292 + * 1.293 + * @throws ParseException in case of a parse error 1.294 + */ 1.295 + public RequestLine parseRequestLine(final CharArrayBuffer buffer, 1.296 + final ParserCursor cursor) 1.297 + throws ParseException { 1.298 + 1.299 + if (buffer == null) { 1.300 + throw new IllegalArgumentException("Char array buffer may not be null"); 1.301 + } 1.302 + if (cursor == null) { 1.303 + throw new IllegalArgumentException("Parser cursor may not be null"); 1.304 + } 1.305 + 1.306 + int indexFrom = cursor.getPos(); 1.307 + int indexTo = cursor.getUpperBound(); 1.308 + 1.309 + try { 1.310 + skipWhitespace(buffer, cursor); 1.311 + int i = cursor.getPos(); 1.312 + 1.313 + int blank = buffer.indexOf(' ', i, indexTo); 1.314 + if (blank < 0) { 1.315 + throw new ParseException("Invalid request line: " + 1.316 + buffer.substring(indexFrom, indexTo)); 1.317 + } 1.318 + String method = buffer.substringTrimmed(i, blank); 1.319 + cursor.updatePos(blank); 1.320 + 1.321 + skipWhitespace(buffer, cursor); 1.322 + i = cursor.getPos(); 1.323 + 1.324 + blank = buffer.indexOf(' ', i, indexTo); 1.325 + if (blank < 0) { 1.326 + throw new ParseException("Invalid request line: " + 1.327 + buffer.substring(indexFrom, indexTo)); 1.328 + } 1.329 + String uri = buffer.substringTrimmed(i, blank); 1.330 + cursor.updatePos(blank); 1.331 + 1.332 + ProtocolVersion ver = parseProtocolVersion(buffer, cursor); 1.333 + 1.334 + skipWhitespace(buffer, cursor); 1.335 + if (!cursor.atEnd()) { 1.336 + throw new ParseException("Invalid request line: " + 1.337 + buffer.substring(indexFrom, indexTo)); 1.338 + } 1.339 + 1.340 + return createRequestLine(method, uri, ver); 1.341 + } catch (IndexOutOfBoundsException e) { 1.342 + throw new ParseException("Invalid request line: " + 1.343 + buffer.substring(indexFrom, indexTo)); 1.344 + } 1.345 + } // parseRequestLine 1.346 + 1.347 + 1.348 + /** 1.349 + * Instantiates a new request line. 1.350 + * Called from {@link #parseRequestLine}. 1.351 + * 1.352 + * @param method the request method 1.353 + * @param uri the requested URI 1.354 + * @param ver the protocol version 1.355 + * 1.356 + * @return a new status line with the given data 1.357 + */ 1.358 + protected RequestLine createRequestLine(final String method, 1.359 + final String uri, 1.360 + final ProtocolVersion ver) { 1.361 + return new BasicRequestLine(method, uri, ver); 1.362 + } 1.363 + 1.364 + 1.365 + 1.366 + public final static 1.367 + StatusLine parseStatusLine(final String value, 1.368 + LineParser parser) 1.369 + throws ParseException { 1.370 + 1.371 + if (value == null) { 1.372 + throw new IllegalArgumentException 1.373 + ("Value to parse may not be null."); 1.374 + } 1.375 + 1.376 + if (parser == null) 1.377 + parser = BasicLineParser.DEFAULT; 1.378 + 1.379 + CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 1.380 + buffer.append(value); 1.381 + ParserCursor cursor = new ParserCursor(0, value.length()); 1.382 + return parser.parseStatusLine(buffer, cursor); 1.383 + } 1.384 + 1.385 + 1.386 + // non-javadoc, see interface LineParser 1.387 + public StatusLine parseStatusLine(final CharArrayBuffer buffer, 1.388 + final ParserCursor cursor) 1.389 + throws ParseException { 1.390 + 1.391 + if (buffer == null) { 1.392 + throw new IllegalArgumentException("Char array buffer may not be null"); 1.393 + } 1.394 + if (cursor == null) { 1.395 + throw new IllegalArgumentException("Parser cursor may not be null"); 1.396 + } 1.397 + 1.398 + int indexFrom = cursor.getPos(); 1.399 + int indexTo = cursor.getUpperBound(); 1.400 + 1.401 + try { 1.402 + // handle the HTTP-Version 1.403 + ProtocolVersion ver = parseProtocolVersion(buffer, cursor); 1.404 + 1.405 + // handle the Status-Code 1.406 + skipWhitespace(buffer, cursor); 1.407 + int i = cursor.getPos(); 1.408 + 1.409 + int blank = buffer.indexOf(' ', i, indexTo); 1.410 + if (blank < 0) { 1.411 + blank = indexTo; 1.412 + } 1.413 + int statusCode = 0; 1.414 + String s = buffer.substringTrimmed(i, blank); 1.415 + for (int j = 0; j < s.length(); j++) { 1.416 + if (!Character.isDigit(s.charAt(j))) { 1.417 + throw new ParseException( 1.418 + "Status line contains invalid status code: " 1.419 + + buffer.substring(indexFrom, indexTo)); 1.420 + } 1.421 + } 1.422 + try { 1.423 + statusCode = Integer.parseInt(s); 1.424 + } catch (NumberFormatException e) { 1.425 + throw new ParseException( 1.426 + "Status line contains invalid status code: " 1.427 + + buffer.substring(indexFrom, indexTo)); 1.428 + } 1.429 + //handle the Reason-Phrase 1.430 + i = blank; 1.431 + String reasonPhrase = null; 1.432 + if (i < indexTo) { 1.433 + reasonPhrase = buffer.substringTrimmed(i, indexTo); 1.434 + } else { 1.435 + reasonPhrase = ""; 1.436 + } 1.437 + return createStatusLine(ver, statusCode, reasonPhrase); 1.438 + 1.439 + } catch (IndexOutOfBoundsException e) { 1.440 + throw new ParseException("Invalid status line: " + 1.441 + buffer.substring(indexFrom, indexTo)); 1.442 + } 1.443 + } // parseStatusLine 1.444 + 1.445 + 1.446 + /** 1.447 + * Instantiates a new status line. 1.448 + * Called from {@link #parseStatusLine}. 1.449 + * 1.450 + * @param ver the protocol version 1.451 + * @param status the status code 1.452 + * @param reason the reason phrase 1.453 + * 1.454 + * @return a new status line with the given data 1.455 + */ 1.456 + protected StatusLine createStatusLine(final ProtocolVersion ver, 1.457 + final int status, 1.458 + final String reason) { 1.459 + return new BasicStatusLine(ver, status, reason); 1.460 + } 1.461 + 1.462 + 1.463 + 1.464 + public final static 1.465 + Header parseHeader(final String value, 1.466 + LineParser parser) 1.467 + throws ParseException { 1.468 + 1.469 + if (value == null) { 1.470 + throw new IllegalArgumentException 1.471 + ("Value to parse may not be null"); 1.472 + } 1.473 + 1.474 + if (parser == null) 1.475 + parser = BasicLineParser.DEFAULT; 1.476 + 1.477 + CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 1.478 + buffer.append(value); 1.479 + return parser.parseHeader(buffer); 1.480 + } 1.481 + 1.482 + 1.483 + // non-javadoc, see interface LineParser 1.484 + public Header parseHeader(CharArrayBuffer buffer) 1.485 + throws ParseException { 1.486 + 1.487 + // the actual parser code is in the constructor of BufferedHeader 1.488 + return new BufferedHeader(buffer); 1.489 + } 1.490 + 1.491 + 1.492 + /** 1.493 + * Helper to skip whitespace. 1.494 + */ 1.495 + protected void skipWhitespace(final CharArrayBuffer buffer, final ParserCursor cursor) { 1.496 + int pos = cursor.getPos(); 1.497 + int indexTo = cursor.getUpperBound(); 1.498 + while ((pos < indexTo) && 1.499 + HTTP.isWhitespace(buffer.charAt(pos))) { 1.500 + pos++; 1.501 + } 1.502 + cursor.updatePos(pos); 1.503 + } 1.504 + 1.505 +} // class BasicLineParser