Skip to content

Commit 5965c6b

Browse files
authored
Merge branch 'dev' into auto-label-area-workflow
2 parents 2742868 + 280ec1d commit 5965c6b

12 files changed

Lines changed: 563 additions & 42 deletions

File tree

eng/ci/templates/java-versions.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
variables:
22
# Linux JDK Versions
3-
JDK8_LINUX_VERSION: '482'
4-
JDK8_LINUX_BUILD: '08'
5-
JDK11_LINUX_VERSION: '11.0.30'
6-
JDK17_LINUX_VERSION: '17.0.18'
7-
JDK21_LINUX_VERSION: '21.0.10'
8-
JDK25_LINUX_VERSION: '25.0.2'
3+
JDK8_LINUX_VERSION: '492'
4+
JDK8_LINUX_BUILD: '09'
5+
JDK11_LINUX_VERSION: '11.0.31'
6+
JDK17_LINUX_VERSION: '17.0.19'
7+
JDK21_LINUX_VERSION: '21.0.11'
8+
JDK25_LINUX_VERSION: '25.0.3'
99

1010
# Windows JDK Versions
11-
JDK8_WINDOWS_VERSION: '482'
12-
JDK8_WINDOWS_BUILD: '08'
13-
JDK11_WINDOWS_VERSION: '11.0.30'
14-
JDK17_WINDOWS_VERSION: '17.0.18'
15-
JDK21_WINDOWS_VERSION: '21.0.10'
16-
JDK25_WINDOWS_VERSION: '25.0.2'
11+
JDK8_WINDOWS_VERSION: '492'
12+
JDK8_WINDOWS_BUILD: '09'
13+
JDK11_WINDOWS_VERSION: '11.0.31'
14+
JDK17_WINDOWS_VERSION: '17.0.19'
15+
JDK21_WINDOWS_VERSION: '21.0.11'
16+
JDK25_WINDOWS_VERSION: '25.0.3'

es-metadata.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
schemaVersion: 1.0.0
2+
providers:
3+
- provider: InventoryAsCode
4+
version: 1.0.0
5+
metadata:
6+
isProduction: true
7+
accountableOwners:
8+
service: e8fdf03b-44f7-48d3-8653-a744c922a342
9+
routing:
10+
defaultAreaPath:
11+
org: azfunc
12+
path: internal\Azure Functions

src/main/java/com/microsoft/azure/functions/worker/Application.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.microsoft.azure.functions.worker;
22

3-
import java.net.MalformedURLException;
3+
import java.net.URI;
44
import java.net.URISyntaxException;
5-
import java.net.URL;
5+
import java.util.Locale;
66
import java.util.logging.*;
77
import javax.annotation.*;
88

@@ -157,7 +157,8 @@ public int getPort() {
157157
return this.port;
158158
}
159159

160-
public String getUri() {
160+
@Override
161+
public String getFunctionsUri() {
161162
return this.uri;
162163
}
163164

@@ -185,17 +186,44 @@ private boolean isCommandlineValid() {
185186

186187
private String parseUri(String uri) throws ParseException {
187188
try {
188-
URL url = new URL(uri);
189-
url.toURI();
190-
this.host = url.getHost();
191-
this.port = url.getPort();
189+
URI parsedUri = new URI(uri);
190+
String host = parsedUri.getHost();
191+
String scheme = parsedUri.getScheme();
192+
int port = parsedUri.getPort();
193+
194+
if (scheme == null || scheme.isEmpty()) {
195+
throw new IllegalArgumentException("URI scheme is missing");
196+
}
197+
198+
switch (scheme.toLowerCase(Locale.ROOT)) {
199+
case "http":
200+
if (port == -1) {
201+
port = 80;
202+
}
203+
break;
204+
case "https":
205+
if (port == -1) {
206+
port = 443;
207+
}
208+
break;
209+
default:
210+
throw new IllegalArgumentException("Unsupported URI scheme");
211+
}
212+
213+
if (host == null || host.isEmpty()) {
214+
throw new IllegalArgumentException("URI host is missing");
215+
}
216+
192217
if (port < 1 || port > 65535) {
193218
throw new IndexOutOfBoundsException("port number out of range");
194219
}
220+
221+
this.host = host;
222+
this.port = port;
195223
return uri;
196-
} catch (MalformedURLException | URISyntaxException | IndexOutOfBoundsException e) {
224+
} catch (URISyntaxException | IllegalArgumentException | IndexOutOfBoundsException e) {
197225
throw new ParseException(String.format(
198-
"Error parsing URI \"%s\". Please provide a valid URI", uri));
226+
"Error parsing URI \"%s\". Please provide a valid http or https URI", uri));
199227
}
200228
}
201229

src/main/java/com/microsoft/azure/functions/worker/IApplication.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ public interface IApplication {
55
String getHost();
66
int getPort();
77
Integer getMaxMessageSize();
8+
default String getFunctionsUri() {
9+
return null;
10+
}
811
}

src/main/java/com/microsoft/azure/functions/worker/JavaWorkerClient.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.util.concurrent.atomic.*;
66
import java.util.function.*;
77
import java.util.logging.*;
8-
import javax.annotation.*;
8+
import javax.annotation.PostConstruct;
99

1010
import io.grpc.*;
1111
import io.grpc.stub.*;
@@ -24,7 +24,12 @@ public class JavaWorkerClient implements AutoCloseable {
2424

2525
public JavaWorkerClient(IApplication app) {
2626
WorkerLogManager.initialize(this, app.logToConsole());
27-
ManagedChannelBuilder<?> chanBuilder = ManagedChannelBuilder.forAddress(app.getHost(), app.getPort()).usePlaintext();
27+
ManagedChannelBuilder<?> chanBuilder = ManagedChannelBuilder.forAddress(app.getHost(), app.getPort());
28+
if (useTransportSecurity(app.getFunctionsUri())) {
29+
chanBuilder.useTransportSecurity();
30+
} else {
31+
chanBuilder.usePlaintext();
32+
}
2833
chanBuilder.maxInboundMessageSize(Integer.MAX_VALUE);
2934

3035
this.channel = chanBuilder.build();
@@ -138,4 +143,12 @@ private synchronized void send(String requestId, MessageHandler<?, ?> marshaller
138143
private final AtomicReference<StreamingMessagePeer> peer;
139144
private final Map<StreamingMessage.ContentCase, Supplier<MessageHandler<?, ?>>> handlerSuppliers;
140145
private final ClassLoaderProvider classPathProvider;
146+
147+
/**
148+
* @param functionsUri Host endpoint URI, or null for legacy startup args that only provide host and port.
149+
*/
150+
static boolean useTransportSecurity(String functionsUri) {
151+
return functionsUri != null
152+
&& functionsUri.regionMatches(true, 0, "https://", 0, "https://".length());
153+
}
141154
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.microsoft.azure.functions.worker;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.lang.reflect.Constructor;
6+
import java.lang.reflect.Method;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertFalse;
10+
import static org.junit.jupiter.api.Assertions.assertNull;
11+
import static org.junit.jupiter.api.Assertions.assertTrue;
12+
13+
public class ApplicationTest {
14+
@Test
15+
public void legacyImplementationsDefaultFunctionsUriToNull() {
16+
IApplication application = new LegacyApplication();
17+
18+
assertNull(application.getFunctionsUri());
19+
}
20+
21+
@Test
22+
public void prefixedStartupArgumentsTakePrecedenceOverLegacyValues() throws Exception {
23+
Application application = createApplication(
24+
"--functions-uri", "https://functions.example:8443",
25+
"--host", "legacy.example",
26+
"--port", "7071",
27+
"--functions-worker-id", "prefixed-worker",
28+
"--workerId", "legacy-worker",
29+
"--functions-request-id", "prefixed-request",
30+
"--requestId", "legacy-request",
31+
"--functions-grpc-max-message-length", "2048",
32+
"--grpcMaxMessageLength", "1024");
33+
34+
assertTrue(isCommandLineValid(application));
35+
assertEquals("https://functions.example:8443", application.getFunctionsUri());
36+
assertEquals("functions.example", application.getHost());
37+
assertEquals(8443, application.getPort());
38+
assertEquals(Integer.valueOf(2048), application.getMaxMessageSize());
39+
assertEquals("prefixed-worker", invokePrivateStringAccessor(application, "getWorkerId"));
40+
assertEquals("prefixed-request", invokePrivateStringAccessor(application, "getRequestId"));
41+
}
42+
43+
@Test
44+
public void legacyHostAndPortRemainAvailableWhenFunctionsUriIsAbsent() throws Exception {
45+
Application application = createApplication(
46+
"--host", "localhost",
47+
"--port", "7001",
48+
"--workerId", "legacy-worker",
49+
"--requestId", "legacy-request",
50+
"--grpcMaxMessageLength", "4096");
51+
52+
assertTrue(isCommandLineValid(application));
53+
assertNull(application.getFunctionsUri());
54+
assertEquals("localhost", application.getHost());
55+
assertEquals(7001, application.getPort());
56+
assertEquals(Integer.valueOf(4096), application.getMaxMessageSize());
57+
}
58+
59+
@Test
60+
public void httpsFunctionsUriDefaultsToPort443WhenPortIsMissing() throws Exception {
61+
Application application = createApplication(
62+
"--functions-uri", "https://functions.example/path",
63+
"--functions-worker-id", "prefixed-worker",
64+
"--functions-request-id", "prefixed-request",
65+
"--functions-grpc-max-message-length", "2048");
66+
67+
assertTrue(isCommandLineValid(application));
68+
assertEquals("functions.example", application.getHost());
69+
assertEquals(443, application.getPort());
70+
}
71+
72+
@Test
73+
public void httpFunctionsUriDefaultsToPort80WhenPortIsMissing() throws Exception {
74+
Application application = createApplication(
75+
"--functions-uri", "http://functions.example/path",
76+
"--functions-worker-id", "prefixed-worker",
77+
"--functions-request-id", "prefixed-request",
78+
"--functions-grpc-max-message-length", "2048");
79+
80+
assertTrue(isCommandLineValid(application));
81+
assertEquals("functions.example", application.getHost());
82+
assertEquals(80, application.getPort());
83+
}
84+
85+
@Test
86+
public void unsupportedFunctionsUriSchemeFailsCommandLineValidation() throws Exception {
87+
Application application = createApplication(
88+
"--functions-uri", "unix:///tmp/functions.sock",
89+
"--functions-worker-id", "prefixed-worker",
90+
"--functions-request-id", "prefixed-request",
91+
"--functions-grpc-max-message-length", "2048");
92+
93+
assertFalse(isCommandLineValid(application));
94+
}
95+
96+
private static Application createApplication(String... args) throws Exception {
97+
Constructor<Application> constructor = Application.class.getDeclaredConstructor(String[].class);
98+
constructor.setAccessible(true);
99+
return constructor.newInstance((Object) args);
100+
}
101+
102+
private static boolean isCommandLineValid(Application application) throws Exception {
103+
Method method = Application.class.getDeclaredMethod("isCommandlineValid");
104+
method.setAccessible(true);
105+
return (boolean) method.invoke(application);
106+
}
107+
108+
private static String invokePrivateStringAccessor(Application application, String methodName) throws Exception {
109+
Method method = Application.class.getDeclaredMethod(methodName);
110+
method.setAccessible(true);
111+
return (String) method.invoke(application);
112+
}
113+
114+
private static final class LegacyApplication implements IApplication {
115+
@Override
116+
public boolean logToConsole() {
117+
return false;
118+
}
119+
120+
@Override
121+
public String getHost() {
122+
return "localhost";
123+
}
124+
125+
@Override
126+
public int getPort() {
127+
return 7071;
128+
}
129+
130+
@Override
131+
public Integer getMaxMessageSize() {
132+
return null;
133+
}
134+
}
135+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.microsoft.azure.functions.worker;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertFalse;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
public class JavaWorkerClientTest {
9+
@Test
10+
public void legacyStartupDefaultsToPlaintextTransport() {
11+
assertFalse(JavaWorkerClient.useTransportSecurity(null));
12+
}
13+
14+
@Test
15+
public void httpFunctionsUriUsesPlaintextTransport() {
16+
assertFalse(JavaWorkerClient.useTransportSecurity("http://functions.example:7071"));
17+
}
18+
19+
@Test
20+
public void httpsFunctionsUriUsesTransportSecurity() {
21+
assertTrue(JavaWorkerClient.useTransportSecurity("https://functions.example:8443"));
22+
}
23+
24+
@Test
25+
public void httpsFunctionsUriSchemeIsCaseInsensitive() {
26+
assertTrue(JavaWorkerClient.useTransportSecurity("HTTPS://functions.example:8443"));
27+
}
28+
}

0 commit comments

Comments
 (0)