Solved

A web server for Unix

Posted on 2004-08-07
7
256 Views
Last Modified: 2010-04-17
Here is the code for a web server:

#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <curses.h>
#include <sys/time.h>
#include <netdb.h>

#define MAXLINE 1000
#define SA struct sockaddr

#define DOCUMENT "index.html"

void
sigchld_handler(int s)
{
     while (wait(NULL) > 0);
}
void
send_doc(int connfd)
{
     FILE *fp;
     char output[1000];
     int len;
     
     if((fp = fopen(DOCUMENT, "r")) == NULL) {
          printf("Cannot open file.\n");
          exit(1);
     }
         
     while(!feof(fp)) {
          fgets(output, 999, fp);
          len = strlen(output);
          if (sendall(connfd, output, &len) == -1) {
               perror("sendall");
               printf("We only sent %d bytes because of the error!\n", len);
          }
     }

}
int
sendall(int s, char *buf, int *len)
{
     int             total = 0;
     
     int             bytesleft = *len;
     
     int             n;

     while (total < *len) {
          n = send(s, buf + total, bytesleft, 0);
          if (n == -1) {
               break;
          }
          total += n;
          bytesleft -= n;
     }
     *len = total;
     
          return n == -1 ? -1 : 0;
     
}

int
main(void)
{
     int             listenfd, connfd;
     pid_t           childpid;
     socklen_t       clilen;
     struct sockaddr_in cliaddr, servaddr;
     struct sigaction sa;
     int             len, yes = 1, sin_size, nbytes;

     char            data[1000];

     if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
          perror("socket");
          exit(1);
     }
     if ((connfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
          perror("socket");
          exit(1);
     }
     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
          perror("setsockopt");
          exit(1);
     }
     bzero(&servaddr, sizeof(servaddr));
     servaddr.sin_family = AF_INET;
     servaddr.sin_addr.s_addr = INADDR_ANY;
     servaddr.sin_port = htons(8080);

     if (bind(listenfd, (struct sockaddr *) & servaddr, sizeof(struct sockaddr)) == -1) {
          perror("bind");
          exit(1);
     }
     if (listen(listenfd, 10) == -1) {
          perror("listen");
          exit(1);
     }
     sa.sa_handler = sigchld_handler;
     
          sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESTART;
     if (sigaction(SIGCHLD, &sa, NULL) == -1) {
          perror("sigaction");
          exit(1);
     }
     for (;;) {
          clilen = sizeof(cliaddr);
          if ((connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) == -1) {
               perror("accept");
               exit(1);
          }
          printf("server: got connection from %s\n", inet_ntoa(cliaddr.sin_addr));
          if ((childpid = fork()) == 0) {
               close(listenfd);
               send_doc(connfd);
               sleep(10);
               exit(0);
               
          }
          close(connfd);
     }
}


It is working properly.  it is displaying index.html as default which is in the root directory. now i want to modify the code so that it can display any requested page stored in the root folder.? how will i do that??
0
Comment
Question by:pritam123456
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
7 Comments
 
LVL 7

Accepted Solution

by:
JugglerW earned 43 total points
ID: 11743550
You must read at least the first line of the HTTP request from the client socket.
Line is like: GET /dir1/dir2/file HTTP/1.x (where x is 0 or 1)
You must send back file dir1/dir2/file under your servers root.
But to have a full web server you have to do much, much more.
Look here to get an idea:

http://www.w3.org/Protocols/rfc2616/rfc2616.html
0
 

Author Comment

by:pritam123456
ID: 11745063
i dont really understand what you mean. can you write a sample code for me where should i accept the page name.  give me some more clear information please
0
 
LVL 45

Assisted Solution

by:sunnycoder
sunnycoder earned 41 total points
ID: 11745507
Pritam,

Are you familiar with HTTP header format? If no, then check the link posted by JugglerW. This is the standard which specifies how HTTP works. Understanding of this standard is essential if you want to write a web server.

The header of an HTTP request carries the name of the file that is desired by the client. You need to get this information from the header, locate the file locally. Open it and send it back as HTTP response.

Since this is academic, I would refrain from posting code unless you are through with the format and have attempted in addiing the functionality to your code.
0
 
LVL 7

Expert Comment

by:JugglerW
ID: 11745635
In your code:

send_doc(int connfd)
{
     FILE *fp;
     char output[1000];
     int len;


read one line (terminated by \r\n)  from the socket with handle connfd. This line is in the format I've descibed above.
Then extract the file name and send file back as you've done with index.html.
E.g. if the line is
GET / HTTP/1.x
this is a special case meaning to send index.html in the root dir.
and:
GET /foo.bar HTTP/1.x
Means to send back file foo.bar in your servers root dir.

But as sunnycoder say. First glance through the RFC doc to get an idea what the HTTP protocol demands.

0
 
LVL 8

Assisted Solution

by:adg080898
adg080898 earned 41 total points
ID: 11749374
The program does not receive anything. You need to receive the request to know what the user wants.

First, you have to parse the request. The user might send something like this:

GET /somedir/somefile.htm HTTP/1.1
User-agent: ..whatever...
Accept: */*
Accept-encoding: deflate/gzip
SomeOtherHeader: someothervalue
AnotherHeader: etc.

Each line will end with \r\n, or more exactly, ASCII value 13 followed by ASCII value 10.
(A robust server should also allow lines to end with JUST \r or JUST \n, but it is not entirely necessary).

Headers also allow line continuations: if the first character on a line is a space, then it is a continuation of the previous line.

To keep it simple, accumulate received data into a buffer. Whenever you append received data, search the buffer for an occurrence of "\r\n\r\n". This marks the end of the request.

Once you have found the end of the request, work your way through the request:
- Search for \r (the ASCII value 10).
- Overwrite the found character with a null terminator.
- The beginning of the buffer is now a null terminated string, in my example, "GET /somedir/somefile.htm HTTP/1.1"

If you plan to support more than simply getting a page, you need to process the rest of the request.

To support line continuations, search for an occurrence of \r\n followed by a space. Replace the \r\n with two spaces in all these cases.

Now, create a pointer array which will be filled with pointers to the beginning of each line of the request.

Search for the first occurrence of "\r" and overwrite it with a null terminator.
Then search within the string you just terminated for a ":". Place a null terminator after the colon. The text after the null terminator is the value. The text at the beginning of the line is the name of the header value.

So in my example, you place a null terminator where I have marked it with an X:
User-agent: ..whatever...X
Accept: */*X
Accept-encoding: deflate/gzipX
SomeOtherHeader: someothervalueX
AnotherHeader: etc.X

Then you process through each of those strings and place more terminators, also marked with X:

User-agent:X..whatever...
Accept:X*/*
Accept-encoding:Xdeflate/gzip
SomeOtherHeader:Xsomeothervalue
AnotherHeader:Xetc.

Now you have chopped the request up into "name" "value" pairs. Fill your pointer array with a pointer to the beginning of each line of the request. Calculate your pointers to where I marked it with a P. I also show all null terminators with X:

PUser-agent:X..whatever...X
PAccept:X*/*X
PAccept-encoding:Xdeflate/gzipX
PSomeOtherHeader:XsomeothervalueX
PAnotherHeader:Xetc.X

Say you need to get the "Accept-encoding" header, you can search your pointer array for the string "Accept-encoding:". Then, to get the value for accept encoding:

char *pHdrLines[MAX_HDR_LINES], *pHdrValue;

for (i = 0; i < nHdrLines; i++) {
    if (strcmp(pHdrLines[i], "Accept-encoding:") == 0) {
        pHdrValue = pHdrLines + strlen(pHdrLines) + 1;
        break;
    }
}
if (i < nHdrLines) {
    // I found it. The name is pHdrLines[i] and the value is at pHdrValue.
    // ...
}

See? An overview of the process:
- Search for all occurrences of "\r\n " (CR, LF, Space) and replace them with "   " (3 spaces).
- Work your way through the buffer, terminating at each "\r" encountered. Save a pointer to the beginning of that line.
- Once you have terminated each line and populated your pointer array, search each row for a ":" and place a null terminator after it.
- At this point, you have chopped the request into easy-to-process pieces, you can retrieve information from the header using the loop shown above.

You can read ALL the details about how to do this and what all the values mean at the URL JugglerW posted.

0

Featured Post

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Does the idea of dealing with bits scare or confuse you? Does it seem like a waste of time in an age where we all have terabytes of storage? If so, you're missing out on one of the core tools in every professional programmer's toolbox. Learn how to …
In this post we will learn different types of Android Layout and some basics of an Android App.
An introduction to basic programming syntax in Java by creating a simple program. Viewers can follow the tutorial as they create their first class in Java. Definitions and explanations about each element are given to help prepare viewers for future …
With the power of JIRA, there's an unlimited number of ways you can customize it, use it and benefit from it. With that in mind, there's bound to be things that I wasn't able to cover in this course. With this summary we'll look at some places to go…

688 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question