📄 httpresponse.java
字号:
/*
* SSL-Explorer
*
* Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.maverick.http;
import java.io.IOException;
import java.io.InputStream;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
/* DEBUG */import org.apache.commons.logging.LogFactory;
/* DEBUG */import org.apache.commons.logging.Log;
import java.io.EOFException;
import java.util.Hashtable;
public class HttpResponse
extends HttpHeader {
/* DEBUG */static Log log = LogFactory.getLog(HttpResponse.class);
protected ByteArrayOutputStream chunked;
protected String begin;
private String version = "";
private int status;
private String reason = "";
boolean closeConnection = false;
int contentLength = 0;
boolean foundContinue = false;
boolean release = true;
InputStream in = new ByteArrayInputStream(new byte[] {} );
HttpConnection con;
HttpContinue cont;
public HttpResponse(HttpConnection con) throws IOException {
this(con, false);
}
public HttpResponse(HttpConnection con, HttpContinue cont) throws IOException {
this.con = con;
this.cont = cont;
doResponse(false);
}
public HttpResponse(HttpConnection con, boolean headerOnly) throws IOException {
this.con = con;
doResponse(headerOnly);
}
private void doResponse(boolean headerOnly) throws IOException {
do {
begin = readLine(con.getInputStream());
while (begin.trim().length() == 0) {
begin = readLine(con.getInputStream());
}
/* DEBUG */log.debug("Received start line: " + begin);
processResponse();
if(status==100) {
if(cont!=null)
cont.continueRequest(con);
foundContinue = true;
String tmp;
while(true) {
/**
* Fix for IIS web servers, they are sending additional headers
* with the 100 Continue response.
*/
tmp = readLine(con.getInputStream());
/* DEBUG */log.debug("Received 100-Continue header: " + tmp);
if(tmp.equals(""))
break;
}
}
} while(status>=100 && status < 200);
processHeaderFields(con.getInputStream());
if(!headerOnly) {
/**
* LDP - Moved transfer encoding check to here, seems the
* more logical place to have it, plus I needed to work around
* the stream being overidden by the Connection: close header.
*/
if (getHeaderField("transfer-encoding") != null) {
if (getHeaderField("transfer-encoding").equalsIgnoreCase(
"chunked")) {
in = new ChunkedInputStream(con.getInputStream());
// Remove the transfer-encoding header
fields.remove("transfer-encoding");
}
} else if (getHeaderField("Content-Length") != null) {
contentLength = Integer.parseInt(getHeaderField("Content-Length"));
in = new ContentInputStream(contentLength);
} else if(getHeaderField("Connection")!=null &&
getHeaderField("Connection").equalsIgnoreCase("close")) {
// Set the connection as unusable by others
con.canReuse = false;
/**
* LDP - Since the connection is being closed we could have some content
* and no content-length header so just make this responses inputstream
* the connections inputstream, it will go EOF once drained.
*/
in = con.getInputStream();
} else if(getHeaderField("Proxy-Connection")!=null &&
getHeaderField("Proxy-Connection").equalsIgnoreCase("close")) {
// Set the connection as unusable by others
con.canReuse = false;
/**
* LDP - Since the connection is being closed we could have some content
* and no content-length header so just make this responses inputstream
* the connections inputstream, it will go EOF once drained.
*/
in = con.getInputStream();
} else {
/**
* No data to read so return an empty stream.
*/
in = new ByteArrayInputStream(new byte[] {} );
}
}
/**
* Finally check the connection close status again to set the canReuse flag on
* the HttpConnection.
*/
if(getHeaderField("Connection")!=null &&
getHeaderField("Connection").equalsIgnoreCase("close")) {
// Set the connection as unusable by others
con.canReuse = false;
} else if(getHeaderField("Proxy-Connection")!=null &&
getHeaderField("Proxy-Connection").equalsIgnoreCase("close")) {
// Set the connection as unusable by others
con.canReuse = false;
}
}
public void close() {
close(true);
}
public boolean hasContinue() {
return foundContinue;
}
public synchronized void close(boolean release) {
/**
* LDP - I've added this release flag because the doAuthentication
* method of HttpClient was calling close to drain the connection so that
* it could be reused. This was correct but the connection was prematurely
* being put back into the connection manager, and was being reused by other
* threads. Since we want to keep hold of the existing connection, in case
* of NTLM authentication we want to drain but not release.
*/
if(con==null)
return;
try {
if(in instanceof ChunkedInputStream) {
((ChunkedInputStream)in).drain();
} else if(in instanceof ContentInputStream) {
((ContentInputStream)in).drain();
}
} catch(IOException ex) {
// Exception during skip better close this connection
con.canReuse = false;
} finally {
if(release) {
con.release();
con = null;
}
}
}
protected String readLine(InputStream in) throws IOException {
StringBuffer lineBuf = new StringBuffer();
int c;
while (true) {
c = in.read();
if (c == -1) {
if(lineBuf.length()==0)
throw new EOFException("Unexpected EOF in HTTP header");
break;
}
if (c != '\n') {
lineBuf.append( (char) c);
}
else {
break;
}
}
return lineBuf.toString().trim();
}
public String getStartLine() {
return begin;
}
protected void processHeaderFields(InputStream in) throws IOException {
fields = new Hashtable();
StringBuffer lineBuf = new StringBuffer();
String lastHeaderName = null;
int c;
while (true) {
c = in.read();
if (c == -1) {
throw new IOException("The HTTP header is corrupt");
}
if (c != '\n') {
lineBuf.append( (char) c);
}
else {
String line = lineBuf.toString().trim();
lineBuf.setLength(0);
if (line.length() != 0) {
lastHeaderName = processNextLine(line, lastHeaderName);
}
else {
break;
}
}
}
}
private String processNextLine(String line, String lastHeaderName) throws
IOException {
String name;
String value;
char c = line.charAt(0);
if ( (c == ' ') || (c == '\t')) {
name = lastHeaderName;
value = getHeaderField(lastHeaderName) + " " + line.trim();
}
else {
int n = line.indexOf(':');
if (n == -1) {
throw new IOException(
"HTTP Header encoutered a corrupt field: '" + line + "'");
}
name = line.substring(0, n);
value = line.substring(n + 1).trim();
}
/* DEBUG */log.debug("Received header: " + name + ": " + value);
setHeaderField(name, value);
return name;
}
public InputStream getInputStream() {
return in;
}
public HttpConnection getConnection() {
return con;
}
public String getVersion() {
return version;
}
public int getStatus() {
return status;
}
public String getReason() {
return reason;
}
private void processResponse() throws IOException {
StringTokenizer tokens = new StringTokenizer(begin, WHITE_SPACE, false);
reason = "";
try {
version = tokens.nextToken();
status = Integer.parseInt(tokens.nextToken());
while(tokens.hasMoreTokens()) {
reason += tokens.nextToken() + " ";
}
reason = reason.trim();
}
catch (NoSuchElementException e) {
throw new IOException("Failed to read HTTP repsonse header");
}
catch (NumberFormatException e) {
throw new IOException("Failed to read HTTP resposne header");
}
}
/**
* We will use this stream to return data that is encoded using the
* "Transfer-Encoding: chunked" header.
*/
class ChunkedInputStream extends InputStream {
long chunkLength;
InputStream in;
ChunkedInputStream(InputStream in) throws IOException {
this.in = in;
// Read from the InputStream until we receive chunk size of zero
chunkLength = Long.parseLong(readLine(in), 16);
}
public int read() throws IOException {
byte[] b = new byte[1];
int read = read(b, 0, 1);
if(read==-1)
return -1;
else
return b[0] & 0xFF;
}
public void drain() throws IOException {
long len;
byte[] buf = new byte[65535];
while(contentLength > 0) {
len = con.getInputStream().read(buf, 0, buf.length);
if(contentLength > 0)
contentLength -= len;
else
break;
}
}
public synchronized int read(byte[] buf, int off, int len) throws IOException {
if(chunkLength==0 || con==null) {
return -1;
}
int read;
int count = 0;
while(len > 0 && chunkLength > 0) {
read = in.read(buf, off, (int) (len > chunkLength ? chunkLength : len));
if(read==-1)
throw new EOFException("Unexpected EOF during chunked transfer encoded read");
chunkLength -= read;
len -= read;
off += read;
count += read;
if(chunkLength == 0) {
readLine(in);
chunkLength = Long.parseLong(readLine(in), 16);
if(chunkLength==0)
close();
}
}
return count;
}
public synchronized void close() throws IOException {
if(con!=null) {
readLine(in);
HttpResponse.this.close(true);
}
}
}
/**
* We will use this to return standard content
*/
class ContentInputStream extends InputStream {
long contentLength;
ContentInputStream(long contentLength) {
this.contentLength = contentLength;
}
public synchronized int available() {
return (int) contentLength;
}
public synchronized long skip(long length) throws IOException {
return con.getInputStream().skip(length);
}
public void drain() throws IOException {
long len;
while(contentLength > 0) {
len = con.getInputStream().skip(contentLength);
if(contentLength > 0)
contentLength -= len;
else
break;
}
}
public synchronized int read() throws IOException {
if(contentLength==0 || con==null) {
return -1;
}
else {
int b = con.getInputStream().read();
if(b==-1)
throw new EOFException("Unexpected EOF in Http response, expected " + contentLength + " more bytes");
contentLength--;
if(contentLength==0)
close();
return b;
}
}
public synchronized int read(byte[] buf, int off, int len) throws IOException {
if(contentLength==0 || con==null) {
return -1;
}
else {
int read = con.getInputStream().read(buf, off, (contentLength > len ? len : (int)contentLength));
if(read==-1)
throw new EOFException("Unexpected EOF in HTTP response, expected " + contentLength + " more bytes");
contentLength -= read;
if(contentLength==0)
close();
return read;
}
}
public synchronized void close() {
// Release the connection back to the client pool
if(con!=null)
HttpResponse.this.close(true);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -