Skip to content

Commit 49083bb

Browse files
authored
Merge pull request #2685 from digma-ai/fix-freeze-in-api-and-jaeger-proxy
Fix freeze in api and jaeger proxy Closes #2684
2 parents 943bdf2 + 226ed9e commit 49083bb

File tree

4 files changed

+130
-83
lines changed

4 files changed

+130
-83
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ dependencies {
5858

5959
implementation(libs.commons.lang3)
6060
implementation(libs.freemarker)
61+
implementation(libs.okhttp)
6162
implementation(project(":model"))
6263
implementation(project(":analytics-provider"))
6364

src/main/java/org/digma/intellij/plugin/ui/jcef/JaegerProxyResourceHandler.java

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
import org.cef.misc.*;
88
import org.cef.network.*;
99
import org.digma.intellij.plugin.auth.account.CredentialsHolder;
10+
import org.digma.intellij.plugin.common.Backgroundable;
1011
import org.digma.intellij.plugin.errorreporting.ErrorReporter;
1112
import org.digma.intellij.plugin.log.Log;
1213
import org.digma.intellij.plugin.settings.SettingsState;
1314
import org.jetbrains.annotations.*;
1415

1516
import java.net.*;
16-
import java.util.HashMap;
17+
import java.util.*;
1718
import java.util.stream.Collectors;
1819

1920
import static org.digma.intellij.plugin.ui.jcef.ProxyUtilsKt.postDataToByteArray;
@@ -68,15 +69,27 @@ public static URL getJaegerQueryUrlOrNull() {
6869

6970
@Override
7071
public boolean processRequest(CefRequest cefRequest, CefCallback callback) {
72+
73+
var url = cefRequest.getURL();
74+
var requestId = cefRequest.getIdentifier();
75+
var headers = getHeaders(cefRequest);
76+
byte[] postData = cefRequest.getPostData() != null ? postDataToByteArray(cefRequest, cefRequest.getPostData()) : null;
77+
var method = cefRequest.getMethod();
78+
79+
Backgroundable.executeOnPooledThread(() -> processRequest(url, requestId, headers, postData, method, callback));
80+
return true;
81+
}
82+
83+
public void processRequest(String requestUrl, Long requestId, Map<String, String> headers, byte[] postData, String method, CefCallback callback) {
7184
try {
7285

73-
Log.log(LOGGER::trace, "processing request {}, [request id:{}]", cefRequest.getURL(), cefRequest.getIdentifier());
86+
Log.log(LOGGER::trace, "processing request {}, [request id:{}]", requestUrl, requestId);
7487

75-
var apiUrl = getApiUrl(cefRequest);
76-
var headers = getHeaders(cefRequest);
77-
var body = getBody(cefRequest, headers);
88+
var apiUrl = getApiUrl(requestUrl);
89+
90+
var body = getBody(postData, headers);
7891
var okHttp3Request = new Request.Builder()
79-
.method(cefRequest.getMethod(), body)
92+
.method(method, body)
8093
.headers(Headers.of(headers))
8194
.url(apiUrl)
8295
.build();
@@ -85,16 +98,32 @@ public boolean processRequest(CefRequest cefRequest, CefCallback callback) {
8598
if (authFailureReason != null && !authFailureReason.isBlank()) {
8699
okHttp3Response = buildAuthFailureResponse(okHttp3Request, okHttp3Response, authFailureReason);
87100
}
88-
callback.Continue();
89-
return true;
90101
} catch (Exception e) {
91-
Log.warnWithException(LOGGER, e, "processRequest failed for request {}, [request id:{}]", cefRequest.getURL(),cefRequest.getIdentifier());
102+
Log.warnWithException(LOGGER, e, "processRequest failed for request {}, [request id:{}]", requestUrl, requestId);
92103
ErrorReporter.getInstance().reportError("JaegerProxyResourceHandler.processRequest", e);
93-
callback.cancel();
94-
return false;
104+
105+
var okHttp3Request = new Request.Builder()
106+
.method(method, null)
107+
.headers(Headers.of(headers))
108+
.url(requestUrl)
109+
.build();
110+
111+
okHttp3Response = new Response.Builder().
112+
request(okHttp3Request).
113+
protocol(Protocol.HTTP_1_1).
114+
message("Internal proxy error " + e).
115+
body(ResponseBody.create(
116+
"Internal proxy error " + e,
117+
MediaType.get("text/plain"))).
118+
code(500).
119+
build();
120+
121+
} finally {
122+
callback.Continue();
95123
}
96124
}
97125

126+
98127
@Nullable
99128
private static String getAuthFailureReason(Response response) {
100129
final var headerName = "X-Auth-Fail-Reason";
@@ -120,8 +149,8 @@ private static Response buildAuthFailureResponse(Request okHttp3Request, Respons
120149
}
121150

122151
@NotNull
123-
private URL getApiUrl(CefRequest cefRequest) throws MalformedURLException {
124-
var requestUrl = new URL(cefRequest.getURL());
152+
private URL getApiUrl(String orgRequestUrl) throws MalformedURLException {
153+
var requestUrl = new URL(orgRequestUrl);
125154
var path = requestUrl.getPath() == null ? "" : requestUrl.getPath();
126155
if (isJaegerQueryCall(requestUrl)) {
127156
//remove the /jaeger prefix and keep /api
@@ -144,9 +173,9 @@ private static HashMap<String, String> getHeaders(CefRequest cefRequest) {
144173
}
145174

146175
@Nullable
147-
private static RequestBody getBody(CefRequest cefRequest, HashMap<String, String> headers) {
148-
return cefRequest.getPostData() != null
149-
? RequestBody.create(postDataToByteArray(cefRequest, cefRequest.getPostData()), MediaType.parse(headers.get("Content-Type")))
176+
private static RequestBody getBody(byte[] postData, Map<String, String> headers) {
177+
return postData != null
178+
? RequestBody.create(postData, MediaType.parse(headers.get("Content-Type")))
150179
: null;
151180
}
152181

src/main/kotlin/org/digma/intellij/plugin/ui/jcef/ApiProxyResourceHandler.kt

Lines changed: 83 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.cef.misc.StringRef
99
import org.cef.network.CefRequest
1010
import org.cef.network.CefResponse
1111
import org.digma.intellij.plugin.analytics.AnalyticsService
12+
import org.digma.intellij.plugin.common.Backgroundable
1213
import org.digma.intellij.plugin.errorreporting.ErrorReporter
1314
import org.digma.intellij.plugin.log.Log
1415
import org.digma.intellij.plugin.model.rest.lowlevel.HttpRequest
@@ -113,78 +114,96 @@ class ApiProxyResourceHandler(val project: Project) : CefResourceHandler {
113114

114115

115116
override fun processRequest(request: CefRequest, callback: CefCallback): Boolean {
116-
117117
Log.log(logger::trace, "processing request {}, [request id:{}]", request.url, request.identifier)
118118

119-
try {
119+
if ((request.postData?.elementCount ?: -1) > 1) {
120+
Log.log(
121+
logger::warn,
122+
"encountered multi part post data. it is not supported by Digma api proxy, [request id:{}]",
123+
request.identifier
124+
)
120125

121-
val apiUrl = buildProxyUrl(buildApiBaseUrl(), request.url)
122-
123-
Log.log(logger::trace, "proxying to api url {}, [request id:{}]", apiUrl, request.identifier)
124-
125-
apiResponse = if ((request.postData?.elementCount ?: -1) > 1) {
126-
127-
//todo: we don't support multi part form data yet
128-
129-
Log.log(
130-
logger::warn,
131-
"encountered multi part post data. it is not supported by Digma api proxy, [request id:{}]",
132-
request.identifier
133-
)
134-
135-
HttpResponse(
136-
500,
137-
mutableMapOf(),
138-
null,
139-
"text/plain",
140-
"encountered multi part post data. it is not supported by Digma api proxy.".byteInputStream()
141-
)
142-
} else if (hasFileElements(request.postData)) {
143-
144-
//todo: we don't support multi part form data yet or file elements in the post data
145-
146-
Log.log(
147-
logger::warn,
148-
"encountered file element in post data. it is not supported by Digma api proxy, [request id:{}]",
149-
request.identifier
150-
)
151-
152-
HttpResponse(
153-
500,
154-
mutableMapOf(),
155-
null,
156-
"text/plain",
157-
"encountered file element in post data. it is not supported by Digma api proxy.".byteInputStream()
158-
)
159-
} else {
160-
val body: HttpRequestBody? =
161-
request.postData?.let {
162-
HttpRequestBody(postDataToByteArray(request, it))
163-
}
164-
165-
Log.log(logger::trace, "body for request is {}, [request id:{}]", body, request.identifier)
166-
167-
val headers = mutableMapOf<String, String>()
168-
request.getHeaderMap(headers)
169-
val httpRequest = HttpRequest(request.method, apiUrl, headers.toMutableMap(), body)
170-
171-
Log.log(logger::trace, "sending request {}, [request id:{}]", httpRequest, request.identifier)
172-
AnalyticsService.getInstance(project).proxyCall(httpRequest)
173-
}
126+
apiResponse = HttpResponse(
127+
500,
128+
mutableMapOf(),
129+
null,
130+
"text/plain",
131+
"encountered multi part post data. it is not supported by Digma api proxy.".byteInputStream()
132+
)
133+
callback.Continue()
134+
return true
135+
}
174136

175-
Log.log(logger::trace, "got api response {}, [request id:{}]", apiResponse, request.identifier)
176137

177-
if (apiResponse == null) {
178-
Log.log(logger::warn, "apiResponse is null , canceling request {}, [request id:{}]", request.url, request.identifier)
179-
callback.cancel()
180-
return false
181-
}
138+
if (hasFileElements(request.postData)) {
139+
140+
//todo: we don't support multi part form data yet or file elements in the post data
141+
142+
Log.log(
143+
logger::warn,
144+
"encountered file element in post data. it is not supported by Digma api proxy, [request id:{}]",
145+
request.identifier
146+
)
182147

148+
apiResponse = HttpResponse(
149+
500,
150+
mutableMapOf(),
151+
null,
152+
"text/plain",
153+
"encountered file element in post data. it is not supported by Digma api proxy.".byteInputStream()
154+
)
183155
callback.Continue()
184156
return true
185-
} catch (e: Throwable) {
157+
}
158+
159+
160+
//the request is valid only in the scope of this method , so take the data we need before starting a background thread
161+
val postData = request.postData?.let {
162+
postDataToByteArray(request, it)
163+
}
164+
val requestId = request.identifier
165+
val requestUrl = request.url
166+
val requestMethod = request.method
167+
val headers = mutableMapOf<String, String>()
168+
request.getHeaderMap(headers)
169+
170+
Backgroundable.executeOnPooledThread {
171+
proxyRequest(requestId, requestUrl, requestMethod, postData, headers, callback)
172+
}
173+
174+
return true
175+
}
176+
177+
178+
private fun proxyRequest(
179+
requestId: Long,
180+
requestUrl: String,
181+
requestMethod: String,
182+
postData: ByteArray?,
183+
headers: Map<String, String>,
184+
callback: CefCallback
185+
) {
186+
try {
187+
188+
val apiUrl = buildProxyUrl(buildApiBaseUrl(), requestUrl)
189+
190+
Log.log(logger::trace, "proxying to api url {}, [request id:{}]", apiUrl, requestId)
191+
192+
val body: HttpRequestBody? =
193+
postData?.let {
194+
HttpRequestBody(postData)
195+
}
196+
197+
Log.log(logger::trace, "body for request is {}, [request id:{}]", body, requestId)
198+
199+
val httpRequest = HttpRequest(requestMethod, apiUrl, headers.toMutableMap(), body)
186200

187-
Log.warnWithException(logger, e, "processRequest {} failed, [request id:{}]", request.url, request.identifier)
201+
Log.log(logger::trace, "sending request {}, [request id:{}]", httpRequest, requestId)
202+
apiResponse = AnalyticsService.getInstance(project).proxyCall(httpRequest)
203+
Log.log(logger::trace, "got api response {}, [request id:{}]", apiResponse, requestId)
204+
205+
} catch (e: Throwable) {
206+
Log.warnWithException(logger, e, "processRequest {} failed, [request id:{}]", requestUrl, requestId)
188207
ErrorReporter.getInstance().reportError("ApiProxyResourceHandler.processRequest", e)
189208

190209
apiResponse = HttpResponse(
@@ -194,14 +213,12 @@ class ApiProxyResourceHandler(val project: Project) : CefResourceHandler {
194213
"text/plain",
195214
"encountered exception in proxy [$e]. please check the logs".byteInputStream()
196215
)
197-
216+
} finally {
198217
callback.Continue()
199-
return true
200218
}
201219
}
202220

203221

204-
205222
override fun getResponseHeaders(response: CefResponse, responseLength: IntRef, redirectUrl: StringRef) {
206223
apiResponse?.let { res ->
207224
response.status = res.status

ui-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9.0.1
1+
9.2.0

0 commit comments

Comments
 (0)