1

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?

5
  • How did you debug the server? Commented Feb 23, 2013 at 18:40
  • 1
    read(sa, buf, BUF_SIZE); You expect a null-terminated string from read(). You don't get one: read() can return anything between -1 and BUF_SIZE, including. And even if it would return all the characters of your "filename", it would still not be necessarily nul-terminated. Commented Feb 23, 2013 at 18:50
  • You are not 'getting open failed', you are saying 'open failed' to yourself. That's not useful. Call perror() so you will know what the error is. Never suppress error infprmation like that. Commented Feb 23, 2013 at 22:05
  • Thank you. That helped a lot. I'm getting no such file or directory which is strange because the file exists. Is it not being read properly? Commented Feb 23, 2013 at 22:23
  • What was in 'buf'? Are you sure it was a complete, valid filename? With a terminating null? Commented Feb 24, 2013 at 10:13

4 Answers 4

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

It's not working completely as yet, but your assistance gave me some head way.
0

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.

1 Comment

I'm using linux that's why.
0
  1. Try to look ERRNO.

    if (fd < 0) perror("open failed");
    
  2. Try to look buf.

    if (fd < 0){ 
      printf("%s\n", buf);
      perror("open failed");
    }
    
  3. 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.

2 Comments

Are you saying to use either 1, 2, 3 in place of fd = open(buf, O_RDONLY); /* open the file to be sent back */ if (fd < 0) fatal("open failed");
Yes. It's just some debug messages, but you will see a cause of error.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.