We have containerized our DotNetCore app using Docker and running on OpenShift.
We are add few custom response headers and we observed that the custom response headers are returned in Lower case though we specify like "Header-Name" it returns as "header-name"
Googling showed that this is a specification with Http2 protocol.
We tried writing custom middleware to remove the header and add a header again. but still the response headers are in lower case
app.Use(
next =>
{
return async context =>
{
var stopWatch = new Stopwatch();
stopWatch.Start();
context.Response.OnStarting(
() =>
{
stopWatch.Stop();
context.Response.Headers.Add("X-ResponseTime-Ms", stopWatch.ElapsedMilliseconds.ToString());
//returns in lower case
return Task.CompletedTask;
});
await next(context);
};
});
How could we solve this, any hack to solve this ?
Related
We have a requirement of disabling the HTTP methods besides POST, GET and Head in an ASPNET Core Web application due as a part of security fixes. How can we disable the HTTP OPTIONS method in ASP.Net core API?
Allowed 3 methods which are POST,GET and Head.
How to block all the others method which I didn't use in middleware like DELETE,TRACE,PATCH and etc.
Needs to return Error Code 405 = Method Not Allowed . Currently it throws the error 500 which is Internal Server Error
my code right now .
app.Use(async (context, next) =>
{
if (context.Request.Method=="TRACE")
{
context.Response.StatusCode = 405;
return;
}
await next.Invoke();
});
How to Block Http Methods in ASP.NET
You could try as below:
app.MapWhen(x => x.Request.Method == "somemethod",
y => y.Use(async(context,next)=>
{
context.Response.StatusCode = 405;
await context.Response.WriteAsync("Method Not Allowed");
}
));
The Result:
I thought that event.passThroughOnException(); should set the fail open strategy for my worker, so that if an exception is raised from my code, original requests are sent to my origin server, but it seems that it’s missing post data. I think that’s because the request body is a readable stream and once read it cannot be read again, but how to manage this scenario?
addEventListener('fetch', (event) => {
event.passThroughOnException();
event.respondWith(handleRequest(event));
});
async function handleRequest(event: FetchEvent): Promise<Response> {
const response = await fetch(event.request);
// do something here that potentially raises an Exception
// #ts-ignore
ohnoez(); // deliberate failure
return response;
}
As you can see in the below image, the origin server did not receive any body (foobar):
Unfortunately, this is a known limitation of passThroughOnException(). The Workers Runtime uses streaming for request and response bodies; it does not buffer the body. As a result, once the body is consumed, it is gone. So if you forward the request, and then throw an exception afterwards, the request body is not available to send again.
Did a workaround by cloning event.request, then add a try/catch in handleRequest. On catch(err), send the request to origin using fetch while passing the cloned request.
// Pass request to whatever it requested
async function passThrough(request: Request): Promise<Response> {
try {
let response = await fetch(request)
// Make the headers mutable by re-constructing the Response.
response = new Response(response.body, response)
return response
} catch (err) {
return ErrorResponse.NewError(err).respond()
}
}
// request handler
async function handleRequest(event: FetchEvent): Promise<Response> {
const request = event.request
const requestClone = event.request.clone()
let resp
try {
// handle request
resp = await handler.api(request)
} catch (err) {
// Pass through manually on exception (because event.passThroughOnException
// does not pass request body, so use that as a last resort)
resp = await passThrough(requestClone)
}
return resp
}
addEventListener('fetch', (event) => {
// Still added passThroughOnException here
// in case the `passThrough` function throws exception
event.passThroughOnException()
event.respondWith(handleRequest(event))
})
Seems to work OK so far. Would love to know if there are other solutions as well.
I have been trying to implement an SSE stream with Koa for hours now but got the following error when trying to send a message to my client after initializing the connection.
Error [ERR_STREAM_WRITE_AFTER_END]: write after end
Here's how I set up my SSE:
Client-side:
const source = new EventSource("http://localhost:8080/stream");
this.source.onmessage = (e) => {
console.log("---- RECEIVED MESSAGE: ", e.data);
};
// Catches errors
this.source.onerror = (e) => {
console.log("---- ERROR: ", e.data);
};
Server-side (Koa):
// Entry point to our SSE stream
router.get('/stream', ctx => {
// Set response status, type and headers
ctx.response.status = 200;
ctx.response.type = 'text/event-stream';
ctx.response.set({
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
// Called when another route is reached
// Should send to the client the following
ctx.app.on('message', data => {
ctx.res.write(`event: Test\n`);
ctx.res.write(`data: This is test data\n\n`);
});
});
The error comes when we call ctx.res.write once a message is received.
Why is my stream ended although nothing explicitly is doing it?
How may I send a message through the stream with Koa?
Koa is entirely promise based and everything is a middleware.
Every middleware returns a promise (or nothing). The middleware chain is effectively 'awaited' and once the middleware returns, Koa knows the response is done and will end the stream.
To make sure that Koa doesn't do this, you have to make sure that the chain of middlewares don't end. To do this, you need to return a promise that only resolves when you're done streaming.
A quick hack to demonstrate would be to return a promise that doesn't resolve:
return new Promise( resolve => { }});
How Can I read Response Header (Content-Disposition)? Please share resolution.
When I check at either Postman or Google Chrome Network tab, I can see 'Content-Disposition' at the response headers section for the HTTP call, but NOT able to read the header parameter at Angular Code.
// Node - Server JS
app.get('/download', function (req, res) {
var file = __dirname + '/db.json';
res.set({
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename=' + req.body.filename
})
res.download(file); // Set disposition and send it.
});
// Angular5 Code
saveFile() {
const headers = new Headers();
headers.append('Accept', 'text/plain');
this.http.get('http://localhost:8090/download', { headers: headers })
.subscribe(
(response => this.saveToFileSystem(response))
);
}
private saveToFileSystem(response) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition'); // <== Getting error here, Not able to read Response Headers
const parts: string[] = contentDispositionHeader.split(';');
const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: 'text/plain' });
saveAs(blob, filename);
}
I have found the solution to this issue. As per Access-Control-Expose-Headers, only default headers would be exposed.
In order to expose 'Content-Disposition', we need to set 'Access-Control-Expose-Headers' header property to either '*' (allow all) or 'Content-Disposition'.
// Node - Server JS
app.get('/download', function (req, res) {
var file = __dirname + '/db.json';
res.set({
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename=' + req.body.filename,
'Access-Control-Expose-Headers': 'Content-Disposition' // <== ** Solution **
})
res.download(file); // Set disposition and send it.
});
It is not the problem with Angular, is the problem with CORS.
If the server does not explicitly allow your code to read the headers, the browser don't allow to read them.
In the server you must add Access-Control-Expose-Headers in the response.
In the response it will be like Access-Control-Expose-Headers:<header_name>,
In asp.net core it can be added while setting up CORS in ConfigureServices method in startup.cs
this solution help me to get the Content-Disposition from response header.
(data)=>{ //the 'data' is response of file data with responseType: ResponseContentType.Blob.
let contentDisposition = data.headers.get('content-disposition');
}
Firstly you need to allow your server to expose these headers. Note that it will show in you browser network tab, regardless if you have these settings. This makes it 'available'.
With C# it would look something like this:
services.AddCors(options => {
options.AddPolicy(AllowSpecificOrigins,
builder => {
builder
.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.WithExposedHeaders("Content-Disposition", "downloadFileName");
});
});
When you send your API request to the server ensure that you include the "observe" in you return. See below:
getFile(path: string): Observable<any> {
// Create headers
let headers = new HttpHeaders();
// Create and return request
return this.http.get<Blob>(
`${environment.api_url}${path}`,
{ headers, observe: 'response', responseType: 'blob' as 'json' }
).pipe();
}
Then in your response of your angular on your subscribe you can access your filename like this (the subscribe method is not complete it attaches to a pipe function)
.....
.subscribe((response: HttpResponse<Blob>) => {
const fileName = response.headers.get('content-disposition')
.split(';')[1]
.split('filename')[1]
.split('=')[1]
.trim();
});
I am trying to implement an ASP.NET SignalR app as mentioned here.
I have implemented the client as mentioned here. For the client I am using code without the generated proxy.
Client and server successfully connect when both are on the same domain but unable to communicate when hosted cross domain. Although the code mentioned for cross domain in the above articles is already implemented. Since my client and server are hosted in Azure, is there a setting in Azure that needs to be enabled for cross domain communication or there is something else that I am missing?
Here is the error i am getting:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin is therefore not allowed access. The response had HTTP status code 500.
My startup class is:
public void Configuration(IAppBuilder app)
{
//app.UseCors(CorsOptions.AllowAll);
// Any connection or hub wire up and configuration should go here
//app.MapSignalR();
// Branch the pipeline here for requests that start with "/signalr"
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
}
And the client code is:`
$(function (){
var ChatServerUrl ="http://chatserverurl.net/home/";
var ChatUrl = ChatServerUrl + "signalr";
var connection = $.hubConnection(ChatUrl, { useDefaultPath: false });
connection.logging = true;
var chatHubProxy = connection.createHubProxy('chatHub');
chatHubProxy.on('addNewMessageToPage', function (name, message) {
console.log("AddNewMessageToPage Function!");
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
});
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
//connection.start({ withCredentials : false }).done(function () {
connection.start({ withCredentials: true }).done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chatHubProxy.invoke('Send', $('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
console.log("SignalR Connected!");
});
});`
Please try this according to what the below links suggest
https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client