1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/message/BasicLineParser.java $ 3 * $Revision: 591798 $ 4 * $Date: 2007-11-04 08:19:29 -0800 (Sun, 04 Nov 2007) $ 5 * 6 * ==================================================================== 7 * Licensed to the Apache Software Foundation (ASF) under one 8 * or more contributor license agreements. See the NOTICE file 9 * distributed with this work for additional information 10 * regarding copyright ownership. The ASF licenses this file 11 * to you under the Apache License, Version 2.0 (the 12 * "License"); you may not use this file except in compliance 13 * with the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, 18 * software distributed under the License is distributed on an 19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 * KIND, either express or implied. See the License for the 21 * specific language governing permissions and limitations 22 * under the License. 23 * ==================================================================== 24 * 25 * This software consists of voluntary contributions made by many 26 * individuals on behalf of the Apache Software Foundation. For more 27 * information on the Apache Software Foundation, please see 28 * <http://www.apache.org/>. 29 * 30 */ 31 32 package org.apache.http.message; 33 34 import org.apache.http.HttpVersion; 35 import org.apache.http.ProtocolVersion; 36 import org.apache.http.ParseException; 37 import org.apache.http.RequestLine; 38 import org.apache.http.StatusLine; 39 import org.apache.http.Header; 40 import org.apache.http.protocol.HTTP; 41 import org.apache.http.util.CharArrayBuffer; 42 43 44 /** 45 * Basic parser for lines in the head section of an HTTP message. 46 * There are individual methods for parsing a request line, a 47 * status line, or a header line. 48 * The lines to parse are passed in memory, the parser does not depend 49 * on any specific IO mechanism. 50 * Instances of this class are stateless and thread-safe. 51 * Derived classes MUST maintain these properties. 52 * 53 * <p> 54 * Note: This class was created by refactoring parsing code located in 55 * various other classes. The author tags from those other classes have 56 * been replicated here, although the association with the parsing code 57 * taken from there has not been traced. 58 * </p> 59 * 60 * @author <a href="mailto:jsdever (at) apache.org">Jeff Dever</a> 61 * @author <a href="mailto:mbowler (at) GargoyleSoftware.com">Mike Bowler</a> 62 * @author <a href="mailto:oleg (at) ural.ru">Oleg Kalnichevski</a> 63 * @author and others 64 * 65 * @deprecated Please use {@link java.net.URL#openConnection} instead. 66 * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> 67 * for further details. 68 */ 69 @Deprecated 70 public class BasicLineParser implements LineParser { 71 72 /** 73 * A default instance of this class, for use as default or fallback. 74 * Note that {@link BasicLineParser} is not a singleton, there can 75 * be many instances of the class itself and of derived classes. 76 * The instance here provides non-customized, default behavior. 77 */ 78 public final static BasicLineParser DEFAULT = new BasicLineParser(); 79 80 81 /** 82 * A version of the protocol to parse. 83 * The version is typically not relevant, but the protocol name. 84 */ 85 protected final ProtocolVersion protocol; 86 87 88 /** 89 * Creates a new line parser for the given HTTP-like protocol. 90 * 91 * @param proto a version of the protocol to parse, or 92 * <code>null</code> for HTTP. The actual version 93 * is not relevant, only the protocol name. 94 */ 95 public BasicLineParser(ProtocolVersion proto) { 96 if (proto == null) { 97 proto = HttpVersion.HTTP_1_1; 98 } 99 this.protocol = proto; 100 } 101 102 103 /** 104 * Creates a new line parser for HTTP. 105 */ 106 public BasicLineParser() { 107 this(null); 108 } 109 110 111 112 public final static 113 ProtocolVersion parseProtocolVersion(String value, 114 LineParser parser) 115 throws ParseException { 116 117 if (value == null) { 118 throw new IllegalArgumentException 119 ("Value to parse may not be null."); 120 } 121 122 if (parser == null) 123 parser = BasicLineParser.DEFAULT; 124 125 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 126 buffer.append(value); 127 ParserCursor cursor = new ParserCursor(0, value.length()); 128 return parser.parseProtocolVersion(buffer, cursor); 129 } 130 131 132 // non-javadoc, see interface LineParser 133 public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer, 134 final ParserCursor cursor) 135 throws ParseException { 136 137 if (buffer == null) { 138 throw new IllegalArgumentException("Char array buffer may not be null"); 139 } 140 if (cursor == null) { 141 throw new IllegalArgumentException("Parser cursor may not be null"); 142 } 143 144 final String protoname = this.protocol.getProtocol(); 145 final int protolength = protoname.length(); 146 147 int indexFrom = cursor.getPos(); 148 int indexTo = cursor.getUpperBound(); 149 150 skipWhitespace(buffer, cursor); 151 152 int i = cursor.getPos(); 153 154 // long enough for "HTTP/1.1"? 155 if (i + protolength + 4 > indexTo) { 156 throw new ParseException 157 ("Not a valid protocol version: " + 158 buffer.substring(indexFrom, indexTo)); 159 } 160 161 // check the protocol name and slash 162 boolean ok = true; 163 for (int j=0; ok && (j<protolength); j++) { 164 ok = (buffer.charAt(i+j) == protoname.charAt(j)); 165 } 166 if (ok) { 167 ok = (buffer.charAt(i+protolength) == '/'); 168 } 169 if (!ok) { 170 throw new ParseException 171 ("Not a valid protocol version: " + 172 buffer.substring(indexFrom, indexTo)); 173 } 174 175 i += protolength+1; 176 177 int period = buffer.indexOf('.', i, indexTo); 178 if (period == -1) { 179 throw new ParseException 180 ("Invalid protocol version number: " + 181 buffer.substring(indexFrom, indexTo)); 182 } 183 int major; 184 try { 185 major = Integer.parseInt(buffer.substringTrimmed(i, period)); 186 } catch (NumberFormatException e) { 187 throw new ParseException 188 ("Invalid protocol major version number: " + 189 buffer.substring(indexFrom, indexTo)); 190 } 191 i = period + 1; 192 193 int blank = buffer.indexOf(' ', i, indexTo); 194 if (blank == -1) { 195 blank = indexTo; 196 } 197 int minor; 198 try { 199 minor = Integer.parseInt(buffer.substringTrimmed(i, blank)); 200 } catch (NumberFormatException e) { 201 throw new ParseException( 202 "Invalid protocol minor version number: " + 203 buffer.substring(indexFrom, indexTo)); 204 } 205 206 cursor.updatePos(blank); 207 208 return createProtocolVersion(major, minor); 209 210 } // parseProtocolVersion 211 212 213 /** 214 * Creates a protocol version. 215 * Called from {@link #parseProtocolVersion}. 216 * 217 * @param major the major version number, for example 1 in HTTP/1.0 218 * @param minor the minor version number, for example 0 in HTTP/1.0 219 * 220 * @return the protocol version 221 */ 222 protected ProtocolVersion createProtocolVersion(int major, int minor) { 223 return protocol.forVersion(major, minor); 224 } 225 226 227 228 // non-javadoc, see interface LineParser 229 public boolean hasProtocolVersion(final CharArrayBuffer buffer, 230 final ParserCursor cursor) { 231 232 if (buffer == null) { 233 throw new IllegalArgumentException("Char array buffer may not be null"); 234 } 235 if (cursor == null) { 236 throw new IllegalArgumentException("Parser cursor may not be null"); 237 } 238 int index = cursor.getPos(); 239 240 final String protoname = this.protocol.getProtocol(); 241 final int protolength = protoname.length(); 242 243 if (buffer.length() < protolength+4) 244 return false; // not long enough for "HTTP/1.1" 245 246 if (index < 0) { 247 // end of line, no tolerance for trailing whitespace 248 // this works only for single-digit major and minor version 249 index = buffer.length() -4 -protolength; 250 } else if (index == 0) { 251 // beginning of line, tolerate leading whitespace 252 while ((index < buffer.length()) && 253 HTTP.isWhitespace(buffer.charAt(index))) { 254 index++; 255 } 256 } // else within line, don't tolerate whitespace 257 258 259 if (index + protolength + 4 > buffer.length()) 260 return false; 261 262 263 // just check protocol name and slash, no need to analyse the version 264 boolean ok = true; 265 for (int j=0; ok && (j<protolength); j++) { 266 ok = (buffer.charAt(index+j) == protoname.charAt(j)); 267 } 268 if (ok) { 269 ok = (buffer.charAt(index+protolength) == '/'); 270 } 271 272 return ok; 273 } 274 275 276 277 public final static 278 RequestLine parseRequestLine(final String value, 279 LineParser parser) 280 throws ParseException { 281 282 if (value == null) { 283 throw new IllegalArgumentException 284 ("Value to parse may not be null."); 285 } 286 287 if (parser == null) 288 parser = BasicLineParser.DEFAULT; 289 290 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 291 buffer.append(value); 292 ParserCursor cursor = new ParserCursor(0, value.length()); 293 return parser.parseRequestLine(buffer, cursor); 294 } 295 296 297 /** 298 * Parses a request line. 299 * 300 * @param buffer a buffer holding the line to parse 301 * 302 * @return the parsed request line 303 * 304 * @throws ParseException in case of a parse error 305 */ 306 public RequestLine parseRequestLine(final CharArrayBuffer buffer, 307 final ParserCursor cursor) 308 throws ParseException { 309 310 if (buffer == null) { 311 throw new IllegalArgumentException("Char array buffer may not be null"); 312 } 313 if (cursor == null) { 314 throw new IllegalArgumentException("Parser cursor may not be null"); 315 } 316 317 int indexFrom = cursor.getPos(); 318 int indexTo = cursor.getUpperBound(); 319 320 try { 321 skipWhitespace(buffer, cursor); 322 int i = cursor.getPos(); 323 324 int blank = buffer.indexOf(' ', i, indexTo); 325 if (blank < 0) { 326 throw new ParseException("Invalid request line: " + 327 buffer.substring(indexFrom, indexTo)); 328 } 329 String method = buffer.substringTrimmed(i, blank); 330 cursor.updatePos(blank); 331 332 skipWhitespace(buffer, cursor); 333 i = cursor.getPos(); 334 335 blank = buffer.indexOf(' ', i, indexTo); 336 if (blank < 0) { 337 throw new ParseException("Invalid request line: " + 338 buffer.substring(indexFrom, indexTo)); 339 } 340 String uri = buffer.substringTrimmed(i, blank); 341 cursor.updatePos(blank); 342 343 ProtocolVersion ver = parseProtocolVersion(buffer, cursor); 344 345 skipWhitespace(buffer, cursor); 346 if (!cursor.atEnd()) { 347 throw new ParseException("Invalid request line: " + 348 buffer.substring(indexFrom, indexTo)); 349 } 350 351 return createRequestLine(method, uri, ver); 352 } catch (IndexOutOfBoundsException e) { 353 throw new ParseException("Invalid request line: " + 354 buffer.substring(indexFrom, indexTo)); 355 } 356 } // parseRequestLine 357 358 359 /** 360 * Instantiates a new request line. 361 * Called from {@link #parseRequestLine}. 362 * 363 * @param method the request method 364 * @param uri the requested URI 365 * @param ver the protocol version 366 * 367 * @return a new status line with the given data 368 */ 369 protected RequestLine createRequestLine(final String method, 370 final String uri, 371 final ProtocolVersion ver) { 372 return new BasicRequestLine(method, uri, ver); 373 } 374 375 376 377 public final static 378 StatusLine parseStatusLine(final String value, 379 LineParser parser) 380 throws ParseException { 381 382 if (value == null) { 383 throw new IllegalArgumentException 384 ("Value to parse may not be null."); 385 } 386 387 if (parser == null) 388 parser = BasicLineParser.DEFAULT; 389 390 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 391 buffer.append(value); 392 ParserCursor cursor = new ParserCursor(0, value.length()); 393 return parser.parseStatusLine(buffer, cursor); 394 } 395 396 397 // non-javadoc, see interface LineParser 398 public StatusLine parseStatusLine(final CharArrayBuffer buffer, 399 final ParserCursor cursor) 400 throws ParseException { 401 402 if (buffer == null) { 403 throw new IllegalArgumentException("Char array buffer may not be null"); 404 } 405 if (cursor == null) { 406 throw new IllegalArgumentException("Parser cursor may not be null"); 407 } 408 409 int indexFrom = cursor.getPos(); 410 int indexTo = cursor.getUpperBound(); 411 412 try { 413 // handle the HTTP-Version 414 ProtocolVersion ver = parseProtocolVersion(buffer, cursor); 415 416 // handle the Status-Code 417 skipWhitespace(buffer, cursor); 418 int i = cursor.getPos(); 419 420 int blank = buffer.indexOf(' ', i, indexTo); 421 if (blank < 0) { 422 blank = indexTo; 423 } 424 int statusCode = 0; 425 try { 426 statusCode = 427 Integer.parseInt(buffer.substringTrimmed(i, blank)); 428 } catch (NumberFormatException e) { 429 throw new ParseException( 430 "Unable to parse status code from status line: " 431 + buffer.substring(indexFrom, indexTo)); 432 } 433 //handle the Reason-Phrase 434 i = blank; 435 String reasonPhrase = null; 436 if (i < indexTo) { 437 reasonPhrase = buffer.substringTrimmed(i, indexTo); 438 } else { 439 reasonPhrase = ""; 440 } 441 return createStatusLine(ver, statusCode, reasonPhrase); 442 443 } catch (IndexOutOfBoundsException e) { 444 throw new ParseException("Invalid status line: " + 445 buffer.substring(indexFrom, indexTo)); 446 } 447 } // parseStatusLine 448 449 450 /** 451 * Instantiates a new status line. 452 * Called from {@link #parseStatusLine}. 453 * 454 * @param ver the protocol version 455 * @param status the status code 456 * @param reason the reason phrase 457 * 458 * @return a new status line with the given data 459 */ 460 protected StatusLine createStatusLine(final ProtocolVersion ver, 461 final int status, 462 final String reason) { 463 return new BasicStatusLine(ver, status, reason); 464 } 465 466 467 468 public final static 469 Header parseHeader(final String value, 470 LineParser parser) 471 throws ParseException { 472 473 if (value == null) { 474 throw new IllegalArgumentException 475 ("Value to parse may not be null"); 476 } 477 478 if (parser == null) 479 parser = BasicLineParser.DEFAULT; 480 481 CharArrayBuffer buffer = new CharArrayBuffer(value.length()); 482 buffer.append(value); 483 return parser.parseHeader(buffer); 484 } 485 486 487 // non-javadoc, see interface LineParser 488 public Header parseHeader(CharArrayBuffer buffer) 489 throws ParseException { 490 491 // the actual parser code is in the constructor of BufferedHeader 492 return new BufferedHeader(buffer); 493 } 494 495 496 /** 497 * Helper to skip whitespace. 498 */ 499 protected void skipWhitespace(final CharArrayBuffer buffer, final ParserCursor cursor) { 500 int pos = cursor.getPos(); 501 int indexTo = cursor.getUpperBound(); 502 while ((pos < indexTo) && 503 HTTP.isWhitespace(buffer.charAt(pos))) { 504 pos++; 505 } 506 cursor.updatePos(pos); 507 } 508 509 } // class BasicLineParser 510