diff --git a/VERSION.txt b/VERSION.txt index 3cc65bad021..606e02c0155 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,4 +1,5 @@ -jetty-7.0.0.M2-SNAPSHOT +jetty-7.0.0.M3-SNAPSHOT + + fixed race with expired async listeners jetty-7.0.0.M2 18 May 2009 + JETTY-937 Work around Sun JVM bugs @@ -64,7 +65,7 @@ jetty-7.0.0.M1 22 April 2009 + Reworked JMX for new layout + JETTY-983 DefaultServlet generates accept-ranges for cached/gzip content + 273011 JETTY-980 JETTY-992 Security / Directory Listing XSS present - + 271536 Add supprot to IO for quietly closing Readers / Writers + + 271536 Add support to IO for quietly closing Readers / Writers + 273101 Fix DefaultServletTest XSS test case + 273153 Test for Nested references in DispatchServlet diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncRequest.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncRequest.java index 820a2351b05..eecebc7c4c4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncRequest.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncRequest.java @@ -70,42 +70,66 @@ public class AsyncRequest implements AsyncContext, Continuation private boolean _keepWrappers; private long _timeoutMs; private AsyncEventState _event; + +// private StringBuilder _history = new StringBuilder(); /* ------------------------------------------------------------ */ protected AsyncRequest() { _state=__IDLE; _initial=true; +// _history.append(super.toString()); +// _history.append('\n'); } /* ------------------------------------------------------------ */ protected void setConnection(final HttpConnection connection) { - _connection=connection; + synchronized(this) + { + _connection=connection; +// _history.append(connection.toString()); +// _history.append('\n'); + } } /* ------------------------------------------------------------ */ public void addContinuationListener(ContinuationListener listener) { - _listeners=LazyList.add(_listeners,listener); + synchronized(this) + { + _listeners=LazyList.add(_listeners,listener); +// _history.append('L'); + } } /* ------------------------------------------------------------ */ public void setAsyncTimeout(long ms) { - _timeoutMs=ms; + synchronized(this) + { + _timeoutMs=ms; +// _history.append('T'); +// _history.append(ms); + } } /* ------------------------------------------------------------ */ public long getAsyncTimeout() { - return _timeoutMs; + synchronized(this) + { + return _timeoutMs; + } } /* ------------------------------------------------------------ */ public AsyncEventState getAsyncEventState() { - return _event; + synchronized(this) + { + return _event; + } } /* ------------------------------------------------------------ */ @@ -114,7 +138,11 @@ public class AsyncRequest implements AsyncContext, Continuation */ public void keepWrappers() { - _keepWrappers=true; + synchronized(this) + { +// _history.append('W'); + _keepWrappers=true; + } } /* ------------------------------------------------------------ */ @@ -123,7 +151,10 @@ public class AsyncRequest implements AsyncContext, Continuation */ public boolean wrappersKept() { - return _keepWrappers; + synchronized(this) + { + return _keepWrappers; + } } /* ------------------------------------------------------------ */ @@ -163,7 +194,10 @@ public class AsyncRequest implements AsyncContext, Continuation /* ------------------------------------------------------------ */ public String toString() { - return getStatusString(); + synchronized (this) + { + return super.toString()+"@"+getStatusString(); + } } /* ------------------------------------------------------------ */ @@ -183,7 +217,9 @@ public class AsyncRequest implements AsyncContext, Continuation (_state==__UNCOMPLETED)?"UNCOMPLETED": (_state==__COMPLETE)?"COMPLETE": ("UNKNOWN?"+_state))+ - (_initial?",initial":""); + (_initial?",initial":"")+ + (_resumed?",resumed":"")+ + (_expired?",expired":""); } } @@ -195,6 +231,9 @@ public class AsyncRequest implements AsyncContext, Continuation { synchronized (this) { +// _history.append('H'); +// _history.append(_connection.getRequest().getUri().toString()); +// _history.append(':'); _keepWrappers=false; switch(_state) @@ -218,7 +257,8 @@ public class AsyncRequest implements AsyncContext, Continuation return false; case __SUSPENDED: - cancelTimeout(); + return false; + case __UNSUSPENDING: _state=__REDISPATCHED; return true; @@ -239,6 +279,7 @@ public class AsyncRequest implements AsyncContext, Continuation { synchronized (this) { +// _history.append('S'); _resumed=false; _expired=false; @@ -288,6 +329,7 @@ public class AsyncRequest implements AsyncContext, Continuation { synchronized (this) { +// _history.append('U'); switch(_state) { case __REDISPATCHED: @@ -337,6 +379,7 @@ public class AsyncRequest implements AsyncContext, Continuation boolean dispatch=false; synchronized (this) { +// _history.append('D'); switch(_state) { case __REDISPATCHED: @@ -354,7 +397,7 @@ public class AsyncRequest implements AsyncContext, Continuation return; case __SUSPENDED: - dispatch=true; + dispatch=!_expired; _state=__UNSUSPENDING; _resumed=true; break; @@ -377,12 +420,15 @@ public class AsyncRequest implements AsyncContext, Continuation /* ------------------------------------------------------------ */ protected void expired() { + Object listeners=null; synchronized (this) { +// _history.append('E'); switch(_state) { case __SUSPENDING: case __SUSPENDED: + listeners=_listeners; break; default: return; @@ -390,13 +436,18 @@ public class AsyncRequest implements AsyncContext, Continuation _expired=true; } - if (_listeners!=null) + if (listeners!=null) { - for(int i=0;i","SLEPT"}, - {"/path?suspend=","TIMEOUT"}, - {"/path?suspend=1000&resume=","RESUMED"}, - {"/path?suspend=1000&complete=","COMPLETED"}, + {"/path/info","NORMAL"}, + {"/path?sleep=","SLEPT"}, + {"/path?suspend=","TIMEOUT"}, + {"/path?suspend=60000&resume=","RESUMED"}, + {"/path?suspend=60000&complete=","COMPLETED"}, }; - - public void doPaths(String name) throws Exception + public void doConnections(int connections,final int loops) throws Throwable { - for (int i=0;i<__tests.length;i++) + Socket[] socket = new Socket[connections]; + int [][] path = new int[connections][loops]; + for (int i=0;i",Integer.toString(timeout)); - - long start=System.currentTimeMillis(); - Socket socket = new Socket(_addr,_port); - socket.setSoTimeout(30000); - String request = "GET "+uri+" HTTP/1.0\r\n\r\n"; - socket.getOutputStream().write(request.getBytes()); - socket.getOutputStream().flush(); - String response = IO.toString(socket.getInputStream()); - socket.close(); - long end=System.currentTimeMillis(); - - response=response.substring(response.indexOf("\r\n\r\n")+4); - - String test=name+"-"+i+" "+uri+" "+__tests[i][1]; - assertEquals(test,__tests[i][1],response); - if (!response.equals("NORMAL")) + socket[i] = new Socket(_addr,_port); + socket[i].setSoTimeout(30000); + if (i%80==0) + System.err.println(); + System.err.print('+'); + } + System.err.println(); + Log.info("Bound "+connections); + + for (int l=0;l=timeout); + int p=path[i][l]=_random.nextInt(__paths.length); + + int period = _random.nextInt(490)+10; + String uri=__paths[p][0].replace("",Integer.toString(period)); + + long start=System.currentTimeMillis(); + String request = + "GET "+uri+" HTTP/1.1\r\n"+ + "Host: localhost\r\n"+ + "start: "+start+"\r\n"+ + "result: "+__paths[p][1]+"\r\n"+ + ((l+1max) - max=l; - total+=l; - if (l==loops) - finished++; - } - } - - Log.info("min/ave/max/target="+min+"/"+(total/threads)+"/"+max+"/"+loops+" errors/finished/loops="+errors+"/"+finished+"/"+threads+" idle/threads="+(_threads.getIdleThreads()+_connector.getAcceptors())+"/"+_threads.getThreads()); - if ((finished+errors)==threads) - break; + results[i]=IO.toString(socket[i].getInputStream(),"UTF-8"); + if (i%80==0) + System.err.println(); + System.err.print('-'); + } + System.err.println(); + + Log.info("Read "+connections+" connections"); + + for (int i=0;i0) baseRequest.setAsyncTimeout(suspend_for); baseRequest.addEventListener(__asyncListener); - baseRequest.startAsync(); + final AsyncContext asyncContext = baseRequest.startAsync(); + + + if (complete_after>0) + { + TimerTask complete = new TimerTask() + { + public void run() + { + try + { + response.setStatus(200); + response.getOutputStream().println("COMPLETED " + request.getHeader("result")); + baseRequest.setHandled(true); + asyncContext.complete(); + } + catch(Exception e) + { + Request br=(Request)asyncContext.getRequest(); + System.err.println("\n"+e.toString()); + System.err.println(baseRequest+"=="+br); + System.err.println(uri+"=="+br.getUri()); + System.err.println(asyncContext+"=="+br.getAsyncRequest()); + + System.err.println(((AsyncRequest)asyncContext).getHistory()); + Log.warn(e); + System.exit(1); + } + } + }; + synchronized (_timer) + { + _timer.schedule(complete,complete_after); + } + } + else if (complete_after==0) + { + response.setStatus(200); + response.getOutputStream().println("COMPLETED "+request.getHeader("result")); + baseRequest.setHandled(true); + asyncContext.complete(); + } + else if (resume_after>0) + { + TimerTask resume = new TimerTask() + { + public void run() + { + asyncContext.dispatch(); + } + }; + synchronized (_timer) + { + _timer.schedule(resume,resume_after); + } + } + else if (resume_after==0) + { + asyncContext.dispatch(); + } + } else if (sleep_for>=0) { @@ -278,82 +293,28 @@ public class AsyncStressTest extends TestCase e.printStackTrace(); } response.setStatus(200); - response.getOutputStream().print("SLEPT"); + response.getOutputStream().println("SLEPT "+request.getHeader("result")); baseRequest.setHandled(true); return; } else { response.setStatus(200); - response.getOutputStream().print("NORMAL"); + response.getOutputStream().println("NORMAL "+request.getHeader("result")); baseRequest.setHandled(true); return; } - - final AsyncContext asyncContext = baseRequest.getAsyncContext(); - - - if (complete_after>0) - { - TimerTask complete = new TimerTask() - { - public void run() - { - try - { - response.setStatus(200); - response.getOutputStream().print("COMPLETED"); - baseRequest.setHandled(true); - asyncContext.complete(); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - }; - synchronized (_timer) - { - _timer.schedule(complete,complete_after); - } - } - else if (complete_after==0) - { - response.setStatus(200); - response.getOutputStream().print("COMPLETED"); - baseRequest.setHandled(true); - asyncContext.complete(); - } - - if (resume_after>0) - { - TimerTask resume = new TimerTask() - { - public void run() - { - asyncContext.dispatch(); - } - }; - synchronized (_timer) - { - _timer.schedule(resume,resume_after); - } - } - else if (resume_after==0) - { - asyncContext.dispatch(); - } } else if (request.getAttribute("TIMEOUT")!=null) { response.setStatus(200); - response.getOutputStream().print("TIMEOUT"); + response.getOutputStream().println("TIMEOUT "+request.getHeader("result")); baseRequest.setHandled(true); } else { response.setStatus(200); - response.getOutputStream().print("RESUMED"); + response.getOutputStream().println("RESUMED "+request.getHeader("result")); baseRequest.setHandled(true); } }