HttpComponents HttpCore
  1. HttpComponents HttpCore
  2. HTTPCORE-270

IllegalStateException in AbstractSessionOutputBuffer

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 4.2-alpha1
    • Fix Version/s: 4.2-alpha2
    • Component/s: HttpCore
    • Labels:
      None

      Description

      I am using httpclient-4.1.2 & httpcore-4.2-alpha1. In making an DefaultHttpClient.execute() call I get the following exception:
      java.lang.IllegalStateException: Current state = RESET, new state = FLUSHED
      at java.nio.charset.CharsetEncoder.throwIllegalStateException(CharsetEncoder.java:951)
      at java.nio.charset.CharsetEncoder.flush(CharsetEncoder.java:640)
      at org.apache.http.impl.io.AbstractSessionOutputBuffer.writeEncoded(AbstractSessionOutputBuffer.java:264)
      at org.apache.http.impl.io.AbstractSessionOutputBuffer.writeLine(AbstractSessionOutputBuffer.java:247)
      at org.apache.http.impl.conn.LoggingSessionOutputBuffer.writeLine(LoggingSessionOutputBuffer.java:99)
      at org.apache.http.impl.io.AbstractMessageWriter.write(AbstractMessageWriter.java:97)
      at org.apache.http.impl.AbstractHttpClientConnection.sendRequestHeader(AbstractHttpClientConnection.java:253)

      On line 264 of AbstractSessionOutputBuffer.java, encoder.flush() is being called. My thought (I have not completely debugged) is that in this instance, cbuf.hasRemaining() is false, and therefor the following calls are being made:

      encoder.reset()
      encoder.flush()

      This is resulting in the above exception.

      I believe the following small patch will short-circuit the code, and fix the issue.

      Thoughts?

      Index: httpcore/src/main/java/org/apache/http/impl/io/AbstractSessionOutputBuffer.java
      ===================================================================
      — httpcore/src/main/java/org/apache/http/impl/io/AbstractSessionOutputBuffer.java (revision 1160689)
      +++ httpcore/src/main/java/org/apache/http/impl/io/AbstractSessionOutputBuffer.java (working copy)
      @@ -256,6 +256,10 @@
      if (this.bbuf == null)

      { this.bbuf = ByteBuffer.allocate(1024); }

      + if(!cbuf.hasRemaining())

      { + return; + }

      +
      this.encoder.reset();
      while (cbuf.hasRemaining()) {
      CoderResult result = this.encoder.encode(cbuf, this.bbuf, true);

      1. SocketOutputBuffer.diff
        6 kB
        William R. Speirs

        Activity

        Hide
        Oleg Kalnichevski added a comment -

        Yes, please do bounce this discussion to the dev list. I'll answer your questions from there.

        Oleg

        Show
        Oleg Kalnichevski added a comment - Yes, please do bounce this discussion to the dev list. I'll answer your questions from there. Oleg
        Hide
        William R. Speirs added a comment -

        I was going to check AbstractSessionInputBuffer, but didn't get a chance... sorry.

        As for the CHARSET setting, I am setting:
        params.setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, HTTP.UTF_8)

        but those params are being used with the AsyncNHttpServiceHandler & DefaultServerIOEventDispatch. How are they making their way over to my client code? (It's a proxy, both are in the same code/program.) Is that setting in some way static? What am I missing here? In both client & server I create a new SyncBasicHttpParams object, and then pass the respective objects to the server & client.

        P.S. I can bounce this over to the mailing list if you'd rather, instead of continuing this discussion in JIRA. Thanks...

        Show
        William R. Speirs added a comment - I was going to check AbstractSessionInputBuffer, but didn't get a chance... sorry. As for the CHARSET setting, I am setting: params.setParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, HTTP.UTF_8) but those params are being used with the AsyncNHttpServiceHandler & DefaultServerIOEventDispatch. How are they making their way over to my client code? (It's a proxy, both are in the same code/program.) Is that setting in some way static? What am I missing here? In both client & server I create a new SyncBasicHttpParams object, and then pass the respective objects to the server & client. P.S. I can bounce this over to the mailing list if you'd rather, instead of continuing this discussion in JIRA. Thanks...
        Hide
        Oleg Kalnichevski added a comment -

        > Any thoughts on how this might be getting set inside the HttpClient?

        Only by explicitly setting the parameter. I double-checked.

        I also discovered that the AbstractSessionInputBuffer had been affected the same way. Fixes and test cases committed to SVN trunk. Please review / retest.

        Oleg

        Show
        Oleg Kalnichevski added a comment - > Any thoughts on how this might be getting set inside the HttpClient? Only by explicitly setting the parameter. I double-checked. I also discovered that the AbstractSessionInputBuffer had been affected the same way. Fixes and test cases committed to SVN trunk. Please review / retest. Oleg
        Hide
        William R. Speirs added a comment -

        I've attached a diff file which includes the original patch and a test file for SocketOutputBuffer.

        I'm curious how I was able to force HttpCore to use chars other than the default. My client creation code is basically:

        final HttpParams params = new SyncBasicHttpParams();
        final SchemeRegistry registry = new SchemeRegistry();

        httpHost = new HttpHost(host);

        params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, handleRedirects);
        params.setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false);
        params.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);

        registry.register(new Scheme("http", HTTP_PORT, PlainSocketFactory.getSocketFactory()));
        registry.register(new Scheme("https", HTTPS_PORT, SSLSocketFactory.getSocketFactory()));

        HttpConnectionParams.setSoTimeout(params, maxTimeout);
        HttpConnectionParams.setConnectionTimeout(params, maxTimeout);

        this.connManager = new ThreadSafeClientConnManager(registry);
        this.connManager.setMaxTotal(maxTotalConnections);
        this.connManager.setDefaultMaxPerRoute(maxTotalConnections);

        this.client = new DefaultHttpClient(this.connManager, params);

        Then I use this client as you'd expect:
        final HttpResponse serviceResponse = client.execute(httpHost, request);

        Any thoughts on how this might be getting set inside the HttpClient?

        Thanks...

        Show
        William R. Speirs added a comment - I've attached a diff file which includes the original patch and a test file for SocketOutputBuffer. I'm curious how I was able to force HttpCore to use chars other than the default. My client creation code is basically: final HttpParams params = new SyncBasicHttpParams(); final SchemeRegistry registry = new SchemeRegistry(); httpHost = new HttpHost(host); params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, handleRedirects); params.setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false); params.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES); registry.register(new Scheme("http", HTTP_PORT, PlainSocketFactory.getSocketFactory())); registry.register(new Scheme("https", HTTPS_PORT, SSLSocketFactory.getSocketFactory())); HttpConnectionParams.setSoTimeout(params, maxTimeout); HttpConnectionParams.setConnectionTimeout(params, maxTimeout); this.connManager = new ThreadSafeClientConnManager(registry); this.connManager.setMaxTotal(maxTotalConnections); this.connManager.setDefaultMaxPerRoute(maxTotalConnections); this.client = new DefaultHttpClient(this.connManager, params); Then I use this client as you'd expect: final HttpResponse serviceResponse = client.execute(httpHost, request); Any thoughts on how this might be getting set inside the HttpClient? Thanks...
        Hide
        Oleg Kalnichevski added a comment -

        Two reasons:
        1. this is a new bit of code added in this release after the minimal jre level requirement was raised to 1.5.
        2. One must explicitly configure HttpCore to use chars other than default ASCII in order to hit that particular execution path.

        Show
        Oleg Kalnichevski added a comment - Two reasons: 1. this is a new bit of code added in this release after the minimal jre level requirement was raised to 1.5. 2. One must explicitly configure HttpCore to use chars other than default ASCII in order to hit that particular execution path.
        Hide
        William R. Speirs added a comment -

        I'll work on a unit test. I'm curious as to why no one has hit this before me though. From AbstractMessageWriter.java 96-97:

        this.lineBuf.clear();
        this.sessionBuffer.writeLine(this.lineBuf);

        Basically just writing a newline (after the headers, before the body of the request). I would think that EVERY request would trigger this. Curious...

        Show
        William R. Speirs added a comment - I'll work on a unit test. I'm curious as to why no one has hit this before me though. From AbstractMessageWriter.java 96-97: this.lineBuf.clear(); this.sessionBuffer.writeLine(this.lineBuf); Basically just writing a newline (after the headers, before the body of the request). I would think that EVERY request would trigger this. Curious...
        Hide
        Oleg Kalnichevski added a comment -

        The fix sounds reasonable. Would it be a big deal for you to create a unit test to verify the fix, though?

        Oleg

        Show
        Oleg Kalnichevski added a comment - The fix sounds reasonable. Would it be a big deal for you to create a unit test to verify the fix, though? Oleg

          People

          • Assignee:
            Unassigned
            Reporter:
            William R. Speirs
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development