Let's say a client makes a request like the following (pulled from iOS):
GET /test.mp4 HTTP/1.1
Host: example.com:80
Range: bytes=0-1
X-Playback-Session-Id: 3DFA3BE3-CB22-4EC5-808F-B59A735DCECE
Accept-Encoding: identity
Accept: */*
Accept-Language: en-us
Connection: keep-alive
User-Agent: AppleCoreMedia/1.0.0.11B554a (iPad; U; CPU OS 7_0_4 like Mac OS X; en_us)
There other such requests out there, I believe Chrome might test the waters by asking for blank Range.
How can the server respond to any such request so that it does not need to honor Range , but rather treat it as a standard HTTP delivery, and the client will play the file?
Sending a regular header response and the data as though the client were not asking for Range does not seem to work.
EDIT: Conversely, if the client does not request a Range, is it okay to respond with HTTP 206 with full filesize in Content-Length and also Content-Range header (which client will ignore)?
If the server does not support the Range header, it would send a normal 200 reply to send the entire file. If the server supports the Range header, it would send a 206 or 416 reply, depending on whether the requested range can be satisfied or not. This is covered in RFC 2616 Section 14.35.
It is not OK to respond with 206 if the client did not request a Range.
Try responding with HTTP 1.0 - it doesn't support range requests at all.
Maybe the client will treat such a reply more gracefully.
Related
I've researched a bit to find out that the Status line is the the start line of an HTTP response. It contains the information such as the protocol version, a status text, status code. But why does it have to contain the protocol version again?
The HTTP version numbers are present in the request and response for HTTP/1.0 and 1.1. (HTTP/2 is quite different. When using a browser, the protocol is negotiated by the TLS connection wrapped around HTTP.)
RFC 2145 says,
An HTTP server SHOULD send a response version equal to the highest
version for which the server is at least conditionally compliant, and
whose major version is less than or equal to the one received in the
request. An HTTP server MUST NOT send a version for which it is not
at least conditionally compliant. A server MAY send a 505 (HTTP
Version Not Supported) response if cannot send a response using the
major version used in the client's request.
This means a server compatible with HTTP/1.1 should respond with HTTP/1.1 to both, HTTP/1.1 and HTTP/1.0 requests. A server supporting only HTTP/1.0, could respond to HTTP/1.1 request with HTTP/1.0 responses.
For example, if you send a HTTP/1.0 request to wikipedia.org,
$ nc wikipedia.org 80
GET / HTTP/1.0
HTTP/1.1 400
Date: Mon, 05 Aug 2019 18:03:20 GMT
Server: Varnish
...
the server responds with HTTP/1.1 (although with an error).
So the version number is also included in the response because it might be different from the one in the request.
I'm writing a simple HTTP server that will serve content from the file system.
I'm a little confused as to how the client and server negotiate content type.
After doing some research, I found that Content-Type specifies the content type of the HTTP message being sent, while the Accept header specifies what the program expects to receive as a response.
When I visit my server from my browser, and read the initial GET request (when visited with a null URI), I get the following:
GET / HTTP/1.1
Host: 127.0.0.1:1234
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
As you can see, the accept header doesn't specify it will accept pdfs, judging by the fact that I can't see the MIME type application/pdf in the accept header value.
Yet, when I send a pdf's bytes along with a content type set to application/pdf, the browser magically displays it.
So, what am I missing? I originally thought the browser might be doing some basic inference on the URI to see if it ends it .pdf, and then accept the corresponding MIME type.
But, when I visit it with a link to a pdf, the Accept header stays the same.
Any help would be really appreciated.
I'm writing a simple HTTP server
Then you should learn to find your way around the various RFCs that describe HTTP.
The relevant one here is RFC 7231, 5.3.2. Accept:
If the header field is
present in a request and none of the available representations for
the response have a media type that is listed as acceptable, the
origin server can either honor the header field by sending a 406 (Not
Acceptable) response or disregard the header field by treating the
response as if it is not subject to content negotiation.
A browser in principle wants to display HTML-formatted documents, for whatever variant of (X)HTML the server is willing to serve, so by default it sends the accept header you observed.
If the request is for another kind of resource however, the server is free to respond with that type of content.
Request:
POST / HTTP/1.0
Content-Type: text/xml; charset=UTF-8
User-Agent: Axis2
Host: localhost:8000
Content-Length: 539
Response from tomcat:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=UTF-8
Date: Sat, 19 Oct 2013 00:28:57 GMT
Connection: close
From tomcat website it says:
If the client (typically a browser) supports only HTTP/1.0, the
Connector will gracefully fall back to supporting this protocol as
well. No special configuration is required to enable this support.
How Tomcat gracefully fall back to HTTP 1.0? From my example it still reply HTTP 1.1. Can anyone explain to me?
The protocol version indicates the protocol capability of the sender. It does not specify the version of the response itself. So as long as the response can be understood by the HTTP 1.0 client, Tomcat is doing exactly what it should.
It's all in RFC2616...
Edit: And it's even in the Tomcat documentation itself, right after the part you quoted:
This Connector supports all of the required features of the HTTP/1.1 protocol, as described in RFC 2616, including persistent connections, pipelining, expectations and chunked encoding. If the client (typically a browser) supports only HTTP/1.0, the Connector will gracefully fall back to supporting this protocol as well. No special configuration is required to enable this support. The Connector also supports HTTP/1.0 keep-alive.
RFC 2616 requires that HTTP servers always begin their responses with the highest HTTP version that they claim to support. Therefore, this Connector will always return HTTP/1.1 at the beginning of its responses.
I have an IPad Safari sending two HTTP requests (two different PNG) files within 30 ms.
I thought that even on a Keep Alive HTTP 1.1 connection there should be a clear sequence of request/response.
What I saw is that the Safari browser sends two GET requests within 30 ms without waiting for an answer. This causes problems in some web servers.
Situation:
I have an HTML5 loading an SVG with further references to other images (like PNG and GIF). The problem does not occur on IPhone 5 but on the IPad.
Please seee this wireshark dump:
http://tinyurl.com/c7m37b9
(Frame 116/117)
IPad (1) Infos:
Version 5.1.1 (9B206)
Model MB2292FD
Safari 5.1
[GET /Licht_3.gif HTTP/1.1
...
User-Agent: Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3
Accept: */*
...
Accept-Language: de-de
Accept-Encoding: gzip, deflate
Connection: keep-alive]
[GET /Licht_3.gif HTTP/1.1
Host: 192.9.225.251:8081
...
Accept-Language: de-de
Accept-Encoding: gzip, deflate
Connection: keep-alive]
[HTTP/1.1 200 OK
...
Content-Type: image/png
Content-Length: 3921]
It looks like HTTP pipelining:
HTTP pipelining is a technique in which multiple HTTP requests are
sent on a single TCP connection without waiting for the corresponding
responses.
I think that this may be due to browser adhering to HTTP 1.1 Spec 8.2.4. which does allow client to retry the request if client is connected to server thru a middle layer and doesn't get a response from server before the connection is closed.
I have seen this error and have found following links to be helpful.
https://www.ravellosystems.com/blog/beware-http-requests-automatic-retries/
http://geek.starbean.net/?p=393
I am seeing this too from a mobile safari client. The only workaround I was able to find is to disable keepalive for the whole server in Nginx. We don't get duplicate requests and/or pipelining might not work from the Safari client when keepalive is disabled.
In Nginx, there is a keepalive_disable option, but it only works for POST requests on certain browsers, and doesn't have many options. We ended up disabling keepalive on that server using keepalive_timeout 0; I hope there is a better solution in the future.
I would think that a correct use of pipelining would submit multiple differing requests, instead of duplicate requests.
Solved: pasting the bytes here made me realise that I was missing empty lines between chunks...
Does an HTTP/1.1 request need to specify a Connection: keep-alive header, or is it always keep-alive by default?
This guide made me think it would; that, when my http server gets a 1.1 request, it is keep-alive unless explicitly receiving a Connection: close header.
I ask since my the different client behaviour of ab and httperf is driving me mad enough to wonder my sanity on this one...
Here's what httperf --hog --port 42042 --print-reply body sends:
GET / HTTP/1.1
User-Agent: httperf/0.9.0
Host: localhost
And here's my server's response:
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked
Content-Length: 18
12
Hello World 1
0
httpref promptly prints out the response, but then just sits there, neither side closing the connection and httpref not exiting.
Where's my bug?
From RFC 2616, section 8.1.2:
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection. That is, unless otherwise indicated, the client SHOULD assume that the server will maintain a persistent connection, even after error responses from the server.