@@ -53,6 +53,7 @@ public class DslHttpDefaults extends BaseConfigElement {
5353 protected String proxyUser ;
5454 protected String proxyPassword ;
5555 protected HttpClientImpl clientImpl ;
56+ protected Boolean useKeepAlive ;
5657 protected Boolean followRedirects ;
5758
5859 public DslHttpDefaults () {
@@ -189,6 +190,18 @@ public DslHttpDefaults followRedirects(boolean enable) {
189190 return this ;
190191 }
191192
193+ /**
194+ * Specifies if by default HTTP connection should be kept alive.
195+ *
196+ * @param enable sets either to enable or disable persistent connection
197+ * @return the config element for further configuration or usage.
198+ * @since 1.30
199+ */
200+ public DslHttpDefaults useKeepAlive (boolean enable ) {
201+ this .useKeepAlive = enable ;
202+ return this ;
203+ }
204+
192205 /**
193206 * Allows enabling automatic download of HTML embedded resources (images, iframes, etc) by
194207 * default.
@@ -430,6 +443,9 @@ public HashTree buildTreeUnder(HashTree parent, BuildTreeContext context) {
430443 if (followRedirects != null ) {
431444 buildEndListener (context .getParent ()).followRedirects = followRedirects ;
432445 }
446+ if (useKeepAlive != null ) {
447+ buildEndListener (context .getParent ()).useKeepAlive = useKeepAlive ;
448+ }
433449 return ret ;
434450 }
435451
@@ -445,11 +461,19 @@ protected static void addPendingFollowRedirectsElement(HTTPSamplerProxy element,
445461 buildEndListener (context .getRoot ()).pendingFollowRedirectsElements .add (element );
446462 }
447463
464+ protected static void addPendingUseKeepAliveElement (HTTPSamplerProxy element ,
465+ BuildTreeContext context ) {
466+ buildEndListener (context .getRoot ()).pendingUseKeepAliveElements .add (element );
467+ }
468+
448469 private static class DefaultsTreeContextEndListener implements TreeContextEndListener {
449470
450471 private Boolean followRedirects ;
451472 private final List <HTTPSamplerProxy > pendingFollowRedirectsElements = new ArrayList <>();
452473
474+ private Boolean useKeepAlive ;
475+ private final List <HTTPSamplerProxy > pendingUseKeepAliveElements = new ArrayList <>();
476+
453477 private DefaultsTreeContextEndListener (BuildTreeContext context ) {
454478 context .addEndListener (this );
455479 }
@@ -463,6 +487,14 @@ public void execute(BuildTreeContext context, HashTree tree) {
463487 .filter (e -> e .getPropertyAsString (HTTPSamplerBase .FOLLOW_REDIRECTS ).isEmpty ())
464488 .forEach (e -> e .setFollowRedirects (true ));
465489 }
490+
491+ if (useKeepAlive != null ) {
492+ setChildrenToUseKeepAlive (tree );
493+ } else {
494+ pendingUseKeepAliveElements .stream ()
495+ .filter (e -> e .getPropertyAsString (HTTPSamplerBase .USE_KEEPALIVE ).isEmpty ())
496+ .forEach (e -> e .setUseKeepAlive (true ));
497+ }
466498 }
467499
468500 private void setChildrenToFollowRedirects (HashTree tree ) {
@@ -478,6 +510,19 @@ private void setChildrenToFollowRedirects(HashTree tree) {
478510 });
479511 }
480512
513+ private void setChildrenToUseKeepAlive (HashTree tree ) {
514+ tree .forEach ((key , value ) -> {
515+ if (key instanceof HTTPSamplerProxy ) {
516+ HTTPSamplerProxy sampler = (HTTPSamplerProxy ) key ;
517+ if (sampler .getPropertyAsString (HTTPSamplerBase .USE_KEEPALIVE ).isEmpty ()) {
518+ sampler .setUseKeepAlive (useKeepAlive );
519+ }
520+ } else {
521+ setChildrenToUseKeepAlive (value );
522+ }
523+ });
524+ }
525+
481526 }
482527
483528 public static class CodeBuilder extends SingleGuiClassCallBuilder {
@@ -524,12 +569,16 @@ public void registerDependency(MethodCallContext context) {
524569 boolean followRedirects =
525570 isDefaultCandidate || testElement .getPropertyAsBoolean (HTTPSamplerBase .FOLLOW_REDIRECTS );
526571 endListener .registerFollowRedirect (followRedirects , context , isDefaultCandidate );
572+ boolean useKeepAlive =
573+ isDefaultCandidate || testElement .getPropertyAsBoolean (HTTPSamplerBase .USE_KEEPALIVE );
574+ endListener .registerUseKeepAlive (useKeepAlive , context , isDefaultCandidate );
527575 }
528576
529577 private static class DefaultsMethodContextEndListener implements MethodCallContextEndListener {
530578
531579 private final List <EncodedCall > encodedCalls = new ArrayList <>();
532580 private final List <RedirectableCall > redirectableCalls = new ArrayList <>();
581+ private final List <UseKeepAliveCall > useKeepAliveCalls = new ArrayList <>();
533582
534583 private DefaultsMethodContextEndListener (MethodCallContext parentCtx ) {
535584 parentCtx .addEndListener (this );
@@ -545,10 +594,16 @@ public void registerFollowRedirect(boolean followRedirects, MethodCallContext re
545594 redirectableCalls .add (new RedirectableCall (followRedirects , ret , isDefaultCandidate ));
546595 }
547596
597+ public void registerUseKeepAlive (boolean useKeepAlive , MethodCallContext ret ,
598+ boolean isDefaultCandidate ) {
599+ useKeepAliveCalls .add (new UseKeepAliveCall (useKeepAlive , ret , isDefaultCandidate ));
600+ }
601+
548602 @ Override
549603 public void execute (MethodCallContext ctx , MethodCall ret ) {
550604 solveDefaultEncoding (ctx );
551605 solveDefaultFollowRedirects (ctx );
606+ solveDefaultUseKeepAlive (ctx );
552607 }
553608
554609 private void solveDefaultEncoding (MethodCallContext ctx ) {
@@ -623,6 +678,21 @@ private void solveDefaultFollowRedirects(MethodCallContext ctx) {
623678 }
624679 }
625680
681+ private void solveDefaultUseKeepAlive (MethodCallContext ctx ) {
682+ UseKeepAliveCall defaultsCall = findDefaultUseKeepAliveCall (ctx );
683+ if (defaultsCall != null ) {
684+ useKeepAliveCalls .stream ()
685+ .filter (c -> c != defaultsCall )
686+ .forEach (UseKeepAliveCall ::removeUseKeepAlive );
687+ }
688+ DefaultsMethodContextEndListener parentListener = buildParentContextEndListener (ctx );
689+ if (parentListener != null ) {
690+ parentListener .registerUseKeepAlive (
691+ defaultsCall == null || defaultsCall .useKeepAlive ,
692+ defaultsCall != null ? defaultsCall .ctx : null , false );
693+ }
694+ }
695+
626696 private RedirectableCall findDefaultRedirectableCall (MethodCallContext ctx ) {
627697 if (redirectableCalls .size () == 1 ) {
628698 return redirectableCalls .get (0 );
@@ -641,6 +711,24 @@ private RedirectableCall findDefaultRedirectableCall(MethodCallContext ctx) {
641711 return null ;
642712 }
643713
714+ private UseKeepAliveCall findDefaultUseKeepAliveCall (MethodCallContext ctx ) {
715+ if (useKeepAliveCalls .size () == 1 ) {
716+ return useKeepAliveCalls .get (0 );
717+ }
718+ if (useKeepAliveCalls .stream ().allMatch (c -> c .isDefaultCandidate || !c .useKeepAlive )) {
719+ UseKeepAliveCall ret = useKeepAliveCalls .stream ()
720+ .filter (c -> c .isDefaultCandidate )
721+ .findAny ()
722+ .orElse (null );
723+ if (ret == null ) {
724+ ret = new UseKeepAliveCall (false , buildDefaultsCall (ctx ), true );
725+ }
726+ ret .setUseKeepAlive (false );
727+ return ret ;
728+ }
729+ return null ;
730+ }
731+
644732 }
645733
646734 }
@@ -702,4 +790,29 @@ public void removeFollowRedirects() {
702790
703791 }
704792
793+ private static class UseKeepAliveCall {
794+
795+ private boolean useKeepAlive ;
796+ private final MethodCallContext ctx ;
797+ private final boolean isDefaultCandidate ;
798+
799+ private UseKeepAliveCall (boolean useKeepAlive , MethodCallContext ctx ,
800+ boolean isDefaultCandidate ) {
801+ this .useKeepAlive = useKeepAlive ;
802+ this .ctx = ctx ;
803+ this .isDefaultCandidate = isDefaultCandidate ;
804+ }
805+
806+ public void setUseKeepAlive (boolean useKeepAlive ) {
807+ this .useKeepAlive = useKeepAlive ;
808+ ctx .getMethodCall ().chain ("useKeepAlive" , new BoolParam (useKeepAlive , true ));
809+ }
810+
811+ public void removeUseKeepAlive () {
812+ ctx .getMethodCall ().unchain ("useKeepAlive" );
813+ removeEmptyDefaultsCall (ctx );
814+ }
815+
816+ }
817+
705818}
0 commit comments