Open TharmiganK opened 1 year ago
Instead of accessing the byteStream inside the passthrough service, respond with the response.
resource function get test/v1(http:Caller caller) returns error? {
http:Response backendResp = check clientEP->/file/pdf/v1;
return caller->respond(backendResp);
}
@TharmiganK Can I Know the rough timeline for this issue? Is this issue prioritized, and When will this be fixed?
@lilRaptor99 we have not prioritised this yet. If this is a blocker for you, you could try reading the file as byte[]
rather than a stream.
Sample backend service :
import ballerina/io;
import ballerina/http;
service /file on new http:Listener(8080) {
resource function get pdf/v1() returns byte[]|error {
byte[] & readonly file = check io:fileReadBytes("resources/sample.pdf");
return file;
}
}
Sample passthrough service :
import ballerina/http;
final http:Client clientEP = check new("http://localhost:8080");
service on new http:Listener(9090) {
resource function get test() returns http:Response|error {
byte[] file = check clientEP->/file/pdf/v1;
http:Response res = new;
res.setBinaryPayload(file, "application/pdf");
// Do any other processing
return res;
}
}
This is not a blocker at the moment. But this will be an issue in the future. Its better if we can get this fixed within at least 2 months. I need to use a steam because the file may be large, and storing it in memory (in a variable) is not really a good solution; Piping the data stream directly to the client is a better approach.
This is not a blocker at the moment. But this will be an issue in the future. Its better if we can get this fixed within at least 2 months. I need to use a steam because the file may be large, and storing it in memory (in a variable) is not really a good solution; Piping the data stream directly to the client is a better approach.
Ack, will try to prioritise this in the next release 👍
Attaching a simple reproducer for this issue:
import ballerina/io;
import ballerina/http;
service /file on new http:Listener(8080) {
resource function get pdf/v1(http:Caller caller) returns error? {
stream<io:Block, io:Error?> fileStream = check io:fileReadBlocksAsStream("resources/sample.pdf");
check caller->respond(fileStream);
}
}
2. passthrough service :
```bal
import ballerina/io;
import ballerina/http;
final http:Client clientEP = check new("http://localhost:8080");
service on new http:Listener(9090) {
resource function get test() returns http:Response|error {
http:Response backendRes = check clientEP->/file/pdf/v1;
stream<byte[], io:Error?> fileStream = check backendRes.getByteStream();
http:Response res = new;
// Do any other processing
res.addHeader("File-Name", "sample.pdf");
res.setByteStream(fileStream, "application/pdf");
return res;
}
}
When analysing this, found out that, returning the http:Response
rather than using caller->respond
has some multiple back and forth calls among ballerina and the native runtime. So after this multiple calls, the runtime.invokeasync
method is not handled properly by the schedular. This will be analyzed and fixed by the runtime. Ill tag the issue here as well.
Transfered this to be followed up from runtime. Will create a separate issue to keep track of this from http module.
Will add a little detail for this.
import ballerina/io;
import ballerina/http;
service /file on new http:Listener(8080) {
// Using the caller to responsd
resource function get pdf/v1(http:Caller caller) returns error? {
check caller->respond(check io:fileReadBlocksAsStream("resources/sample.pdf"));
}
// Using the response object
resource function get pdf/v2() returns http:Response|error {
http:Response res = new;
res.setByteStream(check io:fileReadBlocksAsStream("resources/sample.pdf"));
return res;
}
}
For the above service, pdf/v1
(case 1) works and pdf/v2
(case 2) hangs.
In the implementation, Both of these scenarios go and invoke the mime:next
function via the mime-native
using runtime.invokeAsync
call [1] .
But for case 1, it doesnt go back and forth among ballerina and java. But for case 2, we return from the service to the notifySuccess
of the http-native
and then we invoke a ballerina FunctionA
via that. In the FunctionA
we again call a native method MethodA
. Inside the MethodA
we again call a ballerina function FunctionB
. Here when we call the runtime.invokeMethodAsync
, we use a CountDownLatch
to get the result synchronously.
However this gets stuck and the ballerina function FunctionB
does not get invoked until the CountDownLatch
timesout.
Description:
Steps to reproduce:
Add a sample pdf file(
sample.pdf
) toresources
directory.Run the following backend service :
http://localhost:8080/file/pdf/v1
http://localhost:8080/file/pdf/v2
http://localhost:9090/test
http://localhost:9090/test/v1
http://localhost:9090/test/v2
Affected Versions:
Ballerina SwanLake 2201.3.2