1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/server/SimpleHttpServerConnection.java,v 1.10 2004/09/14 15:50:41 olegk Exp $
3    * $Revision: 1.10 $
4    * $Date: 2004/09/14 15:50:41 $
5    *
6    * ====================================================================
7    *
8    *  Copyright 1999-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   * [Additional notices, if required by prior licensing conditions]
29   *
30   */
31  
32  package org.apache.commons.httpclient.server;
33  
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.OutputStream;
37  import java.io.UnsupportedEncodingException;
38  import java.net.Socket;
39  
40  import org.apache.commons.httpclient.HttpException;
41  import org.apache.commons.httpclient.HttpParser;
42  import org.apache.commons.httpclient.HttpStatus;
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  /***
47   * A connection to the SimpleHttpServer.
48   * 
49   * @author Christian Kohlschuetter
50   */
51  public class SimpleHttpServerConnection implements Runnable {
52  
53      private static final Log LOG = LogFactory.getLog(SimpleHttpServerConnection.class);
54      private static final String HTTP_ELEMENT_CHARSET = "US-ASCII";
55  
56      private SimpleHttpServer server;
57      private Socket socket;
58      private InputStream in;
59      private OutputStream out;
60  
61      private int requestNo = 0;
62  
63      private boolean keepAlive = false;
64  
65      public SimpleHttpServerConnection(SimpleHttpServer server, Socket socket) throws IOException {
66          this.server = server;
67          this.socket = socket;
68          this.in = socket.getInputStream();
69          this.out = socket.getOutputStream();
70      }
71  
72      public synchronized void destroy() {
73          try {
74              if (socket != null) {
75                  in.close();
76                  out.close();
77                  socket.close();
78                  socket = null;
79              }
80          } catch (IOException e) {
81              // fail("Unexpected exception: " + e);
82          }
83          server.removeConnection(this);
84      }
85  
86      public void run() {
87          requestNo = 0;
88          try {
89              do {
90                  keepAlive = false;
91  
92                  ++this.requestNo;
93                  readRequest();
94              } while (keepAlive);
95          } catch (IOException e) {
96              LOG.error("I/O error: " + e.getMessage());
97          } finally {
98              destroy();
99          }
100     }
101 
102     /***
103      * Requests to close connection after processing this request.
104      */
105     public void connectionClose() {
106         keepAlive = false;
107     }
108 
109     /***
110      * Requests to keep the connection alive after processing this request
111      * (must be re-issued for every request if permanent keep-alive is
112      * desired).
113      *  
114      */
115     public void connectionKeepAlive() {
116         keepAlive = true;
117     }
118 
119     /***
120      * Returns the ResponseWriter used to write the output to the socket.
121      * 
122      * @return This connection's ResponseWriter
123      */
124     public ResponseWriter getWriter() {
125         try {
126             return new ResponseWriter(out);
127         } catch (UnsupportedEncodingException e) {
128             throw new RuntimeException(e.toString());
129         }
130     }
131 
132     /***
133      * Returns the number of requests processed (including the current one) for
134      * this connection.
135      * 
136      * @return
137      */
138     public int getRequestNumber() {
139         return requestNo;
140     }
141 
142     private void readRequest() throws IOException {
143         String line;
144         do {
145             line = HttpParser.readLine(in, HTTP_ELEMENT_CHARSET);
146         } while (line != null && line.length() == 0);
147 
148         if (line == null) {
149             connectionClose();
150             return;
151         }
152 
153         SimpleRequest request = null;
154         try {
155             request = new SimpleRequest( 
156                 RequestLine.parseLine(line),
157                 HttpParser.parseHeaders(in, HTTP_ELEMENT_CHARSET),
158                 null);
159         } catch (HttpException e) {
160             connectionClose();
161             SimpleResponse response = ErrorResponse.getInstance().
162                 getResponse(HttpStatus.SC_BAD_REQUEST);
163             server.writeResponse(this, response);
164             return;
165         } catch (IOException e) {
166             connectionClose();
167             LOG.error("I/O error processing request", e);
168             return;
169         }
170         server.processRequest(this, request);
171         out.flush();
172     }
173 
174     public InputStream getInputStream() {
175         return in;
176     }
177 
178     public OutputStream getOutputStream() {
179         return out;
180     }
181 
182     public boolean isKeepAlive() {
183         return this.keepAlive;
184     }
185 
186 }
187