You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2007/06/11 22:59:40 UTC

svn commit: r546283 - in /jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol: HttpRequestExecutionHandler.java ThrottlingHttpClientHandler.java ThrottlingHttpServiceHandler.java

Author: olegk
Date: Mon Jun 11 13:59:39 2007
New Revision: 546283

URL: http://svn.apache.org/viewvc?view=rev&rev=546283
Log:
Added an HTTP client handler implementation that allocates fixed size content buffers upon initialization and is capable of throttling the rate of I/O events in order to guarantee those content buffers do not ever get overflown

Added:
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java   (with props)
Modified:
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/HttpRequestExecutionHandler.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpServiceHandler.java

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/HttpRequestExecutionHandler.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/HttpRequestExecutionHandler.java?view=diff&rev=546283&r1=546282&r2=546283
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/HttpRequestExecutionHandler.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/HttpRequestExecutionHandler.java Mon Jun 11 13:59:39 2007
@@ -31,6 +31,8 @@
 
 package org.apache.http.nio.protocol;
 
+import java.io.IOException;
+
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.nio.reactor.ConnectingIOReactor;
@@ -82,6 +84,7 @@
      * @param response the HTTP response to be processed
      * @param context the actual HTTP context
      */
-    void handleResponse(HttpResponse response, HttpContext context);
+    void handleResponse(HttpResponse response, HttpContext context) 
+        throws IOException;
     
 }

Added: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java?view=auto&rev=546283
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java (added)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java Mon Jun 11 13:59:39 2007
@@ -0,0 +1,617 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.nio.protocol;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpConnection;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.NHttpClientHandler;
+import org.apache.http.nio.entity.ContentBufferEntity;
+import org.apache.http.nio.entity.ContentOutputStream;
+import org.apache.http.nio.params.HttpNIOParams;
+import org.apache.http.nio.util.ByteBufferAllocator;
+import org.apache.http.nio.util.ContentInputBuffer;
+import org.apache.http.nio.util.ContentOutputBuffer;
+import org.apache.http.nio.util.DirectByteBufferAllocator;
+import org.apache.http.nio.util.SharedInputBuffer;
+import org.apache.http.nio.util.SharedOutputBuffer;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpParamsLinker;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpExecutionContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.util.concurrent.Executor;
+
+/**
+ * HTTP client handler implementation that allocates content buffers of limited 
+ * size upon initialization and is capable of controlling the frequency of I/O 
+ * events in order to guarantee those content buffers do not ever get overflown. 
+ * This helps ensure near constant memory footprint of HTTP connections and to 
+ * avoid the 'out of memory' condition while streaming out response content.
+ * 
+ * <p>The client handler will delegate the tasks of sending entity enclosing 
+ * HTTP requests and processing of HTTP responses to an {@link Executor}, 
+ * which is expected to perform those tasks using dedicated worker threads in 
+ * order to avoid blocking the I/O thread.</p>
+ * 
+ * @see HttpNIOParams#CONTENT_BUFFER_SIZE
+ * 
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ */
+public class ThrottlingHttpClientHandler implements NHttpClientHandler {
+
+    private static final String CONN_STATE = "http.nio.conn-state";
+    
+    private final HttpProcessor httpProcessor;
+    private final ConnectionReuseStrategy connStrategy;
+    private final ByteBufferAllocator allocator;
+    private final Executor executor;
+    private final HttpParams params;
+    
+    private HttpRequestExecutionHandler execHandler;
+    private EventListener eventListener;
+    
+    public ThrottlingHttpClientHandler(
+            final HttpProcessor httpProcessor, 
+            final HttpRequestExecutionHandler execHandler,
+            final ConnectionReuseStrategy connStrategy,
+            final ByteBufferAllocator allocator,
+            final Executor executor,
+            final HttpParams params) {
+        super();
+        if (httpProcessor == null) {
+            throw new IllegalArgumentException("HTTP processor may not be null.");
+        }
+        if (execHandler == null) {
+            throw new IllegalArgumentException("HTTP request execution handler may not be null.");
+        }
+        if (connStrategy == null) {
+            throw new IllegalArgumentException("Connection reuse strategy may not be null");
+        }
+        if (allocator == null) {
+            throw new IllegalArgumentException("ByteBuffer allocator may not be null");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Executor may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.httpProcessor = httpProcessor;
+        this.execHandler = execHandler;
+        this.connStrategy = connStrategy;
+        this.allocator = allocator;
+        this.executor = executor;
+        this.params = params;
+    }
+    
+    public ThrottlingHttpClientHandler(
+            final HttpProcessor httpProcessor, 
+            final HttpRequestExecutionHandler execHandler,
+            final ConnectionReuseStrategy connStrategy,
+            final Executor executor,
+            final HttpParams params) {
+        this(httpProcessor, execHandler, connStrategy, 
+                new DirectByteBufferAllocator(), executor, params);
+    }
+    
+    public void setEventListener(final EventListener eventListener) {
+        this.eventListener = eventListener;
+    }
+
+    private void shutdownConnection(final HttpConnection conn) {
+        try {
+            conn.shutdown();
+        } catch (IOException ignore) {
+        }
+    }
+    
+    public void connected(final NHttpClientConnection conn, final Object attachment) {
+        HttpContext context = conn.getContext();
+
+        // Populate the context with a default HTTP host based on the 
+        // inet address of the target host
+        if (conn instanceof HttpInetConnection) {
+            InetAddress address = ((HttpInetConnection) conn).getRemoteAddress();
+            int port = ((HttpInetConnection) conn).getRemotePort();
+            if (address != null) {
+                HttpHost host = new HttpHost(address.getHostName(), port);
+                context.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, host);
+            }
+        }
+        
+        initialize(conn, attachment);
+        
+        int bufsize = this.params.getIntParameter(
+                HttpNIOParams.CONTENT_BUFFER_SIZE, 20480);
+        ClientConnState connState = new ClientConnState(bufsize, conn, this.allocator); 
+        context.setAttribute(CONN_STATE, connState);
+
+        if (this.eventListener != null) {
+            this.eventListener.connectionOpen(conn);
+        }
+        
+        requestReady(conn);        
+    }
+
+    public void closed(final NHttpClientConnection conn) {
+        if (this.eventListener != null) {
+            this.eventListener.connectionClosed(conn);
+        }
+    }
+
+    public void exception(final NHttpClientConnection conn, final HttpException ex) {
+        shutdownConnection(conn);
+        if (this.eventListener != null) {
+            this.eventListener.fatalProtocolException(ex, conn);
+        }
+    }
+
+    public void exception(final NHttpClientConnection conn, final IOException ex) {
+        shutdownConnection(conn);
+        if (this.eventListener != null) {
+            this.eventListener.fatalIOException(ex, conn);
+        }
+    }
+    
+    public void requestReady(final NHttpClientConnection conn) {
+        HttpContext context = conn.getContext();
+
+        final ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE);
+        
+        try {
+
+            if (connState.getInputState() != ClientConnState.READY) {
+                return;
+            }
+
+            final HttpRequest request = this.execHandler.submitRequest(context);
+            if (request == null) {
+                return;
+            }
+            
+            HttpParamsLinker.link(request, this.params);
+            
+            context.setAttribute(HttpExecutionContext.HTTP_REQUEST, request);
+            this.httpProcessor.process(request, context);
+            connState.setRequest(request);
+            conn.submitRequest(request);
+            connState.setOutputState(ClientConnState.REQUEST_SENT);
+            
+            if (request instanceof HttpEntityEnclosingRequest) {
+                if (((HttpEntityEnclosingRequest) request).expectContinue()) {
+                    int timeout = conn.getSocketTimeout();
+                    connState.setTimeout(timeout);
+                    timeout = this.params.getIntParameter(
+                            HttpProtocolParams.WAIT_FOR_CONTINUE, 3000);
+                    conn.setSocketTimeout(timeout);
+                    connState.setOutputState(ClientConnState.EXPECT_CONTINUE);
+                } else {
+                    sendRequestBody(
+                            (HttpEntityEnclosingRequest) request,
+                            connState.getOutbuffer(),
+                            conn);
+                }
+            }
+            
+        } catch (IOException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalIOException(ex, conn);
+            }
+        } catch (HttpException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalProtocolException(ex, conn);
+            }
+        }
+    }
+
+    public void outputReady(final NHttpClientConnection conn, final ContentEncoder encoder) {
+        HttpContext context = conn.getContext();
+
+        ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE);
+        ContentOutputBuffer buffer = connState.getOutbuffer();
+
+        try {
+
+            if (connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) {
+                conn.suspendOutput();
+                return;
+            }
+            buffer.produceContent(encoder);
+            if (encoder.isCompleted()) {
+                connState.setInputState(ClientConnState.REQUEST_BODY_DONE);
+            } else {
+                connState.setInputState(ClientConnState.REQUEST_BODY_STREAM);
+            }
+            
+        } catch (IOException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalIOException(ex, conn);
+            }
+        }
+    }
+
+    public void responseReceived(final NHttpClientConnection conn) {
+        HttpContext context = conn.getContext();
+        ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE);
+
+        try {
+
+            HttpResponse response = conn.getHttpResponse();
+            HttpParamsLinker.link(response, this.params);
+            
+            HttpRequest request = connState.getRequest();
+            
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode < HttpStatus.SC_OK) {
+                // 1xx intermediate response
+                if (statusCode == HttpStatus.SC_CONTINUE 
+                        && connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) {
+                    connState.setOutputState(ClientConnState.REQUEST_SENT);
+                    continueRequest(conn, connState);
+                }
+                return;
+            } else {
+                connState.setResponse(response);
+                connState.setInputState(ClientConnState.RESPONSE_RECEIVED);
+                
+                if (connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) {
+                    cancelRequest(conn, connState);
+                }
+            }
+            if (!canResponseHaveBody(request, response)) {
+                conn.resetInput();
+                response.setEntity(null);
+                
+                processResponse(
+                        response, 
+                        connState.getInbuffer(), 
+                        conn);
+                
+                if (!this.connStrategy.keepAlive(response, context)) {
+                    conn.close();
+                } else {
+                    // Ready for another request
+                    connState.resetInput();
+                    connState.resetOutput();
+                    conn.requestOutput();
+                }
+            }
+            
+        } catch (IOException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalIOException(ex, conn);
+            }
+        } catch (HttpException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalProtocolException(ex, conn);
+            }
+        }
+    }
+
+    public void inputReady(final NHttpClientConnection conn, final ContentDecoder decoder) {
+        HttpContext context = conn.getContext();
+
+        ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE);
+        try {
+
+            HttpResponse response = connState.getResponse();
+            ContentInputBuffer buffer = connState.getInbuffer();
+
+            buffer.consumeContent(decoder);
+            if (decoder.isCompleted()) {
+                connState.setInputState(ClientConnState.RESPONSE_BODY_DONE);
+                if (!this.connStrategy.keepAlive(response, context)) {
+                    conn.close();
+                } else {
+                    // Ready for another request
+                    connState.resetInput();
+                    connState.resetOutput();
+                    conn.requestOutput();
+                }
+            } else {
+                connState.setInputState(ClientConnState.RESPONSE_BODY_STREAM);
+            }
+            
+        } catch (IOException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalIOException(ex, conn);
+            }
+        }
+    }
+
+    public void timeout(final NHttpClientConnection conn) {
+        HttpContext context = conn.getContext();
+        ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE);
+
+        try {
+            
+            if (connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) {
+                connState.setOutputState(ClientConnState.REQUEST_SENT);
+                continueRequest(conn, connState);
+            }
+            
+        } catch (IOException ex) {
+            shutdownConnection(conn);
+            if (this.eventListener != null) {
+                this.eventListener.fatalIOException(ex, conn);
+            }
+        }
+        
+        shutdownConnection(conn);
+        if (this.eventListener != null) {
+            this.eventListener.connectionTimeout(conn);
+        }
+    }
+    
+    private void initialize(
+            final NHttpClientConnection conn,
+            final Object attachment) {
+        HttpContext context = conn.getContext();
+
+        context.setAttribute(HttpExecutionContext.HTTP_CONNECTION, conn);
+        this.execHandler.initalizeContext(context, attachment);
+    }
+    
+    private void continueRequest(
+            final NHttpClientConnection conn, 
+            final ClientConnState connState) throws IOException {
+
+        HttpRequest request = connState.getRequest();
+
+        int timeout = connState.getTimeout();
+        conn.setSocketTimeout(timeout);
+
+        sendRequestBody(
+                (HttpEntityEnclosingRequest) request,
+                connState.getOutbuffer(),
+                conn);
+    }
+    
+    private void cancelRequest(
+            final NHttpClientConnection conn, 
+            final ClientConnState connState) throws IOException {
+
+        int timeout = connState.getTimeout();
+        conn.setSocketTimeout(timeout);
+
+        conn.resetOutput();
+        connState.resetOutput();
+    }
+    
+    private boolean canResponseHaveBody(
+            final HttpRequest request, final HttpResponse response) {
+
+        if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
+            return false;
+        }
+        
+        int status = response.getStatusLine().getStatusCode(); 
+        return status >= HttpStatus.SC_OK 
+            && status != HttpStatus.SC_NO_CONTENT 
+            && status != HttpStatus.SC_NOT_MODIFIED
+            && status != HttpStatus.SC_RESET_CONTENT; 
+    }
+    
+    private void sendRequestBody(
+            final HttpEntityEnclosingRequest request,
+            final ContentOutputBuffer buffer,
+            final NHttpClientConnection conn) throws IOException {
+        HttpEntity entity = request.getEntity();
+        if (entity != null) {
+            
+            this.executor.execute(new Runnable() {
+                
+                public void run() {
+                    try {
+
+                        HttpEntity entity = request.getEntity();
+                        OutputStream outstream = new ContentOutputStream(buffer);
+                        entity.writeTo(outstream);
+                        outstream.flush();
+                        outstream.close();
+                        
+                    } catch (IOException ex) {
+                        shutdownConnection(conn);
+                        if (eventListener != null) {
+                            eventListener.fatalIOException(ex, conn);
+                        }
+                    }
+                }
+                
+            });
+        }
+    }
+
+    private void processResponse(
+            final HttpResponse response,
+            final ContentInputBuffer buffer,
+            final NHttpClientConnection conn) throws IOException, HttpException {
+
+        final HttpContext context = conn.getContext();
+        
+        if (response.getEntity() != null) {
+            response.setEntity(new ContentBufferEntity(
+                    response.getEntity(), 
+                    buffer));
+        }
+        
+        context.setAttribute(HttpExecutionContext.HTTP_RESPONSE, response);
+        
+        this.httpProcessor.process(response, context);
+        
+        this.executor.execute(new Runnable() {
+            
+            public void run() {
+                try {
+
+                    execHandler.handleResponse(response, context);
+                    
+                } catch (IOException ex) {
+                    shutdownConnection(conn);
+                    if (eventListener != null) {
+                        eventListener.fatalIOException(ex, conn);
+                    }
+                }
+            }
+            
+        });
+        
+    }
+    
+    static class ClientConnState {
+        
+        public static final int SHUTDOWN                   = -1;
+        public static final int READY                      = 0;
+        public static final int REQUEST_SENT               = 1;
+        public static final int EXPECT_CONTINUE            = 2;
+        public static final int REQUEST_BODY_STREAM        = 4;
+        public static final int REQUEST_BODY_DONE          = 8;
+        public static final int RESPONSE_RECEIVED          = 16;
+        public static final int RESPONSE_BODY_STREAM       = 32;
+        public static final int RESPONSE_BODY_DONE         = 64;
+        
+        private final SharedInputBuffer inbuffer; 
+        private final SharedOutputBuffer outbuffer;
+
+        private int inputState;
+        private int outputState;
+        
+        private HttpRequest request;
+        private HttpResponse response;
+
+        private int timeout;
+        
+        public ClientConnState(
+                int bufsize, 
+                final IOControl ioControl, 
+                final ByteBufferAllocator allocator) {
+            super();
+            this.inbuffer = new SharedInputBuffer(bufsize, ioControl, allocator);
+            this.outbuffer = new SharedOutputBuffer(bufsize, ioControl, allocator);
+            this.inputState = READY;
+            this.outputState = READY;
+        }
+
+        public ContentInputBuffer getInbuffer() {
+            return this.inbuffer;
+        }
+
+        public ContentOutputBuffer getOutbuffer() {
+            return this.outbuffer;
+        }
+        
+        public int getInputState() {
+            return this.inputState;
+        }
+
+        public void setInputState(int inputState) {
+            this.inputState = inputState;
+        }
+
+        public int getOutputState() {
+            return this.outputState;
+        }
+
+        public void setOutputState(int outputState) {
+            this.outputState = outputState;
+        }
+
+        public HttpRequest getRequest() {
+            return this.request;
+        }
+
+        public void setRequest(final HttpRequest request) {
+            this.request = request;
+        }
+
+        public HttpResponse getResponse() {
+            return this.response;
+        }
+
+        public void setResponse(final HttpResponse response) {
+            this.response = response;
+        }
+
+        public int getTimeout() {
+            return this.timeout;
+        }
+
+        public void setTimeout(int timeout) {
+            this.timeout = timeout;
+        }
+            
+        public void shutdown() {
+            this.inbuffer.shutdown();
+            this.outbuffer.shutdown();
+            this.inputState = SHUTDOWN;
+            this.outputState = SHUTDOWN;
+        }
+
+        public void resetInput() {
+            this.inbuffer.reset();
+            this.request = null;
+            this.inputState = READY;
+        }
+        
+        public void resetOutput() {
+            this.outbuffer.reset();
+            this.response = null;
+            this.outputState = READY;
+        }
+        
+    }
+    
+}
\ No newline at end of file

Propchange: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpClientHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpServiceHandler.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpServiceHandler.java?view=diff&rev=546283&r1=546282&r2=546283
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpServiceHandler.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/ThrottlingHttpServiceHandler.java Mon Jun 11 13:59:39 2007
@@ -298,11 +298,12 @@
         HttpContext context = conn.getContext();
 
         ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
-        ContentInputBuffer buffer = connState.getInbuffer();
-
+        
         try {
 
             synchronized (connState) {
+                ContentInputBuffer buffer = connState.getInbuffer();
+
                 buffer.consumeContent(decoder);
                 if (decoder.isCompleted()) {
                     connState.setInputState(ServerConnState.REQUEST_BODY_DONE);
@@ -329,14 +330,13 @@
 
         try {
         
-            HttpResponse response = connState.getResponse();
-            if (connState.getOutputState() == ServerConnState.READY 
-                    && response != null 
-                    && !conn.isResponseSubmitted()) {
-
-                conn.submitResponse(response);
+            synchronized (connState) {
+                HttpResponse response = connState.getResponse();
+                if (connState.getOutputState() == ServerConnState.READY 
+                        && response != null 
+                        && !conn.isResponseSubmitted()) {
 
-                synchronized (connState) {
+                    conn.submitResponse(response);
                     int statusCode = response.getStatusLine().getStatusCode();
                     HttpEntity entity = response.getEntity();
 
@@ -353,9 +353,9 @@
                     } else {
                         connState.setOutputState(ServerConnState.RESPONSE_SENT);
                     }
-                    
-                    connState.notifyAll();
                 }
+                
+                connState.notifyAll();
             }
 
         } catch (IOException ex) {
@@ -375,12 +375,12 @@
         HttpContext context = conn.getContext();
 
         ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE);
-        ContentOutputBuffer buffer = connState.getOutbuffer();
         
         try {
 
             synchronized (connState) {
                 HttpResponse response = connState.getResponse();
+                ContentOutputBuffer buffer = connState.getOutbuffer();
                 
                 buffer.produceContent(encoder);
                 if (encoder.isCompleted()) {