001: // Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
002: // Released under the terms of the GNU General Public License version 2 or later.
003: package fitnesse;
004:
005: import fitnesse.http.*;
006: import fitnesse.responders.*;
007: import fitnesse.components.*;
008: import java.net.*;
009: import java.io.*;
010: import java.util.GregorianCalendar;
011:
012: public class FitNesseExpediter implements ResponseSender {
013: private Socket socket;
014:
015: private InputStream input;
016:
017: private OutputStream output;
018:
019: private Request request;
020:
021: private Response response;
022:
023: private FitNesseContext context;
024:
025: protected long requestParsingTimeLimit;
026:
027: private long requestProgress;
028:
029: private long requestParsingDeadline;
030:
031: private boolean hasError;
032:
033: public FitNesseExpediter(Socket s, FitNesseContext context)
034: throws Exception {
035: this .context = context;
036: socket = s;
037: input = s.getInputStream();
038: output = s.getOutputStream();
039: requestParsingTimeLimit = 10000;
040: }
041:
042: public void start() throws Exception {
043: try {
044: Request request = makeRequest();
045: makeResponse(request);
046: sendResponse();
047: } catch (SocketException se) {
048: // can be thrown by makeResponse or sendResponse.
049: } catch (Throwable e) {
050: e.printStackTrace();
051: }
052: }
053:
054: public void setRequestParsingTimeLimit(long t) {
055: requestParsingTimeLimit = t;
056: }
057:
058: public long getRequestParsingTimeLimit() {
059: return requestParsingTimeLimit;
060: }
061:
062: public void send(byte[] bytes) throws Exception {
063: try {
064: output.write(bytes);
065: output.flush();
066: } catch (IOException stopButtonPressed_probably) {
067: }
068: }
069:
070: public void close() throws Exception {
071: try {
072: log(socket, request, response);
073: socket.close();
074: } catch (IOException e) {
075: e.printStackTrace();
076: }
077: }
078:
079: public Socket getSocket() throws Exception {
080: return socket;
081: }
082:
083: public Request makeRequest() throws Exception {
084: request = new Request(input);
085: return request;
086: }
087:
088: public void sendResponse() throws Exception {
089: response.readyToSend(this );
090: }
091:
092: private Response makeResponse(Request request) throws Exception {
093: try {
094: Thread parseThread = createParsingThread(request);
095: parseThread.start();
096:
097: waitForRequest(request);
098: if (!hasError)
099: response = createGoodResponse(request);
100: } catch (SocketException se) {
101: throw (se);
102: } catch (Exception e) {
103: response = new ErrorResponder(e).makeResponse(context,
104: request);
105: }
106: return response;
107: }
108:
109: public Response createGoodResponse(Request request)
110: throws Exception {
111: Response response;
112: Responder responder = context.responderFactory.makeResponder(
113: request, context.root);
114: responder = context.authenticator.authenticate(context,
115: request, responder);
116: response = responder.makeResponse(context, request);
117: response.addHeader("Server", "FitNesse-"
118: + FitNesse.FITNESSE_VERSION);
119: response.addHeader("Connection", "close");
120: return response;
121: }
122:
123: private void waitForRequest(Request request)
124: throws InterruptedException {
125: long now = System.currentTimeMillis();
126: requestParsingDeadline = now + requestParsingTimeLimit;
127: requestProgress = 0;
128: while (!hasError && !request.hasBeenParsed()) {
129: Thread.sleep(10);
130: if (timeIsUp(now) && parsingIsUnproductive(request))
131: reportError(
132: 408,
133: "The client request has been unproductive for too long. It has timed out and will now longer be processed");
134: }
135: }
136:
137: private boolean parsingIsUnproductive(Request request) {
138: long updatedRequestProgress = request.numberOfBytesParsed();
139: if (updatedRequestProgress > requestProgress) {
140: requestProgress = updatedRequestProgress;
141: return false;
142: } else
143: return true;
144: }
145:
146: private boolean timeIsUp(long now) {
147: now = System.currentTimeMillis();
148: if (now > requestParsingDeadline) {
149: requestParsingDeadline = now + requestParsingTimeLimit;
150: return true;
151: } else
152: return false;
153: }
154:
155: private Thread createParsingThread(final Request request) {
156: Thread parseThread = new Thread() {
157: public synchronized void run() {
158: try {
159: request.parse();
160: } catch (HttpException e) {
161: reportError(400, e.getMessage());
162: } catch (Exception e) {
163: reportError(e);
164: }
165: }
166: };
167: return parseThread;
168: }
169:
170: void reportError(int status, String message) {
171: try {
172: response = new ErrorResponder(message).makeResponse(
173: context, request);
174: response.setStatus(status);
175: hasError = true;
176: } catch (Exception e) {
177: e.printStackTrace();
178: }
179: }
180:
181: void reportError(Exception e) {
182: try {
183: response = new ErrorResponder(e).makeResponse(context,
184: request);
185: hasError = true;
186: } catch (Exception e1) {
187: e1.printStackTrace();
188: }
189: }
190:
191: public static LogData makeLogData(Socket socket, Request request,
192: Response response) {
193: LogData data = new LogData();
194: data.host = ((InetSocketAddress) socket
195: .getRemoteSocketAddress()).getAddress()
196: .getHostAddress();
197: data.time = new GregorianCalendar();
198: data.requestLine = request.getRequestLine();
199: data.status = response.getStatus();
200: data.size = response.getContentSize();
201:
202: return data;
203: }
204:
205: public void log(Socket s, Request request, Response response)
206: throws Exception {
207: if (context.logger != null)
208: context.logger.log(makeLogData(s, request, response));
209: }
210: }
|