How to ignore request body and redirect request using akka-http - akka-http

Using latest akka-http I want to implement an endpoint which will redirect all incoming upload file requests without consuming request entity.
Current implementation is using :
put {
extractRequest { r: HttpRequest =>
onComplete(r.discardEntityBytes().future) { done =>
redirect(Uri("http://example.com"), TemporaryRedirect)
}
}
}
The problem is that it waits until whole http request body is received (discarded) and only after that sends redirect response. From client perspective it means uploading file twice. I tried to add withSizeLimit(0) to request entity, but it introduces early response problem.
Related documentation:
http://doc.akka.io/docs/akka/2.4.11/scala/http/implications-of-streaming-http-entity.html
http://doc.akka.io/docs/akka/2.4.11/scala/http/routing-dsl/directives/route-directives/redirect.html#description

I did something similar, today. Are you sure that the discarding needs to be completed prior to the redirect?
How about this:
put {
extractRequest { r: HttpRequest =>
r.discardEntityBytes() // runs wild
redirect(Uri("http://example.com"), TemporaryRedirect)
}
}
.discardEntityBytes is used to try to "cancel" unnecessary network traffic that the receiver won't need. Here, we set it up asap. It runs on the background (I hope), and returning the redirect would happen also asap.
I'd be interested to hear, whether this changed anything...

Related

Expressjs send content after listen

I want to make a route in express js to send some content after 1000 ms.
Note: I cant use res.sendFile, it has to be a plain route.
This is the code for the route:
app.get('/r', (req,res)=>{
res.send("a")
setTimeout(()=>{
res.send("fter")
}, 1000)
}
app.listen(8080)
But I get the error: ERR_HTTP_HEADERS_SENT, I assume because the page has already been loaded.
I need my node program to send it after it has already been loaded, so I cant like send a html,js,css script to do it. Is it possible? I cant seem to find how.
Well, if that is not possible, what I am really trying to do is after the page has loaded, execute js or send a message that the page can receive from the node program, like if there was res.execute_js('postMessage(1)')
EDIT based on your edit: So as I understand you want a way to send different values from a node app endpoint without using socketio. I've managed to replicate a similar experimental behavior using readable streams. Starting off, instead of returning response to the request with res.send() you should be using res.write() In my case I did something like this:
app.post('/api', (req, res) => {
res.write("First");
setTimeout(() => {
res.write("Second");
res.end();
}, 1000);
});
This will write to a stream "First" then after 1000ms it'll write another "Second" chunk then end the stream, thus completing the POST request.
Now in the client, you'll make the fetch response callback async, get the ReadableStream from the request like so
const reader = response.body.getReader();
now we should be reading this stream, we'll first initialize an array to collect all what we're reading,
const output = [];
now to actually read the stream,
let finished, current;
while (!finished) {
({ current, finished} = await reader.read());
if (finished) break;
output.push(current);
}
if you read current in the loop, it'll contain each value we passed from res.write() and it should read twice, "First" and after 1000ms "Second".
EDIT: This is very experimental however, and I wouldn't recommend this in a production codebase. I'd suggest trying out socketio or a publish/subscribe mechanism instead.
Old answer: You're already sending "a" back, you should remove the first res.send() invocation at the top of the callback.
So, this is for all the people wondering. No you cannot do this with pure express (there is a workaround, so keep reading).
The reason you cant do this is because, when the user requests to the url, it sends them a response, and the browser renders it. You cant then tell it to change the response, as the browser has already received a response. Even if you send multiple, like with res.write, rather then res.send, the browser will just wait until it receives all the data.
Here are two workarounds:
    1. Use socket.io, cscnode, or another library to have events for updating text,
    2. Send hardcoded html, that updates text (1 was probably better)
That is all I think you can do.
More clarification on the socketio one is basically have an event for changing text that you can fire from node, and the browser will understand, and change the text.

log request for "CORS policy execution failed"

My ASP.NET Core 3.0 in a particular configuration/deployment logs:
[INF] CORS policy execution failed.
[INF] Request origin https://bla.com does not have permission to access the resource.
How can I log at that point the resource that was requested for debugging ?
(note this question is not about the actual issue or solving it)
(note that I am not after globally increasing the log level etc)
Well, that middleware is locked down pretty badly, and I haven't found any sensible way to hook into it.
If you want to replace the CorsMiddleware, you can't just create a decorator that calls Invoke() on the middleware, because you'll have no idea what happened.
Another solution might be to replace the CorsService:ICorsService registration in the service collection with a decorator, and then check the CorsResult after delegating the call to EvaluatePolicy(). That way you could emit an additional log message close to where the original message is emitted.
But there is another possible solution, both very simple and very crude: To check what happened in the request. Albeit that is a bit farther away from the original logged message.
The code below is a delegate added to the pipeline (in Startup/Configure, before .UseCors()) that checks if the request was a preflight request (the same way CorsService does), and if it was successful, i.e. the AccessControlAllowOrigin header is present. If it wasn't successful, it logs a message with the same EventId and source as the CorsService.
app.Use(async (ctx, next) =>
{
await next();
var wasPreflightRequest = HttpMethods.IsOptions(ctx.Request.Method)
&& ctx.Request.Headers.ContainsKey(CorsConstants.AccessControlRequestMethod);
var isCorsHeaderReturned = ctx.Response.Headers.ContainsKey(HeaderNames.AccessControlAllowOrigin);
if (wasPreflightRequest && !isCorsHeaderReturned)
{
ctx.RequestServices.GetRequiredService<ILoggerFactory>()
.CreateLogger<CorsService>()
.LogInformation(new EventId(5, "PolicyFailure"),
$"CORS preflight failed at resource: {ctx.Request.Path}.");
}
});
Based on my testing it seems to work. ¯\_(ツ)_/¯
It might not be what you were looking for, but who knows, maybe it will be useful for someone.
(Obviously a good way to deal with these things is to use a structured logging solution, like Serilog, and add enrichers to capture additional request information, or add stuff manually to a diagnostic context. But setting that up is quite a bit more involved.)

Restangular: How to get HTTP response header?

I have a REST server which returns a Link HTTP header with a response to a PUT to indicate the URL of the newly-created entity:
Link:<entities/6>; rel="created"
Is there any possibility to read that link header with Restangular?
The only facility to intercept HTTP requests with Restangular I've met so far is to register a response interceptor in the app config:
restangular.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
console.log(response.headers())
return response.data;
});
However, with above demo implementation in place, the only HTTP header which gets logged is content-type. Still, I can see in the browser development toolbar that a response comes indeed with many additional HTTP response headers, such as Server, Date, and the aforementioned Link.
Why do I not have access to the full array of HTTP response headers through addResponseInterceptor()? Is there any way to capture the HTTP response header in question using Restangular?
Note: I don't search for an answer using HAL or any other special response body format. I would rather like to know whether I can use plain HTTP headers with Restangular or not (if not, I will probably resort to HAL or something).
You don't need a ResponseInterceptor to do this. You just need to set fullResponse to true to get the whole response (including the http headers) every time you do any request.
Restangular.setFullResponse(true);
You can set it globally in your app configuration. Something like this:
angular.module('YourApp')
.config(['RestangularProvider',
function (RestangularProvider) {
RestangularProvider.setFullResponse(true);
...
Then, every time you receive a response, you can access all the response headers.
Restangular.all('users').getList().then(function(response) {
$scope.users = response.data;
console.log(response.headers);
}
Example:
response.headers('Link')
NOTE: Be careful because using fullResponse, the response data is located in response.data, not directly in response.
EDIT: As #STEVER points, you also need to expose the headers in your server API.
Example:
Access-Control-Expose-Headers: Link
You can get more detailed information in Restangular documentation
Hope it helps.

Intercept Requests With Custom Responses in PhantomJS?

Is there a way to intercept a resource request and give it a response directly from the handler? Something like this:
page.onRequest(function(request){
request.reply({data: 123});
});
My use case is for using PhantomJS to render a page that makes calls to my API. In order to avoid authentication issues, I'd like to intercept all http requests to the API and return the responses manually, without making the actual http request.
onResourceRequest almost does this, but doesn't have any modification capabilities.
Possibilities that I see:
I could store the page as a Handlebars template, and render the data into the page and pass it off as the raw html to PhantomJS (instead of a URL). While this would work, it would make changes difficult since I'd have to write the data layer for each webpage, and the webpages couldn't stand alone.
I could redirect to localhost, and have a server there that listens and responds to the requests. This assumes that it would be ok to have an open, un-authenticated version of the API on localhost.
Add the data via page.evaluate to the page's global window object. This has the same problems as #1: I'd need to know a-priori what data the page needs, and write server side code unique to each page.
I recently needed to do this when generating pdfs with phantom js.
It's slightly hacky, but seems to work.
var page = require('webpage').create(),
server = require('webserver').create(),
totallyRandomPortnumber = 29522,
...
//in my actual code, totallyRandomPortnumber is created by a java application,
//because phantomjs will report the port in use as '0' when listening to a random port
//thereby preventing its reuse in page.onResourceRequested...
server.listen(totallyRandomPortnumber, function(request, response) {
response.statusCode = 200;
response.setHeader('Content-Type', 'application/json;charset=UTF-8');
response.write(JSON.stringify({data: 'somevalue'}));
response.close();
});
page.onResourceRequested = function(requestData, networkRequest) {
if(requestData.url.indexOf('interceptme') != -1) {
networkRequest.changeUrl('http://localhost:' + totallyRandomPortnumber);
}
};
In my actual application I'm sending some data to phantomjs to overwrite request/responses, so I'm doing more checking on urls both in server.listen and page.onResourceRequested.
This feels like a poor-mans-interceptor, but it should get you (or whoever this may concern) going.

How to access error response body with AngularJS

my head is spinning cause of the following issue. I'm accessing my webservice (running on my localhost:4434) with AngularJS and if something goes wrong, the webservice sends a response 400 containing a json body which contains a message that tells you what exactly went wrong.
Problem is I cannot access the message on the client? It is almost as if it never reaches the client?? (This isn't the case, I've confirmed that it reaches the client already) This is the angular code that I use on the client site.
$scope.create = function() {
$http.post('http://localhost:4434/scrapetastic/foo', $scope.bar).
success(function(data, status, headers, config) {
console.log("Call to log: "+status);
console.log("Call to log: "+data);
}).
error(function(data, status) {
console.log("Error|Data:"+data);
console.log(status);
});
}
If I submit malformed data a corresponding error response is generated but as I said ... somehow I cannot access the message that is contained in the response body. This is what I get:
I've tried all sorts of things but am seriously stuck now...perhaps someone has an idea on how to access the payload of the response or at least what to do next? I'm also dealing with CORS perhaps it has something to do with that.
Thanks!
I'm going to take a wild guess here and say that your problem is an XSS issue.
Not only do you not have the data variable, but as far as I can tell from your screenshot, status == 0.
Your screenshot also says Origin: http://localhost, which makes this request considered XSS (since the port is different). That would explain why status is 0.
Edit: You can use jsonp to get around the issue.