ASP.NET core: HTTP HEAD requests and Content-Length header - http

This question is related to an ASP.NET core 2.2 web application (targeting .NET core) exposing some web api controllers implemented used the mvc middleware.
All the action methods available in all the controllers must respond to both the GET and the HEAD http methods.
We noticed that ASP.NET core automatically add the Transfer-Encoding header with the value chunked and, according to the specifications, omits the Content-Length header (see this MDN page for more details).
According to this github issue on the ASP.NET core repository it seems that this behaviour depends on a precise design decision of the Kestrel web server, so this is the intended behaviour.
That said, each time we issue an HEAD request to any route of our application we get a response having the Content-Length header set to 0, even when the corresponding GET request (I mean a GET request having the same path) returns a non empty response body.
According to what I read on various sources, it seems that the Content-Length header is not mandatory for the response to an HEAD request, but when included it should have the same value as the corresponding GET request. So the value 0 that we see on each HEAD request does not seem correct to me.
Is it a side effect of the fact that, for the corresponding GET request, ASP.NET core sends the response by using chunks (as explained above Transfer-Encoding is always chunked for GET requests) ?
Another doubt is related to any kind of cache issuing HEAD requests to our application in order to decide whether or not to purge a cached response: does the zero-valued Content-Length pose a risk for the correctness of caching behaviour ?
EDIT OF 27th MARCH 2018
We repeated our tests and we got different but more meaningful results. I can confirm that both the GET and HEAD requests do not send any Content-Length header. This definitely makes sense according to the fact that the response body is always transmissed by chunks to the client as explained above.
That said I think that the ASP.NET core behaviour definitely makes sense to me.

Related

Supporting both ASP.NET Caching and ETag/Conditional GET in WCF WebHttp Service

I am trying to implement a REST web service with WCF that supports both caching and Conditional GETs.
I implemented basic caching following the instructions in MSDN: Caching Support for WCF Web HTTP Services. That means adding an [AspNetCacheProfile("MyOutputCacheProfile")] attribute to each of my web methods and adding appropriate entries to web.config. That seems to work correctly: cached responses are returned when identical arguments are passed to the web methods.
Then I added support for Conditional GET by calculating an ETag value and setting that on the response like this:
WebOperationContext.Current.OutgoingResponse.SetETag(myETag);
That sorta works: I can see the ETag header in the response the first time I call the web method.
But here's the problem: The next time I invoke that web method with the same arguments, a cached response is returned, and the cached response does not include the ETag header. (If I wait until cache expiration, or disable caching entirely, then the ETag headers are returned properly.)
So, is there any way get the cached responses to include that ETag value?
Update: After some more study and experimentation, I find that doing this causes the ETag header to be included in all cached responses:
HttpContext.Current.Response.Cache.SetETag(myETag);
If I call that, then I don't need to call the associated WebOperationContext...SetETag() operation to make everything work.
Is this the Right Way to do this?
Correct me if I am wrong. Restful service are more close to Http and Http caching says that
The goal of caching in HTTP/1.1 is to eliminate the need to send
requests in many cases, and to eliminate the need to send full
responses in many other cases. The former reduces the number of
network round-trips required for many operations; we use an
"expiration" mechanism for this purpose (see section 13.2). The latter
reduces network bandwidth requirements; we use a "validation"
mechanism for this purpose (see section 13.3).
Asp.net caching does not fall in any one of this category(neither expiration nor validation).The caching is only done on web server and IIS instead of executing the method, sends the stored response. Some how it does not fit in RESTful model.
To implement caching, we should add Cache Control Headers and Etag to response headers and then try to handle conditional Get. Please consult this excellent article.

ASP.NET Response.Cache.SetNoStore() vs. Response.Cache.SetNoServerCaching()

Can anyone break down what these two methods do at a HTTP level.
We are dealing with Akamai edge-caching and have been told that SetNoStore() will cause can exclusion so that (for example) form pages will always post back to the origin server. According to {guy} this sets the HTTP header:
Cache-Control: "no-cache, no-store"
As I was implementing this change to our forms I found SetNoServerCaching(). Well that seems to make a bit more sense semantically, and the documentation says "Explicitly denies caching of the document on the origin-server."
So I went down to the sea sea sea to see what I could see see see. I tried both of these methods and reviewed the headers in Firebug and Fiddler.
And from what I can tell, both these method set the exact same Http Header.
Can anyone explain if there are actual differences between these methods and if so, where are hiding in the http response?!
Theres a few differences,
SetNoStore, essentially stops the browser (and any network resource such as a CDN) from saving any part of the response or request, that includes saving to temp files. This will set the NO-STORE HTTP 1.1 header
SetNoServerCaching, will essentially stop the server from saving files, in ASP.NET There are several levels of caching that can happen, Data only, Partial Requests, Full Pages, and SQL Data. This call should stop the HTTP (Full and Partial) requests being saved on the server. This method should not set the cache-control headers or no-store or no cache.
There is also
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetMaxAge(new TimeSpan(1, 0, 0));
as a possible way of setting cache, this will set the content-expires header.
For a CDN you probably want to set the content-expires header so that he CDN knows when to fetch new content, it if it gets a HIT. You probably don't want no-cache or no-store as this would cause a refetch on every HIT so essentially you are nullifying any benefit the CDN brings to you except they may have a faster backbone connection to the end user than your current ISP but that would be marginal.
Differnce between the two is
HttpCachePolicy.SetNoStore() or Response.Cache.SetNoStore:
Prevents the browser from caching the ASPX page.
HttpCachePolicy.SetNoServerCaching or Response.Cache.SetNoServerCaching:
Stops all origin-server caching for the current response. Explicitly denies caching of the document on the origin-server. Once set, all requests for the document are fully processed.
When these methods are invoked, caching cannot be reenabled for the current response.

HTTP Status 412 (Precondition Failed) and Database Versioning

I am implementing a RESTful web service that accesses a database. Entities in the database are versioned to detect multiple updates. For instance, if the current value is {"name":"Bill", "comment":"tinker", "version":3}, if one user PUTs {"name":"Bill", "comment":"tailor", "version":3}, the request will succeed (200 OK) and the new value will be {"name":"Bill", "comment":"tailor", "version":4}. If a second user PUTs {"name":"Bill", "comment":"sailor", "version":3"} that request will fail (409 Conflict) because the version number does not match.
There are existing non-RESTful interfaces, so the design of the databases cannot be changed. The RESTful interface calls an existing interface that handles the details of checking the version.
A rule of thumb in RESTful web services is to follow the details of HTTP whenever possible. Would it be better in this case to use a conditional header in the request and return 412 Precondition Failed if the version does not match? The appropriate header appears to be If-Match. This header takes an ETag (Entity Tag) which could be a hash of the representation of the current state of the resource.
If I did this, the ETags would be for appearances' sake, because the version would still be the real thing I'm testing for.
Is there any reason I should do this, other than "making it more RESTful", whatever that is supposed to mean?
The appropriate thing to do is always to follow the HTTP spec if you're using HTTP, and the reason is simply to allow people who understand the spec to function correctly.
412 should only be used if a precondition (e.g. If-Match) caused the version matching to fail, whereas 409 should be used if the entity would cause a conflict (the HTTP spec itself alludes to this behaviour in the definition of 409).
Therefore, a client that doesn't send ETags won't be expecting a 412. Conversely, a client that does send ETags won't understand that it's ETags that are causing a 409.
I would stick with one way. You say that "the database schema can't change", but that doesn't stop you (right in the HTTP server layer) to extract the version from the datbase representation and put it in the ETag, and then on the way in, take the If-Match header and put it back in the version field.
But doing it completely in the entity body itself isn't forbidden. It just requires you to explain the concept and how it works, whereas with the ETag solution you can just point people to the HTTP spec.
Edit: And the version flag doesn't have to be a hash of the current resource; a version is quite acceptable. ETag: "3" is a perfectly valid ETag.

Is the HTTP 'HEAD' verb useful in web development?

I've read the w3.org spec on the 'HEAD' verb, and I guess I'm missing something. I can't see how it would be useful.
Is the HTTP 'HEAD' verb useful in web development?
If so, how?
From RFC2616:
This method (HEAD) can be used for obtaining
metainformation about the entity
implied by the request without
transferring the entity-body itself.
This method is often used for testing
hypertext links for validity,
accessibility, and recent
modification.
The reason why HEAD is preferred to GET is due to the absence of the message body in the response making it using in scenarios where you want to determine if the content has changed at all - a change in the last modified time or content length usually signifies this.
Also, a HEAD request will provide some information about the server setup (whether it is IIS/Apache etc.), unless the server was masked; of course, this is available in all responses, but HEAD is preferred especially when you don't know the size of the response. HEAD is also the easiest way to determine if a site is up or down; again the irrelevance of the message body makes HEAD the ideal candidate.
I'm not sure about this, but RSS/ATOM feed readers would use HEAD over GET to ascertain if the contents of the feed have changed.
The HTTP HEAD can also be used to pre-authenticate to web server, before you do HTTP PUT/POST of some large data. Without the first HEAD request, you would be sending the large data to web server twice (because the first request would return 401 unauthorized reponse with WWW-authenticate header).
It's mainly for browsers and proxies to determine whether they can use a cached copy of the web document without having to download the whole thing (which would rather defeat the purpose of a cache).

HOWTO override Axis2 request headers for .NET web service?

I have to use a 3rd party web service implemented in .NET 2.0 (on IIS, of course).
I have to make a java client. I am using wsdl2java to generate the SOAP stub.
As the original Apache Axis project now appears unmaintained, and I was having some problems parsing some responses from the service, I converted the client to use the latest (1.5) version of Axis2. Now, the .NET service won't even recognize my requests.
I managed to get the "chunking" turned off (where "stub" is a variable of type MumbleStub generated by wsdl2java, and I am showing what are several lines of code as one horrific line here):
stub._getServiceClient().getOptions().setProperty( HTTPConstants.CHUNKED, Boolean.FALSE);
.. so at least the service recognizes my request AS a request, albeit a bad one: "HTTP/1.1 400 Bad Request" is the response now (as opposed to an "intro / summary" page offering me a link to the WSDL).
I noticed that the Axis ("1") request had a different Content-TYpe header (text/xml, vs application/soap-xml), and I am wondering how to change this request header, if that is in fact the problem.
Alternately, has anybody else had this problem? Is the problem really the (undisplayable here, as it looks like "element injection" to the blog engine) ... xml version-"1.0" ... "XML meta intro tag" that Axis2 added to the beginning of the request?
WS-Deathstar, indeed.
As you mention the different content-type header I guess your client tries to send SOAP 1.2 requests and the 3rd party app only understands SOAP 1.1
Try changing the used soap version as AFAIK AXIS2 uses SOAP 1.2 by default
stub._getServiceClient().getOptions().setSoapVersionURI(org.apache.axiom.soap.SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);

Resources