1 /* Written and copyright 2001-2003 Benjamin Kohl.
2 * Distributed under the GNU General Public License; see the README file.
3 * This code comes with NO WARRANTY.
6 import java.net.Socket;
7 import java.net.InetAddress;
8 import java.net.UnknownHostException;
10 import java.io.BufferedOutputStream;
11 import java.io.BufferedInputStream;
12 import java.io.DataInputStream;
13 import java.io.IOException;
15 import java.io.FileInputStream;
20 @file Jhttpp2HTTPSession.java
23 public class Jhttpp2HTTPSession extends Thread {
25 public final int SC_OK;
26 public final int SC_CONNECTING_TO_HOST;
27 public final int SC_HOST_NOT_FOUND;
28 public final int SC_URL_BLOCKED;
29 public final int SC_CLIENT_ERROR;
30 public final int SC_INTERNAL_SERVER_ERROR;
31 public final int SC_NOT_SUPPORTED;
32 public final int SC_REMOTE_DEBUG_MODE;
33 public final int SC_CONNECTION_CLOSED;
34 public final int SC_HTTP_OPTIONS_THIS;
35 public final int SC_FILE_REQUEST;
36 public final int SC_MOVED_PERMANENTLY;
37 public final int SC_CONFIG_RQ;
42 SC_CONNECTING_TO_HOST=1;
46 SC_INTERNAL_SERVER_ERROR=5;
48 SC_REMOTE_DEBUG_MODE=7;
49 SC_CONNECTION_CLOSED=8;
50 SC_HTTP_OPTIONS_THIS=9;
52 SC_MOVED_PERMANENTLY=11;
56 private Jhttpp2Server server;
58 /** downstream connections */
59 private Socket client;
60 private BufferedOutputStream out;
61 private Jhttpp2ClientInputStream in;
63 /** upstream connections */
64 private Socket HTTP_Socket;
65 private BufferedOutputStream HTTP_out;
66 private BufferedInputStream HTTP_in;
68 public Jhttpp2HTTPSession(Jhttpp2Server server,Socket client) {
70 in = new Jhttpp2ClientInputStream(server,this,client.getInputStream());//,true);
71 out = new BufferedOutputStream(client.getOutputStream());
76 public Socket getLocalSocket() {
79 public Socket getRemoteSocket() {
82 public boolean isTunnel() {
85 public boolean notConnected() {
86 return HTTP_Socket==null;
88 public void sendHeader(int a,boolean b) {
93 public void sendHeader(int status, String content_type, long content_length) {
95 sendLine("Content-Length", String.valueOf(content_length));
96 sendLine("Content-Type", content_type );
98 public void sendLine(String s) {
99 write(out,s + "\r\n");
101 public void sendLine(String header, String s) {
102 write(out,header + ": " + s + "\r\n");
104 public void endHeader() {
108 if (server.debug)server.writeLog("begin http session");
109 server.increaseNumConnections();
111 in.close(); // since 0.4.10b
114 // close upstream connections (webserver or other proxy)
115 if (!notConnected()) {
120 server.decreaseNumConnections();
121 if (server.debug)server.writeLog("end http session");
123 /** sends a message to the user */
124 public void sendErrorMSG(int a,String info) {
125 String statuscode = sendHeader(a);
126 String localhost = "localhost"+":"+server.port;
127 String msg = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><html>\r"
128 + "<!-- jHTTPp2 error message --><HEAD>\r"
129 + "<TITLE>" + statuscode + "</TITLE>\r"
130 + "<link rel=\"stylesheet\" type=\"text/css\" href=\"http://" + localhost + "/style.css\"></HEAD>\r" // use css style sheet in htdocs
131 + "<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#000080\" VLINK=\"#000080\" ALINK=\"#000080\">\r"
132 + "<h2 class=\"headline\">HTTP " + statuscode + " </h2>\r"
133 + "<HR size=\"4\">\r"
134 + "<p class=\"i30\">Your request for the following URL failed:</p>"
135 + "<p class=\"tiagtext\"><a href=\"" + in.getFullURL() + "\">" + in.getFullURL() + "</A> </p>\r"
136 + "<P class=\"i25\">Reason: " + info + "</P>"
137 + "<HR size=\"4\">\r"
138 + "<p class=\"i25\"><A HREF=\"http://jhttp2.sourceforge.net/\">jHTTPp2</A> HTTP Proxy, Version " + server.getServerVersion() + " at " + localhost
139 + "<br>Copyright © 2001-2003 <A HREF=\"mailto:bkohl@users.sourceforge.net\">Benjamin Kohl</A></p>\r"
140 + "<p class=\"i25\"><A HREF=\"http://" + localhost + "/\">jHTTPp2 local website</A> <A HREF=\"http://" + localhost + "/" + server.WEB_CONFIG_FILE + "\">Configuration</A></p>"
142 sendLine("Content-Length",String.valueOf(msg.length()));
143 sendLine("Content-Type","text/html; charset=iso-8859-1");
149 public String sendHeader(int a) {
156 stat="300 Ambiguous";
158 stat="301 Moved Permanently";
160 stat="400 Bad Request";
164 stat="403 Forbidden";
166 stat="404 Not Found";
168 stat="405 Bad Method";
170 stat="413 Request Entity Too Large";
172 stat="415 Unsupported Media";
174 stat="501 Not Implemented";
176 stat="502 Bad Gateway";
178 stat="504 Gateway Timeout";
180 stat="505 HTTP Version Not Supported";
182 stat="500 Internal Server Error";
183 sendLine(server.getHttpVersion() + " " + stat);
184 sendLine("Server",server.getServerIdentification());
186 sendLine("Allow","GET, HEAD, POST, PUT, DELETE, CONNECT");
187 sendLine("Cache-Control", "no-cache, must-revalidate");
188 sendLine("Connection","close");
192 /** the main routine, where it all happens */
193 public int handleRequest() {
194 InetAddress remote_host;
195 Jhttpp2Read remote_in=null;
197 byte[] b=new byte[65536];
198 int numread=in.read(b);
202 while(cnt) { // with this loop we support persistent connections
203 if (numread==-1) { // -1 signals an error
204 if (in.getStatusCode()!=SC_CONNECTING_TO_HOST) {
205 int code=in.getStatusCode();
206 if (code==SC_CONNECTION_CLOSED) {
207 } else if (code==SC_CLIENT_ERROR) {
208 sendErrorMSG(400,"Your client sent a request that this proxy could not understand. (" + in.getErrorDescription() + ")");
209 } else if (code==SC_HOST_NOT_FOUND)
210 sendErrorMSG(504,"Host not found.<BR>jHTTPp2 was unable to resolve the hostname of this request. <BR>Perhaps the hostname was misspelled, the server is down or you have no connection to the internet.");
211 else if (code==SC_INTERNAL_SERVER_ERROR)
212 sendErrorMSG(500,"Server Error! (" + in.getErrorDescription() + ")");
213 else if (code==SC_NOT_SUPPORTED)
214 sendErrorMSG(501,"Your client used a HTTP method that this proxy doesn't support: (" + in.getErrorDescription() + ")");
215 else if (code==SC_URL_BLOCKED) {
216 if (in.getErrorDescription()!=null && in.getErrorDescription().length()>0)
217 sendErrorMSG(403,in.getErrorDescription());
219 sendErrorMSG(403,"The request for this URL was denied by the jHTTPp2 URL-Filter.");
220 } else if (code==SC_HTTP_OPTIONS_THIS) {
221 sendHeader(200); endHeader();
222 } else if (code==SC_FILE_REQUEST)
224 else if (code==SC_CONFIG_RQ)
226 //case SC_HTTP_TRACE:
227 else if (code==SC_MOVED_PERMANENTLY) {
229 write(out,"Location: " + in.getErrorDescription() + "\r\n");
233 cnt=false;// return from main loop.
234 } else { // also an error because we are not connected (or to the wrong host)
235 // Creates a new connection to a remote host.
236 if (!notConnected()) {
239 numread=in.getHeaderLength(); // get the header length
240 if (!server.use_proxy) {// sets up hostname and port
241 remote_host=in.getRemoteHost();
242 remote_port=in.remote_port;
244 remote_host=server.proxy;
245 remote_port=server.proxy_port;
247 connect(remote_host,remote_port);
248 if (!in.isTunnel() || (in.isTunnel() && server.use_proxy))
249 { // no SSL-Tunnel or SSL-Tunnel with another remote proxy: simply forward the request
250 HTTP_out.write(b, 0, numread);
254 { // SSL-Tunnel with "CONNECT": creates a tunnel connection with the server
255 sendLine(server.getHttpVersion() + " 200 Connection established");
256 sendLine("Proxy-Agent",server.getServerIdentification());
257 endHeader(); out.flush();
259 remote_in = new Jhttpp2Read(server,this, HTTP_in, out); // reads data from the remote server
260 server.addBytesWritten(numread);
264 while(cnt) { // reads data from the client
269 HTTP_out.write(b, 0, numread);
271 server.addBytesWritten(numread);
273 } // end of inner loop
278 if (!notConnected() && remote_in != null)
279 remote_in.close(); // close Jhttpp2Read thread
282 /** connects to the given host and port */
283 public void connect(InetAddress host,int port) {
284 HTTP_Socket = new Socket(host,port);
285 HTTP_in = new BuferedInputStream(HTTP_Socket.getInputStream());
286 HTTP_out = new BufferedOutputStream(HTTP_Socket.getOutputStream());
288 /** converts an String into a Byte-Array to write it with the OutputStream */
289 public void write(BufferedOutputStream o,String p) {
290 o.write(p.getBytes(),0,p.length());
294 * Small webserver for local files in {app}/htdocs
297 public void file_handler() {
298 if (!server.www_server) {
299 sendErrorMSG(500, "The jHTTPp2 built-in WWW server module is disabled.");
302 String filename=in.url;
303 if (filename.equals("/")) filename="index.html"; // convert / to index.html
304 else if (filename.startsWith("/")) filename=filename.substring(1);
305 if (filename.endsWith("/")) filename+="index.html"; // add index.html, if ending with /
306 File file = new File("htdocs/" + filename); // access only files in "htdocs"
307 if (true// !file.exists() || !file.canRead() // be sure that we can read the file
308 || filename.indexOf("..")!=-1 // don't allow ".." !!!
309 // || file.isDirectory()
310 ) { // dont't read if it's a directory
311 sendErrorMSG(404,"The requested file /" + filename + " was not found or the path is invalid.");
314 int pos = filename.lastIndexOf("."); // MIME type of the specified file
315 String content_type="text/plain"; // all unknown content types will be marked as text/plain
317 String extension = filename.substring(pos+1);
318 if (extension.equalsIgnoreCase("htm") || (extension.equalsIgnoreCase("html"))) content_type="text/html; charset=iso-8859-1";
319 else if (extension.equalsIgnoreCase("jpg") || (extension.equalsIgnoreCase("jpeg"))) content_type="image/jpeg";
320 else if (extension.equalsIgnoreCase("gif")) content_type = "image/gif";
321 else if (extension.equalsIgnoreCase("png")) content_type = "image/png";
322 else if (extension.equalsIgnoreCase("css")) content_type = "text/css";
323 else if (extension.equalsIgnoreCase("pdf")) content_type = "application/pdf";
324 else if (extension.equalsIgnoreCase("ps") || extension.equalsIgnoreCase("eps")) content_type = "application/postscript";
325 else if (extension.equalsIgnoreCase("xml")) content_type = "text/xml";
327 sendHeader(200,content_type, file.length() );
329 BufferedInputStream file_in = new BufferedInputStream(new FileInputStream(file));
330 byte[] buffer=new byte[4096];
331 int a=file_in.read(buffer);
332 while (a!=-1) { // read until EOF
333 out.write(buffer,0,a);
334 a = file_in.read(buffer);
337 file_in.close(); // finished!
342 public int getStatus()
344 return in.getStatusCode();
350 public void admin_handler(byte[] b) {