commit f92bddd8107935e9fb4f9dbe185eb7b7f2cac987 Author: Steven Grimm Date: Tue Apr 28 17:16:09 2009 -0700 Initial implementation of postsend callbacks diff --git a/modules/quercus/src/com/caucho/quercus/env/Env.java b/modules/quercus/src/com/caucho/quercus/env/Env.java index d25ca4c..1a0fe8a 100644 --- a/modules/quercus/src/com/caucho/quercus/env/Env.java +++ b/modules/quercus/src/com/caucho/quercus/env/Env.java @@ -48,6 +48,7 @@ import com.caucho.quercus.page.QuercusPage; import com.caucho.quercus.program.*; import com.caucho.quercus.function.AbstractFunction; import com.caucho.quercus.resources.StreamContextResource; +import com.caucho.server.http.HttpResponse; import com.caucho.util.*; import com.caucho.vfs.ByteToChar; import com.caucho.vfs.Encoding; @@ -216,6 +217,7 @@ public class Env { private ArrayList _cleanupList; private ArrayList _objCleanupList; private ArrayList _shutdownList; + private ArrayList _postSendList; private String _defaultIncludePath; private String _includePath; @@ -6245,6 +6247,17 @@ public class Env { _shutdownList.add(new Shutdown(callback, args)); } + + /** + * Registers a post-send function. This is like a shutdown function + * but is called after the client connection is closed. + */ + public void addPostSend(Callback callback, Value []args) { + if (_postSendList == null) + _postSendList = new ArrayList(); + + _postSendList.add(new Shutdown(callback, args)); + } // XXX: hack until can clean up public void setGzStream(Object obj) @@ -6264,6 +6277,19 @@ public class Env { public void close() { try { + preClose(); + } + finally { + postClose(); + } + } + + /** + * Performs cleanup that has to happen before the client connection + * is closed. + */ + public void preClose() { + try { // php/1l0t // output buffers callbacks may throw an exception while (_outputBuffer != null) { @@ -6314,31 +6340,60 @@ public class Env { if (_cleanupList != null) { ArrayList cleanupList - = new ArrayList(_cleanupList); + = new ArrayList(_cleanupList); // cleanup is in reverse order of creation for (int i = cleanupList.size() - 1; i >= 0; i--) { - EnvCleanup envCleanup = cleanupList.get(i); - try { - if (envCleanup != null) - envCleanup.cleanup(); - } - catch (Throwable e) { - log.log(Level.FINER, e.toString(), e); - } + EnvCleanup envCleanup = cleanupList.get(i); + try { + if (envCleanup != null) + envCleanup.cleanup(); + } + catch (Throwable e) { + log.log(Level.FINER, e.toString(), e); + } + } + } + } + + /** + * Performs cleanup that happens after the Quercus servlet + * closes the write stream. + */ + public void postClose() { + if (_postSendList != null) { + // Since post-send processing can take a while, close the response to + // let the client get on with other stuff; otherwise it will sit there + // spinning its wheels while this code runs. + if (_response instanceof HttpResponse) { + try { + ((HttpResponse)_response).close(); + } + catch (IOException e) { + log.log(Level.FINER, e.toString(), e); + } + } + for (int i = 0; i < _postSendList.size(); i++) { + try { + _postSendList.get(i).call(this); + } + catch (Throwable e) { + log.log(Level.FINE, e.toString(), e); + } } } + _threadEnv.set(_oldThreadEnv); for (int i = 0; _removePaths != null && i < _removePaths.size(); i++) { Path path = _removePaths.get(i); try { - path.remove(); + path.remove(); } catch (IOException e) { - log.log(Level.FINER, e.toString(), e); + log.log(Level.FINER, e.toString(), e); } } diff --git a/modules/quercus/src/com/caucho/quercus/lib/FunctionModule.java b/modules/quercus/src/com/caucho/quercus/lib/FunctionModule.java index 50a5141..66d342d 100644 --- a/modules/quercus/src/com/caucho/quercus/lib/FunctionModule.java +++ b/modules/quercus/src/com/caucho/quercus/lib/FunctionModule.java @@ -31,6 +31,7 @@ package com.caucho.quercus.lib; import com.caucho.quercus.Quercus; import com.caucho.quercus.QuercusException; +import com.caucho.quercus.annotation.Name; import com.caucho.quercus.annotation.Optional; import com.caucho.quercus.annotation.VariableArguments; import com.caucho.quercus.env.*; @@ -196,6 +197,19 @@ public class FunctionModule extends AbstractQuercusModule { return NullValue.NULL; } + + /** + * Registers a post-send shutdown function. + */ + @Name("quercus_register_postsend_function") + public static Value register_postsend_function(Env env, + Callback fun, + Value []args) + { + env.addPostSend(fun, args); + + return NullValue.NULL; + } // XXX: register_tick_function // XXX: unregister_tick_function diff --git a/modules/quercus/src/com/caucho/quercus/servlet/QuercusServletImpl.java b/modules/quercus/src/com/caucho/quercus/servlet/QuercusServletImpl.java index 59f8da9..41d368d 100644 --- a/modules/quercus/src/com/caucho/quercus/servlet/QuercusServletImpl.java +++ b/modules/quercus/src/com/caucho/quercus/servlet/QuercusServletImpl.java @@ -213,12 +213,20 @@ public class QuercusServletImpl extends HttpServlet throw e; } finally { - if (env != null) - env.close(); + try { + // Do pre-send cleanup, e.g., shutdown handlers that can output data + if (env != null) + env.preClose(); - // don't want a flush for an exception - if (ws != null) - ws.close(); + // don't want a flush for an exception + if (ws != null) + ws.close(); + } + finally { + // Do post-send cleanup (no output allowed) + if (env != null) + env.postClose(); + } } } catch (QuercusDieException e) {