File upload in case of HTTP 307 - java

I am writing a Spring controller that handles the HTTP PUT request from client, and generates S3 pre-signed url and issues a HTTP 307 status (Temp redirect) code. So basically I am authenticating the client and if it succeeds then I am asking him to write to a s3 folder. The client is able to write to signed url location.
Now my concern is the client will have to do upload two times. Once to my application server and then to s3, so the operation will take double the time.
Is my understanding correct?Does the client actually does 2 write in this case? Or is the client smart enough and just pushes the part of payload first and if it succeeds then pushes entire payload?
I read about HTTP 100 status code, but looks like the app server/tomcat already issues it and is not in my control.
Here is my spring controller
#RequestMapping("/upload")
public ResponseEntity<Void> execute(HttpServletRequest request) throws IOException, ServletException {
HttpHeaders headers = new HttpHeaders();
String redirectUrl = getRedirectUrl(requestURI, request.getMethod());
headers.setLocation(new URI(redirectUrl));
ResponseEntity<Void> redirectEntity = new ResponseEntity<Void>(null,headers,HttpStatus.TEMPORARY_REDIRECT);
return redirectEntity;
}
How can i prevent clint from uploading the entire payload to my app server?

So my understanding correct?
Answer is YES. Server will send the response of PUT request after reading the full request including body. when you client will repeat the request, in response 307 (Temporary Redirect), it will be like a new http request.
Also an important point on using 307 response code from spec(see below) should be considered for this approach.
If the 307 status code is received in response to a request other
than GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.
On point
How can i prevent client from uploading the entire payload to my app server?
You may do upload to s3 in background from your controller and return the redirect response (301?) point to an URL which will return the status of upload request.

This just isn’t how HTTP works, HTTP has no mechanism to halt a file upload other than closing the connection, but if you close the connection you cant return the redirect information.
If you want the client to upload directly to S3, you will need to do it in two steps.
Have the client request the URL for the file transfer, then have them initiate the transfer with the desired URL.

Related

400 Bad request on Java Webclient multipart/formdata post request

Im having problems on posting a multipart/formdata request to a REST api. The request returns an 400 Bad Request response.
This is how the request should look like. The link shows you a screenshot captured on a successful request by the web interface.
Successful request
This is the Java code I created.
public void importModel(String projectId, String modelId, MultipartFile file, String fileName) throws IOException {
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("data", file.getBytes(), MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "form-data; name=data; filename=" + fileName);
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
WebClient webClient = WebClient.builder()
.filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
})
.build();
String request = webClient.post()
.uri(getBaseUriBuilder()
.pathSegment(getTeamSlug())
.path(API_PATH_PROJECTS)
.pathSegment(projectId)
.path(API_PATH_MODEL)
.pathSegment(modelId)
.path("/importasync")
.build())
.contentType(MediaType.MULTIPART_FORM_DATA)
.contentLength(file.getSize())
.header(HttpHeaders.AUTHORIZATION, getPrefixedAuthToken())
.body(BodyInserters.fromMultipartData(parts))
.exchange()
.flatMap(FlatService::apply)
.block();
return;
}
Any help is much appreciated. Thank in advance!
Have you tried to send the request with alternative Software like POSTMAN.
There you can check for the request properties that are being sent with the request
a 400 error can occur due to the following issues with your request
Wrong URL: Same as 404-Error a Bad Request is generated, when the user types in a wrong internet address or he adds special chars to the address.
Error full Cookies: If the Cookie inside your browser is to old or broken it can also be a 400.
Old outdated DNS-Entries: In your DNS-Cache could lie files that point to wrong or outdated IP- addresses
Too big files: when you try to upload very large files, the server can deny the request.
Too long header lines: the communication between the client and server is done with header information about the request. some servers set a limit to the header length.
Also if you can find out the more specific 400 error like this:
400.1: Invalid Destination Header
400.2: Invalid Depth Header
400.3: Invalid If Header
400.4: Invalid Overwrite Header
400.5: Invalid Translate Header
400.6: Invalid Request Body
400.7: Invalid Content
400.8: Invalid Timeout
400.9: Invalid Lock Token
If you are not the server admin you could ask him about specifications of the server. or use tools like postman where you can try to send requests to the server and find out more specific error codes.

How to send body as a Json in RestTemplate to get data from API [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
HTTP GET with request body
I've read few discussions here which do not advocate sending content via HTTP GET. There are restrictions on the size of data that can be sent via clients (web browsers). And handling GET data also depends on servers. Please refer section Resources below.
However, I've been asked to test the possibility to send content via HTTP GET using RestTemplate. I refered few discussions on spring forum but they were not answered. (Please note sending data via http Post works fine). The discussion here suggests using POST instead.
dev env - JBoss AS 5.1, Spring 3.1.3
Client
#Test
public void testGetWithBody()
{
// acceptable media type
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.TEXT_PLAIN);
// header
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
// body
String body = "hello world";
HttpEntity<String> entity = new HttpEntity<String>(body, headers);
Map<String, Object> uriVariables = new HashMap<String, Object>();
uriVariables.put("id", "testFile");
// Send the request as GET
ResponseEntity<String> result = restTemplate.exchange(
"http://localhost:8080/WebApp/test/{id}/body",
HttpMethod.GET, entity, String.class, uriVariables);
Assert.assertNotNull(result.getBody());
}
Server #Controller
#RequestMapping(value = "/{id}/body", method = RequestMethod.GET)
public #ResponseBody
String testGetWithBody(#PathVariable String id,
#RequestBody String bodyContent)
{
return id + bodyContent;
}
The problem -
executing this test case returns 500 Internal Server Error. On debugging, I found that the controller is not hit.
Is it correct to understand that the RestTemplate provides the way to send data as request body, but the error occurs because the server could not handle the request body ?
If the request body sent via HTTP Get is not conventional why does RestTemplate provide the APIs to allow sending it ? Does this mean there are few servers capable of handling the Request body via GET ?
Resources - discussions on sending body via HTTP GET using RestTemplate at spring forum
http://forum.springsource.org/showthread.php?129510-Message-body-with-HTTP-GET&highlight=resttemplate+http+get
http://forum.springsource.org/showthread.php?94201-GET-method-on-RestTemplate-exchange-with-a-Body&highlight=resttemplate+http+get
Resources - General discussions on sending body via HTTP GET
get-with-request-body
is-this-statement-correct-http-get-method-always-has-no-message-body
get-or-post-when-reading-request-body
http-uri-get-limit
Is it correct to understand that the RestTemplate provides the way to send data as request body, but the error occurs because the server could not handle the request body ?
You can tell by looking at network traffic (does the request get sent with a request body and a GET method?) and at server logs (the 500 result you receive must have a server-side effect that gets logged, and if not, configure the server to do so).
If the request body sent via HTTP Get is not conventional why does RestTemplate provide the APIs to allow sending it ? Does this mean there are few servers capable of handling the Request body via GET ?
Because it is a generic class that also allows you to craft requests that can include a message body.
As stated in HTTP GET with request body:
In other words, any HTTP request message is allowed to contain a message body, and thus [a server] must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
A body on a GET cannot do anything semantically, because you are requesting a resource. It's like you tell the server: "Give me resource X, oh, and have some apples!". The server won't care about your apples and happily serve resource X - or throw an error because it doesn't like any offers in a request.
However, I've been asked to test the possibility to send content via HTTP GET
Please tell the one who requested this that this is a case that should not have to be tested, because no sensible implementation supports it.

Java Server Side: send Http POST response - status only

Some third party is sending an Http Post request whenever something changes in their DB (e.g. when a contact has been updated, they send the contactID and 'contact_updated'). I have build a socket listener that catches those requests and is able to parse the information. However, I just can't get it to work to send back a response with the status '200 - OK'. Thus, the server on the client side keeps on trying (four times or so) to re-send the request.
Is there any, simple way to just send the response status without the need of adding external libs etc.?
It should be enough to send the string HTTP/1.1 200 OK back in your socket-listener.
If you have troubles, you can check out this answer, it shows how to use a HttpServer in Java just via plain JavaSE features.
Use
response.setStatus(HttpServletResponse.SC_OK);
to set the status code in your response header.
You may also set the content type.
response.setContentType("text/html;charset=UTF-8");

How to make client not disconnect when server response takes a long time

I have a Java backend with REST-API and an Angularjs frontend. Users can use the frontend to request information from the backend. When this happens, the backend generates a file on the fly and sends it to the frontend. So far so good.
The problem occurs when users request large amounts of information. This makes generating the file take so long that the frontend times out and aborts the connection.
Is there a way to reassure the client that a response is actually going to come, or increase the timeout limit for this one endpoint only? Alternatively, is there a way for the server to send two responses, one immediately after receiving the request and one after the file is generated?
The API endpoint looks like this:
#Path("download")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
#Consumes(MediaType.APPLICATION_JSON)
public Response download() {
StreamingOutput stream = //stream containing file
return Response.ok(stream)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"download.xlsx\"")
.build();
}
The frontend makes the request by doing window.open(download url, '_blank', '') (the content of the file depends on previous input from the user).

HTTP: what is the correct way to send "retry/redirect" response

I need to force the client to retry its request (meaning to send the same request one more time). What I'm thinking of is a response with status-code 307 and header Location: <original-url> (that's good enough for now, unless there's a better way).
My question is, from HTTP specification point of view, what is the correct value for Location in this specific context. Or more specifically in Java having request of type HttpServletRequest, which one should I use: getRequestURI (Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request) or getRequestURL (Reconstructs the URL the client used to make the request containing protocol, server name, port number, and server path, but it does not include query string parameters).
Any other suggestion/comment is appreciated.
getRequestURL() returns complete URL used by the client where as getRequestURI() returns just the basic path resides in server.
i am using this technique to redirect with a response status this is my code this is useful:-
httpServletResponse.reset();
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpServletResponse.setHeader("SERVER-RESPONSE", "bad request");
return;
and also you can set headers in response.
I believe a redirect is the wrong status code in the first place.
Isn't this what 503 is for? (https://www.greenbytes.de/tech/webdav/rfc7231.html#status.503)

Categories