Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8158196

WebView Form Post fails if connection is closed before keepAlive-Timeout

XMLWordPrintable

    • web
    • x86_64
    • windows_7

        FULL PRODUCT VERSION :
        java version "1.8.0_65"
        Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
        Java HotSpot(TM) Client VM (build 25.65-b01, mixed mode, sharing)

        ADDITIONAL OS VERSION INFORMATION :
        Microsoft Windows [Version 6.1.7601]

        A DESCRIPTION OF THE PROBLEM :
        Load a page in a JavaFX WebView, which adds "Connection: keepAlive" and "Keep-Alive: timeout=..." headers.

        If a POST request is done before the timeout is reached, but the connection is already closed by the server, the connection close is not recognized and the POST request fails.

        If a debugger is attached to the WebEngine via webEngine.impl_getDebugger() with "Network.enable" this can be observed as
        "{"response":{"method":"Network.loadingFailed","params":{"errorText":"Software caused connection abort: recv failed","requestId":"0.502","timestamp":1.4641818912680063E9}},"timestamp":"15:11:31.278"}"

        In the attaches sample the Keep-Alive-Timeout is set to 60 seconds, but Tomcat closes the connection after 30 seconds. So the POST fails if it is done between 30 and 60 seconds after initial page load.
        Note that this does not need an configuration error on server side as used to reproduce, this also happens if the connection is closed through other means.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        The samples requieres an embedded Tomcat, therefore the samples contains multiple files with a gradle build to load the dependencies.

        1. Start the main class "TimeoutSample": A JavaFX WebView should open with a simple form.
        2. Wait till "click now" is ouput to the console and then click the submit button on the form.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        After the click the form should be submitted and the new page should display "post ok!"
        ACTUAL -
        Nothing happens.

        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        src/main/TimeoutSample.java
        ###
        package main;
        import java.io.File;
        import java.util.Timer;

        import org.apache.catalina.Context;
        import org.apache.catalina.startup.Tomcat;

        import javafx.application.Application;
        import javafx.scene.Scene;
        import javafx.scene.layout.StackPane;
        import javafx.scene.web.WebView;
        import javafx.stage.Stage;
        import servlet.TimeoutServlet;

        public class TimeoutSample extends Application {

          public final static int TOMCAT_KEEPALIVE = 30000;
          
          public final static int HEADER_KEEPALIVE = 60000;
          
          @Override
          public void init() throws Exception {
            super.init();

            Tomcat tomcat = new Tomcat();
            tomcat.setPort( 8080 );
            tomcat.getConnector().setAttribute( "keepAliveTimeout", TOMCAT_KEEPALIVE );
            
            File base = new File(System.getProperty("java.io.tmpdir"));
            Context rootCtx = tomcat.addContext("/", base.getAbsolutePath());
            Tomcat.addServlet(rootCtx, "TimeoutServlet", new TimeoutServlet());
            rootCtx.addServletMapping("/test", "TimeoutServlet");
         
            tomcat.start();
          }

          @Override
          public void start( Stage primaryStage ) throws Exception {
            WebView webView = new WebView();
            StackPane root = new StackPane();
            root.getChildren().add( webView );
            Scene scene = new Scene( root, 300, 250 );

            primaryStage.setTitle( "TimeoutSample" );
            primaryStage.setScene( scene );
            primaryStage.show();

            webView.getEngine().load( "http://localhost:8080/test" );
            new Thread(()->{
              try {
                Thread.sleep( 30000 );
              } catch (Exception e) {
                e.printStackTrace();
              }
              System.out.println( "click now" );
            }).start();
          }

          public static void main( String[] args ) {
            launch( args );
          }
        }
        ###

        src/servlet/TimeoutServlet .java
        ###
        package servlet;

        import java.io.IOException;

        import javax.servlet.ServletException;
        import javax.servlet.ServletOutputStream;
        import javax.servlet.annotation.WebServlet;
        import javax.servlet.http.HttpServlet;
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;

        import main.TimeoutSample;

        @WebServlet(name = "TimeoutServlet", urlPatterns = { "/test" })
        public class TimeoutServlet extends HttpServlet {
          private static final long serialVersionUID = 425610949406102966L;

          @Override
          protected void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
            ServletOutputStream out = resp.getOutputStream();
            resp.addHeader("Connection", "Keep-Alive");
            resp.addHeader("Keep-Alive", "timeout=" + TimeoutSample.HEADER_KEEPALIVE);

            String form = "<html>" + "<head><title>Form</title></head>" + "<body>" + "<form method=\"post\">"
                + "<input type=\"text\" id=\"inputField\">" + "<button type=\"submit\">submit</button>" + "</form>" + "</body>"
                + "</html>";

            out.write( form.getBytes() );
            out.flush();
            out.close();
          }

          @Override
          protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
            ServletOutputStream out = resp.getOutputStream();
            out.write( "post ok!".getBytes() );
            out.flush();
            out.close();
          }
        }

        ###

        build.gradle
        ###
        plugins {
          id 'java'
          id 'eclipse'
          id 'application'

        }
        sourceCompatibility = 1.8

        repositories {
            mavenCentral()
        }

        dependencies {
          compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.2'
          compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-logging-juli', version: '8.5.2'
          
        }

        configurations.all {
          transitive = true
        }

        sourceSets {
          main {
            java {
              srcDirs = ['src']
            }
            resources {
              srcDirs = ['src']
            }
            resources.srcDirs = ['src']
          }
        }
        ###

        ---------- END SOURCE ----------

              ghb Guru Hb (Inactive)
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Created:
                Updated:
                Resolved: