1 28 29 package HTTPClient; 30 31 import java.io.InputStream ; 32 import java.io.SequenceInputStream ; 33 import java.io.ByteArrayInputStream ; 34 import java.io.IOException ; 35 import java.io.InterruptedIOException ; 36 import java.io.EOFException ; 37 import java.net.URL ; 38 import java.net.ProtocolException ; 39 import java.util.Date ; 40 import java.util.Vector ; 41 import java.util.StringTokenizer ; 42 import java.util.NoSuchElementException ; 43 44 45 53 54 public final class Response implements RoResponse, GlobalConstants 55 { 56 57 private HTTPConnection connection; 58 59 60 private StreamDemultiplexor stream_handler; 61 62 63 HTTPResponse http_resp; 64 65 66 int timeout = 0; 67 68 70 public InputStream inp_stream; 71 72 73 private RespInputStream resp_inp_stream = null; 74 75 76 private String method; 77 78 79 String resource; 80 81 82 private boolean used_proxy; 83 84 85 private boolean sent_entity; 86 87 88 int StatusCode = 0; 89 90 91 String ReasonLine; 92 93 94 String Version; 95 96 97 URI EffectiveURI = null; 98 99 100 CIHashtable Headers = new CIHashtable(); 101 102 103 CIHashtable Trailers = new CIHashtable(); 104 105 108 int ContentLength = -1; 109 110 111 int cd_type = CD_HDRS; 112 113 114 byte[] Data = null; 115 116 117 boolean reading_headers = false; 118 119 120 boolean got_headers = false; 121 122 123 boolean got_trailers = false; 124 125 126 private IOException exception = null; 127 128 129 boolean final_resp = false; 130 131 132 134 137 Response(Request request, boolean used_proxy, 138 StreamDemultiplexor stream_handler) 139 throws IOException 140 { 141 this.connection = request.getConnection(); 142 this.method = request.getMethod(); 143 this.resource = request.getRequestURI(); 144 this.used_proxy = used_proxy; 145 this.stream_handler = stream_handler; 146 sent_entity = (request.getData() != null) ? true : false; 147 148 stream_handler.register(this, request); 149 resp_inp_stream = stream_handler.getStream(this); 150 inp_stream = resp_inp_stream; 151 } 152 153 154 163 Response(Request request, InputStream is) throws IOException 164 { 165 this.connection = request.getConnection(); 166 this.method = request.getMethod(); 167 this.resource = request.getRequestURI(); 168 used_proxy = false; 169 stream_handler = null; 170 sent_entity = (request.getData() != null) ? true : false; 171 inp_stream = is; 172 } 173 174 175 192 public Response(String version, int status, String reason, NVPair[] headers, 193 byte[] data, InputStream is, int cont_len) 194 { 195 this.Version = version; 196 this.StatusCode = status; 197 this.ReasonLine = reason; 198 if (headers != null) 199 for (int idx=0; idx<headers.length; idx++) 200 setHeader(headers[idx].getName(), headers[idx].getValue()); 201 if (data != null) 202 this.Data = data; 203 else if (is == null) 204 this.Data = new byte[0]; 205 else 206 { 207 this.inp_stream = is; 208 ContentLength = cont_len; 209 } 210 211 got_headers = true; 212 got_trailers = true; 213 } 214 215 216 218 230 public final int getStatusCode() throws IOException 231 { 232 if (!got_headers) getHeaders(true); 233 return StatusCode; 234 } 235 236 241 public final String getReasonLine() throws IOException 242 { 243 if (!got_headers) getHeaders(true); 244 return ReasonLine; 245 } 246 247 252 public final String getVersion() throws IOException 253 { 254 if (!got_headers) getHeaders(true); 255 return Version; 256 } 257 258 263 int getContinue() throws IOException 264 { 265 getHeaders(false); 266 return StatusCode; 267 } 268 269 277 public final URI getEffectiveURI() throws IOException 278 { 279 if (!got_headers) getHeaders(true); 280 return EffectiveURI; 281 } 282 283 286 public void setEffectiveURI(URI final_uri) 287 { 288 EffectiveURI = final_uri; 289 } 290 291 300 public final URL getEffectiveURL() throws IOException 301 { 302 return getEffectiveURI().toURL(); 303 } 304 305 311 public void setEffectiveURL(URL final_url) 312 { 313 try 314 { setEffectiveURI(new URI(final_url)); } 315 catch (ParseException pe) 316 { throw new Error (pe.toString()); } } 318 319 326 public String getHeader(String hdr) throws IOException 327 { 328 if (!got_headers) getHeaders(true); 329 return (String ) Headers.get(hdr.trim()); 330 } 331 332 342 public int getHeaderAsInt(String hdr) 343 throws IOException , NumberFormatException 344 { 345 return Integer.parseInt(getHeader(hdr)); 346 } 347 348 362 public Date getHeaderAsDate(String hdr) 363 throws IOException , IllegalArgumentException 364 { 365 String raw_date = getHeader(hdr); 366 if (raw_date == null) return null; 367 368 if (raw_date.toUpperCase().indexOf("GMT") == -1) 370 raw_date += " GMT"; 371 372 Date date; 373 374 try 375 { date = new Date (raw_date); } 376 catch (IllegalArgumentException iae) 377 { 378 long time; 379 try 380 { time = Long.parseLong(raw_date); } 381 catch (NumberFormatException nfe) 382 { throw iae; } 383 if (time < 0) time = 0; 384 date = new Date (time * 1000L); 385 } 386 387 return date; 388 } 389 390 391 401 public void setHeader(String header, String value) 402 { 403 Headers.put(header.trim(), value.trim()); 404 } 405 406 407 414 public void deleteHeader(String header) 415 { 416 Headers.remove(header.trim()); 417 } 418 419 420 429 public String getTrailer(String trailer) throws IOException 430 { 431 if (!got_trailers) getTrailers(); 432 return (String ) Trailers.get(trailer.trim()); 433 } 434 435 436 446 public int getTrailerAsInt(String trailer) 447 throws IOException , NumberFormatException 448 { 449 return Integer.parseInt(getTrailer(trailer)); 450 } 451 452 453 469 public Date getTrailerAsDate(String trailer) 470 throws IOException , IllegalArgumentException 471 { 472 String raw_date = getTrailer(trailer); 473 if (raw_date == null) return null; 474 475 if (raw_date.toUpperCase().indexOf("GMT") == -1) 477 raw_date += " GMT"; 478 479 Date date; 480 481 try 482 { date = new Date (raw_date); } 483 catch (IllegalArgumentException iae) 484 { 485 long time; 487 try 488 { time = Long.parseLong(raw_date); } 489 catch (NumberFormatException nfe) 490 { throw iae; } if (time < 0) time = 0; 492 date = new Date (time * 1000L); 493 } 494 495 return date; 496 } 497 498 499 509 public void setTrailer(String trailer, String value) 510 { 511 Trailers.put(trailer.trim(), value.trim()); 512 } 513 514 515 522 public void deleteTrailer(String trailer) 523 { 524 Trailers.remove(trailer.trim()); 525 } 526 527 528 542 public synchronized byte[] getData() throws IOException 543 { 544 if (!got_headers) getHeaders(true); 545 546 if (Data == null) 547 { 548 try 549 { readResponseData(inp_stream); } 550 catch (InterruptedIOException ie) { throw ie; } 552 catch (IOException ioe) 553 { 554 if (DebugResp) 555 { 556 System.err.print("Resp: (" + inp_stream.hashCode() + 557 ") (" + Thread.currentThread() + ")"); 558 ioe.printStackTrace(); 559 } 560 try { inp_stream.close(); } catch (Exception e) { } 561 throw ioe; 562 } 563 564 inp_stream.close(); 565 } 566 567 return Data; 568 } 569 570 579 public synchronized InputStream getInputStream() throws IOException 580 { 581 if (!got_headers) getHeaders(true); 582 583 if (Data == null) 584 return inp_stream; 585 else 586 return new ByteArrayInputStream (Data); 587 } 588 589 599 public synchronized boolean hasEntity() throws IOException 600 { 601 if (!got_headers) getHeaders(true); 602 603 return (cd_type != CD_0); 604 } 605 606 607 609 615 private synchronized void getHeaders(boolean skip_cont) throws IOException 616 { 617 if (got_headers) return; 618 if (exception != null) 619 throw (IOException ) exception.fillInStackTrace(); 620 621 reading_headers = true; 622 try 623 { 624 do 625 { 626 Headers.clear(); String headers = readResponseHeaders(inp_stream); 628 parseResponseHeaders(headers); 629 } while ((StatusCode == 100 && skip_cont) || (StatusCode > 101 && StatusCode < 200)); } 632 catch (IOException ioe) 633 { 634 if (!(ioe instanceof InterruptedIOException )) 635 exception = ioe; 636 if (ioe instanceof ProtocolException ) { 638 cd_type = CD_CLOSE; 639 if (stream_handler != null) 640 stream_handler.markForClose(this); 641 } 642 throw ioe; 643 } 644 finally 645 { reading_headers = false; } 646 if (StatusCode == 100) return; 647 648 649 651 int cont_len = -1; 652 String cl_hdr = (String ) Headers.get("Content-Length"); 653 if (cl_hdr != null) 654 { 655 try 656 { 657 cont_len = Integer.parseInt(cl_hdr); 658 if (cont_len < 0) 659 throw new NumberFormatException (); 660 } 661 catch (NumberFormatException nfe) 662 { 663 throw new ProtocolException ("Invalid Content-length header"+ 664 " received: "+cl_hdr); 665 } 666 } 667 668 669 671 boolean te_chunked = false, te_is_identity = true, ct_mpbr = false; 672 Vector te_hdr = null; 673 try 674 { te_hdr = Util.parseHeader((String ) Headers.get("Transfer-Encoding")); } 675 catch (ParseException pe) 676 { } 677 if (te_hdr != null) 678 { 679 te_chunked = ((HttpHeaderElement) te_hdr.lastElement()).getName(). 680 equalsIgnoreCase("chunked"); 681 for (int idx=0; idx<te_hdr.size(); idx++) 682 if (((HttpHeaderElement) te_hdr.elementAt(idx)).getName(). 683 equalsIgnoreCase("identity")) 684 te_hdr.removeElementAt(idx--); 685 else 686 te_is_identity = false; 687 } 688 689 690 692 try 693 { 694 String hdr; 695 if ((hdr = (String ) Headers.get("Content-Type")) != null) 696 { 697 Vector phdr = Util.parseHeader(hdr); 698 ct_mpbr = phdr.contains(new HttpHeaderElement("multipart/byteranges")) || 699 phdr.contains(new HttpHeaderElement("multipart/x-byteranges")); 700 } 701 } 702 catch (ParseException pe) 703 { } 704 705 706 708 if (StatusCode < 200 || StatusCode == 204 || StatusCode == 205 || 709 StatusCode == 304) 710 { 711 cd_type = CD_0; 712 } 713 else if (te_chunked) 714 { 715 cd_type = CD_CHUNKED; 716 717 te_hdr.removeElementAt(te_hdr.size()-1); 718 if (te_hdr.size() > 0) 719 setHeader("Transfer-Encoding", Util.assembleHeader(te_hdr)); 720 else 721 deleteHeader("Transfer-Encoding"); 722 } 723 else if (cont_len != -1 && te_is_identity) 724 cd_type = CD_CONTLEN; 725 else if (ct_mpbr && te_is_identity) 726 cd_type = CD_MP_BR; 727 else if (!method.equals("HEAD")) 728 { 729 cd_type = CD_CLOSE; 730 if (stream_handler != null) 731 stream_handler.markForClose(this); 732 733 if (Version.equals("HTTP/0.9")) 734 { 735 inp_stream = 736 new SequenceInputStream (new ByteArrayInputStream (Data), 737 inp_stream); 738 Data = null; 739 } 740 } 741 742 if (cd_type == CD_CONTLEN) 743 ContentLength = cont_len; 744 else 745 deleteHeader("Content-Length"); 747 750 if (method.equals("HEAD")) 751 cd_type = CD_0; 752 753 if (cd_type == CD_0) 754 { 755 ContentLength = 0; 756 Data = new byte[0]; 757 inp_stream.close(); } 759 760 if (DebugResp) 761 { 762 System.err.println("Resp: Response entity delimiter: " + 763 (cd_type == CD_0 ? "No Entity" : 764 cd_type == CD_CLOSE ? "Close" : 765 cd_type == CD_CONTLEN ? "Content-Length" : 766 cd_type == CD_CHUNKED ? "Chunked" : 767 cd_type == CD_MP_BR ? "Multipart" : 768 "???" ) + " (" + inp_stream.hashCode() + ") (" + 769 Thread.currentThread() + ")"); 770 } 771 772 773 775 if (connection.ServerProtocolVersion >= HTTP_1_1) 776 deleteHeader("Proxy-Connection"); 777 else { 779 if (connection.getProxyHost() != null) 780 deleteHeader("Connection"); 781 else 782 deleteHeader("Proxy-Connection"); 783 784 Vector pco; 785 try 786 { pco = Util.parseHeader((String ) Headers.get("Connection")); } 787 catch (ParseException pe) 788 { pco = null; } 789 790 if (pco != null) 791 { 792 for (int idx=0; idx<pco.size(); idx++) 793 { 794 String name = 795 ((HttpHeaderElement) pco.elementAt(idx)).getName(); 796 if (!name.equalsIgnoreCase("keep-alive")) 797 { 798 pco.removeElementAt(idx); 799 deleteHeader(name); 800 idx--; 801 } 802 } 803 804 if (pco.size() > 0) 805 setHeader("Connection", Util.assembleHeader(pco)); 806 else 807 deleteHeader("Connection"); 808 } 809 810 try 811 { pco = Util.parseHeader((String ) Headers.get("Proxy-Connection")); } 812 catch (ParseException pe) 813 { pco = null; } 814 815 if (pco != null) 816 { 817 for (int idx=0; idx<pco.size(); idx++) 818 { 819 String name = 820 ((HttpHeaderElement) pco.elementAt(idx)).getName(); 821 if (!name.equalsIgnoreCase("keep-alive")) 822 { 823 pco.removeElementAt(idx); 824 deleteHeader(name); 825 idx--; 826 } 827 } 828 829 if (pco.size() > 0) 830 setHeader("Proxy-Connection", Util.assembleHeader(pco)); 831 else 832 deleteHeader("Proxy-Connection"); 833 } 834 } 835 836 837 got_headers = true; 839 840 if (isFirstResponse) 842 { 843 if (!connection.handleFirstRequest(req, this)) 844 { 845 Response resp; 847 try 848 { resp = connection.sendRequest(req, timeout); } 849 catch (ModuleException me) 850 { throw new IOException (me.toString()); } 851 resp.getVersion(); 852 853 this.StatusCode = resp.StatusCode; 854 this.ReasonLine = resp.ReasonLine; 855 this.Version = resp.Version; 856 this.EffectiveURI = resp.EffectiveURI; 857 this.ContentLength = resp.ContentLength; 858 this.Headers = resp.Headers; 859 this.inp_stream = resp.inp_stream; 860 this.Data = resp.Data; 861 862 req = null; 863 } 864 } 865 } 866 867 868 871 private byte[] buf = new byte[7]; 872 private int buf_pos = 0; 873 private StringBuffer hdrs = new StringBuffer (400); 874 private boolean reading_lines = false; 875 private boolean bol = true; 876 private boolean got_cr = false; 877 878 888 private String readResponseHeaders(InputStream inp) throws IOException 889 { 890 if (DebugResp) 891 { 892 if (buf_pos == 0) 893 System.err.println("Resp: Reading Response headers " + 894 inp_stream.hashCode() + " (" + 895 Thread.currentThread() + ")"); 896 else 897 System.err.println("Resp: Resuming reading Response headers " + 898 inp_stream.hashCode() + " (" + 899 Thread.currentThread() + ")"); 900 } 901 902 903 if (!reading_lines) 905 { 906 try 907 { 908 if (buf_pos == 0) 910 { 911 int c; 912 do 913 { 914 if ((c = inp.read()) == -1) 915 throw new EOFException ("Encountered premature EOF " 916 + "while reading Version"); 917 } while (Character.isSpace( (char) (c & 0xFF) )) ; 918 buf[0] = (byte) (c & 0xFF); 919 buf_pos = 1; 920 } 921 922 while (buf_pos < buf.length) 924 { 925 int got = inp.read(buf, buf_pos, buf.length-buf_pos); 926 if (got == -1) 927 throw new EOFException ("Encountered premature EOF " + 928 "while reading Version"); 929 buf_pos += got; 930 } 931 } 932 catch (EOFException eof) 933 { 934 if (DebugResp) 935 { 936 System.err.print("Resp: (" + inp_stream.hashCode() + ") (" 937 + Thread.currentThread() + ")"); 938 eof.printStackTrace(); 939 } 940 throw eof; 941 } 942 for (int idx=0; idx<buf.length; idx++) 943 hdrs.append((char) buf[idx]); 944 945 reading_lines = true; 946 } 947 948 if (hdrs.toString().startsWith("HTTP/") || hdrs.toString().startsWith("HTTP ")) readLines(inp); 951 952 buf_pos = 0; 954 reading_lines = false; 955 bol = true; 956 got_cr = false; 957 958 String tmp = hdrs.toString(); 959 hdrs.setLength(0); 960 return tmp; 961 } 962 963 964 boolean trailers_read = false; 965 966 973 void readTrailers(InputStream inp) throws IOException 974 { 975 try 976 { 977 readLines(inp); 978 trailers_read = true; 979 } 980 catch (IOException ioe) 981 { 982 if (!(ioe instanceof InterruptedIOException )) 983 exception = ioe; 984 throw ioe; 985 } 986 } 987 988 989 1000 private void readLines(InputStream inp) throws IOException 1001 { 1002 1007 loop: while (true) 1008 { 1009 int b = inp.read(); 1010 switch (b) 1011 { 1012 case -1: 1013 throw new EOFException ("Encountered premature EOF while reading headers:\n" + hdrs); 1014 case '\r': 1015 got_cr = true; 1016 break; 1017 case '\n': 1018 if (bol) break loop; hdrs.append('\n'); 1020 bol = true; 1021 got_cr = false; 1022 break; 1023 case ' ': 1024 case '\t': 1025 if (bol) { 1027 hdrs.setCharAt(hdrs.length()-1, ' '); 1029 bol = false; 1030 break; 1031 } 1032 default: 1033 if (got_cr) 1034 { 1035 hdrs.append('\r'); 1036 got_cr = false; 1037 } 1038 hdrs.append((char) (b & 0xFF)); 1039 bol = false; 1040 break; 1041 } 1042 } 1043 } 1044 1045 1046 1053 private void parseResponseHeaders(String headers) throws ProtocolException 1054 { 1055 String sts_line = null; 1056 StringTokenizer lines = new StringTokenizer (headers, "\r\n"), 1057 elem; 1058 1059 1060 if (DebugResp) 1061 System.err.println("Resp: Parsing Response headers from Request "+ 1062 "\"" + method + " " + resource + "\": (" + 1063 inp_stream.hashCode() + ") (" + 1064 Thread.currentThread() + ")\n\n"+headers); 1065 1066 1067 1069 if (!headers.regionMatches(true, 0, "HTTP/", 0, 5) && 1070 !headers.regionMatches(true, 0, "HTTP ", 0, 5)) { 1072 Version = "HTTP/0.9"; 1073 StatusCode = 200; 1074 ReasonLine = "OK"; 1075 1076 Data = new byte[headers.length()]; 1077 headers.getBytes(0, headers.length(), Data, 0); 1078 1079 return; 1080 } 1081 1082 1083 1085 try 1086 { 1087 sts_line = lines.nextToken(); 1088 elem = new StringTokenizer (sts_line, " \t"); 1089 1090 Version = elem.nextToken(); 1091 StatusCode = Integer.valueOf(elem.nextToken()).intValue(); 1092 1093 if (Version.equalsIgnoreCase("HTTP")) Version = "HTTP/1.0"; 1095 } 1096 catch (NoSuchElementException e) 1097 { 1098 throw new ProtocolException ("Invalid HTTP status line received: " + 1099 sts_line); 1100 } 1101 try 1102 { ReasonLine = elem.nextToken("").trim(); } 1103 catch (NoSuchElementException e) 1104 { ReasonLine = ""; } 1105 1106 1107 1112 if (StatusCode >= 300 && sent_entity) 1113 { 1114 if (stream_handler != null) 1115 stream_handler.markForClose(this); 1116 } 1117 1118 1119 1121 parseHeaderFields(lines, Headers); 1122 1123 1124 1127 if (Headers.get("Trailer") != null && resp_inp_stream != null) 1128 resp_inp_stream.dontTruncate(); 1129 1130 1132 int vers; 1133 if (Version.equalsIgnoreCase("HTTP/0.9") || 1134 Version.equalsIgnoreCase("HTTP/1.0")) 1135 vers = 0; 1136 else 1137 vers = 1; 1138 1139 try 1140 { 1141 String con = (String ) Headers.get("Connection"), 1142 pcon = (String ) Headers.get("Proxy-Connection"); 1143 1144 if ((vers == 1 && con != null && Util.hasToken(con, "close")) 1146 || 1147 (vers == 0 && 1148 !((!used_proxy && con != null && 1149 Util.hasToken(con, "keep-alive")) || 1150 (used_proxy && pcon != null && 1151 Util.hasToken(pcon, "keep-alive"))) 1152 ) 1153 ) 1154 if (stream_handler != null) 1155 stream_handler.markForClose(this); 1156 } 1157 catch (ParseException pe) { } 1158 } 1159 1160 1161 1169 private synchronized void getTrailers() throws IOException 1170 { 1171 if (got_trailers) return; 1172 if (exception != null) 1173 throw (IOException ) exception.fillInStackTrace(); 1174 1175 if (DebugResp) 1176 System.err.println("Resp: Reading Response trailers " + 1177 inp_stream.hashCode() + " (" + 1178 Thread.currentThread() + ")"); 1179 1180 try 1181 { 1182 if (!trailers_read) 1183 { 1184 if (resp_inp_stream != null) 1185 resp_inp_stream.readAll(timeout); 1186 } 1187 1188 if (trailers_read) 1189 { 1190 if (DebugResp) 1191 System.err.println("Resp: Parsing Response trailers from "+ 1192 "Request \"" + method + " " + resource + 1193 "\": (" + inp_stream.hashCode() + ") ("+ 1194 Thread.currentThread() + ")\n\n"+hdrs); 1195 1196 parseHeaderFields(new StringTokenizer (hdrs.toString(), "\r\n"), 1197 Trailers); 1198 } 1199 } 1200 finally 1201 { 1202 got_trailers = true; 1203 } 1204 } 1205 1206 1207 1216 private void parseHeaderFields(StringTokenizer lines, CIHashtable list) 1217 throws ProtocolException 1218 { 1219 while (lines.hasMoreTokens()) 1220 { 1221 String hdr = lines.nextToken(); 1222 int sep = hdr.indexOf(':'); 1223 1224 1228 if (sep == -1) 1229 sep = hdr.indexOf(' '); 1230 if (sep == -1) 1231 { 1232 throw new ProtocolException ("Invalid HTTP header received: " + 1233 hdr); 1234 } 1235 1236 String hdr_name = hdr.substring(0, sep).trim(); 1237 1238 int len = hdr.length(); 1239 sep++; 1240 while (sep < len && Character.isSpace(hdr.charAt(sep))) sep++; 1241 String hdr_value = hdr.substring(sep); 1242 1243 String old_value = (String ) list.get(hdr_name); 1244 if (old_value == null) 1245 list.put(hdr_name, hdr_value); 1246 else 1247 list.put(hdr_name, old_value + ", " + hdr_value); 1248 } 1249 } 1250 1251 1252 1259 private void readResponseData(InputStream inp) throws IOException 1260 { 1261 if (ContentLength == 0) 1262 return; 1263 1264 if (Data == null) 1265 Data = new byte[0]; 1266 1267 1268 1270 int off = Data.length; 1271 1272 try 1273 { 1274 if (getHeader("Content-Length") != null) 1276 { 1277 int rcvd = 0; 1278 Data = new byte[ContentLength]; 1279 1280 do 1281 { 1282 off += rcvd; 1283 rcvd = inp.read(Data, off, ContentLength-off); 1284 } while (rcvd != -1 && off+rcvd < ContentLength); 1285 1286 1298 } 1299 else 1300 { 1301 int inc = 1000, 1302 rcvd = 0; 1303 1304 do 1305 { 1306 off += rcvd; 1307 Data = Util.resizeArray(Data, off+inc); 1308 } while ((rcvd = inp.read(Data, off, inc)) != -1); 1309 1310 Data = Util.resizeArray(Data, off); 1311 } 1312 } 1313 catch (IOException ioe) 1314 { 1315 Data = Util.resizeArray(Data, off); 1316 throw ioe; 1317 } 1318 finally 1319 { 1320 try 1321 { inp.close(); } 1322 catch (IOException ioe) 1323 { } 1324 } 1325 } 1326 1327 1328 Request req = null; 1329 boolean isFirstResponse = false; 1330 1340 void markAsFirstResponse(Request req) 1341 { 1342 this.req = req; 1343 isFirstResponse = true; 1344 } 1345} 1346 1347 | Popular Tags |