• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 29010809949

09 Jul 2026 10:12AM UTC coverage: 72.484% (+0.3%) from 72.201%
29010809949

Pull #2138

github

web-flow
Merge a29ca6e33 into c56a44423
Pull Request #2138: #2137: fix fix-vpn-tls-problem not detecting TLS certificate issues behind HTTP redirects

4898 of 7470 branches covered (65.57%)

Branch coverage included in aggregate %.

12641 of 16727 relevant lines covered (75.57%)

3.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.08
cli/src/main/java/com/devonfw/tools/ide/network/NetworkStatusImpl.java
1
package com.devonfw.tools.ide.network;
2

3
import java.io.IOException;
4
import java.net.URL;
5
import java.net.URLConnection;
6
import java.util.Locale;
7
import java.util.concurrent.Callable;
8

9
import org.slf4j.Logger;
10
import org.slf4j.LoggerFactory;
11

12
import com.devonfw.tools.ide.cache.CachedValue;
13
import com.devonfw.tools.ide.cli.CliOfflineException;
14
import com.devonfw.tools.ide.context.AbstractIdeContext;
15
import com.devonfw.tools.ide.log.IdeLogLevel;
16

17
/**
18
 * Implementation of {@link NetworkStatus}.
19
 */
20
public class NetworkStatusImpl implements NetworkStatus {
21

22
  private static final Logger LOG = LoggerFactory.getLogger(NetworkStatusImpl.class);
4✔
23

24
  private final AbstractIdeContext context;
25

26
  private NetworkProxy networkProxy;
27

28
  private final String onlineCheckUrl;
29

30
  protected final CachedValue<Throwable> onlineCheck;
31

32
  private static final String ERROR_TEXT_PKIX = "pkix path building failed";
33

34
  /**
35
   * @param ideContext the {@link AbstractIdeContext}.
36
   */
37
  public NetworkStatusImpl(AbstractIdeContext ideContext) {
38
    this(ideContext, null, CachedValue.DEFAULT_RETENTION);
5✔
39
  }
1✔
40

41
  /**
42
   * @param context the {@link AbstractIdeContext}.
43
   * @param onlineCheckUrl the URL to test for the online-check.
44
   * @param retention the retention of the {@link CachedValue}.
45
   */
46
  protected NetworkStatusImpl(AbstractIdeContext context, String onlineCheckUrl, long retention) {
2✔
47
    this.context = context;
3✔
48
    if (onlineCheckUrl == null) {
2✔
49
      onlineCheckUrl = "https://www.github.com";
2✔
50
    }
51
    this.onlineCheckUrl = onlineCheckUrl;
3✔
52
    this.onlineCheck = new CachedValue<>(this::doOnlineCheck, retention);
8✔
53
  }
1✔
54

55
  @Override
56
  public boolean isOfflineMode() {
57

58
    return this.context.isOfflineMode();
4✔
59
  }
60

61
  @Override
62
  public boolean isOnline() {
63

64
    return getError() == null;
7✔
65
  }
66

67
  @Override
68
  public Throwable getError() {
69

70
    return this.onlineCheck.get();
5✔
71
  }
72

73
  private Throwable doOnlineCheck() {
74
    configureNetworkProxy();
×
75
    try {
76
      int timeout = 1000;
×
77
      //open a connection to URL and try to retrieve data
78
      //getContent fails if there is no connection
79
      URLConnection connection = new URL(this.onlineCheckUrl).openConnection();
×
80
      connection.setConnectTimeout(timeout);
×
81
      connection.getContent();
×
82
      return null;
×
83
    } catch (Exception e) {
×
84
      if (LOG.isDebugEnabled()) {
×
85
        LOG.debug("Error when trying to connect to {}", this.onlineCheckUrl, e);
×
86
      }
87
      return e;
×
88
    }
89
  }
90

91
  private void configureNetworkProxy() {
92

93
    if (this.networkProxy == null) {
3✔
94
      this.networkProxy = new NetworkProxy(this.context);
7✔
95
      this.networkProxy.configure();
3✔
96
    }
97
  }
1✔
98

99
  @Override
100
  public void logStatusMessage() {
101

102
    if (isOfflineMode()) {
3✔
103
      LOG.warn("You are offline because you have enabled offline mode via CLI option.");
3✔
104
      return;
1✔
105
    }
106
    Throwable error = getError();
3✔
107
    if (error == null) {
2✔
108
      IdeLogLevel.SUCCESS.log(LOG, "You are online.");
4✔
109
      return;
1✔
110
    }
111
    String message = "You are offline because of the following error:";
2✔
112
    if (LOG.isDebugEnabled()) {
3!
113
      LOG.error(message, error);
5✔
114
    } else {
115
      LOG.error(message);
×
116
      LOG.error(error.toString());
×
117
    }
118
    if (isTlsTrustIssue(error)) {
4✔
119
      logTruststoreFixHint(this.onlineCheckUrl);
5✔
120
    } else {
121
      IdeLogLevel.INTERACTION.log(LOG, "Please check potential proxy settings, ensure you are properly connected to the internet and retry this operation.");
4✔
122
    }
123
  }
1✔
124

125
  @Override
126
  public <T> T invokeNetworkTask(Callable<T> callable, String uri) {
127

128
    if (isOfflineMode()) {
3!
129
      throw CliOfflineException.ofDownloadViaUrl(uri);
×
130
    }
131
    configureNetworkProxy();
2✔
132
    try {
133
      return callable.call();
3✔
134
    } catch (Exception e) {
1✔
135
      if (e instanceof IOException ioException) {
3!
136
        this.onlineCheck.set(ioException);
×
137
      }
138
      // the underlying SSLHandshakeException is often wrapped (e.g. as an IllegalStateException by the downloader), hence we scan the entire cause chain and
139
      // must not rely on the exception being an IOException.
140
      if (isTlsTrustIssue(e)) {
4!
141
        logTruststoreFixHint(uri);
3✔
142
        throw new IllegalStateException("TLS certificate trust error whilst communicating to " + uri, e);
7✔
143
      }
144
      if (e instanceof IOException) {
×
145
        throw new IllegalStateException("Network error whilst communicating to " + uri, e);
×
146
      }
147
      throw new IllegalStateException("Unexpected error whilst communicating to " + uri, e);
×
148
    }
149
  }
150

151
  private void logTruststoreFixHint(String uri) {
152

153
    LOG.warn("The TLS connection to {} failed due to a certificate trust error (PKIX / certificate-path / SSL handshake). "
4✔
154
        + "This commonly happens behind a corporate VPN or proxy that intercepts TLS traffic.", uri);
155
    LOG.warn("Please first verify that the URL above is a legitimate endpoint that you trust and that you are reaching it securely. "
4✔
156
        + "If it is trustworthy, you can register its certificate in a custom truststore by running:\nide fix-vpn-tls-problem {}", uri);
157
    IdeLogLevel.INTERACTION.log(LOG, "For more details see: https://github.com/devonfw/IDEasy/blob/main/documentation/proxy-support.adoc#tls-certificate-issues");
4✔
158
  }
1✔
159

160
  boolean isTlsTrustIssue(Throwable throwable) {
161
    Throwable current = throwable;
2✔
162
    while (current != null) {
2✔
163
      String message = current.getMessage();
3✔
164
      if (containsTlsTrustIndicator(message)) {
4✔
165
        return true;
2✔
166
      }
167
      current = current.getCause();
3✔
168
    }
1✔
169
    return false;
2✔
170
  }
171

172
  boolean containsTlsTrustIndicator(String text) {
173
    if ((text == null) || text.isBlank()) {
5!
174
      return false;
×
175
    }
176
    String normalized = text.toLowerCase(Locale.ROOT);
4✔
177
    return normalized.contains(ERROR_TEXT_PKIX);
4✔
178
  }
179

180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc