Skip to content

Commit 8527e2a

Browse files
committed
fix(transport): sync endpoint policy contracts to Java 21
1 parent a4b4b85 commit 8527e2a

9 files changed

Lines changed: 342 additions & 63 deletions

File tree

‎src/main/java/io/github/easy4j/hermes/HermesClient.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ private static void copyHttpConfig(HermesHttpClientConfig src, HermesHttpClientC
323323
target.setEnabled(src.isEnabled());
324324
target.setStartupCheckEnabled(src.isStartupCheckEnabled());
325325
target.setFailFastOnUnavailable(src.isFailFastOnUnavailable());
326+
target.setEndpointPolicy(src.getEndpointPolicy());
326327
target.setBaseUrl(src.getBaseUrl());
327328
target.setApiKey(src.getApiKey());
328329
target.setConnectTimeoutMillis(src.getConnectTimeoutMillis());

‎src/main/java/io/github/easy4j/hermes/HermesOkHttpClientFactory.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ public static OkHttpClient create(HermesHttpClientConfig config) {
4343
.readTimeout(Math.max(0, config.getReadTimeoutMillis()), TimeUnit.MILLISECONDS)
4444
.writeTimeout(Math.max(1, config.getWriteTimeoutMillis()), TimeUnit.MILLISECONDS)
4545
.callTimeout(Math.max(0, config.getCallTimeoutMillis()), TimeUnit.MILLISECONDS)
46-
.retryOnConnectionFailure(config.isRetryOnConnectionFailure());
46+
.retryOnConnectionFailure(config.isRetryOnConnectionFailure())
47+
.followRedirects(false)
48+
.followSslRedirects(false);
4749
if (!config.isVerifySsl()) {
4850
builder.hostnameVerifier((hostname, session) -> true);
4951
}

‎src/main/java/io/github/easy4j/hermes/api/HermesHttpClient.java‎

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ public CompletableFuture<ResponseResult> createResponseAsync(ResponseRequest req
297297
* @since 1.0.0
298298
*/
299299
public ResponseResult getResponse(String responseId) {
300-
return get("/v1/responses/" + responseId, ResponseResult.class);
300+
return get(PATH_RESPONSES + "/" + encodePathSegment(responseId), ResponseResult.class);
301301
}
302302

303303
/**
@@ -310,7 +310,7 @@ public ResponseResult getResponse(String responseId) {
310310
* @since 1.0.0
311311
*/
312312
public boolean deleteResponse(String responseId) {
313-
return deleteBoolean(PATH_RESPONSES + "/" + responseId);
313+
return deleteBoolean(PATH_RESPONSES + "/" + encodePathSegment(responseId));
314314
}
315315

316316
// ============================================================
@@ -336,8 +336,25 @@ public boolean deleteResponse(String responseId) {
336336
*/
337337
public CompletableFuture<ModelsResponse> listModelsAsync() { return getAsync(PATH_MODELS, ModelsResponse.class); }
338338

339-
private static String encodePathSegment(String value) {
340-
return java.net.URLEncoder.encode(value, StandardCharsets.UTF_8);
339+
static String encodePathSegment(String value) {
340+
Objects.requireNonNull(value, "path segment");
341+
byte[] bytes = value.getBytes(java.nio.charset.StandardCharsets.UTF_8);
342+
char[] hex = "0123456789ABCDEF".toCharArray();
343+
StringBuilder encoded = new StringBuilder(bytes.length);
344+
for (byte raw : bytes) {
345+
int valueByte = raw & 0xFF;
346+
if ((valueByte >= 'a' && valueByte <= 'z')
347+
|| (valueByte >= 'A' && valueByte <= 'Z')
348+
|| (valueByte >= '0' && valueByte <= '9')
349+
|| valueByte == '-' || valueByte == '.' || valueByte == '_' || valueByte == '~') {
350+
encoded.append((char) valueByte);
351+
} else {
352+
encoded.append('%')
353+
.append(hex[(valueByte >>> 4) & 0x0F])
354+
.append(hex[valueByte & 0x0F]);
355+
}
356+
}
357+
return encoded.toString();
341358
}
342359

343360
/**
@@ -418,7 +435,7 @@ public RunStatus createRun(RunCreateRequest request) {
418435
* @return Agent Run 状态
419436
* @since 1.0.0
420437
*/
421-
public RunStatus getRun(String runId) { return get("/v1/runs/" + runId, RunStatus.class); }
438+
public RunStatus getRun(String runId) { return get(PATH_RUNS + "/" + encodePathSegment(runId), RunStatus.class); }
422439

423440
/**
424441
* <p>停止 Agent Run。</p>
@@ -442,7 +459,7 @@ public void stopRun(String runId) {
442459
* @since 1.0.0
443460
*/
444461
public CompletableFuture<Void> stopRunAsync(String runId) {
445-
Request request = authedRequest(url(PATH_RUNS + "/" + runId + "/stop"))
462+
Request request = authedRequest(url(PATH_RUNS + "/" + encodePathSegment(runId) + "/stop"))
446463
.post(RequestBody.create(new byte[0], null)).build();
447464
return executeResponseAsync(request, null).thenApply(response -> {
448465
if (!response.isSuccessful()) {
@@ -463,7 +480,7 @@ public CompletableFuture<Void> stopRunAsync(String runId) {
463480
* @since 1.0.0
464481
*/
465482
public Map<String, Object> approveRun(String runId, Map<String, Object> decision) {
466-
return postMap(PATH_RUNS + "/" + runId + "/approval", decision);
483+
return postMap(PATH_RUNS + "/" + encodePathSegment(runId) + "/approval", decision);
467484
}
468485

469486
// ============================================================
@@ -527,7 +544,7 @@ public List<Session> listSessions(Integer limit, Integer offset, String source,
527544
* @return 会话对象或会话列表
528545
* @since 1.0.0
529546
*/
530-
public Session getSession(String id) { return get(PATH_SESSIONS + "/" + id, Session.class); }
547+
public Session getSession(String id) { return get(PATH_SESSIONS + "/" + encodePathSegment(id), Session.class); }
531548

532549
/**
533550
* <p>查询会话消息。</p>
@@ -539,7 +556,7 @@ public List<Session> listSessions(Integer limit, Integer offset, String source,
539556
* @since 1.0.0
540557
*/
541558
public List<Map<String, Object>> getSessionMessages(String id) {
542-
return getList(PATH_SESSIONS + "/" + id + "/messages",
559+
return getList(PATH_SESSIONS + "/" + encodePathSegment(id) + "/messages",
543560
new TypeReference<List<Map<String, Object>>>() {});
544561
}
545562

@@ -556,7 +573,7 @@ public List<Map<String, Object>> getSessionMessages(String id) {
556573
public Session forkSession(String id, String title) {
557574
Map<String, Object> body = new LinkedHashMap<>();
558575
if (title != null) body.put("title", title);
559-
return post(PATH_SESSIONS + "/" + id + "/fork", body, Session.class);
576+
return post(PATH_SESSIONS + "/" + encodePathSegment(id) + "/fork", body, Session.class);
560577
}
561578

562579
/**
@@ -569,7 +586,7 @@ public Session forkSession(String id, String title) {
569586
* @since 1.0.0
570587
*/
571588
public boolean deleteSession(String id) {
572-
return deleteBoolean(PATH_SESSIONS + "/" + id);
589+
return deleteBoolean(PATH_SESSIONS + "/" + encodePathSegment(id));
573590
}
574591

575592
/**
@@ -583,7 +600,7 @@ public boolean deleteSession(String id) {
583600
* @since 1.0.0
584601
*/
585602
public Session updateSession(String id, Map<String, Object> patch) {
586-
Request request = authedRequest(url(PATH_SESSIONS + "/" + id))
603+
Request request = authedRequest(url(PATH_SESSIONS + "/" + encodePathSegment(id)))
587604
.patch(RequestBody.create(toJson(patch), JSON)).build();
588605
return execute(request, Session.class);
589606
}
@@ -599,7 +616,7 @@ public Session updateSession(String id, Map<String, Object> patch) {
599616
* @since 1.0.0
600617
*/
601618
public ChatResponse sessionChat(String id, String input) {
602-
return post(PATH_SESSIONS + "/" + id + "/chat", Collections.singletonMap("input", input), ChatResponse.class);
619+
return post(PATH_SESSIONS + "/" + encodePathSegment(id) + "/chat", Collections.singletonMap("input", input), ChatResponse.class);
603620
}
604621

605622
// ============================================================
@@ -641,7 +658,7 @@ public Map<String, Object> createJob(Map<String, Object> job) {
641658
* @since 1.0.0
642659
*/
643660
public Map<String, Object> getJob(String jobId) {
644-
Request request = authedRequest(url(PATH_JOBS + "/" + jobId)).get().build();
661+
Request request = authedRequest(url(PATH_JOBS + "/" + encodePathSegment(jobId))).get().build();
645662
return executeList(request, new TypeReference<Map<String, Object>>() {});
646663
}
647664

@@ -656,7 +673,7 @@ public Map<String, Object> getJob(String jobId) {
656673
* @since 1.0.0
657674
*/
658675
public Map<String, Object> updateJob(String jobId, Map<String, Object> patch) {
659-
Request request = authedRequest(url(PATH_JOBS + "/" + jobId))
676+
Request request = authedRequest(url(PATH_JOBS + "/" + encodePathSegment(jobId)))
660677
.patch(RequestBody.create(toJson(patch), JSON)).build();
661678
return executeList(request, new TypeReference<Map<String, Object>>() {});
662679
}
@@ -671,7 +688,7 @@ public Map<String, Object> updateJob(String jobId, Map<String, Object> patch) {
671688
* @since 1.0.0
672689
*/
673690
public boolean deleteJob(String jobId) {
674-
Request request = authedRequest(url(PATH_JOBS + "/" + jobId)).delete().build();
691+
Request request = authedRequest(url(PATH_JOBS + "/" + encodePathSegment(jobId))).delete().build();
675692
return awaitFuture(executeResponseAsync(request, null).thenApply(response -> {
676693
if (!response.isSuccessful() && response.getStatusCode() != 404) {
677694
log.warn("deleteJob {} failed: {}", jobId, response.getStatusCode());
@@ -690,7 +707,7 @@ public boolean deleteJob(String jobId) {
690707
* @since 1.0.0
691708
*/
692709
public Map<String, Object> pauseJob(String jobId) {
693-
return postMap("/api/jobs/" + jobId + "/pause", Collections.emptyMap());
710+
return postMap("/api/jobs/" + encodePathSegment(jobId) + "/pause", Collections.emptyMap());
694711
}
695712

696713
/**
@@ -703,7 +720,7 @@ public Map<String, Object> pauseJob(String jobId) {
703720
* @since 1.0.0
704721
*/
705722
public Map<String, Object> resumeJob(String jobId) {
706-
return postMap("/api/jobs/" + jobId + "/resume", Collections.emptyMap());
723+
return postMap("/api/jobs/" + encodePathSegment(jobId) + "/resume", Collections.emptyMap());
707724
}
708725

709726
/**
@@ -716,7 +733,7 @@ public Map<String, Object> resumeJob(String jobId) {
716733
* @since 1.0.0
717734
*/
718735
public Map<String, Object> runJobNow(String jobId) {
719-
return postMap("/api/jobs/" + jobId + "/run", Collections.emptyMap());
736+
return postMap("/api/jobs/" + encodePathSegment(jobId) + "/run", Collections.emptyMap());
720737
}
721738

722739
// ============================================================

‎src/main/java/io/github/easy4j/hermes/api/HermesSseClient.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public SseSubscription subscribeChat(ChatRequest request, Map<String, String> he
182182
* @since 1.0.0
183183
*/
184184
public SseSubscription subscribeRunEvents(String runId, Consumer<SseEvent> consumer) {
185-
String url = config.getBaseUrl() + PATH_RUNS + "/" + runId + "/events";
185+
String url = config.getBaseUrl() + PATH_RUNS + "/" + HermesHttpClient.encodePathSegment(runId) + "/events";
186186
return start(() -> buildGetSseRequest(url), consumer, () -> { },
187187
error -> log.warn("Hermes run SSE stopped: runId={}, error={}", runId, error.getMessage()),
188188
true, "run:" + runId);
@@ -215,7 +215,7 @@ public SseQueueSubscription subscribeRunEventsQueue(String runId) {
215215
*/
216216
public SseSubscription subscribeSessionEvents(String sessionId, String input,
217217
Consumer<SseEvent> consumer) {
218-
String url = config.getBaseUrl() + PATH_SESSIONS + "/" + sessionId + "/chat/stream";
218+
String url = config.getBaseUrl() + PATH_SESSIONS + "/" + HermesHttpClient.encodePathSegment(sessionId) + "/chat/stream";
219219
return start(() -> buildPostSseRequest(url, Collections.singletonMap("input", input), null),
220220
consumer, () -> { },
221221
error -> log.warn("Hermes session SSE stopped: sessionId={}, error={}",

0 commit comments

Comments
 (0)