HTTP 1.1 message protocol and server sent events - http

As I understand it, in 1.1 normally the browser issues a request and the server makes a single message response. The browser will not issue a new request until it has received the response to the previous message. So what ever response it receives is always interpreted as a response to the last message. Is my understanding correct?
When I open a page in Firefox, the server application parses the following request:
HGet / http/1.1
HeaderField(Host, localhost:8080)
HeaderField(User-Agent, Mozilla/5.0 (X11; Ubuntu; Linux x86_64;rv:41.0) Gecko/20100101 Firefox/41.0)
HeaderField(Accept, text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8)
HeaderField(Accept-Language, en-GB,en;q=0.5)
HeaderField(Accept-Encoding, gzip, deflate)
HeaderField(Connection, keep-alive)
After responding with the page this would normally be followed by a /favicon.ico request and things precede as expected. But now I have inserted the following line into my javascript to enable server sent events:
var evtSource = new EventSource("/");
which produces a second request:
HGet / http/1.1
HeaderField(Host, localhost:8080)
HeaderField(User-Agent, Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:41.0) Gecko/20100101 Firefox/41.0)
HeaderField(Accept, text/event-stream)
HeaderField(Accept-Language, en-GB,en;q=0.5)
HeaderField(Accept-Encoding, gzip, deflate)
HeaderField(Referer, http://localhost:8080/)
HeaderField(Connection, keep-alive)
HeaderField(Pragma, no-cache)
HeaderField(Cache-Control, no-cache
So can I now send multiple messages when ever I want from the server? (Leaving aside timeout issues) If so how does the browser know to which request, the message (coming from the server) is a response? Does it rely on the contentType header field? Should I use a different uri in the event source? As I'm learning to keep things simple I'm not using encryption, which stops me using HTTP 2. but later I intend to use https. My preference for using the same uri for the normal get and post requests as for the Server Sent Events is that I don't want to put unnecessary information in the unencrypted response line.
Edit: my confusion came from forgetting that http 1.1 browsers will open multiple connections if they need them. So in my simple setup the browser only has one connection open, it converts that one into a Server Sent Event connection and then makes future requests on a new connection with a different client side port number.

So can I now send multiple messages whenever I want from the server?
Yes.
how does the browser know to which request, the message (coming from the server) is a response?
It's not a request. It is an open HTTP connection. So the browser is only receiving events on the open connection that it is holding open.
Make sense?

Related

Server does not receive HTTP headers that are sent by the browser?

Now this is extremely weird. Somehow my api backend does not receive the http headers (specifically the authorization header), that are clearly sent by the web application, as one can see in the dev tools. But when I try to send a request from GraphiQL or Postman with a authorization header the server DOES get them. How is this possible?
Log of received http headers in my backend:
map[Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9] Accept-Encoding:[gzip, deflate, br] Accept-Language:[de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7] Cache-Control:[no-cache] Connection:[keep-alive] Pragma:[no-cache] Sec-Fetch-Dest:[document] Sec-Fetch-Mode:[navigate] Sec-Fetch-Site:[none] Sec-Fetch-User:[?1] Upgrade-Insecure-Requests:[1] User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36]]
I found out what was the problem... It was sending the OPTIONS request first, which is sent without this header. My api denied the request because of the missing auth header and so the final POST request never happened.

Is there an error in these HTTP request headers?

I'm trying to write a .NET web API that will receive HTTP requests from some devices and handle the data sent. I know the exact format of the data being sent and the ip/port that the data is sent to. The problem is that the API does not even seem to respond to the request as the controller method to handle the POST is never called.
I have tested the API with Postman; using the correct data format and host information and it works as intended. In order to ensure some kind of connection attempt is being made by the device, I listened to the port using a nodejs TCP server. There is data being sent and this is the header info that precedes it:
POST / HTTP/1.0
Host: xxx
Connection: keep-alive
User-Agent: xxx
Content-Type: application/json
Transfer-Encoding: chunked
Transfer-Content: chunked
I can't post the body data, but it is in JSON format as expected (but separated into chunks).
Since there are requests being made, data being sent but the API doesn't acknowledge it despite working when tested using Postman, I'm wondering if there is an issue with the head. I've been researching about the headers and I did read that HTTP 1.0 doesn't support chunked transfer-encoding. Could it be that the devices are making erroneous requests? Or are the headers fine and the problem could be elsewhere?
Thank you for your help.

How to force full HTTP delivery when client requests Range?

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.

When does an HTTP 1.0 server close the connection?

Background: I am trying to get ApacheBench working on my custom server. I tried issuing ab -n 1 -c 1 http://localhost:1337/index.html and I sniffing the connection (with wireshark) I see wayyy more than one request is sent.
Example Request:
GET /index.html HTTP/1.0
Host: localhost:1337
User-Agent: ApacheBench/2.3
Accept: */*
(repeats more times than I care to count)
I assumed as RFC 1945 says "Except for experimental applications, current practice requires that the connection be established by the client prior to each request and closed by the server after sending the response." This works with ApacheBench when I request one page. However, if I up the number of requests to 10, I get "Connection reset by peer." This makes sense considering that I closed the connection.
I tried the same procedure with Google, however, and it works fine there for both cases. So, how am I supposed to know when to close the connection for HTTP 1.0?
In HTTP 0.9, the server always closes the connection after sending the response. The client must close its end of the connection after receiving the response.
In HTTP 1.0, the server always closes the connection after sending the response UNLESS the client sent a Connection: keep-alive request header and the server sent a Connection: keep-alive response header. If no such response header exists, the client must close its end of the connection after receiving the response.
In HTTP 1.1, the server does not close the connection after sending the response UNLESS the client sent a Connection: close request header, or the server sent a Connection: close response header. If such a response header exists, the client must close its end of the connection after receiving the response.

Qt4 QSslSocket triggers HTTP 406 response

I have an application that is working as a proxy using QTcpSocket to intercept HTTP requests. I'm trying to extend that to work for HTTPS as well, but it seems that it's not working how I anticipated. Using QHttp is not possible for me due to the uniqueness of the application.
Currently, I have something like the following:
serverConnection = new QTcpSocket();
serverConnection->setProxy(proxy);
serverConnection->connectToHost(url_hostname, url_port);
serverConnection->write(request.toAscii());
connect(serverConnection, SIGNAL(readyRead()), this, SLOT(readServerData()), Qt::DirectConnection);
I've attempted to do something quite similar using QSslSocket, but unfortunately the outcome isn't what I expected.
serverSConnection = new QSslSocket();
serverSConnection->connectToHostEncrypted(url_hostname, url_port);
if (!serverSConnection->waitForEncrypted()) {
qDebug() << "waitForEncrypted failed";
}
serverSConnection->write(request.toAscii());
connect(serverSConnection, SIGNAL(readyRead()), this, SLOT(readSServerData()), Qt::DirectConnection);
Using the QSslSocket to do the request appears to trigger:
HTTP/1.1 406 Not Acceptable
The request I'm sending through looks something along the lines of the following:
Received Request: "CONNECT www.somesslhost.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.13) Gecko/2009080315 Ubuntu/9.04 (jaunty) Firefox/3.0.13
Proxy-Connection: keep-alive
Host: www.somesslhost.com
Is there something special about sending an HTTPS request through my application using QSslSocket that I'm missing?
With current information I can only get the idea that you're doing it wrong.
Your method is fine if your client is supposed to send plain HTTP requests to your application and it is going to forward them to HTTPS server.
If your client is instead capable of using HTTPS and uses your application as a standard proxy server, you aren't supposed to connect to the server like that. You get 406 because you're trying to convince the destination server to act as a proxy between you and itself.
You can take a look at Tunneling SSL Through a WWW Proxy, I think. Basically, you should parse the incoming request, establish a ‘raw’ connection to server, respond the client with establishment confirmation and then just forward the packets forth and back.
The whole SSL negotiation is to be done between your client and destination server, proxy server is only supposed to forward encrypted packets without being able to decrypt them.
I'm confused - you say:
I have an application that is working
as a proxy using QTcpSocket to
intercept HTTP requests.
What do you mean by "intercept"? Are you trying to write an HTTP proxy application? Please be more clear & specific, and we can probably help you out.

Resources