Reputation: 154
I tried to write a simple http server using socket programming in C. I want to first try that the server could send back the HTML file without considering what kind of request is been received.
Here's the header and the body :
char httpHeader[100000] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html>\r\n"
"<html><head><title>Testing</title></head>\r\n"
"<body><p>Testing</p></body><html>\r\n";
Here's the procedure of sending package:
while(1) {
if ( (connfd = accept(listenfd, NULL, NULL) ) < 0 ) {
fprintf(stderr, "accept error\n");
exit(EXIT_FAILURE);
}
if (send(connfd, httpHeader, sizeof(httpHeader), 0) == -1) {
fprintf(stderr, "send error\n");
exit(EXIT_FAILURE);
};
if ( close(connfd) < 0 ) {
fprintf(stderr, "close error\n");
exit(EXIT_FAILURE);
}
}
I bind the socket on port 8080, and using telnet to test whether it's working or not. After running the server and send request by telnet in the same machine, the client side successfully receive the whole package without any error.
But when I try to connect the server using Chrome, it failed. The GET error : "net::ERR_CONNECTION_RESET 200 (OK)" showed up. When I check the network session in DevTool, I saw that the response header is received by Chrome, showing "HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8", but did not see the HTML file.
Is the header format wrong? I check the protocol again but not sure where I do wrong.
Upvotes: 1
Views: 3085
Reputation: 123320
Such kind of problems typically occur when the server is closing the connection while there are still unread data from the client. In your case the server is closing the connection without reading the request from the client. But even if you've read the full request the problem might still happen since the client might have sent multiple requests (HTTP pipelining).
The HTTP standard even has a recommendation how a proper close should be done and specifically addresses the problem you get. From RFC 7230 section 6.6 "Tear-down":
If a server performs an immediate close of a TCP connection, there is a significant risk that the client will not be able to read the last HTTP response. If the server receives additional data from the client on a fully closed connection, such as another request that was sent by the client before receiving the server's response, the server's TCP stack will send a reset packet to the client; unfortunately, the reset packet might erase the client's unacknowledged input buffers before they can be read and interpreted by the client's HTTP parser.
To avoid the TCP reset problem, servers typically close a connection in stages. First, the server performs a half-close by closing only the write side of the read/write connection. The server then continues to read from the connection until it receives a corresponding close by the client, or until the server is reasonably certain that its own TCP stack has received the client's acknowledgement of the packet(s) containing the server's last response. Finally, the server fully closes the connection.
Apart from that: it is strongly recommended (SHOULD in the standard) that you clearly mark the size of the body either by using a Content-length
header or by using chunked transfer encoding - instead of just closing the connection. It is also strongly recommended that you signal the client that no more requests will be accepted on this connection by adding a Connection: close
header.
In general: HTTP is way more complex then one might think just by looking at a few examples. There is a standard which describes how clients and server should behave and there is a reason that this standard is long. Please follow it nevertheless if you intend to implement your own HTTP stack instead of just using existing implementations.
Upvotes: 4
Reputation: 1521
I’m afraid this isn’t as tested as I’d like it to be, but what strikes me is that you’re not listening to the client before sending data to it. My hypothesis is that Chrome may be detecting that somehow so /maybe/ adding a receive might make a difference.
To be sure though, you could try using Wireshark to monitor the request and compare with the traffic sent request to a full server, which should help diagnose differences.
Upvotes: 0