Reputation: 111
void callback (struct Request req) {
char buffer[8196];
int file;
bzero(buffer, sizeof(buffer));
if(!strncmp(req.method, "GET", 3)){
if (!strcmp(req.path, "/")){
memcpy(buffer, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n", 44);
response(buffer);
int file = open("./static/index.html", O_RDONLY);
long ret;
while ((ret = read(file, buffer, 8196)) > 0) {
response(buffer);
}
}else if (!strcmp(req.path, "/login")){
memcpy(buffer, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n", 44);
response(buffer);
int file = open("./static/login.html", O_RDONLY);
long ret;
while ((ret = read(file, buffer, 8196)) > 0) {
response(buffer);
}
}else{
char path[80];
char header[240];
sprintf(path, "./static%s", req.path);
int ext = readFile(path, buffer);
char type[15];
struct stat st;
if(!ext){
strcpy(type, "text/html\0");
}else if(ext == 1){
strcpy(type, "text/javascript\0");
}else if(ext == 2){
strcpy(type, "text/css\0");
}else if(ext == 3){
strcpy(type, "image/jpeg\0");
}
stat(path, &st);
sprintf(header, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", type, st.st_size);
response(header);
int file = open(path, O_RDONLY);
long ret;
while ((ret = read(file, buffer, 8196)) > 0) {
buffer[8196-1] = 0;
response(buffer);
}
}
}else if(!strncmp(req.method, "POST", 4)){
}
bzero(buffer, sizeof(buffer));
};
void response (char message[]) {
write(clientsocket, message, strlen(message));
};
This is the code I have for my HTTP server coded in C. The callback function is executed everytime the socket receives a request. The struct Request has 2 char []
, method that can be POST
or GET
, and path, which contains the request's path. The server sends properly the static files such html, js and css files. It serves the bootstrap statics properly, although if buffer[8196-1] = 0;
is not when I send the file, the file contains wrong characters.
I try to send a .jpg
image, and the headers are properly sended, but the file is not. When I access to 127.0.0.1:8080/image.jpg
, Firefox displays an error that the image contains errors and can't be displayed, or just display a bunch of caracters and the server stops serving the rest of js and css files.
I read all the questions releated to this and I didn't file a solution jet. The image size is 1'1Mb, maybe is that the problem? I think not because I send the data in 'pieces', but I'm not sure at all. The program doesn't show me an error about the file, seems to be opened and read correctly.
It's a header problem maybe? The rest of the js and css files are sended in the same pice of code that the image (the else
statement), ad they don't have any trouble, and the FF console show me the headers of every file, and they're correct so I think that's not the problem.
Thank's advanced!
Upvotes: 1
Views: 4909
Reputation: 598021
When the client requests either /
or /login
, you are not sending any Content-Length
or Transfer-Encoding: chunked
response header. Without that, the client can't know whether a message body exists or when to stop reading it, unless you close the socket connection immediately after sending the response (see RFC 1945 Section 7.2.2 and RFC 2616 Section 4.4). Which you should be doing anyway since you are sending an HTTP 1.0 response without a Connection: keep-alive
response header.
You should not be doing anything with buffer[8196-1]
at all. You should be telling response()
how many bytes are actually in buffer
and let it send the buffer
as-is up to that number of bytes. Don't rely on strlen()
for binary data, since binary data can contain embedded nulls.
You should be sending an HTTP error if open()
fails to open the file being requested.
With that said, try something more like this instead:
void callback (struct Request req)
{
char buffer[8196];
int file;
bzero(buffer, sizeof(buffer));
if (strncmp(req.method, "GET") == 0)
{
char path[258];
char header[240];
char *type;
if (strcmp(req.path, "/") == 0)
{
strcpy(path, "./static/index.html");
type = "text/html";
}
else if (strcmp(req.path, "/login") == 0)
{
strcpy(path, "./static/login.html");
type = "text/html";
}
else
{
sprintf(path, "./static%.258s", req.path);
// or: snprintf(path, 258, "./static%s", req.path);
int ext = determineFileType(path);
switch (ext)
{
case 0: // HTML
type = "text/html";
break;
case 1: // Javascript
type = "text/javascript";
break;
case 2: // CSS
type = "text/css";
break;
case 3: // JPG
type = "image/jpeg";
break;
default: // something else
type = "application/octet-stream";
break;
}
}
int file = open(path, O_RDONLY);
if (file == -1)
{
// send different errors depending on the failure, like 404 if the file is not found, etc...
if (errno == ENOENT) {
strcpy(header, "HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n");
} else {
strcpy(header, "HTTP/1.0 500 Internal Error\r\nContent-Length: 0\r\n\r\n");
}
response(header, strlen(header));
}
else
{
struct stat st;
stat(path, &st);
sprintf(header, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", type, st.st_size);
response(header, strlen(header));
long ret;
while ((ret = read(file, buffer, sizeof(buffer))) > 0)
response(buffer, ret);
close(file);
}
}
else if (strncmp(req.method, "POST") == 0)
{
//...
}
}
void response (void *message, int msglen)
{
char *msg = (char*) message;
while (msglen > 0)
{
int len = write(clientsocket, msg, msglen);
if (len <= 0) return;
msg += len;
msglen -= len;
}
}
Upvotes: 3
Reputation: 1
HTTP is a complex protocol (the specifications have hundreds of pages). So better use some existing libraries for it. On the server side (to embed some web server in your application), use e.g. libonion. On the client side (to make HTTP requests in your application), use e.g. libcurl
BTW, you probably could strace(1) your program to understand the really executed syscalls(2).
Upvotes: 2
Reputation: 229854
The call to write()
in response()
does not necessarily write the complete buffer to the socket.
If there is already to much data buffered by the OS it might only write a part of the data in the buffer and you will need another (or several more) calls to write()
to get everything transmitted. To see how much data was transmitted look at the return value ofwrite()
. It returns the number that were written.
Additionally you use strlen()
to determine the length of the data to transmit, but images are not strings. They can contain zero bytes and strlen()
will not give useful results. Better pass the length of the data explicitly to the response()
function.
Also, when reading the image file, buffer[8196-1] = 0
will overwrite bytes of the image with zeros.
Upvotes: 2