Вы не читаете достаточно данных.
read()
не гарантирует возвращение столько байтов, сколько вы просите. И нет никакой гарантии, что read()
получит полное HTTP-сообщение за одно чтение. TCP является байтовым потоком, вам нужно вызывать read()
в al oop, пока полный HTTP-запрос не будет получен полностью. См. RF C 2616, раздел 4.4 и RF C 7230, раздел 3.3.3 , чтобы узнать, как определить конец HTTP-сообщения.
4.4 Message Length
The transfer-length of a message is the length of the message-body as
it appears in the message; that is, after any transfer-codings have
been applied. When a message-body is included with a message, the
transfer-length of that body is determined by one of the following
(in order of precedence):
1.Any response message which "MUST NOT" include a message-body (such
as the 1xx, 204, and 304 responses and any response to a HEAD
request) is always terminated by the first empty line after the
header fields, regardless of the entity-header fields present in
the message.
2.If a Transfer-Encoding header field (section 14.41) is present and
has any value other than "identity", then the transfer-length is
defined by use of the "chunked" transfer-coding (section 3.6),
unless the message is terminated by closing the connection.
3.If a Content-Length header field (section 14.13) is present, its
decimal value in OCTETs represents both the entity-length and the
transfer-length. The Content-Length header field MUST NOT be sent
if these two lengths are different (i.e., if a Transfer-Encoding
header field is present). If a message is received with both a
Transfer-Encoding header field and a Content-Length header field,
the latter MUST be ignored.
4.If the message uses the media type "multipart/byteranges", and the
ransfer-length is not otherwise specified, then this self-
elimiting media type defines the transfer-length. This media type
UST NOT be used unless the sender knows that the recipient can arse
it; the presence in a request of a Range header with ultiple byte-
range specifiers from a 1.1 client implies that the lient can parse
multipart/byteranges responses.
A range header might be forwarded by a 1.0 proxy that does not
understand multipart/byteranges; in this case the server MUST
delimit the message using methods defined in items 1,3 or 5 of
this section.
5.By the server closing the connection. (Closing the connection
cannot be used to indicate the end of a request body, since that
would leave no possibility for the server to send back a response.)
For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
containing a message-body MUST include a valid Content-Length header
field unless the server is known to be HTTP/1.1 compliant. If a
request contains a message-body and a Content-Length is not given,
the server SHOULD respond with 400 (bad request) if it cannot
determine the length of the message, or with 411 (length required) if
it wishes to insist on receiving a valid Content-Length.
All HTTP/1.1 applications that receive entities MUST accept the
"chunked" transfer-coding (section 3.6), thus allowing this mechanism
to be used for messages when the message length cannot be determined
in advance.
Messages MUST NOT include both a Content-Length header field and a
non-identity transfer-coding. If the message does include a non-
identity transfer-coding, the Content-Length MUST be ignored.
When a Content-Length is given in a message where a message-body is
allowed, its field value MUST exactly match the number of OCTETs in
the message-body. HTTP/1.1 user agents MUST notify the user when an
invalid length is received and detected.
3.3.3. Message Body Length
The length of a message body is determined by one of the following
(in order of precedence):
1. Any response to a HEAD request and any response with a 1xx
(Informational), 204 (No Content), or 304 (Not Modified) status
code is always terminated by the first empty line after the
header fields, regardless of the header fields present in the
message, and thus cannot contain a message body.
2. Any 2xx (Successful) response to a CONNECT request implies that
the connection will become a tunnel immediately after the empty
line that concludes the header fields. A client MUST ignore any
Content-Length or Transfer-Encoding header fields received in
such a message.
3. If a Transfer-Encoding header field is present and the chunked
transfer coding (Section 4.1) is the final encoding, the message
body length is determined by reading and decoding the chunked
data until the transfer coding indicates the data is complete.
If a Transfer-Encoding header field is present in a response and
the chunked transfer coding is not the final encoding, the
message body length is determined by reading the connection until
it is closed by the server. If a Transfer-Encoding header field
is present in a request and the chunked transfer coding is not
the final encoding, the message body length cannot be determined
reliably; the server MUST respond with the 400 (Bad Request)
status code and then close the connection.
If a message is received with both a Transfer-Encoding and a
Content-Length header field, the Transfer-Encoding overrides the
Content-Length. Such a message might indicate an attempt to
perform request smuggling (Section 9.5) or response splitting
(Section 9.4) and ought to be handled as an error. A sender MUST
remove the received Content-Length field prior to forwarding such
a message downstream.
4. If a message is received without Transfer-Encoding and with
either multiple Content-Length header fields having differing
field-values or a single Content-Length header field having an
invalid value, then the message framing is invalid and the
recipient MUST treat it as an unrecoverable error. If this is a
request message, the server MUST respond with a 400 (Bad Request)
status code and then close the connection. If this is a response
message received by a proxy, the proxy MUST close the connection
to the server, discard the received response, and send a 502 (Bad
Gateway) response to the client. If this is a response message
received by a user agent, the user agent MUST close the
connection to the server and discard the received response.
5. If a valid Content-Length header field is present without
Transfer-Encoding, its decimal value defines the expected message
body length in octets. If the sender closes the connection or
the recipient times out before the indicated number of octets are
received, the recipient MUST consider the message to be
incomplete and close the connection.
6. If this is a request message and none of the above are true, then
the message body length is zero (no message body is present).
7. Otherwise, this is a response message without a declared message
body length, so the message body length is determined by the
number of octets received prior to the server closing the
connection.
Since there is no way to distinguish a successfully completed,
close-delimited message from a partially received message interrupted
by network failure, a server SHOULD generate encoding or
length-delimited messages whenever possible. The close-delimiting
feature exists primarily for backwards compatibility with HTTP/1.0.
A server MAY reject a request that contains a message body but not a
Content-Length by responding with 411 (Length Required).
Unless a transfer coding other than chunked has been applied, a
client that sends a request containing a message body SHOULD use a
valid Content-Length header field if the message body length is known
in advance, rather than the chunked transfer coding, since some
existing services respond to chunked with a 411 (Length Required)
status code even though they understand the chunked transfer coding.
This is typically because such services are implemented via a gateway
that requires a content-length in advance of being called and the
server is unable or unwilling to buffer the entire request before
processing.
A user agent that sends a request containing a message body MUST send
a valid Content-Length header field if it does not know the server
will handle HTTP/1.1 (or later) requests; such knowledge can be in
the form of specific user configuration or by remembering the version
of a prior received response.
If the final response to the last request on a connection has been
completely received and there remains additional data to read, a user
agent MAY discard the remaining data or attempt to determine if that
data belongs as part of the prior response body, which might be the
case if the prior message's Content-Length value is incorrect. A
client MUST NOT process, cache, or forward such extra data as a
separate response, since such behavior would be vulnerable to cache
poisoning.
Аналогично, write()
может не посылать столько байтов, сколько вы просите, поэтому вам также необходимо вызвать write()
в al oop.
Например, попытаться что-то сделать Более того:
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <stdbool.h>
#define PORT 8088
struct data_buffer
{
void *data;
size_t size;
size_t cap;
};
void initDataBuffer(struct data_buffer *buffer)
{
buffer->data = NULL;
buffer->size = 0;
buffer->cap = 0;
}
void freeDataBuffer(struct data_buffer *buffer)
{
if (buffer->data) free(buffer->data);
initDataBuffer(buffer);
}
int appendBytes(struct data_buffer *buffer, void *data, size_t data_size)
{
if ((buffer->cap - buffer->size) < data_size)
{
size_t new_cap = (((buffer->size + data_size) + 255) / 256) * 256;
char *new_buffer = realloc(buffer->data, new_cap);
if (!new_buffer)
return -1;
buffer->data = new_buffer;
buffer->cap = new_cap;
}
memcpy(buffer->data, data, data_size);
buffer->size += data_size;
return 0;
}
int readRaw(int fd, void *data, size_t size)
{
char *ptr = data;
ssize_t recvd;
while (size > 0)
{
recvd = read(fd, ptr, size);
if (recvd <= 0)
return -1;
printf("%.*s", recvd, ptr);
ptr += sent;
size -= sent;
}
return 0;
}
char* readLine(int fd)
{
struct data_buffer line_buffer;
char ch;
line_buffer.data = NULL;
line_buffer.size = 0;
line_buffer.cap = 0;
while (true)
{
if (read(fd, &ch, 1) <= 0)
{
freeDataBuffer(&line_buffer);
return NULL;
}
if (ch == '\r')
{
if (read(fd, &ch, 1) <= 0)
{
freeDataBuffer(&line_buffer);
return NULL;
}
if (ch != '\n')
{
printf("%c", '\r');
if (appendBytes(&line_buffer, "\r", 1) < 0)
{
freeDataBuffer(&line_buffer);
return NULL;
}
}
}
printf("%c", ch);
if (ch == '\n')
break;
if (appendBytes(&line_buffer, &ch, 1) < 0)
{
freeDataBuffer(&line_buffer);
return NULL;
}
}
if (!appendBytes(&line_buffer, "\0", 1))
{
freeDataBuffer(&line_buffer);
return NULL;
}
return line_buffer.data;
}
struct string_list
{
char **strings;
size_t count;
size_t cap;
};
void initStringList(struct string_list *list)
{
list->strings = NULL;
list->count = 0;
list->cap = 0;
}
void freeStringList(struct string_list *list)
{
if (list->strings)
{
for(size_t i = 0 ; i < list->count; ++i)
free(list->strings[i]);
free(list->strings);
}
initStringList(list);
}
int appendString(struct string_list *list, char *str)
{
if (list->count >= list->cap)
{
size_t new_cap = list->cap + 10;
char **new_strings = realloc(list->strings, sizeof(char*) * new_cap);
if (!new_strings)
return -1;
list->strings = new_strings;
list->cap = new_cap;
}
list->strings[list->count] = str;
list->count += 1;
return 0;
}
int readHeaders(int fd, struct string_list *headers)
{
char *line;
initStringList(headers);
while (true)
{
line = readLine(fd);
if (!line)
{
freeStringList(headers);
return -1;
}
if (*line == '\0')
{
free(line);
break;
}
if (!appendLine(headers, line))
{
free(line);
freeStringList(headers);
return -1;
}
}
return 0;
}
int sendRaw(int fd, void *data, size_t size)
{
char *ptr = data;
ssize_t sent;
while (size > 0)
{
sent = write(fd, ptr, size);
if (sent < 1)
return -1;
ptr += sent;
size -= sent;
}
return 0;
}
int sendString(int fd, char *str)
{
return sendRaw(fd, line, strlen(line));
}
int sendLine(int fd, char *line)
{
if (sendString(fd, line) < 0)
return -1;
return sendRaw(fd, "\r\n", 2);
}
int sendResponse(int fd, int code, char *status, char **headers, void *data, size_t data_size)
{
char tmp[50];
sprintf(tmp, "HTTP/1.1 %d ", code);
if (sendString(fd, tmp) < 0)
return -1;
if (sendLine(fd, status) < 0)
return -1;
if (headers)
{
for(size_t i = 0; headers[i] != NULL; ++i)
{
if (sendLine(fd, headers[i]) < 0)
return -1;
}
}
sprintf(tmp, "Content-Length: %u ", data_size);
if (sendLine(fd, tmp) < 0)
return -1;
if (sendRaw(fd, "\r\n", 2) < 0)
return -1;
return sendRaw(fd, data, data_size);
}
struct httpRequest
{
struct string_list headers;
struct data_buffer body;
};
void initHttpRequest(struct httpRequest *req)
{
initStringList(&(req->headers));
initDataBuffer(&(req->body));
}
void freeHttpRequest(struct httpRequest *req)
{
freeStringList(&(req->headers));
freeDataBuffer(&(req->body));
}
int readRequest(int fd, struct httpRequest *req)
{
char *transferEncoding = NULL;
char *contentLength = NULL;
char* error_headers[] = {"Connection: close", NULL};
initHttpRequest(req);
if (readHeaders(fd, &(req->headers)) < 0)
{
freeHttpRequest(req);
sendResponse(fd, 500, "Internal Server Error", error_headers, NULL, 0);
return -1;
}
for (size_t i = 0; req->headers[i] != NULL; ++i)
{
if (strncmpi(req->headers[i], "Transfer-Encoding:", 18) == 0)
transferEncoding = req->headers[i] + 18;
else if (strncmpi(req->headers[i], "Content-Length:", 15) == 0)
contentLength = req->headers[i] + 15;
}
if (transferEncoding)
{
size_t len = strlen(transferEncoding);
if ((len < 7) || (strncmpi(transferEncoding + len - 7, "chunked", 7) != 0))
{
freeHttpRequest(req);
sendResponse(fd, 400, "Bad Request", error_headers, NULL, 0);
return -1;
}
char chunk_data[1024];
while (true)
{
char *chunk_line = readLine(fd);
if (!chunk_line)
{
freeHttpRequest(req);
sendResponse(fd, 500, "Internal Server Error", error_headers, NULL, 0);
return -1;
}
long chunk_size = strtol(chunk_line, NULL, 16);
free(chunk_line);
if ((chunk_size < 0) || (chunk_size >= LONG_MAX))
{
freeHttpRequest(req);
sendResponse(fd, 400, "Bad Request", error_headers, NULL, 0);
return -1;
}
if (chunk_size == 0)
break;
while (chunk_size > 0)
{
size_t size = min(chunk_size, sizeof(chunk_data));
if (readRaw(fd, chunk_data, size) < 0)
{
freeHttpRequest(req);
return -1;
}
if (!appendBytes(&(req->body), chunk_data, size))
{
freeHttpRequest(req);
sendResponse(fd, 500, "Internal Server Error", error_headers, NULL, 0);
return -1;
}
}
}
struct string_list trailer_headers;
if (readHeaders(fd, &trailer_headers) < 0)
{
freeHttpRequest(req);
sendResponse(fd, 500, "Internal Server Error", error_headers, NULL, 0);
return -1;
}
// update entries of req->headers with entries of trailer_headers as needed...
freeStringList(&trailer_headers);
}
else if (contentLength)
{
long content_size = strtol(contentLength, NULL, 10);
if ((content_size < 0) || (content_size >= LONG_MAX))
{
freeHttpRequest(req);
sendResponse(fd, 400, "Bad Request", error_headers, NULL, 0);
return -1;
}
req->body.data = malloc(content_size);
if (!req->body.data)
{
freeHttpRequest(req);
sendResponse(fd, 500, "Internal Server Error", error_headers, NULL, 0);
return -1;
}
req->data_cap = content_size;
if (readRaw(fd, req->body.data, content_size) < 0)
{
freeHttpRequest(req);
return -1;
}
req->data_size = content_size;
}
return 0;
}
int main(void)
{
int server_fd, new_socket;
struct sockaddr_in address;
struct httpRequest req;
int addrlen = sizeof(address);
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0)
{
perror("socket");
return 1;
}
memset(&address, 0, sizeof address);
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("bind");
return 1;
}
if (listen(server_fd, 10) < 0)
{
perror("listen");
return 1;
}
while (true)
{
printf("\n+++++++ Waiting for new connection ++++++++\n\n");
addrlen = sizeof(address);
new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (new_socket < 0)
{
perror("accept");
return 1;
}
if (readRequest(new_socket, &req) < 0)
{
close(new_socket);
continue;
}
// use req as needed...
printf("\n");
for (size_t i = 0; i < req->body.size; i++)
printf("%02X ", (int) ((char*)req->body.data)[i]);
printf("\n\n");
freeHttpRequest(&req);
char* resp_headers[] = {"Connection: close", "Content-Type: text/plain", NULL};
if (sendResponse(new_socket, 200, "OK", resp_headers, "Hello world!", 12) == 0)
printf("------------------Hello message sent-------------------\n");
close(new_socket);
}
return 0;
}