Is the header comparison in vertx web case sensitive - http

I have a vertx web routers defined as follows:
router.route(POST, "/customers")
.consumes("application/json;charset=utf-8")
.handler { runHandler(it) }
When I hit this route, I am getting a 404 error with header set as:
Content-Type: application/json;charset=UTF-8
It works for header:
Content-Type: application/json;charset=utf-8
This beg the question whether the header value comparison is case sensitive. Is this the standard or an implementation issue with vertx web?

1) Field values in general are case-sensitive. However, the charset parameter value is not, so it shouldn't matter (see https://greenbytes.de/tech/webdav/rfc7231.html#rfc.section.3.1.1.2)
2) That said: there is no "charset" parameter on application/json anyway, so just get rid of it (see https://greenbytes.de/tech/webdav/rfc8259.html#rfc.section.11)

Related

How to specify to accept multipart/related content type with particular content types for body part in the accept header field

RFC 7231 - HTTP/1.1 Semantics and Content, 5.3 Content Negotiation does not define how to specify to accept a multipart/related content type with particular content types for body parts in the accept header field.
For instance, how to express acceptance of multipart/related content with text/html body parts
Accept: multipart/related;type=text/html
or
Accept: multipart/related,text/html
And if you want to specify precedences for different html flavours?
Accept: multipart/related;type=text/html;q=0.7,
multipart/related;type=text/html;level=1,
multipart/related;type=text/html;level=2;q=0.4
or
Accept: multipart/related,text/html;q=0.7,
text/html;level=1,
text/html;level=2;q=0.4
What's right? Both?
To start off, HTTP is a MIME-like protocol, not a MIME-compliant one. To quote RFC 7230, section 2.1:
Messages are passed in a format similar to that used by Internet mail [RFC5322] and the Multipurpose Internet Mail Extensions (MIME) [RFC2045] (see Appendix A of [RFC7231] for the differences between HTTP and MIME messages).
This is important to keep in mind, as this grants us some liberties when dealing with MIME content.
The Accept header is subject to RFC 7231, sec. 5.3.2. The syntax described there allows for a list of comma-seperated mediatypes (see RFC 7230, sec. 7) with an arbitrary number of mediatype-specific parameters each in addition to the HTTP-specific weight parameter q (see RFC 7231, sec. 5.3.1).
Section 3.1.1.1 discusses which mediatypes are considered valid for the Accept and Content-Type headers:
HTTP uses Internet media types [RFC2046] in the Content-Type and Accept header fields in order to provide open and extensible data typing and type negotiation. [...] Internet media types ought to be registered with IANA according to the procedures defined in [BCP13]
[BCP13] is referring to RFC 6838, eventually leading to the IANA Media Types Registry.
It bears mentioning that the syntax of the Accept header does not require any parameters to be present; they are all optional as far as the HTTP spec is concerned. If there are required parameters, they must be required directly by the mediatype in question:
The presence or absence of a parameter might be significant to the processing of a media-type, depending on its definition within the media type registry.
The multipart/related MIME type itself is subject to RFC 2387. Section 3.1 of which explicitly makes the type paramater mandatory. It is also a single value, not a list. Interestingly, the HTTP spec is stressing out the importance of the presence of the boundary parameter over RFC 2046, section 5.1.1. From RFC 7231, section 3.1.1.4:
All multipart types share a common syntax, as defined in Section 5.1.1 of [RFC2046], and include a boundary parameter as part of the media type value.
My guess is that it never occured to the authors that one would put a multipart mediatype into an Accept header, which would render the boundary useless. This could indeed be a candidate for an errata (Julian?). So technically, the absolutely correctâ„¢ way to request this would be:
Accept: multipart/related; type=text/html; boundary=--my-top-notch-boundary-
In reality, implementors seem to be inclined to deliberately ignore these requirements as this example shows. I usually do not advocate against following the RFC, but I think it actually makes sense here to skip the boundary parameter. Bearing in mind that this is a request header used in content negotiation and not a dedscription of seom actual content with a specified boundary between message parts, I cannot think of a use case where requesting such a boundary were legit; unles you are out for causing some mischief. But then again you were requesting a manipulated request for yourself. I am undecided on omitting the type parameter, though. IMHO doing so would imply type=*/*, which is efectively an "I don't care, send whatever you see fit." While this may result in a response perfectly in line with RFC2387, I would personally feel uneasy about having this little control over the returned content type. (On a side note: You may always want to check the content type of responses. A 2xx code is no guarantee that you got what you requested)
Now if you send out a request with Accept: mutlipart/related, text/html, you are requesting either several parts of unspecified type or alternatively a single HTML document. If you want to negotiate the content, you will need to request several variations of multipart/related with different types:
Accept: multipart/related; type=text/html,
multipart/related; type=text/plaintext
(Note: Line continuation added for improved legibility. Please take note that line continuation has been deprecated and should no longer be used in the context of HTTP.)
Regarding your example, I was quite surprised to find that the syntax for this mediatype is extraordinarily strict when it comes to parameters. The situation is as follows:
The Accept header as such is subject to RFC 7231, sec. 5.3.2
The mediatype(s) and subtype(s) are straight out of the IANA Media Types Registry per RFC 6838
The parameters are being handled as follows:
q is under authority of RFC 7231, sec. 5.3.1
boundary is under authority of RFC 2046, sec. 5.1.1
Remaining parameters are subject to the mediatypes' respective RFCs. In this case this means that type is required, followed by the optional parameters start and start-info
Unrecognized parameters are to be discarded as per RFC 2046, section 1:
MIME implementations must also ignore any parameters whose names they do not recognize.
So, if level were a recognized parameter (currently this is not even the case for the text/html mediatype. And yes, I am aware it appears in multiple examples), the correct solution were indeed this:
Accept: multipart/related; type=text/html; q=0.7,
multipart/related; type=text/html; level=1,
multipart/related; type=text/html; level=2; q=0.4
But stripping out the level parameter, we're down to this:
Accept: multipart/related; type=text/html; q=0.7,
multipart/related; type=text/html,
multipart/related; type=text/html; q=0.4
which is sementically the same as:
Accept: multipart/related; type=text/html
Actually, it does define it -- it says that optional parameters are allowed. How these are interpreted depends on the media type definition, not the syntax of the Accept header field.

text/html vs application/x-www-form-urlencoded and FormUrlEncodedMediaTypeFormatter

For a POST request, I got back a response in text/html format and the response body was containing the below info:
oauth_token=XXXXXXXXXXXXXXXXXXXXXXXXX&oauth_token_secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&oauth_callback_confirmed=true
I made this request through System.Net.Http.HttpClient and I throught I could read the response with FormUrlEncodedMediaTypeFormatter as FormDataCollection but it turned out that FormUrlEncodedMediaTypeFormatter only supports application/x-www-form-urlencoded format by default. So, I worked around this with the following code:
using (OAuthHttpClient client = new OAuthHttpClient(creds)) {
var response = await client.PostAsync(requestUri, new EmptyContent());
var formatter = new FormUrlEncodedMediaTypeFormatter();
formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
var result = await response.Content.ReadAsAsync<FormDataCollection>(new List<MediaTypeFormatter>() { formatter });
}
The question here is:
Is the response provider (in this case it is Twitter) doing it wrong by sending this response as text/html or should FormUrlEncodedMediaTypeFormatter support text/html type by default?
Your question is missing some key info i.e. what is the requestUri supposed to return by default, is it a Web API service or an external one etc. It seems it's not Web API because it's little odd that it returns "text/html".
But the fact that FormUrlEncodedMediaTypeFormatter doesn't support formatting back from text/html is absolutely fine. Because why would it? "application/x-www-form-urlencoded" is effectively a key-value dictionary and text/html is a rich media type.
In Web API, with the way content negotiation works, it looks at
Mediatype mappings (I assume not in place in your case)
Accept headers - looking at your request you don't set them
Request content type - again, looking at your request you don't set it so it's empty
Can the formatter serialize a given type
So if you make the request as you showed to any Web API action, it would return text/xml (if you didn't tweak conneg manually).
I agree with Filip that this is a fine work around to an incorrect content type header.
Henrik

Does the HTTP Protocol support multiple content types in response headers?

I'm wondering if I can return the response header
Content-Type: application/json text/json
or isn't this legal?
The HTTP protocol states that Content-Type should be of mediaType and that mediaType is defined as the following:
media-type = type "/" subtype *( ";" parameter )
type = token
subtype = token
But I'm having trouble interpreting what this means, can anyone enlighten me?
Thanks!
You need to look at the definition of the header field:
http://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.14.17
Content-Type = "Content-Type" ":" media-type
so it takes a single media-type, which is defined by the grammar you quoted above.
So the answer is: a single type/subtype, followed by optional parameters.
No. As pointed out in the accepted answer, the header value itself does not allow for multiple values, and it is also not allowed to send multiple Content-Type headers:
A sender MUST NOT generate multiple header fields with the same field
name in a message unless either the entire field value for that header
field is defined as a comma-separated list [i.e., #(values)] or the
header field is a well-known exception (as noted below).
https://www.rfc-editor.org/rfc/rfc7230#section-3.2.2
None of the "escape clauses" holds, because media-type does not allow a comma-separated list of values, and Content-Type evidently is not a well-known exception, either.

Is an HTTP PUT request required to include a body?

I'm having trouble finding a definite specification of this in the standard. I have an HTTP client that's not including a Content-Length: 0 header when doing a PUT request where I don't specify a body, and a server that gets confused by such requests, and I'm wondering which program I should be blaming.
HTTP requests have a body if they have a Content-Length or Transfer-Encoding header (RFC 2616 4.3). If the request has neither, it has no body, and your server should treat it as such.
That said it is unusual for a PUT request to have no body, and so if I were designing a client that really wanted to send an empty body, I'd pass Content-Length: 0. Indeed, depending on one's reading of the POST and PUT method definitions (RFC 2616 9.5, 9.6) one might argue that the body is implied to be required - but a reasonable way to handle no body would be to assume a zero-length body.
Not answering the question, but asserting how jaxrs allows me to frequent use of bodyless PUTs:
Example of bodyless put:
Give user an additional permission.
PUT /admin/users/{username}/permission/{permission}
A body is not required by the IETF standard, though the content-length should be 0 if there's no body. Use the method that's appropriate for what you're doing. If you were to put it into code, given
int x;
int f(){ return x; }
and a remote variable called r.
A post is equivalent to
r=f();
A put is equivalent to
r=x;
and a get is equivalent to
x=r;
What is being PUT (in the verb sense) onto the server if there's no content? The spec refers to the content as "the enclosed entity", but a request with no content would have no enclosed entity, and therefore nothing to put on the server.
Unless, of course, you wanted to PUT nothing onto the server, in which case you'd probably want a DELETE instead.
The content length field is required as per the following section in the HTTP/1.1 standard http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13

How can I find out whether a server supports the Range header?

I have been trying to stream audio from a particular point by using the Range header values but I always get the song right from the beginning. I am doing this through a program so am not sure whether the problem lies in my code or on the server.
How can I find out whether the server supports the Range header param?
Thanks.
The way the HTTP spec defines it, if the server knows how to support the Range header, it will. That in turn, requires it to return a 206 Partial Content response code with a Content-Range header, when it returns content to you. Otherwise, it will simply ignore the Range header in your request, and return a 200 response code.
This might seem silly, but are you sure you're crafting a valid HTTP request header? All too commonly, I forget to specify HTTP/1.1 in the request, or forget to specify the Range specifier, such as "bytes".
Oh, and if all you want to do is check, then just send a HEAD request instead of a GET request. Same headers, same everything, just "HEAD" instead of "GET". If you receive a 206 response, you'll know Range is supported, and otherwise you'll get a 200 response.
This is for others searching how to do this. You can use curl:
curl -I http://exampleserver.com/example_video.mp4
In the header you should see
Accept-Ranges: bytes
You can go further and test retrieving a range
curl --header "Range: bytes=100-107" -I http://exampleserver.com/example_vide0.mp4
and in the headers you should see
HTTP/1.1 206 Partial Content
and
Content-Range: bytes 100-107/10000000
Content-Length: 8
[instead of 10000000 you'll see the length of the file]
Although I am a bit late in answering this question, I think my answer will help future visitors. Here is a python method that detects whether a server supports range queries or not.
def accepts_byte_ranges(self, effective_url):
"""Test if the server supports multi-part file download. Method expects effective (absolute) url."""
import pycurl
import cStringIO
import re
c = pycurl.Curl()
header = cStringIO.StringIO()
# Get http header
c.setopt(c.URL, effective_url)
c.setopt(c.NOBODY, 1)
c.setopt(c.HEADERFUNCTION, header.write)
c.perform()
c.close()
header_text = header.getvalue()
header.close()
verbose_print(header_text)
# Check if server accepts byte-ranges
match = re.search('Accept-Ranges:\s+bytes', header_text)
if match:
return True
else:
# If server explicitly specifies "Accept-Ranges: none" in the header, we do not attempt partial download.
match = re.search('Accept-Ranges:\s+none', header_text)
if match:
return False
else:
c = pycurl.Curl()
# There is still hope, try a simple byte range query
c.setopt(c.RANGE, '0-0') # First byte
c.setopt(c.URL, effective_url)
c.setopt(c.NOBODY, 1)
c.perform()
http_code = c.getinfo(c.HTTP_CODE)
c.close()
if http_code == 206: # Http status code 206 means byte-ranges are accepted
return True
else:
return False
One way is just to try, and check the response. In your case, it appears the server doesn't support ranges.
Alternatively, do a GET or HEAD on the URI, and check for the Accept-Ranges response header.
You can use GET method with 0-0 Range request header, and check whether the response code is 206 or not, which will respond with
the first and last bytes of the response body
You also can use HEAD method do the same thing as the first session which will get the same response header and code without response body
Furthermore, you can check Accept-Ranges on the response header to judge whether it can support range, but please notice if the value is none on Accept-Ranges field, it means it can't support range, and if the response header doesn't have Accept-Ranges field you also can't finger out it can't support range from it.
There is another thing you have to know if you are using 0- Range on the request header with GET method to check the response code, the response body message will be cached automatically on the TCP receive window until the cache is full.

Resources