[grizzly~git:78187e75] [glassfishv4] + fix the issue #1600

  • From: oleksiys@...
  • To: commits@...
  • Subject: [grizzly~git:78187e75] [glassfishv4] + fix the issue #1600
  • Date: Fri, 1 Nov 2013 19:30:53 +0000

Project:    grizzly
Repository: git
Revision:   78187e7563572cb8ab8ed7cde9e6451dc9d6c51c
Author:     oleksiys
Date:       2013-10-31 01:05:54 UTC
Link:       

Log Message:
------------
[glassfishv4] Alternate fix for https://java.net/jira/browse/GRIZZLY-1594 ;
(ReadHandler callbacks should not be performed in I/O thread.)
  (Thanks Alexey)

[glassfishv4] Fix a reversion in some previous changes.

[glassfishv4] + fix SameThreadIOStrategy suspend/setManualIOEventControl, 
allow to disableIOEvent just once per context life-cycle

[glassfishv4] + Minor update for the latest fix for 
https://java.net/jira/browse/GRIZZLY-1594 ;(ReadHandler callbacks should not 
be performed in I/O thread.)

[glassfishv4] + fix copyright

[glassfishv4] Clarify the javadocs for getWorkerThreadPoolConfig() after 
builder changes introduced in 2.3.6.

[glassfishv4] + implement improvement #1596
https://java.net/jira/browse/GRIZZLY-1596
"Input/OutputBuffer notification has to be performed in the 
HttpHandler.getThreadPool() thread"

[glassfishv4] + fix the issue #1600
https://java.net/jira/browse/GRIZZLY-1600

"Redundant attempt to resolve a host name during AJP request parsing"



Revisions:
----------
a2eccde6065f94747dd1e167eee7cb26d7c369d3
9f8a327a385400d1389d8ca6804f0f59a55e7c59
cf52f818633b50e7042f5bec3bdae79cc3e32ebb
e5ea1931db56337b61a5721fa40078785bdfdd42
8299b42a273633ae2d3832b34dbfd2294734d754
5337d4a24a03cb7c5b62a9d96cba41a4ce7f74a4
812d63125457b46db37cc73b7d9c05e5425230ab
78187e7563572cb8ab8ed7cde9e6451dc9d6c51c


Modified Paths:
---------------
modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpServerFilter.java
modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
modules/grizzly/src/main/java/org/glassfish/grizzly/Context.java
modules/grizzly/src/main/java/org/glassfish/grizzly/strategies/SameThreadIOStrategy.java
modules/grizzly/src/main/java/org/glassfish/grizzly/NIOTransportBuilder.java
modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpHandler.java
modules/http-server/src/main/java/org/glassfish/grizzly/http/server/Request.java
modules/http-server/src/main/java/org/glassfish/grizzly/http/server/io/ServerOutputBuffer.java
modules/http/src/main/java/org/glassfish/grizzly/http/io/OutputBuffer.java
samples/http-server-samples/src/main/java/org/glassfish/grizzly/samples/httpserver/priorities/HighPriorityHandler.java
samples/http-server-samples/src/main/java/org/glassfish/grizzly/samples/httpserver/priorities/LowPriorityHandler.java
modules/http-ajp/src/main/java/org/glassfish/grizzly/http/ajp/AjpHttpRequest.java
modules/http-ajp/src/main/java/org/glassfish/grizzly/http/ajp/AjpMessageUtils.java


Added Paths:
------------
modules/http-server/src/main/java/org/glassfish/grizzly/http/server/RequestExecutorProvider.java
modules/http-server/src/main/java/org/glassfish/grizzly/http/server/io/ServerInputBuffer.java


Diffs:
------
--- 
a/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpServerFilter.java
+++ 
b/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpServerFilter.java
@@ -191,7 +191,7 @@ public class HttpServerFilter extends BaseFilter
                         }
                     }
                 } catch (Exception t) {
-                    LOGGER.log(Level.WARNING, "Exception during HttpHandler 
invokation", t);
+                    LOGGER.log(Level.WARNING, "Exception during HttpHandler 
invocation", t);
                     
                     request.getProcessingState().setError(true);
                     
@@ -219,7 +219,7 @@ public class HttpServerFilter extends BaseFilter
             } else {
                 // We're working with suspended HTTP request
                 try {
-                    if 
(!handlerRequest.getInputBuffer().append(httpContent)) {
+                    if (!handlerRequest.getInputBuffer().append(httpContent, 
ctx)) {
                         // we don't want this thread/context to reset
                         // OP_READ on Connection
 --- 
a/modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
+++ 
b/modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
@@ -63,11 +63,11 @@ import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.Grizzly;
 import org.glassfish.grizzly.http.HttpBrokenContentException;
 import org.glassfish.grizzly.memory.Buffers;
 import org.glassfish.grizzly.memory.CompositeBuffer;
@@ -80,6 +80,9 @@ import static org.glassfish.grizzly.http.util.Constants.*;
  * from the HTTP messaging system in Grizzly.
  */
 public class InputBuffer {
+    private static final Logger LOGGER = Grizzly.logger(InputBuffer.class);
+    private static final Level LOGGER_LEVEL = Level.FINER;
+    
     /**
      * The {@link org.glassfish.grizzly.http.HttpHeader} associated with 
this <code>InputBuffer</code>
      */
@@ -170,8 +173,7 @@ public class InputBuffer {
     /**
      * {@link CharBuffer} for converting a single character at a time.
      */
-    private final CharBuffer singleCharBuf =
-            (CharBuffer) CharBuffer.allocate(1).position(1); // create 
CharBuffer w/ 0 chars remaining
+    private final CharBuffer singleCharBuf = CharBuffer.allocate(1);
 
     /**
      * Used to estimate how many characters can be produced from a variable
@@ -218,14 +220,20 @@ public class InputBuffer {
             contentRead = content.isLast();
             content.recycle();
             inputContentBuffer.allowBufferDispose(true);
+
+            if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+                log("InputBuffer %s initialize with ready content: %s",
+                        this, inputContentBuffer);
+            }
         }
 
     }
 
     /**
      * Set the default character encoding for this <tt>InputBuffer</tt>, 
which
-     * would be applied if no encoding was explicitly set on HTTP request
-     * and character decoding wasn't started yet.
+     * would be applied if no encoding was explicitly set on HTTP
+     * {@link org.glassfish.grizzly.http.HttpRequestPacket} and character 
decoding
+     * wasn't started yet.
      */
     @SuppressWarnings("UnusedDeclaration")
     public void setDefaultEncoding(final String encoding) {
@@ -242,8 +250,6 @@ public class InputBuffer {
         inputContentBuffer.tryDispose();
         inputContentBuffer = null;
 
-        singleCharBuf.position(singleCharBuf.limit());
-        
         connection = null;
         decoder = null;
         ctx = null;
@@ -299,6 +305,10 @@ public class InputBuffer {
      * @see java.io.InputStream#read()
      */
     public int readByte() throws IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s readByte. Ready content: %s",
+                    this, inputContentBuffer);
+        }
 
         if (closed) {
             throw new IOException();
@@ -324,6 +334,10 @@ public class InputBuffer {
      * @see java.io.InputStream#read(byte[], int, int)
      */
     public int read(final byte b[], final int off, final int len) throws 
IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s read byte array of len: %s. Ready content: 
%s",
+                    this, len, inputContentBuffer);
+        }
 
         if (closed) {
             throw new IOException();
@@ -385,6 +399,11 @@ public class InputBuffer {
      * {@link Buffer} used to buffer incoming request data.
      */
     public Buffer getBuffer() {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s getBuffer. Ready content: %s",
+                    this, inputContentBuffer);
+        }
+
         return inputContentBuffer.duplicate();
     }
 
@@ -395,6 +414,11 @@ public class InputBuffer {
      * the {@link Buffer}.
      */
     public Buffer readBuffer() {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s readBuffer. Ready content: %s",
+                    this, inputContentBuffer);
+        }
+        
         return readBuffer(inputContentBuffer.remaining());
     }
     
@@ -407,6 +431,11 @@ public class InputBuffer {
      * {@link Buffer}, so user code becomes responsible for handling its 
life-cycle.
      */
     public Buffer readBuffer(final int size) {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s readBuffer(size), size: %s. Ready content: 
%s",
+                    this, size, inputContentBuffer);
+        }
+        
         final int remaining = inputContentBuffer.remaining();
         if (size > remaining) {
             throw new IllegalStateException("Can not read more bytes than 
available");
@@ -441,6 +470,10 @@ public class InputBuffer {
      * @see java.io.Reader#read(java.nio.CharBuffer)
      */
     public int read(final CharBuffer target) throws IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s read(CharBuffer). Ready content: %s",
+                    this, inputContentBuffer);
+        }
 
         if (closed) {
             throw new IOException();
@@ -467,6 +500,10 @@ public class InputBuffer {
      * @see java.io.Reader#read()
      */
     public int readChar() throws IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s readChar. Ready content: %s",
+                    this, inputContentBuffer);
+        }
       
         if (closed) {
             throw new IOException();
@@ -475,15 +512,16 @@ public class InputBuffer {
             throw new IllegalStateException();
         }
 
-        if (!singleCharBuf.hasRemaining()) {
-            singleCharBuf.clear();
-            int read = read(singleCharBuf);
-            if (read == -1) {
-                return -1;
-            }
+        singleCharBuf.position(0);
+        int read = read(singleCharBuf);
+        if (read == -1) {
+            singleCharBuf.limit(1); // singleCharBuf.clear();
+            return -1;
         }
+        final char c = singleCharBuf.get(0);
 
-        return singleCharBuf.get();
+        singleCharBuf.position(0); // singleCharBuf.clear();
+        return c;
 
     }
 
@@ -493,6 +531,10 @@ public class InputBuffer {
      */
     public int read(final char cbuf[], final int off, final int len)
     throws IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s read char array, len: %s. Ready content: %s",
+                    this, len, inputContentBuffer);
+        }
 
         if (closed) {
             throw new IOException();
@@ -511,6 +553,24 @@ public class InputBuffer {
     }
 
     /**
+     * @see java.io.Reader#ready()
+     */
+    public boolean ready() {
+
+        if (closed) {
+            return false;
+        }
+        if (!processingChars) {
+            throw new IllegalStateException();
+        }
+        return (inputContentBuffer.hasRemaining()
+                   || httpHeader.isExpectContent());
+
+    }
+    
+    /**
+
+    /**
      * Fill the buffer (blocking) up to the requested length.
      * 
      * @param length how much content should attempt to buffer,
@@ -519,6 +579,11 @@ public class InputBuffer {
      * @throws IOException
      */
     public void fillFully(final int length) throws IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s fillFully, len: %s. Ready content: %s",
+                this, length, inputContentBuffer);
+        }
+        
         if (length == 0) return;
         
         if (length > 0) {
@@ -534,19 +599,7 @@ public class InputBuffer {
 
     public int availableChar() {
 
-        if (!singleCharBuf.hasRemaining()) {
-            // fill the singleCharBuf to make sure we have at least one char
-            singleCharBuf.clear();
-            if (fillAvailableChars(1, singleCharBuf) == 0) {
-                singleCharBuf.position(singleCharBuf.limit());
-                return 0;
-            }
-            
-            singleCharBuf.flip();
-        }
-        
-        // we have 1 char pre-decoded + estimation for the rest 
byte[]->char[] count.
-        return 1 + ((int) (inputContentBuffer.remaining() * 
averageCharsPerByte));
+        return ((int) (inputContentBuffer.remaining() * 
averageCharsPerByte));
 
     }
 
@@ -626,7 +679,7 @@ public class InputBuffer {
 
     }
 
-
+    
     /**
      * Skips the specified number of bytes/characters.
      *
@@ -646,6 +699,10 @@ public class InputBuffer {
      * @see java.io.Reader#skip(long)
      */
     public long skip(final long n) throws IOException {
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s skip %s bytes. Ready content: %s",
+                    this, n, inputContentBuffer);
+        }
 
         if (closed) {
             throw new IOException();
@@ -693,34 +750,58 @@ public class InputBuffer {
      *
      * This method shouldn't be invoked by developers directly.
      */
-    public void finished() throws IOException {
+    public void finished() {
         if (!contentRead) {
             contentRead = true;
             final ReadHandler localHandler = handler;
             if (localHandler != null) {
                 handler = null;
-                executeReadHandler(new Callable<Throwable>() {
-                    @Override
-                    public Throwable call() throws Exception {
-                        try {
-                            localHandler.onAllDataRead();
-                            return null;
-                        } catch (Throwable t) {
-                            localHandler.onError(t);
-                            return t;
-                        }
-                    }
-                });
+                invokeHandlerAllRead(localHandler, getThreadPool());
+            }
+        }
+    }
+    
+    private void finishedInTheCurrentThread(final ReadHandler readHandler) {
+        if (!contentRead) {
+            contentRead = true;
+            if (readHandler != null) {
+                invokeHandlerAllRead(readHandler, null);
+            }
+        }
+    }
 
+    private void invokeHandlerAllRead(final ReadHandler readHandler,
+            final ExecutorService es) {
+        if (es != null) {
+            es.submit(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        readHandler.onAllDataRead();
+                    } catch (Throwable t) {
+                        readHandler.onError(t);
+                    }
+                }
+            });
+        } else {
+            try {
+                readHandler.onAllDataRead();
+            } catch (Throwable t) {
+                readHandler.onError(t);
             }
         }
     }
+    
 
     public void replayPayload(final Buffer buffer) {
         if (!isFinished()) {
             throw new IllegalStateException("Can't replay when InputBuffer 
is not closed");
         }
         
+        if (LOGGER.isLoggable(LOGGER_LEVEL)) {
+            log("InputBuffer %s replayPayload to %s", this, buffer);
+        }
+        
         closed = false;
         readCount = 0;
         
@@ -823,13 +904,16 @@ public class InputBuffer {
      * {@link Buffer}.
      *
      * @param httpContent the {@link HttpContent} to append
+     * @param asyncContext The FilterChainContext that initiated the
+     *                     append call.
      *
      * @return <code>true</code> if {@link ReadHandler}
      *  callback was invoked, otherwise returns <code>false</code>.
      *
      * @throws IOException if an error occurs appending the {@link Buffer}
      */
-    public boolean append(final HttpContent httpContent) throws IOException {
+    public boolean append(final HttpContent httpContent,
+                          final FilterChainContext asyncContext) throws 
IOException {
         
         // Stop waiting for data asynchronously and enable it again
         // only if we have a handler registered, which requirement
@@ -847,68 +931,48 @@ public class InputBuffer {
             
             final ReadHandler localHandler = handler;
             
+            final boolean isLast = httpContent.isLast();
+            
             // if we have a handler registered - switch the flag to true
-            boolean askForMoreDataInThisThread = localHandler != null;
-        
+            boolean askForMoreDataInThisThread = !isLast && localHandler != 
null;
+            boolean invokeDataAvailable = false;
+
             if (buffer.hasRemaining()) {
                 updateInputContentBuffer(buffer);
-                if (!httpContent.isLast() && localHandler != null) {
-                    // it's not the last chunk
+                if (localHandler != null) {
                     final int available = readyData();
                     if (available >= requestedSize) {
-                        // handler is going to be notified,
-                        // switch flags to false
+                        invokeDataAvailable = true;
                         askForMoreDataInThisThread = false;
-                        
-                        handler = null;
-                        executeReadHandler(new Callable<Throwable>() {
-                            @Override
-                            public Throwable call() throws Exception {
-                                try {
-                                    localHandler.onDataAvailable();
-                                    return null;
-                                } catch (Throwable t) {
-                                    localHandler.onError(t);
-                                    return t;
-                                }
-                            }
-                        });
                     }
                 }
             }
-
-            if (httpContent.isLast()) {
-                // handler is going to be notified,
-                // switch flags to false
-                askForMoreDataInThisThread = false;
-                checkHttpTrailer(httpContent);
-                finished();
-            }
             
             if (askForMoreDataInThisThread) {
-                // Note: it can't be changed to:
-                // isWaitingDataAsynchronously = askForMoreDataInThisThread;
-                // because if askForMoreDataInThisThread == false - some 
other
-                // thread might have gained control over
-                // isWaitingDataAsynchronously field
+                // There is a ReadHandler registered, but it requested more
+                // data to be available before we can notify it - so wait for
+                // more data to come
                 isWaitingDataAsynchronously = true;
+                return true;
             }
             
-            return askForMoreDataInThisThread;
-        } else { // broken content
-            final ReadHandler localHandler = handler;
             handler = null;
-            if (!closed && localHandler != null) {
-                executeReadHandler(new Runnable() {
-                    @Override
-                    public void run() {
-                        localHandler.onError(((HttpBrokenContent) 
httpContent).getException());
-                    }
-                });
+            
+            if (isLast) {
+                checkHttpTrailer(httpContent);
             }
             
-            return false;
+            invokeHandlerOnProperThread(localHandler,
+                    invokeDataAvailable, isLast, asyncContext);
+            
+        } else { // broken content
+            final ReadHandler localHandler = handler;
+            handler = null;
+            invokeErrorHandlerOnProperThread(localHandler,
+                                             ((HttpBrokenContent) 
httpContent).getException());
         }
+        
+        return false;
     }
 
 
@@ -950,9 +1014,11 @@ public class InputBuffer {
         if (localHandler != null) {
             handler = null;
             if (connection.isOpen()) {
-                localHandler.onError(new CancellationException());
+                invokeErrorHandlerOnProperThread(localHandler,
+                                                 new 
CancellationException());
             } else {
-                localHandler.onError(new EOFException());
+                invokeErrorHandlerOnProperThread(localHandler,
+                                                 new EOFException());
             }
         }
     }
@@ -970,47 +1036,72 @@ public class InputBuffer {
     
     // --------------------------------------------------------- Private 
Methods
 
-
-    private void executeReadHandler(final Callable<Throwable> callable) 
throws IOException {
-        final ExecutorService es =
-                connection.getTransport().getWorkerThreadPool();
-        Throwable result = null;
-        if (Threads.isService() && es != null) {
-            // Invoke ReadHandler callback on worker thread if available...
-            final Future<Throwable> f = es.submit(callable);
-            try {
-                // We have to wait for the result in order to
-                // finish processing of the current append
-                // call...
-                result = f.get();
-            } catch (Exception e) {
-                throw Exceptions.makeIOException(e);
-            }
+    private ExecutorService getThreadPool() {
+        if (!Threads.isService()) {
+            return null;
+        }
+        final ExecutorService es = 
connection.getTransport().getWorkerThreadPool();
+        if (es != null && !es.isShutdown()) {
+            return es;
         } else {
-            try {
-                result = callable.call();
-            } catch (Exception ignored) {
-                // won't happen;
-            }
+            return null;
         }
-        if (result != null) {
-            throw Exceptions.makeIOException(result);
+    }
+
+    private void invokeErrorHandlerOnProperThread(final ReadHandler 
localHandler,
+                                                  final Throwable error) {
+        if (!closed && localHandler != null) {
+            final ExecutorService es = getThreadPool();
+            if (es != null) {
+                es.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        localHandler.onError(error);
+                    }
+                });
+            } else {
+                localHandler.onError(error);
+            }
         }
     }
 
-    private void executeReadHandler(final Runnable runnable)
+    private void invokeHandlerOnProperThread(final ReadHandler localHandler,
+                                             final boolean 
invokeDataAvailable,
+                                             final boolean isLast,
+                                             final FilterChainContext 
asyncContext)
     throws IOException {
-        final ExecutorService es =
-                connection.getTransport().getWorkerThreadPool();
+        final ExecutorService es = getThreadPool();
 
-        if (Threads.isService() && es != null) {
-            try {
-                es.submit(runnable).get();
-            } catch (Exception e) {
-                throw Exceptions.makeIOException(e);
+        if (es != null) {
+            if (invokeDataAvailable) {
+                asyncContext.getInternalContext().setManualIOEventControl();
             }
+            es.submit(new Runnable() {
+
+                @Override
+                public void run() {
+                    invokeHandler(localHandler, invokeDataAvailable, isLast);
+                }
+            });
         } else {
-            runnable.run();
+            invokeHandler(localHandler, invokeDataAvailable,
+                          isLast);
+        }
+    }
+
+    private void invokeHandler(final ReadHandler localHandler,
+                               final boolean invokeDataAvailable,
+                               final boolean isLast) {
+        try {
+            if (invokeDataAvailable) {
+                localHandler.onDataAvailable();
+            }
+
+            if (isLast) {
+                finishedInTheCurrentThread(localHandler);
+            }
+        } catch (Throwable t) {
+            localHandler.onError(t);
         }
     }
 
@@ -1035,7 +1126,7 @@ public class InputBuffer {
             
             final ReadResult rr = ctx.read();
             final HttpContent c = (HttpContent) rr.getMessage();
-
+            
             final boolean isLast = c.isLast();
             // Check if HttpContent is chunked message trailer w/ headers
             checkHttpTrailer(c);
@@ -1052,7 +1143,7 @@ public class InputBuffer {
             updateInputContentBuffer(b);
             rr.recycle();
             c.recycle();
-
+            
             if (isLast) {
                 finished();
                 break;
@@ -1084,15 +1175,8 @@ public class InputBuffer {
 
         int read = 0;
         
-        // 1) Check pre-decoded singleCharBuf
-        if (dst != singleCharBuf && singleCharBuf.hasRemaining()) {
-            dst.put(singleCharBuf.get());
-            read = 1;
-        }
-        
-        // 2) Decode available byte[] -> char[]
         if (inputContentBuffer.hasRemaining()) {
-            read += fillAvailableChars(requestedLen - read, dst);
+            read = fillAvailableChars(requestedLen, dst);
         }
         
         if (read >= requestedLen) {
@@ -1106,7 +1190,6 @@ public class InputBuffer {
             return read > 0 ? read : -1;
         }
         
-        // 4) Try to read more data (we may block)
         CharsetDecoder decoderLocal = getDecoder();
 
         boolean isNeedMoreInput = false; // true, if content in composite 
buffer is not enough to produce even 1 char
@@ -1300,4 +1383,14 @@ public class InputBuffer {
             }
         }
     }    
+
+    private static void log(final String message, Object... params) {
+        final String preparedMsg = String.format(message, params);
+
+        if (LOGGER.isLoggable(Level.FINEST)) {
+            LOGGER.log(Level.FINEST, preparedMsg, new Exception("Logged 
at"));
+        } else {
+            LOGGER.log(LOGGER_LEVEL, preparedMsg);
+        }
+    }
 }

--- 
a/modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
+++ 
b/modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
@@ -173,7 +173,8 @@ public class InputBuffer {
     /**
      * {@link CharBuffer} for converting a single character at a time.
      */
-    private final CharBuffer singleCharBuf = CharBuffer.allocate(1);
+    private final CharBuffer singleCharBuf =
+            (CharBuffer) CharBuffer.allocate(1).position(1); // create 
CharBuffer w/ 0 chars remaining
 
     /**
      * Used to estimate how many characters can be produced from a variable
@@ -250,6 +251,8 @@ public class InputBuffer {
         inputContentBuffer.tryDispose();
         inputContentBuffer = null;
 
+        singleCharBuf.position(singleCharBuf.limit());
+        
         connection = null;
         decoder = null;
         ctx = null;
@@ -512,16 +515,15 @@ public class InputBuffer {
             throw new IllegalStateException();
         }
 
-        singleCharBuf.position(0);
-        int read = read(singleCharBuf);
-        if (read == -1) {
-            singleCharBuf.limit(1); // singleCharBuf.clear();
-            return -1;
+        if (!singleCharBuf.hasRemaining()) {
+            singleCharBuf.clear();
+            int read = read(singleCharBuf);
+            if (read == -1) {
+                return -1;
+            }
         }
-        final char c = singleCharBuf.get(0);
 
-        singleCharBuf.position(0); // singleCharBuf.clear();
-        return c;
+        return singleCharBuf.get();
 
     }
 
@@ -599,7 +601,19 @@ public class InputBuffer {
 
     public int availableChar() {
 
-        return ((int) (inputContentBuffer.remaining() * 
averageCharsPerByte));
+        if (!singleCharBuf.hasRemaining()) {
+            // fill the singleCharBuf to make sure we have at least one char
+            singleCharBuf.clear();
+            if (fillAvailableChars(1, singleCharBuf) == 0) {
+                singleCharBuf.position(singleCharBuf.limit());
+                return 0;
+            }
+            
+            singleCharBuf.flip();
+        }
+        
+        // we have 1 char pre-decoded + estimation for the rest 
byte[]->char[] count.
+        return 1 + ((int) (inputContentBuffer.remaining() * 
averageCharsPerByte));
 
     }
 
@@ -625,7 +639,6 @@ public class InputBuffer {
 
     }
 
-
     /**
      * <p>
      * Only supported with binary data.
@@ -1175,8 +1188,15 @@ public class InputBuffer {
 
         int read = 0;
         
+        // 1) Check pre-decoded singleCharBuf
+        if (dst != singleCharBuf && singleCharBuf.hasRemaining()) {
+            dst.put(singleCharBuf.get());
+            read = 1;
+        }
+        
+        // 2) Decode available byte[] -> char[]
         if (inputContentBuffer.hasRemaining()) {
-            read = fillAvailableChars(requestedLen, dst);
+            read += fillAvailableChars(requestedLen - read, dst);
         }
         
         if (read >= requestedLen) {
@@ -1190,6 +1210,7 @@ public class InputBuffer {
             return read > 0 ? read : -1;
         }
         
+        // 4) Try to read more data (we may block)
         CharsetDecoder decoderLocal = getDecoder();
 
         boolean isNeedMoreInput = false; // true, if content in composite 
buffer is not enough to produce even 1 char

--- a/modules/grizzly/src/main/java/org/glassfish/grizzly/Context.java
+++ b/modules/grizzly/src/main/java/org/glassfish/grizzly/Context.java
@@ -122,7 +122,6 @@ public class Context implements AttributeStorage, 
Cacheable {
      * Notify Context its processing will be suspended in the current thread.
      */
     public void suspend() {
-        wasSuspended = true;
         final IOEventProcessingHandler processingHandlerLocal = 
this.processingHandler;
         if (processingHandlerLocal != null) {
             try {
@@ -131,6 +130,8 @@ public class Context implements AttributeStorage, 
Cacheable {
                 throw new IllegalStateException(e);
             }
         }
+        
+        wasSuspended = true;
     }
     
     /**
@@ -166,7 +167,6 @@ public class Context implements AttributeStorage, 
Cacheable {
      * explicitly called.
      */
     public void setManualIOEventControl() {
-        isManualIOEventControl = true;
         final IOEventProcessingHandler processingHandlerLocal = 
this.processingHandler;
         if (processingHandlerLocal != null) {
             try {
@@ -175,6 +175,8 @@ public class Context implements AttributeStorage, 
Cacheable {
                 throw new IllegalStateException(e);
             }
         }
+        
+        isManualIOEventControl = true;
     }
 
     /**--- 
a/modules/grizzly/src/main/java/org/glassfish/grizzly/strategies/SameThreadIOStrategy.java
+++ 
b/modules/grizzly/src/main/java/org/glassfish/grizzly/strategies/SameThreadIOStrategy.java
@@ -144,7 +144,7 @@ public final class SameThreadIOStrategy extends 
AbstractIOStrategy {
         @Override
         public void onContextSuspend(final Context context) throws 
IOException {
             // check manual io event control, to not disable ioevent twice
-            if (!context.isManualIOEventControl()) {
+            if (!context.wasSuspended() && 
!context.isManualIOEventControl()) {
                 disableIOEvent(context);
             }
         }
@@ -153,7 +153,7 @@ public final class SameThreadIOStrategy extends 
AbstractIOStrategy {
         public void onContextManualIOEventControl(final Context context)
                 throws IOException {
             // check suspended mode, to not disable ioevent twice
-            if (!context.wasSuspended()) {
+            if (!context.wasSuspended() && 
!context.isManualIOEventControl()) {
                 disableIOEvent(context);
             }
         }

--- 
a/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpServerFilter.java
+++ 
b/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpServerFilter.java
@@ -219,15 +219,20 @@ public class HttpServerFilter extends BaseFilter
             } else {
                 // We're working with suspended HTTP request
                 try {
-                    if (!handlerRequest.getInputBuffer().append(httpContent, 
ctx)) {
+                    ctx.suspend();
+                    final NextAction action = ctx.getSuspendAction();
+                    
+                    if 
(!handlerRequest.getInputBuffer().append(httpContent)) {
                         // we don't want this thread/context to reset
                         // OP_READ on Connection
 
                         // we have enough data? - terminate filter chain 
execution
-                        final NextAction action = ctx.getSuspendAction();
                         ctx.completeAndRecycle();
-                        return action;
+                    } else {
+                        ctx.resume(ctx.getStopAction());
                     }
+                    
+                    return action;
                 } finally {
                     httpContent.recycle();
                 }
@@ -245,8 +250,6 @@ public class HttpServerFilter extends BaseFilter
                         request, response);
             }
         }
-
-        return ctx.getStopAction();
     }
 
 --- 
a/modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
+++ 
b/modules/http/src/main/java/org/glassfish/grizzly/http/io/InputBuffer.java
@@ -917,16 +917,13 @@ public class InputBuffer {
      * {@link Buffer}.
      *
      * @param httpContent the {@link HttpContent} to append
-     * @param asyncContext The FilterChainContext that initiated the
-     *                     append call.
      *
      * @return <code>true</code> if {@link ReadHandler}
      *  callback was invoked, otherwise returns <code>false</code>.
      *
      * @throws IOException if an error occurs appending the {@link Buffer}
      */
-    public boolean append(final HttpContent httpContent,
-                          final FilterChainContext asyncContext) throws 
IOException {
+    public boolean append(final HttpContent httpContent) throws IOException {
         
         // Stop waiting for data asynchronously and enable it again
         // only if we have a handler registered, which requirement
@@ -976,7 +973,7 @@ public class InputBuffer {
             }
             
             invokeHandlerOnProperThread(localHandler,
-                    invokeDataAvailable, isLast, asyncContext);
+                    invokeDataAvailable, isLast);
             
         } else { // broken content
             final ReadHandler localHandler = handler;
@@ -1080,15 +1077,11 @@ public class InputBuffer {
 
     private void invokeHandlerOnProperThread(final ReadHandler localHandler,
                                              final boolean 
invokeDataAvailable,
-                                             final boolean isLast,
-                                             final FilterChainContext 
asyncContext)
+                                             final boolean isLast)
     throws IOException {
         final ExecutorService es = getThreadPool();
 
         if (es != null) {
-            if (invokeDataAvailable) {
-                asyncContext.getInternalContext().setManualIOEventControl();
-            }
             es.submit(new Runnable() {
 
                 @Override
@@ -1097,8 +1090,7 @@ public class InputBuffer {
                 }
             });
         } else {
-            invokeHandler(localHandler, invokeDataAvailable,
-                          isLast);
+            invokeHandler(localHandler, invokeDataAvailable, isLast);
         }
     }
 

--- 
a/modules/grizzly/src/main/java/org/glassfish/grizzly/strategies/SameThreadIOStrategy.java
+++ 
b/modules/grizzly/src/main/java/org/glassfish/grizzly/strategies/SameThreadIOStrategy.java
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright (c) 2009-2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common Development

--- 
a/modules/grizzly/src/main/java/org/glassfish/grizzly/NIOTransportBuilder.java
+++ 
b/modules/grizzly/src/main/java/org/glassfish/grizzly/NIOTransportBuilder.java
@@ -116,8 +116,8 @@ public abstract class NIOTransportBuilder<T extends 
NIOTransportBuilder> {
     /**
      * @return the {@link ThreadPoolConfig} that will be used to construct 
the
      *  {@link java.util.concurrent.ExecutorService} for 
<code>IOStrategies</code>
-     *  that require worker threads.  Depending on the {@link IOStrategy} 
being
-     *  used, this may return <code>null</code>.
+     *  that require worker threads.  This method will return 
<code>null</code>
+     *  if a {@link ThreadPoolConfig} had not been previously set.
      */
     public ThreadPoolConfig getWorkerThreadPoolConfig() {
         return transport.getWorkerThreadPoolConfig();

--- 
a/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpHandler.java
+++ 
b/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/HttpHandler.java
@@ -44,12 +44,14 @@ import java.io.CharConversionException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
-import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executor;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import org.glassfish.grizzly.Connection;
 import org.glassfish.grizzly.Grizzly;
+import org.glassfish.grizzly.ReadHandler;
+import org.glassfish.grizzly.WriteHandler;
 import org.glassfish.grizzly.filterchain.FilterChainContext;
 import org.glassfish.grizzly.http.HttpRequestPacket;
 import org.glassfish.grizzly.http.io.OutputBuffer;
@@ -59,7 +61,6 @@ import org.glassfish.grizzly.http.server.util.MappingData;
 import org.glassfish.grizzly.http.util.Header;
 import org.glassfish.grizzly.http.util.HttpStatus;
 import org.glassfish.grizzly.http.util.RequestURIRef;
-import org.glassfish.grizzly.threadpool.Threads;
 import org.glassfish.grizzly.utils.Charsets;
 
 /**
@@ -78,6 +79,9 @@ public abstract class HttpHandler {
     
     private final static Logger LOGGER = Grizzly.logger(HttpHandler.class);
     
+    private final static RequestExecutorProvider 
DEFAULT_REQUEST_EXECUTOR_PROVIDER =
+            new RequestExecutorProvider.WorkerThreadProvider();
+    
     /**
      * Allow request that uses encoded slash.
      */
@@ -102,7 +106,7 @@ public abstract class HttpHandler {
      * HttpHandler name
      */
     private final String name;
-
+    
     /**
      * Create <tt>HttpHandler</tt>.
      */
@@ -137,6 +141,7 @@ public abstract class HttpHandler {
      *  from the invocation of {@link #service(Request, Response)}
      */
     boolean doHandle(final Request request, final Response response) throws 
Exception {
+        request.setRequestExecutorProvider(getRequestExecutorProvider());
 
         if (request.requiresAcknowledgement()) {
             if (!sendAcknowledgment(request, response)) {
@@ -178,7 +183,7 @@ public abstract class HttpHandler {
     private boolean runService(final Request request, final Response 
response)
             throws Exception {
         
-        final ExecutorService threadPool = getThreadPool(request);
+        final Executor threadPool = 
getRequestExecutorProvider().getExecutor(request);
         final HttpServerFilter httpServerFilter = request.getServerFilter();
         final Connection connection = request.getContext().getConnection();
         
@@ -322,6 +327,15 @@ public abstract class HttpHandler {
     }
 
     /**
+     * @return the {@link RequestExecutorProvider} responsible for executing
+     * user's code in {@link 
HttpHandler#service(org.glassfish.grizzly.http.server.Request, 
org.glassfish.grizzly.http.server.Response)}
+     * and notifying {@link ReadHandler}, {@link WriteHandler} registered by 
the user.
+     */
+    public RequestExecutorProvider getRequestExecutorProvider() {
+        return DEFAULT_REQUEST_EXECUTOR_PROVIDER;
+    }
+    
+    /**
      * Customize the error page.
      * @param req The {@link Request} object
      * @param res The {@link Response} object
@@ -402,27 +416,4 @@ public abstract class HttpHandler {
 
     protected void setDispatcherHelper(final DispatcherHelper 
dispatcherHelper) {
     }
-    
-    /**
-     * Returns the <tt>HttpHandler</tt> preferred {@link ExecutorService} to 
process
-     * passed {@link Request}. The <tt>null</tt> return value means process 
in 
-     * current thread.
-     * 
-     * The default implementation returns <tt>null</tt> if current thread is 
not
-     * {@link Transport} service thread ({@link Threads#isService()}). 
Otherwise
-     * returns worker thread pool of the {@link Transport} this {@link 
Request}
-     * belongs to ({@link 
org.glassfish.grizzly.Transport#getWorkerThreadPool()}).
-     * 
-     * @param request the {@link Request} to be processed.
-     * @return the <tt>HttpHandler</tt> preferred {@link ExecutorService} to 
process
-     * passed {@link Request}. The <tt>null</tt> return value means process 
in 
-     * current thread.
-     */
-    protected ExecutorService getThreadPool(final Request request) {
-        if (!Threads.isService()) {
-            return null; // Execute in the current thread
-        }
-        
-        return 
request.getContext().getConnection().getTransport().getWorkerThreadPool();
-    }
 }--- 
a/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/Request.java
+++ 
b/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/Request.java
@@ -76,6 +76,7 @@ import java.util.Random;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
@@ -96,6 +97,7 @@ import org.glassfish.grizzly.http.Protocol;
 import org.glassfish.grizzly.http.io.InputBuffer;
 import org.glassfish.grizzly.http.io.NIOInputStream;
 import org.glassfish.grizzly.http.io.NIOReader;
+import org.glassfish.grizzly.http.server.io.ServerInputBuffer;
 import org.glassfish.grizzly.http.server.util.Globals;
 import org.glassfish.grizzly.http.server.util.MappingData;
 import org.glassfish.grizzly.http.server.util.ParameterMap;
@@ -219,11 +221,13 @@ public class Request {
     /**
      * Scheduled Thread that clean the cache every XX seconds.
      */
-    private final static ScheduledThreadPoolExecutor sessionExpirer =
+    private final static ScheduledThreadPoolExecutor SESSION_EXPIRER =
             new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
         @Override
         public Thread newThread(Runnable r) {
-            return new SchedulerThread(r, "Grizzly-HttpSession-Expirer");
+            final Thread t = new Thread(r, "Grizzly-HttpSession-Expirer");
+            t.setDaemon(true);
+            return t;
         }
     });
 
@@ -231,7 +235,7 @@ public class Request {
      * That code is far from optimal and needs to be rewrite appropriately.
      */
     static {
-        sessionExpirer.scheduleAtFixedRate(new Runnable() {
+        SESSION_EXPIRER.scheduleAtFixedRate(new Runnable() {
 
             @Override
             public void run() {
@@ -263,17 +267,6 @@ public class Request {
         return cachedMappingData;
     }
 
-    /**
-     * Simple daemon thread.
-     */
-    private static class SchedulerThread extends Thread {
-
-        public SchedulerThread(Runnable r, String name) {
-            super(r, name);
-            setDaemon(true);
-        }
-    }
-
 
     // --------------------------------------------------------------------- 
//
 
@@ -339,7 +332,7 @@ public class Request {
     /**
      * The associated input buffer.
      */
-    protected final InputBuffer inputBuffer = new InputBuffer();
+    protected final ServerInputBuffer inputBuffer = new ServerInputBuffer();
 
 
     /**
@@ -443,7 +436,7 @@ public class Request {
     /**
      * The string parser we will use for parsing request lines.
      */
-    private StringParser parser = new StringParser();
+    private StringParser parser;
 
     // START S1AS 4703023
     /**
@@ -466,6 +459,13 @@ public class Request {
     private boolean sendFileAttributeInitialized;
 
     /**
+     * The {@link RequestExecutorProvider} responsible for executing user's 
code
+     * in {@link 
HttpHandler#service(org.glassfish.grizzly.http.server.Request, 
org.glassfish.grizzly.http.server.Response)}
+     * and notifying {@link ReadHandler}, {@link WriteHandler} registered by 
the user
+     */
+     private RequestExecutorProvider requestExecutorProvider;
+    
+    /**
      * The response with which this request is associated.
      */
     protected final Response response;
@@ -487,7 +487,7 @@ public class Request {
         this.request = request;
         this.ctx = ctx;
         this.httpServerFilter = httpServerFilter;
-        inputBuffer.initialize(request, ctx);
+        inputBuffer.initialize(this, ctx);
         
         parameters.setHeaders(request.getHeaders());
         parameters.setQuery(request.getQueryStringDC());
@@ -517,20 +517,40 @@ public class Request {
     }
 
     /**
-     * Get the Coyote request.
+     * @return the Coyote request.
      */
     public HttpRequestPacket getRequest() {
         return this.request;
     }
 
     /**
-     * Return the Response with which this Request is associated.
+     * @return the Response with which this Request is associated.
      */
     public Response getResponse() {
         return response;
     }
 
     /**
+     * @return the {@link Executor} responsible for notifying {@link 
ReadHandler},
+     * {@link WriteHandler} associated with this <tt>Request</tt> processing.
+     */    
+    public Executor getRequestExecutor() {
+        return requestExecutorProvider.getExecutor(this);
+    }
+
+    /**
+     * Sets @return the {@link RequestExecutorProvider} responsible for 
executing
+     * user's code in {@link 
HttpHandler#service(org.glassfish.grizzly.http.server.Request, 
org.glassfish.grizzly.http.server.Response)}
+     * and notifying {@link ReadHandler}, {@link WriteHandler} registered by 
the user.
+     * 
+     * @param requestExecutorProvider {@link RequestExecutorProvider}
+     */
+    protected void setRequestExecutorProvider(
+            final RequestExecutorProvider requestExecutorProvider) {
+        this.requestExecutorProvider = requestExecutorProvider;
+    }
+
+    /**
      * Add the listener, which will be notified, once <tt>Request</tt> 
processing will be finished.
      * @param listener the listener, which will be notified, once 
<tt>Request</tt> processing will be finished.
      */
@@ -609,6 +629,8 @@ public class Request {
         parameterMap.clear();
         parameters.recycle();
 
+        requestExecutorProvider = null;
+        
         afterServicesList.clear();
 
         // Notes holder shouldn't be recycled.
@@ -617,18 +639,8 @@ public class Request {
         if (cachedMappingData != null) {
             cachedMappingData.recycle();
         }
-//        if (System.getSecurityManager() != null) {
-//            if (inputStream != null) {
-//                inputStream.clear();
-//                inputStream = null;
-//            }
-//            if (reader != null) {
-//                reader.clear();
-//                reader = null;
-//            }
-//        }
-        ThreadCache.putToCache(CACHE_IDX, this);
 
+        ThreadCache.putToCache(CACHE_IDX, this);
     }
 
 
@@ -2112,8 +2124,8 @@ public class Request {
         if (white < 0)
             white = value.indexOf('\t');
         if (white >= 0) {
-            StringBuilder sb = new StringBuilder();
             int len = value.length();
+            StringBuilder sb = new StringBuilder(len - 1);
             for (int i = 0; i < len; i++) {
                 char ch = value.charAt(i);
                 if ((ch != ' ') && (ch != '\t'))
@@ -2122,6 +2134,10 @@ public class Request {
             value = sb.toString();
         }
 
+        if (parser == null) {
+            parser = new StringParser();
+        }
+        
         // Process each comma-delimited language specification
         parser.setString(value);        // ASSERT: parser is available to us
         int length = parser.getLength();--- /dev/null
+++ 
b/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/RequestExecutorProvider.java
@@ -0,0 +1,97 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the 
License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or 
GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+package org.glassfish.grizzly.http.server;
+
+import java.util.concurrent.Executor;
+import org.glassfish.grizzly.threadpool.Threads;
+
+/**
+ * An implementation of this interface will be  responsible for executing
+ * user's code in {@link 
HttpHandler#service(org.glassfish.grizzly.http.server.Request, 
org.glassfish.grizzly.http.server.Response)}
+ * and notifying {@link ReadHandler}, {@link WriteHandler} registered by the
+ * user.
+ *
+ * @author Alexey Stashok
+ */
+public interface RequestExecutorProvider {
+
+    /**
+     * Returns the {@link Executor} to execute user's code associated with 
the
+     * {@link Request} processing.
+     * 
+     * @param request {@link Request}
+     * @return the {@link Executor} to execute user's code associated with 
the
+     * {@link Request} processing, or <tt>null</tt> if the {@link Request} 
has
+     * to be executed on the current {@link Thread}
+     */
+    Executor getExecutor(final Request request);
+
+    /**
+     * The {@link RequestExecutorProvider} implementation, which always 
returns
+     * <tt>null</tt> to force the user code to be executed on the current 
{@link Thread}.
+     */
+    public static class SameThreadProvider implements 
RequestExecutorProvider {
+
+        @Override
+        public Executor getExecutor(final Request request) {
+            return null;
+        }
+    }
+
+    /**
+     * The {@link RequestExecutorProvider} implementation, which checks if 
the
+     * current {@link Thread} is a service {@link Thread} (see {@link 
Threads#isService()}).
+     * If the current {@link Thread} is a service {@link Thread} - the
+     * implementation returns a worker thread pool associated with the 
{@link Request},
+     * or, if the current {@link Thread} is not a service {@link Thread} - 
<tt>null</tt>
+     * will be return to force the user code to be executed on the current 
{@link Thread}.
+     */
+    public static class WorkerThreadProvider implements 
RequestExecutorProvider {
+
+        @Override
+        public Executor getExecutor(final Request request) {
+            if (!Threads.isService()) {
+                return null; // Execute in the current thread
+            }
+
+            return 
request.getContext().getConnection().getTransport().getWorkerThreadPool();
+        }
+    }
+}--- /dev/null
+++ 
b/modules/http-server/src/main/java/org/glassfish/grizzly/http/server/io/ServerInputBuffer.java
@@ -0,0 +1,71 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms
[truncated due to length]



[grizzly~git:78187e75] [glassfishv4] + fix the issue #1600

oleksiys 11/01/2013
Terms of Use; Privacy Policy; Copyright ©2013-2015 (revision 20150626.29986a4)
 
 
Close
loading
Please Confirm
Close