-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFzHttpClient.java
More file actions
127 lines (113 loc) · 4.93 KB
/
Copy pathFzHttpClient.java
File metadata and controls
127 lines (113 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Example fz-http client using only the JDK (java.net.http, Java 11+).
//
// Runs the full flow against a running server:
// 1. health check
// 2. parse an input template (POST /parse)
// 3. launch a parametric run (POST /runs)
// 4. poll the job until it completes (GET /runs/{id})
//
// No external dependencies: request bodies are built with a tiny JSON-string
// escaper, and the few fields we need are extracted from responses with regex.
// For real projects, prefer a JSON library (Jackson, Gson).
//
// Run (Java 17):
// java FzHttpClient.java [BASE_URL] // default http://localhost:8000
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FzHttpClient {
static String base = "http://localhost:8000";
// Force HTTP/1.1: the default HTTP/2 upgrade is mishandled by the
// uvicorn/h11 (HTTP/1.1-only) server and would drop the request body.
static final HttpClient http =
HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
// fz input template and calculator script (normal Java escapes).
static final String INPUT_TXT =
"n_mol=$n_mol\nT_kelvin=@{$T_celsius + 273.15}\nV_m3=$V_L\n";
static final String CALC_SH =
"#!/bin/bash\nsource \"$1\"\n"
+ "awk \"BEGIN{printf \\\"pressure = %.4f\\\", "
+ "$n_mol*8.314*$T_kelvin/$V_m3}\" > output.txt\n";
static final String OUTPUT_CMD =
"grep 'pressure = ' output.txt | awk '{print $3}'";
public static void main(String[] args) throws Exception {
if (args.length > 0) base = args[0];
System.out.println("== health ==");
System.out.println(get("/health"));
System.out.println("\n== parse ==");
String parseBody = "{"
+ "\"input_files\":{\"input.txt\":" + js(INPUT_TXT) + "},"
+ "\"model\":{\"varprefix\":\"$\",\"delim\":\"{}\"}"
+ "}";
System.out.println(post("/parse", parseBody));
System.out.println("\n== submit run ==");
String runBody = "{"
+ "\"input_files\":{"
+ "\"input.txt\":" + js(INPUT_TXT) + ","
+ "\"calc.sh\":" + js(CALC_SH)
+ "},"
+ "\"input_path\":\"input.txt\","
+ "\"model\":{\"varprefix\":\"$\",\"delim\":\"{}\","
+ "\"output\":{\"pressure\":" + js(OUTPUT_CMD) + "}},"
+ "\"input_variables\":{\"n_mol\":[1,2],\"T_celsius\":25,\"V_L\":10},"
+ "\"calculators\":[\"sh://bash calc.sh\"]"
+ "}";
String ref = post("/runs", runBody);
String jobId = extract(ref, "\"job_id\"\\s*:\\s*\"([^\"]+)\"");
System.out.println("job_id = " + jobId);
System.out.println("\n== poll ==");
String status;
String body;
while (true) {
body = get("/runs/" + jobId);
status = extract(body, "\"status\"\\s*:\\s*\"([^\"]+)\"");
System.out.println("status: " + status);
if (status.equals("completed") || status.equals("failed")) break;
Thread.sleep(1000);
}
System.out.println("\n== result ==");
System.out.println(body);
}
static String get(String path) throws Exception {
return send(HttpRequest.newBuilder(URI.create(base + path)).GET().build());
}
static String post(String path, String jsonBody) throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create(base + path))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
return send(req);
}
static String send(HttpRequest req) throws Exception {
HttpResponse<String> resp =
http.send(req, HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() >= 400) {
throw new RuntimeException(
"HTTP " + resp.statusCode() + ": " + resp.body());
}
return resp.body();
}
// Minimal JSON string escaper: wraps s in quotes and escapes it.
static String js(String s) {
StringBuilder b = new StringBuilder("\"");
for (char c : s.toCharArray()) {
switch (c) {
case '"': b.append("\\\""); break;
case '\\': b.append("\\\\"); break;
case '\n': b.append("\\n"); break;
case '\r': b.append("\\r"); break;
case '\t': b.append("\\t"); break;
default: b.append(c);
}
}
return b.append("\"").toString();
}
static String extract(String json, String regex) {
Matcher m = Pattern.compile(regex).matcher(json);
if (!m.find()) throw new RuntimeException("field not found in: " + json);
return m.group(1);
}
}