Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions jdtls-bin-override/jdtls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import subprocess
from pathlib import Path
import tempfile
from urllib.parse import urlparse

def get_java_executable(known_args):
if known_args.java_executable is not None:
Expand Down Expand Up @@ -69,6 +70,54 @@ def get_shared_config_path(jdtls_base_path):

return jdtls_base_path / config_dir

def get_proxy_jvm_args():
jvm_args = []

# HTTP Proxy
http_proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
if http_proxy:
# Add default scheme if missing
if '://' not in http_proxy:
http_proxy = 'http://' + http_proxy
parsed = urlparse(http_proxy)
if parsed.hostname:
jvm_args.append(f'-Dhttp.proxyHost={parsed.hostname}')
if parsed.port:
jvm_args.append(f'-Dhttp.proxyPort={parsed.port}')
elif parsed.scheme == 'http':
jvm_args.append('-Dhttp.proxyPort=80')
if parsed.username:
jvm_args.append(f'-Dhttp.proxyUser={parsed.username}')
if parsed.password:
jvm_args.append(f'-Dhttp.proxyPassword={parsed.password}')

# HTTPS Proxy
https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
if https_proxy:
# Add default scheme if missing
if '://' not in https_proxy:
https_proxy = 'https://' + https_proxy
parsed = urlparse(https_proxy)
if parsed.hostname:
jvm_args.append(f'-Dhttps.proxyHost={parsed.hostname}')
if parsed.port:
jvm_args.append(f'-Dhttps.proxyPort={parsed.port}')
elif parsed.scheme == 'https':
jvm_args.append('-Dhttps.proxyPort=443')
if parsed.username:
jvm_args.append(f'-Dhttps.proxyUser={parsed.username}')
if parsed.password:
jvm_args.append(f'-Dhttps.proxyPassword={parsed.password}')

# NO_PROXY / Non-proxy hosts
no_proxy = os.environ.get('NO_PROXY') or os.environ.get('no_proxy')
if no_proxy:
# Convert comma-separated list to pipe-separated for Java
no_proxy_hosts = no_proxy.replace(',', '|')
jvm_args.append(f'-Dhttp.nonProxyHosts={no_proxy_hosts}')

return jvm_args

def main(args):
cwd_name = os.path.basename(os.getcwd())
jdtls_data_path = os.path.join(tempfile.gettempdir(), "jdtls-" + sha1(cwd_name.encode()).hexdigest())
Expand Down Expand Up @@ -103,6 +152,7 @@ def main(args):
"--add-modules=ALL-SYSTEM",
"--add-opens", "java.base/java.util=ALL-UNNAMED",
"--add-opens", "java.base/java.lang=ALL-UNNAMED"] \
+ get_proxy_jvm_args() \
+ known_args.jvm_arg \
+ ["-jar", jar_path,
"-data", known_args.data] \
Expand Down
180 changes: 180 additions & 0 deletions jdtls-bin-override/test_jdtls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import os
import pytest
from unittest.mock import patch
from jdtls import get_proxy_jvm_args


class TestGetProxyJvmArgs:
"""Test suite for get_proxy_jvm_args function."""

def test_no_proxy_variables(self):
"""Test with no proxy environment variables set."""
with patch.dict(os.environ, {}, clear=True):
result = get_proxy_jvm_args()
assert result == []

def test_http_proxy_with_hostname_only(self):
"""Test HTTP_PROXY with just hostname."""
with patch.dict(os.environ, {'HTTP_PROXY': 'http://proxy.example.com'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=80' in result

def test_http_proxy_with_port(self):
"""Test HTTP_PROXY with hostname and port."""
with patch.dict(os.environ, {'HTTP_PROXY': 'http://proxy.example.com:8080'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=8080' in result

def test_http_proxy_lowercase(self):
"""Test http_proxy (lowercase) environment variable."""
with patch.dict(os.environ, {'http_proxy': 'http://proxy.example.com:3128'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=3128' in result

def test_http_proxy_with_credentials(self):
"""Test HTTP_PROXY with username and password."""
with patch.dict(os.environ, {'HTTP_PROXY': 'http://user:pass@proxy.example.com:8080'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=8080' in result
assert '-Dhttp.proxyUser=user' in result
assert '-Dhttp.proxyPassword=pass' in result

def test_https_proxy_with_hostname_only(self):
"""Test HTTPS_PROXY with just hostname."""
with patch.dict(os.environ, {'HTTPS_PROXY': 'https://secure-proxy.example.com'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttps.proxyHost=secure-proxy.example.com' in result
assert '-Dhttps.proxyPort=443' in result

def test_https_proxy_with_port(self):
"""Test HTTPS_PROXY with hostname and port."""
with patch.dict(os.environ, {'HTTPS_PROXY': 'https://secure-proxy.example.com:8443'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttps.proxyHost=secure-proxy.example.com' in result
assert '-Dhttps.proxyPort=8443' in result

def test_https_proxy_lowercase(self):
"""Test https_proxy (lowercase) environment variable."""
with patch.dict(os.environ, {'https_proxy': 'https://secure-proxy.example.com:8443'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttps.proxyHost=secure-proxy.example.com' in result
assert '-Dhttps.proxyPort=8443' in result

def test_https_proxy_with_credentials(self):
"""Test HTTPS_PROXY with username and password."""
with patch.dict(os.environ, {'HTTPS_PROXY': 'https://user:pass@secure-proxy.example.com:8443'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttps.proxyHost=secure-proxy.example.com' in result
assert '-Dhttps.proxyPort=8443' in result
assert '-Dhttps.proxyUser=user' in result
assert '-Dhttps.proxyPassword=pass' in result

def test_no_proxy_single_host(self):
"""Test NO_PROXY with a single host."""
with patch.dict(os.environ, {'NO_PROXY': 'localhost'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.nonProxyHosts=localhost' in result

def test_no_proxy_multiple_hosts(self):
"""Test NO_PROXY with comma-separated hosts."""
with patch.dict(os.environ, {'NO_PROXY': 'localhost,127.0.0.1,.example.com'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.nonProxyHosts=localhost|127.0.0.1|.example.com' in result

def test_no_proxy_lowercase(self):
"""Test no_proxy (lowercase) environment variable."""
with patch.dict(os.environ, {'no_proxy': 'localhost,127.0.0.1'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.nonProxyHosts=localhost|127.0.0.1' in result

def test_all_proxies_combined(self):
"""Test with all proxy environment variables set."""
env_vars = {
'HTTP_PROXY': 'http://user:pass@proxy.example.com:8080',
'HTTPS_PROXY': 'https://user:pass@secure-proxy.example.com:8443',
'NO_PROXY': 'localhost,127.0.0.1,.local'
}
with patch.dict(os.environ, env_vars, clear=True):
result = get_proxy_jvm_args()

# HTTP proxy assertions
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=8080' in result
assert '-Dhttp.proxyUser=user' in result
assert '-Dhttp.proxyPassword=pass' in result

# HTTPS proxy assertions
assert '-Dhttps.proxyHost=secure-proxy.example.com' in result
assert '-Dhttps.proxyPort=8443' in result
assert '-Dhttps.proxyUser=user' in result
assert '-Dhttps.proxyPassword=pass' in result

# NO_PROXY assertions
assert '-Dhttp.nonProxyHosts=localhost|127.0.0.1|.local' in result

def test_uppercase_takes_precedence(self):
"""Test that uppercase environment variables take precedence over lowercase."""
env_vars = {
'HTTP_PROXY': 'http://upper.example.com:8080',
'http_proxy': 'http://lower.example.com:3128'
}
with patch.dict(os.environ, env_vars, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=upper.example.com' in result
assert '-Dhttp.proxyPort=8080' in result

def test_proxy_without_scheme(self):
"""Test proxy URL without scheme (should add default scheme)."""
with patch.dict(os.environ, {'HTTP_PROXY': 'proxy.example.com:8080'}, clear=True):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supporting also proxy without scheme prefix (http:// or https:// based on variable it is read from).

result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=8080' in result

def test_https_proxy_without_scheme(self):
"""Test HTTPS proxy URL without scheme (should add default https scheme)."""
with patch.dict(os.environ, {'HTTPS_PROXY': 'secure-proxy.example.com:8443'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttps.proxyHost=secure-proxy.example.com' in result
assert '-Dhttps.proxyPort=8443' in result

def test_proxy_with_username_no_password(self):
"""Test proxy with username but no password."""
with patch.dict(os.environ, {'HTTP_PROXY': 'http://user@proxy.example.com:8080'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
assert '-Dhttp.proxyPort=8080' in result
assert '-Dhttp.proxyUser=user' in result
# Password should not be in result since it's None
assert not any('proxyPassword' in arg for arg in result)

def test_http_proxy_no_port_non_http_scheme(self):
"""Test HTTP proxy without port and non-http scheme doesn't add default port."""
with patch.dict(os.environ, {'HTTP_PROXY': 'socks://proxy.example.com'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttp.proxyHost=proxy.example.com' in result
# Should not add default port for non-http scheme
assert not any('proxyPort' in arg for arg in result)

def test_https_proxy_no_port_non_https_scheme(self):
"""Test HTTPS proxy without port and non-https scheme doesn't add default port."""
with patch.dict(os.environ, {'HTTPS_PROXY': 'socks://proxy.example.com'}, clear=True):
result = get_proxy_jvm_args()
assert '-Dhttps.proxyHost=proxy.example.com' in result
# Should not add default port for non-https scheme
assert not any('proxyPort' in arg for arg in result)

def test_empty_proxy_value(self):
"""Test with empty proxy environment variable."""
with patch.dict(os.environ, {'HTTP_PROXY': ''}, clear=True):
result = get_proxy_jvm_args()
assert result == []

def test_invalid_url_no_hostname(self):
"""Test with invalid URL that has no hostname."""
with patch.dict(os.environ, {'HTTP_PROXY': 'http://'}, clear=True):
result = get_proxy_jvm_args()
assert result == []
Loading