1 /* 2 * Copyright (c) 2014 The Android Open Source Project 3 * Copyright (c) 2008-2009, Motorola, Inc. 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * - Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * - Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * - Neither the name of the Motorola, Inc. nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 package javax.obex; 35 36 import java.io.ByteArrayOutputStream; 37 import java.io.IOException; 38 import java.util.Calendar; 39 import java.security.SecureRandom; 40 41 /** 42 * This class implements the javax.obex.HeaderSet interface for OBEX over 43 * RFCOMM or OBEX over l2cap. 44 * @hide 45 */ 46 public final class HeaderSet { 47 48 /** 49 * Represents the OBEX Count header. This allows the connection statement to 50 * tell the server how many objects it plans to send or retrieve. 51 * <P> 52 * The value of <code>COUNT</code> is 0xC0 (192). 53 */ 54 public static final int COUNT = 0xC0; 55 56 /** 57 * Represents the OBEX Name header. This specifies the name of the object. 58 * <P> 59 * The value of <code>NAME</code> is 0x01 (1). 60 */ 61 public static final int NAME = 0x01; 62 63 /** 64 * Represents the OBEX Type header. This allows a request to specify the 65 * type of the object (e.g. text, html, binary, etc.). 66 * <P> 67 * The value of <code>TYPE</code> is 0x42 (66). 68 */ 69 public static final int TYPE = 0x42; 70 71 /** 72 * Represents the OBEX Length header. This is the length of the object in 73 * bytes. 74 * <P> 75 * The value of <code>LENGTH</code> is 0xC3 (195). 76 */ 77 public static final int LENGTH = 0xC3; 78 79 /** 80 * Represents the OBEX Time header using the ISO 8601 standards. This is the 81 * preferred time header. 82 * <P> 83 * The value of <code>TIME_ISO_8601</code> is 0x44 (68). 84 */ 85 public static final int TIME_ISO_8601 = 0x44; 86 87 /** 88 * Represents the OBEX Time header using the 4 byte representation. This is 89 * only included for backwards compatibility. It represents the number of 90 * seconds since January 1, 1970. 91 * <P> 92 * The value of <code>TIME_4_BYTE</code> is 0xC4 (196). 93 */ 94 public static final int TIME_4_BYTE = 0xC4; 95 96 /** 97 * Represents the OBEX Description header. This is a text description of the 98 * object. 99 * <P> 100 * The value of <code>DESCRIPTION</code> is 0x05 (5). 101 */ 102 public static final int DESCRIPTION = 0x05; 103 104 /** 105 * Represents the OBEX Target header. This is the name of the service an 106 * operation is targeted to. 107 * <P> 108 * The value of <code>TARGET</code> is 0x46 (70). 109 */ 110 public static final int TARGET = 0x46; 111 112 /** 113 * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be 114 * included in a request or reply. 115 * <P> 116 * The value of <code>HTTP</code> is 0x47 (71). 117 */ 118 public static final int HTTP = 0x47; 119 120 /** 121 * Represents the OBEX BODY header. 122 * <P> 123 * The value of <code>BODY</code> is 0x48 (72). 124 */ 125 public static final int BODY = 0x48; 126 127 /** 128 * Represents the OBEX End of BODY header. 129 * <P> 130 * The value of <code>BODY</code> is 0x49 (73). 131 */ 132 public static final int END_OF_BODY = 0x49; 133 134 /** 135 * Represents the OBEX Who header. Identifies the OBEX application to 136 * determine if the two peers are talking to each other. 137 * <P> 138 * The value of <code>WHO</code> is 0x4A (74). 139 */ 140 public static final int WHO = 0x4A; 141 142 /** 143 * Represents the OBEX Connection ID header. Identifies used for OBEX 144 * connection multiplexing. 145 * <P> 146 * The value of <code>CONNECTION_ID</code> is 0xCB (203). 147 */ 148 149 public static final int CONNECTION_ID = 0xCB; 150 151 /** 152 * Represents the OBEX Application Parameter header. This header specifies 153 * additional application request and response information. 154 * <P> 155 * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76). 156 */ 157 public static final int APPLICATION_PARAMETER = 0x4C; 158 159 /** 160 * Represents the OBEX authentication digest-challenge. 161 * <P> 162 * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77). 163 */ 164 public static final int AUTH_CHALLENGE = 0x4D; 165 166 /** 167 * Represents the OBEX authentication digest-response. 168 * <P> 169 * The value of <code>AUTH_RESPONSE</code> is 0x4E (78). 170 */ 171 public static final int AUTH_RESPONSE = 0x4E; 172 173 /** 174 * Represents the OBEX Object Class header. This header specifies the OBEX 175 * object class of the object. 176 * <P> 177 * The value of <code>OBJECT_CLASS</code> is 0x4F (79). 178 */ 179 public static final int OBJECT_CLASS = 0x4F; 180 181 /** 182 * Represents the OBEX Single Response Mode (SRM). This header is used 183 * for Single response mode, introduced in OBEX 1.5. 184 * <P> 185 * The value of <code>SINGLE_RESPONSE_MODE</code> is 0x97 (151). 186 */ 187 public static final int SINGLE_RESPONSE_MODE = 0x97; 188 189 /** 190 * Represents the OBEX Single Response Mode Parameters. This header is used 191 * for Single response mode, introduced in OBEX 1.5. 192 * <P> 193 * The value of <code>SINGLE_RESPONSE_MODE_PARAMETER</code> is 0x98 (152). 194 */ 195 public static final int SINGLE_RESPONSE_MODE_PARAMETER = 0x98; 196 197 private Long mCount; // 4 byte unsigned integer 198 199 private String mName; // null terminated Unicode text string 200 201 private boolean mEmptyName; 202 203 private String mType; // null terminated ASCII text string 204 205 private Long mLength; // 4 byte unsigend integer 206 207 private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ 208 209 private Calendar mByteTime; // 4 byte unsigned integer 210 211 private String mDescription; // null terminated Unicode text String 212 213 private byte[] mTarget; // byte sequence 214 215 private byte[] mHttpHeader; // byte sequence 216 217 private byte[] mWho; // length prefixed byte sequence 218 219 private byte[] mAppParam; // byte sequence of the form tag length value 220 221 private byte[] mObjectClass; // byte sequence 222 223 private String[] mUnicodeUserDefined; // null terminated unicode string 224 225 private byte[][] mSequenceUserDefined; // byte sequence user defined 226 227 private Byte[] mByteUserDefined; // 1 byte 228 229 private Long[] mIntegerUserDefined; // 4 byte unsigned integer 230 231 private SecureRandom mRandom = null; 232 233 private Byte mSingleResponseMode; // byte to indicate enable/disable/support for SRM 234 235 private Byte mSrmParam; // byte representing the SRM parameters - only "wait" 236 // is supported by Bluetooth 237 238 /*package*/ byte[] nonce; 239 240 public byte[] mAuthChall; // The authentication challenge header 241 242 public byte[] mAuthResp; // The authentication response header 243 244 public byte[] mConnectionID; // THe connection ID 245 246 public int responseCode; 247 248 /** 249 * Creates new <code>HeaderSet</code> object. 250 * @param size the max packet size for this connection 251 */ 252 public HeaderSet() { 253 mUnicodeUserDefined = new String[16]; 254 mSequenceUserDefined = new byte[16][]; 255 mByteUserDefined = new Byte[16]; 256 mIntegerUserDefined = new Long[16]; 257 responseCode = -1; 258 } 259 260 /** 261 * Sets flag for special "value" of NAME header which should be empty. This 262 * is not the same as NAME header with empty string in which case it will 263 * have length of 5 bytes. It should be 3 bytes with only header id and 264 * length field. 265 */ 266 public void setEmptyNameHeader() { 267 mName = null; 268 mEmptyName = true; 269 } 270 271 /** 272 * Gets flag for special "value" of NAME header which should be empty. See 273 * above. 274 */ 275 public boolean getEmptyNameHeader() { 276 return mEmptyName; 277 } 278 279 /** 280 * Sets the value of the header identifier to the value provided. The type 281 * of object must correspond to the Java type defined in the description of 282 * this interface. If <code>null</code> is passed as the 283 * <code>headerValue</code> then the header will be removed from the set of 284 * headers to include in the next request. 285 * @param headerID the identifier to include in the message 286 * @param headerValue the value of the header identifier 287 * @throws IllegalArgumentException if the header identifier provided is not 288 * one defined in this interface or a user-defined header; if the 289 * type of <code>headerValue</code> is not the correct Java type as 290 * defined in the description of this interface\ 291 */ 292 public void setHeader(int headerID, Object headerValue) { 293 long temp = -1; 294 295 switch (headerID) { 296 case COUNT: 297 if (!(headerValue instanceof Long)) { 298 if (headerValue == null) { 299 mCount = null; 300 break; 301 } 302 throw new IllegalArgumentException("Count must be a Long"); 303 } 304 temp = ((Long)headerValue).longValue(); 305 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { 306 throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF"); 307 } 308 mCount = (Long)headerValue; 309 break; 310 case NAME: 311 if ((headerValue != null) && (!(headerValue instanceof String))) { 312 throw new IllegalArgumentException("Name must be a String"); 313 } 314 mEmptyName = false; 315 mName = (String)headerValue; 316 break; 317 case TYPE: 318 if ((headerValue != null) && (!(headerValue instanceof String))) { 319 throw new IllegalArgumentException("Type must be a String"); 320 } 321 mType = (String)headerValue; 322 break; 323 case LENGTH: 324 if (!(headerValue instanceof Long)) { 325 if (headerValue == null) { 326 mLength = null; 327 break; 328 } 329 throw new IllegalArgumentException("Length must be a Long"); 330 } 331 temp = ((Long)headerValue).longValue(); 332 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { 333 throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF"); 334 } 335 mLength = (Long)headerValue; 336 break; 337 case TIME_ISO_8601: 338 if ((headerValue != null) && (!(headerValue instanceof Calendar))) { 339 throw new IllegalArgumentException("Time ISO 8601 must be a Calendar"); 340 } 341 mIsoTime = (Calendar)headerValue; 342 break; 343 case TIME_4_BYTE: 344 if ((headerValue != null) && (!(headerValue instanceof Calendar))) { 345 throw new IllegalArgumentException("Time 4 Byte must be a Calendar"); 346 } 347 mByteTime = (Calendar)headerValue; 348 break; 349 case DESCRIPTION: 350 if ((headerValue != null) && (!(headerValue instanceof String))) { 351 throw new IllegalArgumentException("Description must be a String"); 352 } 353 mDescription = (String)headerValue; 354 break; 355 case TARGET: 356 if (headerValue == null) { 357 mTarget = null; 358 } else { 359 if (!(headerValue instanceof byte[])) { 360 throw new IllegalArgumentException("Target must be a byte array"); 361 } else { 362 mTarget = new byte[((byte[])headerValue).length]; 363 System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length); 364 } 365 } 366 break; 367 case HTTP: 368 if (headerValue == null) { 369 mHttpHeader = null; 370 } else { 371 if (!(headerValue instanceof byte[])) { 372 throw new IllegalArgumentException("HTTP must be a byte array"); 373 } else { 374 mHttpHeader = new byte[((byte[])headerValue).length]; 375 System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length); 376 } 377 } 378 break; 379 case WHO: 380 if (headerValue == null) { 381 mWho = null; 382 } else { 383 if (!(headerValue instanceof byte[])) { 384 throw new IllegalArgumentException("WHO must be a byte array"); 385 } else { 386 mWho = new byte[((byte[])headerValue).length]; 387 System.arraycopy(headerValue, 0, mWho, 0, mWho.length); 388 } 389 } 390 break; 391 case OBJECT_CLASS: 392 if (headerValue == null) { 393 mObjectClass = null; 394 } else { 395 if (!(headerValue instanceof byte[])) { 396 throw new IllegalArgumentException("Object Class must be a byte array"); 397 } else { 398 mObjectClass = new byte[((byte[])headerValue).length]; 399 System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length); 400 } 401 } 402 break; 403 case APPLICATION_PARAMETER: 404 if (headerValue == null) { 405 mAppParam = null; 406 } else { 407 if (!(headerValue instanceof byte[])) { 408 throw new IllegalArgumentException( 409 "Application Parameter must be a byte array"); 410 } else { 411 mAppParam = new byte[((byte[])headerValue).length]; 412 System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length); 413 } 414 } 415 break; 416 case SINGLE_RESPONSE_MODE: 417 if (headerValue == null) { 418 mSingleResponseMode = null; 419 } else { 420 if (!(headerValue instanceof Byte)) { 421 throw new IllegalArgumentException( 422 "Single Response Mode must be a Byte"); 423 } else { 424 mSingleResponseMode = (Byte)headerValue; 425 } 426 } 427 break; 428 case SINGLE_RESPONSE_MODE_PARAMETER: 429 if (headerValue == null) { 430 mSrmParam = null; 431 } else { 432 if (!(headerValue instanceof Byte)) { 433 throw new IllegalArgumentException( 434 "Single Response Mode Parameter must be a Byte"); 435 } else { 436 mSrmParam = (Byte)headerValue; 437 } 438 } 439 break; 440 default: 441 // Verify that it was not a Unicode String user Defined 442 if ((headerID >= 0x30) && (headerID <= 0x3F)) { 443 if ((headerValue != null) && (!(headerValue instanceof String))) { 444 throw new IllegalArgumentException( 445 "Unicode String User Defined must be a String"); 446 } 447 mUnicodeUserDefined[headerID - 0x30] = (String)headerValue; 448 449 break; 450 } 451 // Verify that it was not a byte sequence user defined value 452 if ((headerID >= 0x70) && (headerID <= 0x7F)) { 453 454 if (headerValue == null) { 455 mSequenceUserDefined[headerID - 0x70] = null; 456 } else { 457 if (!(headerValue instanceof byte[])) { 458 throw new IllegalArgumentException( 459 "Byte Sequence User Defined must be a byte array"); 460 } else { 461 mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length]; 462 System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70], 463 0, mSequenceUserDefined[headerID - 0x70].length); 464 } 465 } 466 break; 467 } 468 // Verify that it was not a Byte user Defined 469 if ((headerID >= 0xB0) && (headerID <= 0xBF)) { 470 if ((headerValue != null) && (!(headerValue instanceof Byte))) { 471 throw new IllegalArgumentException("ByteUser Defined must be a Byte"); 472 } 473 mByteUserDefined[headerID - 0xB0] = (Byte)headerValue; 474 475 break; 476 } 477 // Verify that is was not the 4 byte unsigned integer user 478 // defined header 479 if ((headerID >= 0xF0) && (headerID <= 0xFF)) { 480 if (!(headerValue instanceof Long)) { 481 if (headerValue == null) { 482 mIntegerUserDefined[headerID - 0xF0] = null; 483 break; 484 } 485 throw new IllegalArgumentException("Integer User Defined must be a Long"); 486 } 487 temp = ((Long)headerValue).longValue(); 488 if ((temp < 0L) || (temp > 0xFFFFFFFFL)) { 489 throw new IllegalArgumentException( 490 "Integer User Defined must be between 0 and 0xFFFFFFFF"); 491 } 492 mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue; 493 break; 494 } 495 throw new IllegalArgumentException("Invalid Header Identifier"); 496 } 497 } 498 499 /** 500 * Retrieves the value of the header identifier provided. The type of the 501 * Object returned is defined in the description of this interface. 502 * @param headerID the header identifier whose value is to be returned 503 * @return the value of the header provided or <code>null</code> if the 504 * header identifier specified is not part of this 505 * <code>HeaderSet</code> object 506 * @throws IllegalArgumentException if the <code>headerID</code> is not one 507 * defined in this interface or any of the user-defined headers 508 * @throws IOException if an error occurred in the transport layer during 509 * the operation or if the connection has been closed 510 */ 511 public Object getHeader(int headerID) throws IOException { 512 513 switch (headerID) { 514 case COUNT: 515 return mCount; 516 case NAME: 517 return mName; 518 case TYPE: 519 return mType; 520 case LENGTH: 521 return mLength; 522 case TIME_ISO_8601: 523 return mIsoTime; 524 case TIME_4_BYTE: 525 return mByteTime; 526 case DESCRIPTION: 527 return mDescription; 528 case TARGET: 529 return mTarget; 530 case HTTP: 531 return mHttpHeader; 532 case WHO: 533 return mWho; 534 case CONNECTION_ID: 535 return mConnectionID; 536 case OBJECT_CLASS: 537 return mObjectClass; 538 case APPLICATION_PARAMETER: 539 return mAppParam; 540 case SINGLE_RESPONSE_MODE: 541 return mSingleResponseMode; 542 case SINGLE_RESPONSE_MODE_PARAMETER: 543 return mSrmParam; 544 default: 545 // Verify that it was not a Unicode String user Defined 546 if ((headerID >= 0x30) && (headerID <= 0x3F)) { 547 return mUnicodeUserDefined[headerID - 0x30]; 548 } 549 // Verify that it was not a byte sequence user defined header 550 if ((headerID >= 0x70) && (headerID <= 0x7F)) { 551 return mSequenceUserDefined[headerID - 0x70]; 552 } 553 // Verify that it was not a byte user defined header 554 if ((headerID >= 0xB0) && (headerID <= 0xBF)) { 555 return mByteUserDefined[headerID - 0xB0]; 556 } 557 // Verify that it was not a integer user defined header 558 if ((headerID >= 0xF0) && (headerID <= 0xFF)) { 559 return mIntegerUserDefined[headerID - 0xF0]; 560 } 561 throw new IllegalArgumentException("Invalid Header Identifier"); 562 } 563 } 564 565 /** 566 * Retrieves the list of headers that may be retrieved via the 567 * <code>getHeader</code> method that will not return <code>null</code>. In 568 * other words, this method returns all the headers that are available in 569 * this object. 570 * @see #getHeader 571 * @return the array of headers that are set in this object or 572 * <code>null</code> if no headers are available 573 * @throws IOException if an error occurred in the transport layer during 574 * the operation or the connection has been closed 575 */ 576 public int[] getHeaderList() throws IOException { 577 ByteArrayOutputStream out = new ByteArrayOutputStream(); 578 579 if (mCount != null) { 580 out.write(COUNT); 581 } 582 if (mName != null) { 583 out.write(NAME); 584 } 585 if (mType != null) { 586 out.write(TYPE); 587 } 588 if (mLength != null) { 589 out.write(LENGTH); 590 } 591 if (mIsoTime != null) { 592 out.write(TIME_ISO_8601); 593 } 594 if (mByteTime != null) { 595 out.write(TIME_4_BYTE); 596 } 597 if (mDescription != null) { 598 out.write(DESCRIPTION); 599 } 600 if (mTarget != null) { 601 out.write(TARGET); 602 } 603 if (mHttpHeader != null) { 604 out.write(HTTP); 605 } 606 if (mWho != null) { 607 out.write(WHO); 608 } 609 if (mAppParam != null) { 610 out.write(APPLICATION_PARAMETER); 611 } 612 if (mObjectClass != null) { 613 out.write(OBJECT_CLASS); 614 } 615 if(mSingleResponseMode != null) { 616 out.write(SINGLE_RESPONSE_MODE); 617 } 618 if(mSrmParam != null) { 619 out.write(SINGLE_RESPONSE_MODE_PARAMETER); 620 } 621 622 for (int i = 0x30; i < 0x40; i++) { 623 if (mUnicodeUserDefined[i - 0x30] != null) { 624 out.write(i); 625 } 626 } 627 628 for (int i = 0x70; i < 0x80; i++) { 629 if (mSequenceUserDefined[i - 0x70] != null) { 630 out.write(i); 631 } 632 } 633 634 for (int i = 0xB0; i < 0xC0; i++) { 635 if (mByteUserDefined[i - 0xB0] != null) { 636 out.write(i); 637 } 638 } 639 640 for (int i = 0xF0; i < 0x100; i++) { 641 if (mIntegerUserDefined[i - 0xF0] != null) { 642 out.write(i); 643 } 644 } 645 646 byte[] headers = out.toByteArray(); 647 out.close(); 648 649 if ((headers == null) || (headers.length == 0)) { 650 return null; 651 } 652 653 int[] result = new int[headers.length]; 654 for (int i = 0; i < headers.length; i++) { 655 // Convert the byte to a positive integer. That is, an integer 656 // between 0 and 256. 657 result[i] = headers[i] & 0xFF; 658 } 659 660 return result; 661 } 662 663 /** 664 * Sets the authentication challenge header. The <code>realm</code> will be 665 * encoded based upon the default encoding scheme used by the implementation 666 * to encode strings. Therefore, the encoding scheme used to encode the 667 * <code>realm</code> is application dependent. 668 * @param realm a short description that describes what password to use; if 669 * <code>null</code> no realm will be sent in the authentication 670 * challenge header 671 * @param userID if <code>true</code>, a user ID is required in the reply; 672 * if <code>false</code>, no user ID is required 673 * @param access if <code>true</code> then full access will be granted if 674 * successful; if <code>false</code> then read-only access will be 675 * granted if successful 676 * @throws IOException 677 */ 678 public void createAuthenticationChallenge(String realm, boolean userID, boolean access) 679 throws IOException { 680 681 nonce = new byte[16]; 682 if(mRandom == null) { 683 mRandom = new SecureRandom(); 684 } 685 for (int i = 0; i < 16; i++) { 686 nonce[i] = (byte)mRandom.nextInt(); 687 } 688 689 mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID); 690 } 691 692 /** 693 * Returns the response code received from the server. Response codes are 694 * defined in the <code>ResponseCodes</code> class. 695 * @see ResponseCodes 696 * @return the response code retrieved from the server 697 * @throws IOException if an error occurred in the transport layer during 698 * the transaction; if this method is called on a 699 * <code>HeaderSet</code> object created by calling 700 * <code>createHeaderSet()</code> in a <code>ClientSession</code> 701 * object; if this object was created by an OBEX server 702 */ 703 public int getResponseCode() throws IOException { 704 if (responseCode == -1) { 705 throw new IOException("May not be called on a server"); 706 } else { 707 return responseCode; 708 } 709 } 710 } 711