1 /* Written and copyright 2001-2003 Benjamin Kohl.
\r
2 * Distributed under the GNU General Public License; see the README file.
\r
3 * This code comes with NO WARRANTY.
\r
6 import java.io.IOException;
\r
7 import java.io.InputStream;
\r
8 import java.io.BufferedInputStream;
\r
9 import java.net.InetAddress;
\r
10 import java.net.UnknownHostException;
\r
14 File: Jhttpp2BufferedFilterStream.java
\r
15 @author Benjamin Kohl
\r
17 public class Jhttpp2ClientInputStream extends BufferedInputStream {
\r
18 private boolean filter;
\r
22 * The length of the header (with body, if one)
\r
24 private int header_length;
\r
26 * The length of the (optional) body of the actual request
\r
28 private int content_len;
\r
30 * This is set to true with requests with bodies, like "POST"
\r
32 private boolean body;
\r
33 private static Jhttpp2Server server;
\r
34 private Jhttpp2HTTPSession connection;
\r
35 private InetAddress remote_host;
\r
36 private String remote_host_name;
\r
37 private String errordescription;
\r
38 private int statuscode;
\r
41 public String method;
\r
42 public int remote_port;
\r
43 public int post_data_len;
\r
46 public int getHeaderLength() {
\r
47 return header_length;
\r
50 public InetAddress getRemoteHost() { return remote_host; }
\r
51 public String getRemoteHostName() { return remote_host_name; }
\r
53 private void init() {
\r
63 public Jhttpp2ClientInputStream(Jhttpp2Server server,Jhttpp2HTTPSession connection,InputStream a) {
\r
66 this.server = server;
\r
67 this.connection=connection;
\r
70 * Handler for the actual HTTP request
\r
71 * @exception IOException
\r
73 public int read(byte[] a) {
\r
74 statuscode = connection.SC_OK;
\r
75 if (ssl) return super.read(a);
\r
76 boolean cookies_enabled=server.enableCookiesByDefault();
\r
81 boolean start_line=true;
\r
82 buf = getLine(); // reads the first line
\r
86 while (lread>2&&cnt) {
\r
89 int methodID = server.getHttpMethod(buf);
\r
91 statuscode = connection.SC_NOT_SUPPORTED;
\r
96 InetAddress host = parseRequest(buf,methodID);
\r
98 if (statuscode == connection.SC_OK) {
\r
99 if (!server.use_proxy && !ssl) {
\r
100 /* creates a new request without the hostname */
\r
101 buf = method + " " + url + " " + server.getHttpVersion() + "\r\n";
\r
102 lread = buf.length();
\r
104 if ((server.use_proxy && connection.notConnected()) || !host.equals(remote_host)) {
\r
105 if (server.debug) server.writeLog("read_f: STATE_CONNECT_TO_NEW_HOST");
\r
106 statuscode = connection.SC_CONNECTING_TO_HOST;
\r
107 remote_host = host;
\r
109 /* -------------------------
\r
110 * url blocking (only "GET" method)
\r
111 * -------------------------*/
\r
112 if (server.block_urls && methodID==0 && statuscode!=connection.SC_FILE_REQUEST) {
\r
113 if (server.debug) System.printString("Searching match...");
\r
114 Jhttpp2URLMatch match=server.findMatch(this.remote_host_name+url);
\r
116 if (server.debug) System.printString("Match found!");
\r
117 cookies_enabled=match.getCookiesEnabled();
\r
118 if (match.getActionIndex()==-1) cnt=false; else {
\r
119 OnURLAction action=(OnURLAction)server.getURLActions().elementAt(match.getActionIndex());
\r
120 if (action.onAccesssDeny()) {
\r
121 statuscode=connection.SC_URL_BLOCKED;
\r
122 if (action.onAccessDenyWithCustomText()) errordescription=action.getCustomErrorText();
\r
123 } else if (action.onAccessRedirect()) {
\r
124 statuscode=connection.SC_MOVED_PERMANENTLY;
\r
125 errordescription=action.newLocation();
\r
128 }//end if match!=null)
\r
129 } //end if (server.block...
\r
132 } else { // end if(startline)
\r
133 /*-----------------------------------------------
\r
134 * Content-Length parsing
\r
135 *-----------------------------------------------*/
\r
136 if(server.startsWith(buf.toUpperCase(),"CONTENT-LENGTH")) {
\r
137 String clen=buf.substring(16);
\r
138 if (clen.indexOf("\r")!=-1) clen=clen.substring(0,clen.indexOf("\r"));
\r
139 else if(clen.indexOf("\n")!=-1) clen=clen.substring(0,clen.indexOf("\n"));
\r
140 content_len=Integer.parseInt(clen);
\r
141 if (server.debug) server.writeLog("read_f: content_len: " + content_len);
\r
142 if (!ssl) body=true; // Note: in HTTP/1.1 any method can have a body, not only "POST"
\r
144 else if (server.startsWith(buf,"Proxy-Connection:")) {
\r
145 if (!server.use_proxy) buf=null;
\r
147 buf="Proxy-Connection: Keep-Alive\r\n";
\r
148 lread=buf.length();
\r
151 /*-----------------------------------------------
\r
152 * cookie crunch section
\r
153 *-----------------------------------------------*/
\r
154 else if(server.startsWith(buf,"Cookie:")) {
\r
155 if (!cookies_enabled) buf=null;
\r
157 /*------------------------------------------------
\r
158 * Http-Header filtering section
\r
159 *------------------------------------------------*/
\r
160 else if (server.filter_http) {
\r
161 if(server.startsWith(buf,"Referer:")) {// removes "Referer"
\r
163 } else if(server.startsWith(buf,"User-Agent")) // changes User-Agent
\r
165 buf="User-Agent: " + server.getUserAgent() + "\r\n";
\r
166 lread=buf.length();
\r
172 if (server.debug) server.writeLog(buf);
\r
173 header_length+=lread;
\r
179 rq+=buf; //adds last line (should be an empty line) to the header String
\r
180 header_length+=lread;
\r
182 if (header_length==0) {
\r
183 if (server.debug) server.writeLog("header_length=0, setting status to SC_CONNECTION_CLOSED (buggy request)");
\r
184 statuscode=connection.SC_CONNECTION_CLOSED;
\r
187 for (int i=0;i<header_length;i++) a[i]=(byte)rq.charAt(i);
\r
189 if(body) {// read the body, if "Content-Length" given
\r
191 while(post_data_len < content_len) {
\r
192 a[header_length + post_data_len]=(byte)read(); // writes data into the array
\r
195 header_length += content_len; // add the body-length to the header-length
\r
198 if (statuscode==connection.SC_OK) {
\r
199 return header_length;
\r
206 * @exception IOException
\r
208 public String getLine()
\r
210 int l=0; String line=""; lread=0;
\r
212 while(l!='\n'&&cnt) {
\r
219 if (!line.equals(""))
\r
226 * Parser for the first (!) line from the HTTP request<BR>
\r
227 * Sets up the URL, method and remote hostname.
\r
228 * @return an InetAddress for the hostname, null on errors with a statuscode!=SC_OK
\r
230 public InetAddress parseRequest(String a,int method_index) {
\r
232 server.writeLog(a);
\r
237 f = a.substring(8);
\r
239 method = a.substring(0,a.indexOf(" ")); //first word in the line
\r
240 pos = a.indexOf(":"); // locate first :
\r
241 if (pos == -1) { // occours with "GET / HTTP/1.1"
\r
242 url = a.substring(a.indexOf(" ")+1,a.lastIndexOf(" "));
\r
243 if (method_index == 0) { // method_index==0 --> GET
\r
244 if (url.indexOf(server.WEB_CONFIG_FILE) != -1) {
\r
245 statuscode = connection.SC_CONFIG_RQ;
\r
247 statuscode = connection.SC_FILE_REQUEST;
\r
250 if (method_index == 1 && url.indexOf(server.WEB_CONFIG_FILE) != -1) { // allow "POST" for admin log in
\r
251 statuscode = connection.SC_CONFIG_RQ;
\r
253 statuscode=connection.SC_INTERNAL_SERVER_ERROR;
\r
254 errordescription="This WWW proxy supports only the \"GET\" method while acting as webserver.";
\r
259 f = a.substring(pos+3); //removes "http://"
\r
261 pos=f.indexOf(" "); // locate space, should be the space before "HTTP/1.1"
\r
262 if (pos==-1) { // buggy request
\r
263 statuscode=connection.SC_CLIENT_ERROR;
\r
264 errordescription="Your browser sent an invalid request: \""+ a + "\"";
\r
267 f = f.substring(0,pos); //removes all after space
\r
268 // if the url contains a space... it's not our mistake...(url's must never contain a space character)
\r
269 pos=f.indexOf("/"); // locate the first slash
\r
271 url=f.substring(pos); // saves path without hostname
\r
272 f=f.substring(0,pos); // reduce string to the hostname
\r
274 else url="/"; // occurs with this request: "GET http://localhost HTTP/1.1"
\r
275 pos = f.indexOf(":"); // check for the portnumber
\r
277 String l_port =f.substring(pos+1);
\r
278 if (l_port.indexOf(" ")!=-1)
\r
279 l_port=l_port.substring(0,l_port.indexOf(" "));
\r
282 i_port = Integer.parseInt(l_port);
\r
283 f = f.substring(0,pos);
\r
284 remote_port=i_port;
\r
287 remote_host_name = f;
\r
288 InetAddress address = null;
\r
289 if (server.log_access)
\r
290 server.logAccess(method + " " + getFullURL());
\r
292 address = InetAddress.getByName(f);
\r
294 if (remote_port == server.port && address.equals(InetAddress.getLocalHost())) {
\r
295 if (url.indexOf(server.WEB_CONFIG_FILE) != -1 && (method_index == 0 || method_index == 1))
\r
296 statuscode = connection.SC_CONFIG_RQ;
\r
297 else if (method_index > 0 ) {
\r
298 statuscode=connection.SC_INTERNAL_SERVER_ERROR;
\r
299 errordescription="This WWW proxy supports only the \"GET\" method while acting as webserver.";
\r
301 statuscode = connection.SC_FILE_REQUEST;
\r
306 * @return boolean whether the actual connection was established with the CONNECT method.
\r
309 public boolean isTunnel() {
\r
313 * @return the full qualified URL of the actual request.
\r
316 public String getFullURL() {
\r
320 if (remote_port!=80)
\r
321 return "http" + sh + "://" + getRemoteHostName()
\r
322 + ":" + remote_port + url;
\r
324 return "http" + sh + "://" + getRemoteHostName()
\r
328 * @return status-code for the actual request
\r
331 public int getStatusCode()
\r
336 * @return the (optional) error-description for this request
\r
338 public String getErrorDescription()
\r
340 return errordescription;
\r