Skip to content

Commit 209b5c8

Browse files
committed
fix: correct version to 2.7.x.20260605-SNAPSHOT, use Apache HttpClient
1 parent 5d55772 commit 209b5c8

5 files changed

Lines changed: 210 additions & 72 deletions

File tree

pom.xml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>io.github.hiwepy</groupId>
66
<artifactId>opencode-java-sdk</artifactId>
7-
<version>1.0.x.20260605-SNAPSHOT</version>
7+
<version>2.7.x.20260605-SNAPSHOT</version>
88
<packaging>jar</packaging>
99
<name>${project.groupId}:${project.artifactId}</name>
1010
<description>OpenCode Java SDK (HTTP Server + local CLI)</description>
@@ -33,14 +33,9 @@
3333
<dependencies>
3434
<!-- HTTP client -->
3535
<dependency>
36-
<groupId>com.konghq</groupId>
37-
<artifactId>unirest-java-core</artifactId>
38-
<version>${unirest.version}</version>
39-
</dependency>
40-
<dependency>
41-
<groupId>com.konghq</groupId>
42-
<artifactId>unirest-modules-jackson</artifactId>
43-
<version>${unirest.version}</version>
36+
<groupId>org.apache.httpcomponents</groupId>
37+
<artifactId>httpclient</artifactId>
38+
<version>4.5.14</version>
4439
</dependency>
4540
<!-- JSON -->
4641
<dependency>

src/main/java/io/github/hiwepy/opencode/http/OpenCodeHttpClient.java

Lines changed: 160 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,25 @@
66
import io.github.hiwepy.opencode.OpenCodeClientConfig;
77
import io.github.hiwepy.opencode.exception.OpenCodeHttpException;
88
import io.github.hiwepy.opencode.model.*;
9-
import kong.unirest.core.*;
10-
import kong.unirest.jackson.JacksonObjectMapper;
9+
import org.apache.http.client.config.RequestConfig;
10+
import org.apache.http.client.methods.*;
11+
import org.apache.http.conn.ssl.NoopHostnameVerifier;
12+
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
13+
import org.apache.http.entity.ContentType;
14+
import org.apache.http.entity.StringEntity;
15+
import org.apache.http.impl.client.CloseableHttpClient;
16+
import org.apache.http.impl.client.HttpClients;
17+
import org.apache.http.ssl.SSLContextBuilder;
18+
import org.apache.http.util.EntityUtils;
1119
import org.slf4j.Logger;
1220
import org.slf4j.LoggerFactory;
1321

22+
import javax.net.ssl.SSLContext;
23+
import java.nio.charset.StandardCharsets;
1424
import java.util.Base64;
1525
import java.util.List;
1626
import java.util.Map;
27+
import java.util.Objects;
1728

1829
/**
1930
* OpenCode Server HTTP 客户端,封装 REST API。
@@ -23,28 +34,15 @@
2334
public class OpenCodeHttpClient implements AutoCloseable {
2435

2536
private static final Logger log = LoggerFactory.getLogger(OpenCodeHttpClient.class);
37+
private static final ObjectMapper MAPPER = new ObjectMapper()
38+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
2639

2740
private final OpenCodeClientConfig config;
28-
private final UnirestInstance unirest;
41+
private final CloseableHttpClient http;
2942

3043
public OpenCodeHttpClient(OpenCodeClientConfig config) {
31-
this.config = config;
32-
ObjectMapper mapper = new ObjectMapper()
33-
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
34-
this.unirest = Unirest.primaryInstance();
35-
this.unirest.config()
36-
.baseUrl(config.getServerUrl())
37-
.connectTimeout(config.getConnectTimeoutMillis())
38-
.socketTimeout(config.getReadTimeoutMillis())
39-
.verifySsl(config.isVerifySsl())
40-
.setObjectMapper(new JacksonObjectMapper(mapper));
41-
42-
String password = config.resolvePassword();
43-
if (!password.isEmpty()) {
44-
String credentials = Base64.getEncoder()
45-
.encodeToString((config.getUsername() + ":" + password).getBytes());
46-
this.unirest.config().addDefaultHeader("Authorization", "Basic " + credentials);
47-
}
44+
this.config = Objects.requireNonNull(config, "config");
45+
this.http = buildHttpClient(config);
4846
}
4947

5048
// ============================================================
@@ -69,12 +67,12 @@ public Session getSession(String sessionId) {
6967
}
7068

7169
public List<Session> listSessions() {
72-
return get("/session", new TypeReference<List<Session>>() {});
70+
String json = getRaw("/session");
71+
return parseList(json, new TypeReference<List<Session>>() {});
7372
}
7473

7574
public boolean deleteSession(String sessionId) {
76-
HttpResponse<String> resp = unirest.delete("/session/" + sessionId).asString();
77-
return resp.isSuccess();
75+
return delete("/session/" + sessionId);
7876
}
7977

8078
// ============================================================
@@ -83,86 +81,185 @@ public boolean deleteSession(String sessionId) {
8381

8482
/**
8583
* 发送消息并同步等待 AI 响应(POST /session/:id/message)。
86-
* <p>此方法会阻塞直到 AI 完成响应,可能耗时较长。</p>
8784
*/
8885
public PromptResult prompt(String sessionId, PromptRequest request) {
8986
return post("/session/" + sessionId + "/message", request, PromptResult.class);
9087
}
9188

9289
/**
9390
* 异步发送消息,不等待响应(POST /session/:id/prompt_async)。
94-
* <p>返回 204 No Content 表示已接受。</p>
95-
*
96-
* @return 是否成功提交
9791
*/
9892
public boolean promptAsync(String sessionId, PromptRequest request) {
99-
HttpResponse<String> resp = unirest.post("/session/" + sessionId + "/prompt_async")
100-
.body(request)
101-
.asString();
102-
return resp.isSuccess();
93+
return postNoContent("/session/" + sessionId + "/prompt_async", request);
10394
}
10495

10596
public List<PromptResult> getMessages(String sessionId) {
106-
return get("/session/" + sessionId + "/message", new TypeReference<List<PromptResult>>() {});
97+
String json = getRaw("/session/" + sessionId + "/message");
98+
return parseList(json, new TypeReference<List<PromptResult>>() {});
10799
}
108100

109-
/**
110-
* 中止正在运行的会话。
111-
*/
112101
public boolean abortSession(String sessionId) {
113-
HttpResponse<String> resp = unirest.post("/session/" + sessionId + "/abort").asString();
114-
return resp.isSuccess();
102+
return postNoContent("/session/" + sessionId + "/abort", null);
115103
}
116104

117105
// ============================================================
118106
// Agent
119107
// ============================================================
120108

121109
public List<Agent> listAgents() {
122-
return get("/agent", new TypeReference<List<Agent>>() {});
110+
String json = getRaw("/agent");
111+
return parseList(json, new TypeReference<List<Agent>>() {});
123112
}
124113

125114
// ============================================================
126-
// MCP
115+
// Internal HTTP helpers
127116
// ============================================================
128117

129-
public Map<String, Object> getMcpStatus() {
130-
return get("/mcp", new TypeReference<Map<String, Object>>() {});
118+
private <T> T get(String path, Class<T> type) {
119+
String json = getRaw(path);
120+
try {
121+
return MAPPER.readValue(json, type);
122+
} catch (Exception e) {
123+
throw new OpenCodeHttpException(200, "JSON parse error: " + e.getMessage());
124+
}
131125
}
132126

133-
// ============================================================
134-
// Internal helpers
135-
// ============================================================
127+
private String getRaw(String path) {
128+
String url = config.getServerUrl() + path;
129+
HttpGet request = new HttpGet(url);
130+
addAuthHeader(request);
131+
try (CloseableHttpResponse response = http.execute(request)) {
132+
int status = response.getStatusLine().getStatusCode();
133+
String body = response.getEntity() != null
134+
? EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8) : "";
135+
if (status < 200 || status >= 300) {
136+
throw new OpenCodeHttpException(status, body);
137+
}
138+
return body;
139+
} catch (OpenCodeHttpException e) {
140+
throw e;
141+
} catch (Exception e) {
142+
throw new OpenCodeHttpException(0, "Request failed: " + e.getMessage());
143+
}
144+
}
136145

137-
private <T> T get(String path, Class<T> type) {
138-
HttpResponse<T> resp = unirest.get(path).asObject(type);
139-
if (!resp.isSuccess()) {
140-
throw new OpenCodeHttpException(resp.getStatus(), resp.getBody() != null ? resp.getBody().toString() : "");
146+
private <T> T post(String path, Object body, Class<T> type) {
147+
String json = postRaw(path, body);
148+
try {
149+
return MAPPER.readValue(json, type);
150+
} catch (Exception e) {
151+
throw new OpenCodeHttpException(200, "JSON parse error: " + e.getMessage());
141152
}
142-
return resp.getBody();
143153
}
144154

145-
private <T> T get(String path, TypeReference<T> typeRef) {
146-
HttpResponse<T> resp = unirest.get(path).asObject(typeRef);
147-
if (!resp.isSuccess()) {
148-
throw new OpenCodeHttpException(resp.getStatus(), resp.getBody() != null ? resp.getBody().toString() : "");
155+
private boolean postNoContent(String path, Object body) {
156+
String url = config.getServerUrl() + path;
157+
HttpPost request = new HttpPost(url);
158+
addAuthHeader(request);
159+
if (body != null) {
160+
try {
161+
String json = MAPPER.writeValueAsString(body);
162+
request.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
163+
} catch (Exception e) {
164+
throw new OpenCodeHttpException(0, "JSON serialize error: " + e.getMessage());
165+
}
166+
}
167+
try (CloseableHttpResponse response = http.execute(request)) {
168+
int status = response.getStatusLine().getStatusCode();
169+
return status >= 200 && status < 300;
170+
} catch (Exception e) {
171+
return false;
149172
}
150-
return resp.getBody();
151173
}
152174

153-
private <T> T post(String path, Object body, Class<T> type) {
154-
HttpResponse<T> resp = unirest.post(path)
155-
.header("Content-Type", "application/json")
156-
.body(body)
157-
.asObject(type);
158-
if (!resp.isSuccess()) {
159-
throw new OpenCodeHttpException(resp.getStatus(), resp.getBody() != null ? resp.getBody().toString() : "");
175+
private String postRaw(String path, Object body) {
176+
String url = config.getServerUrl() + path;
177+
HttpPost request = new HttpPost(url);
178+
addAuthHeader(request);
179+
if (body != null) {
180+
try {
181+
String json = MAPPER.writeValueAsString(body);
182+
request.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
183+
} catch (Exception e) {
184+
throw new OpenCodeHttpException(0, "JSON serialize error: " + e.getMessage());
185+
}
186+
}
187+
try (CloseableHttpResponse response = http.execute(request)) {
188+
int status = response.getStatusLine().getStatusCode();
189+
String responseBody = response.getEntity() != null
190+
? EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8) : "";
191+
if (status < 200 || status >= 300) {
192+
throw new OpenCodeHttpException(status, responseBody);
193+
}
194+
return responseBody;
195+
} catch (OpenCodeHttpException e) {
196+
throw e;
197+
} catch (Exception e) {
198+
throw new OpenCodeHttpException(0, "Request failed: " + e.getMessage());
199+
}
200+
}
201+
202+
private boolean delete(String path) {
203+
String url = config.getServerUrl() + path;
204+
HttpDelete request = new HttpDelete(url);
205+
addAuthHeader(request);
206+
try (CloseableHttpResponse response = http.execute(request)) {
207+
int status = response.getStatusLine().getStatusCode();
208+
return status >= 200 && status < 300;
209+
} catch (Exception e) {
210+
return false;
211+
}
212+
}
213+
214+
private void addAuthHeader(HttpRequestBase request) {
215+
String password = config.resolvePassword();
216+
if (!password.isEmpty()) {
217+
String credentials = Base64.getEncoder()
218+
.encodeToString((config.getUsername() + ":" + password).getBytes());
219+
request.setHeader("Authorization", "Basic " + credentials);
220+
}
221+
}
222+
223+
private <T> List<T> parseList(String json, TypeReference<List<T>> typeRef) {
224+
try {
225+
return MAPPER.readValue(json, typeRef);
226+
} catch (Exception e) {
227+
throw new OpenCodeHttpException(200, "JSON parse error: " + e.getMessage());
228+
}
229+
}
230+
231+
private CloseableHttpClient buildHttpClient(OpenCodeClientConfig config) {
232+
try {
233+
RequestConfig requestConfig = RequestConfig.custom()
234+
.setConnectTimeout(config.getConnectTimeoutMillis())
235+
.setSocketTimeout(config.getReadTimeoutMillis())
236+
.setConnectionRequestTimeout(config.getConnectTimeoutMillis())
237+
.build();
238+
239+
if (!config.isVerifySsl()) {
240+
SSLContext sslContext = new SSLContextBuilder()
241+
.loadTrustMaterial(null, (chain, authType) -> true)
242+
.build();
243+
SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(
244+
sslContext, NoopHostnameVerifier.INSTANCE);
245+
return HttpClients.custom()
246+
.setDefaultRequestConfig(requestConfig)
247+
.setSSLSocketFactory(sslFactory)
248+
.build();
249+
}
250+
return HttpClients.custom()
251+
.setDefaultRequestConfig(requestConfig)
252+
.build();
253+
} catch (Exception e) {
254+
throw new RuntimeException("Failed to build HTTP client", e);
160255
}
161-
return resp.getBody();
162256
}
163257

164258
@Override
165259
public void close() {
166-
unirest.shutDown();
260+
try {
261+
http.close();
262+
} catch (Exception ignored) {
263+
}
167264
}
168265
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
artifactId=opencode-java-sdk
2+
groupId=io.github.hiwepy
3+
version=2.7.x.20260605-SNAPSHOT
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
io/github/hiwepy/opencode/exception/OpenCodeHttpException.class
2+
io/github/hiwepy/opencode/model/HealthStatus.class
3+
io/github/hiwepy/opencode/OpenCodeClientConfig.class
4+
io/github/hiwepy/opencode/http/OpenCodeHttpClient$3.class
5+
io/github/hiwepy/opencode/model/Message.class
6+
io/github/hiwepy/opencode/model/Event.class
7+
io/github/hiwepy/opencode/model/Part.class
8+
io/github/hiwepy/opencode/mapper/OpenCodeCallbackParser$3.class
9+
io/github/hiwepy/opencode/exception/OpenCodeException.class
10+
io/github/hiwepy/opencode/http/OpenCodeHttpClient$2.class
11+
io/github/hiwepy/opencode/mapper/OpenCodeCallbackParser$1.class
12+
io/github/hiwepy/opencode/mapper/OpenCodeCallbackParser.class
13+
io/github/hiwepy/opencode/cli/OpenCodeCliResult.class
14+
io/github/hiwepy/opencode/cli/OpenCodeCli.class
15+
io/github/hiwepy/opencode/http/OpenCodeHttpClient.class
16+
io/github/hiwepy/opencode/model/PromptResult.class
17+
io/github/hiwepy/opencode/http/OpenCodeHttpClient$1.class
18+
io/github/hiwepy/opencode/model/Agent.class
19+
io/github/hiwepy/opencode/model/Session.class
20+
io/github/hiwepy/opencode/cli/OpenCodeCliExecutor.class
21+
io/github/hiwepy/opencode/model/PromptRequest$ModelRef.class
22+
io/github/hiwepy/opencode/http/OpenCodeSseClient.class
23+
io/github/hiwepy/opencode/mapper/OpenCodeCallbackParser$2.class
24+
io/github/hiwepy/opencode/OpenCodeClient.class
25+
io/github/hiwepy/opencode/model/PromptRequest.class
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/OpenCodeClient.java
2+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/OpenCodeClientConfig.java
3+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/cli/OpenCodeCli.java
4+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/cli/OpenCodeCliExecutor.java
5+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/cli/OpenCodeCliResult.java
6+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/exception/OpenCodeException.java
7+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/exception/OpenCodeHttpException.java
8+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/http/OpenCodeHttpClient.java
9+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/http/OpenCodeSseClient.java
10+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/mapper/OpenCodeCallbackParser.java
11+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/Agent.java
12+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/Event.java
13+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/HealthStatus.java
14+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/Message.java
15+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/Part.java
16+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/PromptRequest.java
17+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/PromptResult.java
18+
/Users/wandl/workspaces/workspace-ddd4j/workspace-ddd4j-boot-starters/opencode-java-sdk/src/main/java/io/github/hiwepy/opencode/model/Session.java

0 commit comments

Comments
 (0)