I am using RetroFit on android in order to send images to a REST server (developed using Python and Flask). The app sends a first image to the server which stores it and that returns that image's ID to the client. The client then takes another photo and sends it to the server, this time with the previous image ID in the endpoint URL. The server should then compute the rotation from image A to image B and return the result. Once the client has sent the second image, it should repeatedly poll the endpoint for a result until the calculations are completed server side.
The first image sends correctly, and the server stores it. The client then correctly receives the response from the server. The problem occurs when the client sends the second image. The server receives the POST correctly, but the client never seems to get a response. This is also true of the third message, the GET request that polls for a result, it is received by the server but the client never sees a response.
The code for my REST server is here
class Image(Resource):
def post(self):
file = request.files['uploaded_file']
centerX = request.form['center_x']
centerY = request.form['center_y']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], str(id(True)) + ".jpg"))
result = {'id' : id(), 'error' : True, 'result' : 5.5}
return jsonify(result)
else:
return {'id' : -1, 'error' : False}
class Find_Transform(Resource):
def post(self, first_id):
result = {'id' : 1, 'error' : True, 'result' : 1.2}
return jsonify(result)
class Find_Transform_Result(Resource):
def get(self, first_id, second_id):
print("Hello")
result = {'id' : 0, 'error' : True, 'result' : 1.1}
return jsonify(result)
api.add_resource(Image,'/image')
api.add_resource(Find_Transform,'/image/<first_id>/find-transform')
api.add_resource(Find_Transform_Result, '/image/<first_id>/find-transform/<second_id>')
if __name__ == '__main__':
app.run(host='0.0.0.0',port='5002')
As you can see some of the endpoints are still just stubs to test the REST api.
My java Respond.class is here
public class Respond {
private int id;
private Boolean error;
private float result;
public int getMessage() {
return id;
}
public void setMessage(int id) {
this.id = id;
}
public Boolean getError() {
return error;
}
public void setError(Boolean error) {
this.error = error;
}
public void setResult(float result) {
System.out.println("Got Result");
this.result = result;
}
public float getResult() {
return result;
}
}
My Retrofit interface is here
public interface FileApi {
@Multipart
@POST("/image")
Call<Respond> uploadCropImage(@Part MultipartBody.Part file, @Part MultipartBody.Part x, @Part MultipartBody.Part y);
@Multipart
@POST("/image/{first_id}/find-transform")
Call<Respond> uploadCompareImage(@Path("first_id") int id, @Part MultipartBody.Part file);
@GET("/image/{first-id}/find-transform/{second-id}")
Call<Respond> getCompareResult(@Path("first-id") int firstId, @Path("second-id") int secondId);
}
The reason for the two separate file upload functions is that the first image needs to be sent alongside a user selected centre point, while the second is just an image.
Finally the sections where I build my requests are here:
private void uploadFirstImage(byte[] imageBytes) {
FileApi service = RetroClient.getApiService();
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), imageBytes);
MultipartBody.Part fileBody =
MultipartBody.Part.createFormData("uploaded_file", "first_image.jpg", requestFile);
RequestBody image_center_x = RequestBody.create(MediaType.parse("multipart/form-data"), Float.toString(points[0].x));
MultipartBody.Part xBody = MultipartBody.Part.createFormData("center_x", null, image_center_x);
RequestBody image_center_y = RequestBody.create(MediaType.parse("multipart/form-data"), Float.toString(points[0].y));
MultipartBody.Part yBody = MultipartBody.Part.createFormData("center_y", null, image_center_y);
Call<Respond> resultCall = service.uploadCropImage(fileBody, xBody, yBody);
resultCall.enqueue(new Callback<Respond>() {
@Override
public void onResponse(Call<Respond> call, Response<Respond> response) {
// Response Success or Fail
if (response.isSuccessful()) {
if (response.body().getError()==true){
System.out.println(Double.toString(response.body().getResult()));
session_id = response.body().getMessage();
}
} else {
Toast.makeText(getApplicationContext(),"Failed to communicate with server",Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<Respond> call, Throwable t) {
}
});
}
and here:
private void uploadSecondImage(byte[] imageBytes) {
FileApi service = RetroClient.getApiService();
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), imageBytes);
MultipartBody.Part fileBody =
MultipartBody.Part.createFormData("uploaded_file", "first_image.jpg", requestFile);
Call<Respond> resultCall = service.uploadCompareImage(session_id,fileBody);
resultCall.enqueue(new Callback<Respond>() {
@Override
public void onResponse(Call<Respond> call, Response<Respond> response) {
// Response Success or Fail
if (response.isSuccessful()) {
if (response.body().getError()==true){
System.out.println("Got Second Response");
resultID = response.body().getMessage();
}
} else {
Toast.makeText(getApplicationContext(),"Failed to communicate with server",Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<Respond> call, Throwable t) {
System.out.println("Failure");
}
});
}
and finally, for the GET request
final boolean[] resultReady = {false};
while (!resultReady[0]) {
FileApi service = RetroClient.getApiService();
Call<Respond> resultCall = service.getCompareResult(session_id, resultID);
resultCall.enqueue(new Callback<Respond>() {
@Override
public void onResponse(Call<Respond> call, Response<Respond> response) {
// Response Success or Fail
if (response.isSuccessful()) {
if (response.body().getError()){
System.out.println("Got Response");
resultReady[0] = true;
theta[0] = response.body().getResult();
Toast.makeText(getApplicationContext(),"Measurement Recieved",Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(),"Error Recieved",Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(getApplicationContext(),"Failed to communicate with server",Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<Respond> call, Throwable t) {
}
});
}
I've tried putting breakpoints in the OnResponse functions, and have put debug statements in both OnResponse and OnFailure and it seems that that code is never run. What could be going wrong here?
EDIT: Okay so I've got some logging data. I've tested the server from a python console
In [1]: import requests
In [2]: requests.get("http://localhost:5002/image/1/find-transform/2")
Out[2]: <Response [200]>
In [3]: _.text
Out[3]: '{"error": true, "id": 0, "result": 1.1}'
In [4]: requests.post("http://localhost:5002/image/find-transform")
Out[4]: <Response [404]>
In [5]: requests.post("http://localhost:5002/image/1/find-transform")
Out[5]: <Response [200]>
In [6]: _.text
Out[6]: '{"error": true, "id": 1, "result": 1.2}'
I also put the http logging in the client side as suggested by a comment, which gave this logging output.
D/OkHttp: --> POST http://192.168.0.19:5002/image
Content-Type: multipart/form-data; boundary=bc4ae771-2762-4124-8f07-d00dc925d886
D/OkHttp: Content-Length: 2044497
D/OkHttp: --bc4ae771-2762-4124-8f07-d00dc925d886
Content-Disposition: form-data; name="uploaded_file"; filename="first_image.jpg"
Content-Type: multipart/form-data
Content-Length: 2043935
(Truncated raw image data)
V/RenderScript: 0x7f77b4c000 Launching thread(s), CPUs 8
D/OkHttp: <-- 200 OK http://192.168.0.19:5002/image (410ms)
Content-Type: application/json
Content-Length: 39
Server: Werkzeug/0.14.1 Python/3.6.8
Date: Wed, 24 Jul 2019 17:00:47 GMT
D/OkHttp: {"error": true, "id": 9, "result": 5.5}
<-- END HTTP (39-byte body)
I/System.out: 5.5
W/ImageReader_JNI: Unable to acquire a lockedBuffer, very likely client tries to lock more than maxImages buffers
I/System.out: Resultcall
D/OkHttp: --> POST http://192.168.0.19:5002/image/9/find-transform
Content-Type: multipart/form-data; boundary=63de36cb-0961-4802-a97f-57b98ebcd8cb
D/OkHttp: Content-Length: 1655614
D/OkHttp: --63de36cb-0961-4802-a97f-57b98ebcd8cb
Content-Disposition: form-data; name="uploaded_file"; filename="first_image.jpg"
Content-Type: multipart/form-data
Content-Length: 1655386
(Truncated raw image data)
D/OkHttp: <-- HTTP FAILED: java.net.SocketException: sendto failed: EPIPE (Broken pipe)
D/OkHttp: --> GET http://192.168.0.19:5002/image/9/find-transform/-1
--> END GET
D/OkHttp: <-- 200 OK http://192.168.0.19:5002/image/9/find-transform/-1 (90ms)
It seems that the server is both correctly receiving the request, and sending the correct response. So something must be failing to be processed correctly client side, but I can't think what.