Reputation: 13
I'm trying to write a web server but with the code I have I'm getting 'open failed'. The html document (ht.html) which is supposed to be opened in the browser.
The code I have is:
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10
int main(int argc, char *argv[])
{
int s, b, l, fd, sa, bytes, on = 1;
char buf[BUF_SIZE]; //buffer for outgoing file
struct hostent *h; //info about server
struct sockaddr_in channel; //holds IP address
//Build address structure to bind to socket
memset(&channel, 0, sizeof(channel)); //zero channel
channel.sin_family = AF_INET;
channel.sin_addr.s_addr = htonl(INADDR_ANY);
channel.sin_port = htons(SERVER_PORT);
//Passive open. Wait for connection
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
if (s < 0) fatal("socket failed");
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
if (b < 0) fatal("bind failed");
l = listen(s, QUEUE_SIZE); /* specify queue size */
if (l < 0) fatal("listen failed");
/* Socket is now set up and bound. Wait for connection and process it. */
while(1) {
sa = accept(s, 0, 0); /* block for connection request */
if (sa < 0) fatal("accept failed");
read(sa, buf, BUF_SIZE); /* read file name from socket */
/* Get and return the file. */
fd = open(buf, O_RDONLY); /* open the file to be sent back */
if (fd < 0) fatal("open failed");
while(1){
bytes = read(fd, buf, BUF_SIZE); /* read from file */
if (bytes <= 0) break; /* check for end of file */
write(sa, buf, bytes); /*write bytes to socket*/
}
close(fd); //close file
close(sa); //close connection
}
}
fatal(char*string)
{
printf("%s", string);
exit(1);
}
Where's my error? Or what has to be added?
Upvotes: 1
Views: 1396
Reputation: 59987
The stuff you are reading from the browser is a HTTP request.
You will need to decode this - so read the spec for HTTP.
Example of HTTP requests can be found here
Upvotes: 0
Reputation: 1700
Maybe you can start with outputting the data received from the socket, or at least run in a debugger, otherwise everything will be run in the dark, without you knowing what is going on. In the code below I have added a printf
to print what we get from the web browser.
Also like others have pointed out, it is good to know what errno
is trying to tell us. It is a bit awkward/annoying to use perror
+ exit
, so in Linux and BSD you can use err(3) and warn(3). err will print errno message and then exit, while warn will just print errno message and not exit, I replaced your fatal
function with these.
The web browser most likely will send GET /ht.html HTTP/1.1\r\n
and this is what you are attempting to open. In order to open the file we need to extract the ht.html
part. I have updated your code below and now strchr(3) and strstr(3) are used to extract ht.html
.
We also need to send a HTTP response code and tell the web browser we want to send HTML, that is why the HTTP/1.1 200 OK
is sent. Remember all HTTP headers need to be separated by \r\n
(carriage return - newline). You will find more info about the HTTP protocol in RFC 2616.
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#define SERVER_PORT 12345
#define BUF_SIZE 4096
#define QUEUE_SIZE 10
int main(int argc, char *argv[])
{
int s, b, l, fd, sa, bytes, on = 1;
char buf[BUF_SIZE]; /* buffer for outgoing file */
char *p, *endp, *cp;
struct sockaddr_in channel; /* holds IP address */
/* Build address structure to bind to socket */
memset(&channel, 0, sizeof(channel)); /* zero channel */
channel.sin_family = AF_INET; /* ipv4 */
channel.sin_addr.s_addr = htonl(INADDR_ANY); /* 0.0.0.0 */
channel.sin_port = htons(SERVER_PORT);
/* Passive open. Wait for connection */
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */
if (s < 0) err(1, "socket failed");
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
b = bind(s, (struct sockaddr *) &channel, sizeof(channel));
if (b < 0) err(1, "bind failed");
l = listen(s, QUEUE_SIZE); /* specify queue size */
if (l < 0) err(1, "listen failed");
/* Socket is now set up and bound. Wait for connection and process it. */
while(1) {
sa = accept(s, NULL, NULL); /* block for connection request */
if(sa < 0) {
warn("accept failed");
continue;
}
bytes = 0;
endp = buf + sizeof(buf); /* pointer to end of buf */
cp = NULL;
buf[0] = '\0';
/* read first line from socket */
/* should be "GET /[file] HTTP/1.1" */
for(p = buf; (bytes = read(sa, p, endp - p)) > 0; p += bytes) {
p[bytes] = '\0'; /* read(2) doesn't NUL terminate buf */
if((cp = strchr(p, '\r')) != NULL) /* break at first carriage return */
break;
}
printf("incoming request %lu bytes:\n%s\n", strlen(buf), buf);
/* no carrige return or no "GET /" was found */
if(cp == NULL || strstr(buf, "GET /") != buf) {
warnx("incomplete request");
close(sa);
continue;
}
*cp = '\0'; /* replace '\r' with '\0' */
p = buf + sizeof("GET /") - 1; /* point to after "GET /" */
cp = strchr(p, ' '); /* find " HTTP/1.1" */
if(cp == NULL) {
warnx("HTTP version was not found");
close(sa);
continue;
}
*cp = '\0'; /* replace ' ' with '\0' */
/* Get and return the file. */
fd = open(p, O_RDONLY); /* open the file to be sent back */
if(fd < 0) {
warn("open failed: %s", p);
close(fd);
close(sa);
continue;
}
/* Send HTTP header */
/* Should probably also send Content-Length: <sizeof file>, */
/* this can be checked using fstat(2) */
write(sa, "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html;charset=UTF-8\r\n\r\n", 58);
while(1) {
bytes = read(fd, buf, sizeof(buf)); /* read from file */
if (bytes <= 0) break; /* check for end of file */
write(sa, buf, bytes); /*write bytes to socket*/
}
close(fd); /* close file */
close(sa); /* close connection */
}
return 0;
}
To connect from your web browser to your HTTP server go to: http://127.0.0.1:12345/ht.html
.
Upvotes: 1
Reputation: 189
Try to look ERRNO.
if (fd < 0) perror("open failed");
Try to look buf.
if (fd < 0){
printf("%s\n", buf);
perror("open failed");
}
Try to look buf this way:
if (fd < 0){
for(i=0;i<strlen(buf);i++)
printf("%d", buf[i]);
perror("open failed");
}
This will be enough to understand the error because your application simply does not open the file.
Upvotes: 0
Reputation: 3366
Try to add some debug messages or run with a debugger.
I think that the problem relies in the buffer passed to open statement. It looks like buf is not initialized with zeroes and also not NULL terminated by "read".
n = read(sa, buf, BUF_SIZE);
buf[n] = '\0';
In general, when working with read, it should be called in a loop until 0 or -1 returned. It might fill only a fraction of the buffer.
Upvotes: 0