I have a GWT module and in it I navigate to a different URL via:
Window.Location.assign(url);
The navigated url is then handled by a servlet, up until this point if there was an error it was handle by the resp.sendError methode
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed.");
Which would then navigate to the browsers error page. However I wanted to know is there away I can not navigate to an error page? i.e. I would be able to check in my GWT code if there was an error and then do something? Like resend the request ect.
Thanks!
When you navigate away from your webapplication that's that. Instead of using Window.Location.assign you should make an HTTP request still from your webapplication, for example using RequestBuilder.
Example from the docs mentioned earlier:
import com.google.gwt.http.client.*;
...
String url = "http://www.myserver.com/getData?type=3";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
try {
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
// Couldn't connect to server (could be timeout, SOP violation, etc.)
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
// Process the response in response.getText()
} else {
// Handle the error. Can get the status text from response.getStatusText()
}
}
});
} catch (RequestException e) {
// Couldn't connect to server
}
Note that this will work only if your servlet and webapplication are on the same address (domain, port, protocol), because of Same Origin Policy. If that's not the case, there are still some options, like JSON with padding (which GWT supports via JsonpRequestBuilder).
Related
I have a function in my #Service class that is marked with #HystrixCommand.
This method acts as a client which sends a request to another service URL and gets back a response.
What I want to do is to trigger a fallback function when the response status code is anything other than 200. It will also trigger a fallback for any other exceptions (RuntimeExceptions etc.).
I want to do this by making use of the #HystrixProperty or #HystrixCommandProperty.
I want the client to ping the URL and listen for a 200 response status and if it does not get back a 200 status within a certain time-frame I want it to fallback.
If it gets back a 200 status normally within a certain time it should not trigger the fallback.
#HystrixCommand(fallbackMethod="fallbackPerformOperation")
public Future<Object> performOperation(String requestString) throws InterruptedException
return new AsyncResult<Object>() {
#Override
public Object invoke() {
Client client = null;
WebResource webResource = null;
ClientResponse response =null;
String results = null;
try{
client = Client.create();
webResource = client.resource(URL);
client.setConnectTimeout(10000);
client.setReadTimeout(10000);
response = webResource.type("application/xml")
.post(ClientResponse.class, requestString);
} finally {
client.destroy();
webResource = null;
}
return results;
}
};
}
I specifically want to make use of the #HystrixProperty or #HystrixCommandProperty so performing a check inside the method for response status code not being 200 and then throwing an Exception is not acceptable.
Instead of using Annotations will creating my own Command by extending the HystrixCommand Interface work?
Any ideas or resources for where I can start with this are more than welcome.
I don’t understand why you don’t want to check the response http status code and throw an exception if it is not 200? Doing that will give you the behaviour you desire. i.e. it will trigger a fall back for exceptions or non 200 responses.
You can set the timeout in the client, however I would opt for using the hystrix timeout values. That way you can use Archaius to dynamically change the value at runtime if desired.
You can use the Hystrix command annotation or extend the HystrixCommand class. Both options will provide you with your desired behaviour
Here is an example using the annotation.
#HystrixCommand(fallbackMethod = "getRequestFallback")
public String performGetRequest(String uri) {
Client client = Client.create();
WebResource webResource = client.resource(uri);
ClientResponse response = webResource.get(ClientResponse.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Invalid response status");
}
return response.getEntity(String.class);
}
public String getRequestFallback(String uri) {
return "Fallback Value";
}
I've devloped a chat bot application using the Facebook Messenger platform.
I used Spring Boot with embedded Tomcat for the web platform.
The application should run on Amazon aws, open to the WWW, and to be used as a webhook for recieving callbacks from Messenger over https.
I need an advice how to secure the application, so it won't be hacked or flooded with requests that are not coming from Facebook.
I thought to make the application require secured (ssl) connection, but using the "security.require_ssl=true" in application.properties didn't do the work. Perhaps I don't know what is the meaning of this and how to configure it propertly.
Is there a best practice how to block requests which are not https requests? Or a way to block requests which are coming outside Messenger in the application level?
Thank you very much!
EDIT
In the meantime, I blocked requests from other IPs in application layer using the handler interceptor:
#Configuration
public class MyWebApplicationInitializer implements WebApplicationInitializer, WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (! (request.getRemoteAddr().equals("173.252.88.66") || request.getRemoteAddr().equals("127.0.0.1")|| request.getRemoteAddr().equals("0:0:0:0:0:0:0:1"))){
logger.warn("Request is not coming from authorized remote address: " + request.getRemoteAddr()+". Rejecting");
response.getWriter().write("Unauthorized Address");
response.setStatus(401);
return false;
} else {
return true;
}
}
}
You should check the X-Hub-signature HTTP header available in the requests sent by Facebook to your webhook URL.
In your case, you may define a filter or interceptor for the verification of the signature. You can also do it in your controller as in the this example I found in RealTimeUpdateController.java from the spring social project.
private boolean verifySignature(String payload, String signature) throws Exception {
if (!signature.startsWith("sha1=")) {
return false;
}
String expected = signature.substring(5);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
SecretKeySpec signingKey = new SecretKeySpec(applicationSecret.getBytes(), HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(payload.getBytes());
String actual = new String(Hex.encode(rawHmac));
return expected.equals(actual);
}
a lot to say so I am sure I will miss some points.
setting SSL is a first good thing but make sure you get a certificate. lets encrypt is a good thing if you dont want to pay for SSL certificate.
Just seeing aws provides an alternative to letsencrypt
Security Group You can see Security Group as something similar to a firewall so you can control which port is opened, external and internal flows.
Look at IAM which control who and how can get access to your AWS account
obvious : change your password. do not let default password for installation you could make on the instance
read some of https://aws.amazon.com/security/security-resources/ to get more information about what you can do
it won't be hacked or flooded with requests
sorry to say but most probably it will be - It does not need to be an advanced hacker to run scanner and scan IPs and check open ports / brute force login etc ...
Thanks to Guy Bouallet help I added the signature check.
I added it in my controller and not in the interceptor, to avoid the problem of How to read data twice in spring which seems a little complicated.
So here is it:
#RequestMapping(path = "/")
public void doHandleCallback(#RequestBody String body, #RequestHeader(value = "X-Hub-Signature") String signature) throws IOException {
if (!verifyRequestSignature(body.getBytes(), signature)){
logger.error ("Signature mismatch.");
throw new MismatchSignatureException(signature);
}
MessengerCallback callback = mapper.readValue(body, MessengerCallback.class);
logger.info("Incoming Callback: " + body );
for (EventData entry : callback.getEntry()) {
for (ReceivedMessagingObject message : entry.getMessaging()) {
if (message.isMessage() || message.isPostback()) {
doHandleMessage(message);
}
else if (message.isDelivery()){
doHandleDelivery(message);
}
}
}
}
private boolean verifyRequestSignature(byte[] payload, String signature) {
if (!signature.startsWith("sha1="))
return false;
String expected = signature.substring(5);
System.out.println("Expected signature: " + expected); //for debugging purposes
String hashResult = HmacUtils.hmacSha1Hex(APP_SECRET.getBytes(), payload);
System.out.println("Calculated signature: " + hashResult);
if (hashResult.equals(expected)) {
return true;
} else {
return false;
}
}
And this is the Exception handling class:
#ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Request Signature mismatch")
public class MismatchSignatureException extends RuntimeException {
private String signature;
public MismatchSignatureException(String signature) {
this.signature = signature;
}
#Override
public String getMessage() {
return "Signature mismatch: " + signature;
}
I am facing a rather simple situation but I cannot wrap my head around it. Maybe the OkHttp gurus could light my path.
I am using Picasso, Retrofit and OkHttp for multiple purposes on my android app. yay!. As I read properly, the developer should make an effort in keeping on OkHttpClient (as read here ).
With the approach in mind, I want any of my HTTP calls (be it an API call, an Image loading, a resource download) to :
Send the request
If HTTP401 is received, then Send another HTTP Request that sends a token back
When that token is received, the call is re-emitted with that token included in the headers
Any subsequent call (be it an API, a resource or image call) should use that token until the next HTTP401 is received (invalid token).
Of course, I would reuse the same client for Retrofit and Picasso.
One route I am considering is to use a mix of Authenticator and an application Interceptor. Authenticator should catch HTTP401, but can I make it make another sync request in the meantime, store the token and activate the new interceptor ?
Looks like I found the solution myself to that problem so let's share the knowledge to everyone.
In order to this, OkHttp already gives all the necessary hooks.
Make sure to use Authenticator
Install an interceptor once the authenticator succeed
Return a request with the good token.
This also imply that the Authenticator handles an HTTP to set your token back (done in another android service).
okHttpClient.setAuthenticator(new Authenticator() {
#Override
public Request authenticate(Proxy proxy, Response response) {
AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType(Authenticator.ACCOUNT_TYPE);
// No account, do not even try to authenticate
if (accounts.length == 0) {
Log.i(TAG, "... But we dont have any account yet, so I will just back off for now.");
return null;
}
Account account = accounts[0];
try {
final String mCurrentToken = accountManager.blockingGetAuthToken(account, "", false);
// For now, we just re-install blindly an interceptor
okHttpClient.interceptors().clear();
Log.i(TAG, "... Installing interceptor after authentication");
okHttpClient.interceptors().add(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("Authorization", mCurrentToken)
.build();
Response response = chain.proceed(newReq);
return response;
}
});
Log.i(TAG, "Install temporary auth token in request");
return response.request().newBuilder()
.addHeader("Authorization", mCurrentToken)
.build();
} catch (OperationCanceledException e) {
Log.e(TAG, "Interrupted exception");
return null;
} catch (AuthenticatorException e) {
Log.e(TAG, "Authentication error");
return null;
} catch (IOException e) {
Log.e(TAG, "IO Error");
return null;
}
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) {
return null; // Null indicates no attempt to authenticate.
}
})
With this, just use this OkClient in Picasso and Retrofit.
I have a asp.net web api page where the user can upload some files. I am using jquery-file-upload. Based on some condition, I want to cancel the upload from the server side but it is not working. No matter what I do, the file always goes to the server before asp.net returns the error. Example, I can keep just this when uploading:
public async Task<HttpResponseMessage> Post(int id, CancellationToken token)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Cant upload");
}
If I put a breakpoint on the return, I can see that it is hitted as soon as the upload starts but I have to wait the upload to end and only then the javascript error handler gets called. Is it not possible to end the request imediatelly, cancelling the upload?
Update 1:
I replaced jquery-file-upload with jquery-form and now I am using ajaxSubmit on my form. This doen't changed anything.
I also tried to implement a DelegatingHandler, like this:
public class TestErrorHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
//throw new HttpException(403, "You can't upload");
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.ReasonPhrase = "You can't upload";
return Task.FromResult<HttpResponseMessage>(response).Result;
}
}
config.MessageHandlers.Add(new TestErrorHandler());
That did not work either.
And I tried to disable buffer on requests:
public class NoBufferPolicySelector : WebHostBufferPolicySelector
{
public override bool UseBufferedInputStream(object hostContext)
{
return false;
}
}
config.Services.Replace(typeof(IHostBufferPolicySelector), new NoBufferPolicySelector());
No game - it still upload all the file before returning the error.
All I need is to cancel a upload request. Is this impossible with web api or I am missing something here?
I had a similar problem, and the only (admittedly ham-fisted) solution I could find to stop the client from uploading the data was to close the TCP connection:
var ctx = Request.Properties["MS_HttpContext"] as HttpContextBase;
if (ctx != null) ctx.Request.Abort();
This works for IIS hosting.
I am a GWT noob but am working with someone else who is more advanced than I, and we cannot figure out why a cookie being returned by the server as a Set-Cookie HTTP header is not actually being set in the browser.
I wrote a server using Tomcat that has an authentication call. I wrote a dummy website all in HTML that uses web forms to send a request to the server with the authentication information and receives a response that contains a Set-Cookie header. This all works. It then has a second button in a different form on the same page that sends a different request to my server with some form data, and the browser automatically injects the cookie into the header as expected. Therefore, the server, for the second call, can pull the cookie header out of the request and authenticate the request. This all works and is great.
Now, for the test GWT application we have developed, I have used the code that is automatically generated when a new GWT application is developed (no AppEngine) and modified it in the following ways on the client side's EntryPoint class. I removed the TextBox for entering my name and the GWT RPC calls. I modified MyHandler so that it no longer implemented KeyPressedListener or whatever and does implement RequestCallback. I edited the contents of the onClick to create a new RequestBuilder that sends a POST with the authentication information. So far, this all works as I can watch the logs on my server and it receives the request, processes it, and places the authentication cookie in the response. Using Firebug, I can see that the response contains the Set-Cookie header with the necessary cookie information. However, the browser never actually saves this information. Unsurprisingly, a subsequent call to the server doesn't include the cookie.
GWT is just compiled into JavaScript when deployed, correct? And JavaScript can't inject itself between the HTTP response and the browser can it? I have checked the Response object that is a parameter to the onResponseReceived() call from the RequestCallback interface, and it doesn't contain any method to get access to the cookie except through the getHeaders() call. I have dumped the results of this call, though, and it doesn't exist there. Anyway, the browser should at least be getting access to the HTTP header before the code and should be grabbing and setting the cookie values before handing the code to GWT. Not only am I new to GWT, I am new to most HTTP client-side development, but am I really that far off track?
Thank you,
John
Edit:
Here is the code I ended up with. I didn't change anything else in the project.
public void onModuleLoad() {
final Button loginButton = new Button("Login");
final Button requestBuilderButton = new Button("Campaign Read");
final Label errorLabel = new Label();
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("sendButtonContainer").add(loginButton);
RootPanel.get("sendButtonContainer").add(requestBuilderButton);
RootPanel.get("errorLabelContainer").add(errorLabel);
// Create the popup dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Remote Procedure Call");
dialogBox.setAnimationEnabled(true);
final Button closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
final Label textToServerLabel = new Label();
final HTML serverResponseLabel = new HTML();
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);
// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
}
});
// Create a handler for the sendButton and nameField
class LoginHandler implements ClickHandler, RequestCallback {
/**
* Fired when the user clicks on the sendButton.
*/
public void onClick(ClickEvent event) {
dialogBox.show();
serverResponseLabel.setText(Cookies.getCookie("auth_token"));
final String url = "http://localhost:8080/app/user/auth_token";
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, URL.encode(url));
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
StringBuilder parameters = new StringBuilder();
parameters.append("user=username&password=password&client=gwt");
try {
builder.sendRequest(URL.encode(parameters.toString()), this);
}
catch(RequestException e) {
serverResponseLabel.setText(e.toString());
}
}
public void onError(Request request, Throwable exception) {
serverResponseLabel.setText("Failure.");
}
public void onResponseReceived(Request request, Response response) {
textToServerLabel.setText(Integer.toString(response.getStatusCode()));
serverResponseLabel.setText(serverResponseLabel.getText() + Cookies.getCookie("auth_token"));
}
};
class CampaignReadHandler implements ClickHandler, RequestCallback {
public void onClick(ClickEvent event) {
dialogBox.show();
final String url = "http://localhost:8080/app/campaign/read";
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, URL.encode(url));
builder.setHeader("Content-Type", "application/x-www-form-urlencoded");
StringBuilder parameters = new StringBuilder();
parameters.append("output_format=short&client=gwt&campaign_urn_list=urn:andwellness:nih");
try {
builder.sendRequest(URL.encode(parameters.toString()), this);
}
catch(RequestException e) {
serverResponseLabel.setText(e.toString());
}
}
public void onError(Request request, Throwable exception) {
serverResponseLabel.setText("Failure.");
}
public void onResponseReceived(Request request, Response response) {
textToServerLabel.setText(Integer.toString(response.getStatusCode()));
serverResponseLabel.setText(response.getText());
}
};
// Add a handler to send the name to the server
LoginHandler loginHandler = new LoginHandler();
loginButton.addClickHandler(loginHandler);
CampaignReadHandler campaignReadHandler = new CampaignReadHandler();
requestBuilderButton.addClickHandler(campaignReadHandler);
}
This is the expected behavior of browsers: http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method (GWT's Response#getHeaders simply calls getAllResponseHeaders and parses the string).
If you want to get cookies, you have to use the cookies object (Cookies class in GWT); which obviously filters out httponly cookies.
If you are using RequestBuilder to contact the RPC servlet that may be the problem. Especially if you are using a different host in your request, than what you have in your browser.
Say navigating to http://localhost/app
But your RequestBuilder builds a request for http://machinename/app/servlet.
If you are just using RPC without RequestBuilder you shouldn't have these problems.
As well if you are using RequestBuilder you may have to manually provide the cookies via setting that particular header
In browser client development cookies are handled on a host name basis.