13
13
import com .fasterxml .jackson .core .JsonToken ;
14
14
import dev .restate .sdk .common .Serde ;
15
15
import dev .restate .sdk .common .Target ;
16
+ import java .io .ByteArrayInputStream ;
16
17
import java .io .IOException ;
17
18
import java .io .InputStream ;
18
19
import java .net .URI ;
19
20
import java .net .http .HttpClient ;
20
21
import java .net .http .HttpRequest ;
21
22
import java .net .http .HttpResponse ;
22
- import java .nio .charset .StandardCharsets ;
23
23
import java .util .Map ;
24
+ import java .util .concurrent .CompletableFuture ;
24
25
25
26
public class DefaultIngressClient implements IngressClient {
26
27
@@ -37,53 +38,58 @@ public DefaultIngressClient(HttpClient httpClient, String baseUri, Map<String, S
37
38
}
38
39
39
40
@ Override
40
- public <Req , Res > Res call (
41
+ public <Req , Res > CompletableFuture < Res > callAsync (
41
42
Target target ,
42
43
Serde <Req > reqSerde ,
43
44
Serde <Res > resSerde ,
44
45
Req req ,
45
46
RequestOptions requestOptions ) {
46
47
HttpRequest request = prepareHttpRequest (target , false , reqSerde , req , requestOptions );
47
- HttpResponse <byte []> response ;
48
- try {
49
- response = httpClient .send (request , HttpResponse .BodyHandlers .ofByteArray ());
50
- } catch (IOException | InterruptedException e ) {
51
- throw new RuntimeException ("Error when executing the request" , e );
52
- }
53
-
54
- if (response .statusCode () != 200 ) {
55
- // Try to parse as string
56
- String error = new String (response .body (), StandardCharsets .UTF_8 );
57
- throw new RuntimeException (
58
- "Received non OK status code: " + response .statusCode () + ". Body: " + error );
59
- }
60
-
61
- return resSerde .deserialize (response .body ());
48
+ return httpClient
49
+ .sendAsync (request , HttpResponse .BodyHandlers .ofByteArray ())
50
+ .handle (
51
+ (response , throwable ) -> {
52
+ if (throwable != null ) {
53
+ throw new IngressException ("Error when executing the request" , throwable );
54
+ }
55
+
56
+ if (response .statusCode () >= 300 ) {
57
+ handleNonSuccessResponse (response );
58
+ }
59
+
60
+ try {
61
+ return resSerde .deserialize (response .body ());
62
+ } catch (Exception e ) {
63
+ throw new IngressException (
64
+ "Cannot deserialize the response" , response .statusCode (), response .body (), e );
65
+ }
66
+ });
62
67
}
63
68
64
69
@ Override
65
- public <Req > String send (Target target , Serde <Req > reqSerde , Req req , RequestOptions options ) {
70
+ public <Req > CompletableFuture <String > sendAsync (
71
+ Target target , Serde <Req > reqSerde , Req req , RequestOptions options ) {
66
72
HttpRequest request = prepareHttpRequest (target , true , reqSerde , req , options );
67
- HttpResponse < InputStream > response ;
68
- try {
69
- response = httpClient . send ( request , HttpResponse . BodyHandlers . ofInputStream ());
70
- } catch ( IOException | InterruptedException e ) {
71
- throw new RuntimeException ( "Error when executing the request" , e );
72
- }
73
-
74
- try ( InputStream in = response . body ()) {
75
- if (response .statusCode () >= 300 ) {
76
- // Try to parse as string
77
- String error = new String ( in . readAllBytes (), StandardCharsets . UTF_8 );
78
- throw new RuntimeException (
79
- "Received non OK status code: " + response . statusCode () + ". Body: " + error );
80
- }
81
- return deserializeInvocationId ( in );
82
- } catch (IOException e ) {
83
- throw new RuntimeException (
84
- "Error when trying to read the response, when status code was " + response .statusCode (),
85
- e );
86
- }
73
+ return httpClient
74
+ . sendAsync ( request , HttpResponse . BodyHandlers . ofByteArray ())
75
+ . handle (
76
+ ( response , throwable ) -> {
77
+ if ( throwable != null ) {
78
+ throw new IngressException ( "Error when executing the request" , throwable );
79
+ }
80
+
81
+ if (response .statusCode () >= 300 ) {
82
+ handleNonSuccessResponse ( response );
83
+ }
84
+
85
+ try {
86
+ return findStringFieldInJsonObject (
87
+ new ByteArrayInputStream ( response . body ()), "invocationId" );
88
+ } catch (Exception e ) {
89
+ throw new IngressException (
90
+ "Cannot deserialize the response" , response .statusCode (), response . body (), e );
91
+ }
92
+ });
87
93
}
88
94
89
95
private URI toRequestURI (Target target , boolean isSend ) {
@@ -128,23 +134,43 @@ private <Req> HttpRequest prepareHttpRequest(
128
134
return reqBuilder .POST (HttpRequest .BodyPublishers .ofByteArray (reqSerde .serialize (req ))).build ();
129
135
}
130
136
131
- private static String deserializeInvocationId (InputStream body ) throws IOException {
137
+ private void handleNonSuccessResponse (HttpResponse <byte []> response ) {
138
+ if (response .headers ().firstValue ("content-type" ).orElse ("" ).contains ("application/json" )) {
139
+ String errorMessage ;
140
+ // Let's try to parse the message field
141
+ try {
142
+ errorMessage =
143
+ findStringFieldInJsonObject (new ByteArrayInputStream (response .body ()), "message" );
144
+ } catch (Exception e ) {
145
+ throw new IngressException (
146
+ "Can't decode error response from ingress" , response .statusCode (), response .body (), e );
147
+ }
148
+ throw new IngressException (errorMessage , response .statusCode (), response .body ());
149
+ }
150
+
151
+ // Fallback error
152
+ throw new IngressException (
153
+ "Received non success status code" , response .statusCode (), response .body ());
154
+ }
155
+
156
+ private static String findStringFieldInJsonObject (InputStream body , String fieldName )
157
+ throws IOException {
132
158
try (JsonParser parser = JSON_FACTORY .createParser (body )) {
133
159
if (parser .nextToken () != JsonToken .START_OBJECT ) {
134
160
throw new IllegalStateException (
135
161
"Expecting token " + JsonToken .START_OBJECT + ", got " + parser .getCurrentToken ());
136
162
}
137
- String fieldName = parser .nextFieldName ();
138
- if (fieldName == null || !fieldName .equalsIgnoreCase ("invocationid" )) {
139
- throw new IllegalStateException (
140
- "Expecting token \" invocationId\" , got " + parser .getCurrentToken ());
141
- }
142
- String invocationId = parser .nextTextValue ();
143
- if (invocationId == null ) {
144
- throw new IllegalStateException (
145
- "Expecting token " + JsonToken .VALUE_STRING + ", got " + parser .getCurrentToken ());
163
+ for (String actualFieldName = parser .nextFieldName ();
164
+ actualFieldName != null ;
165
+ actualFieldName = parser .nextFieldName ()) {
166
+ if (actualFieldName .equalsIgnoreCase (fieldName )) {
167
+ return parser .nextTextValue ();
168
+ } else {
169
+ parser .nextValue ();
170
+ }
146
171
}
147
- return invocationId ;
172
+ throw new IllegalStateException (
173
+ "Expecting field name \" " + fieldName + "\" , got " + parser .getCurrentToken ());
148
174
}
149
175
}
150
176
}
0 commit comments