1
\$\begingroup\$

I have created a simple server that host html that has css. Please make sure if ur testing then change the paths relative to ur system in listhell.c in respond_main(..) function.

The html file has to be named as html9.html and css has to be named as style9.css should be path to ../project directory/css directory. this is the code along with makefile

This is main function file server.c

    #include "headers/accept.h"

    int main()
    {
        pthread_t thread_css;
        pthread_t thread_html;
        pthread_t thread_get;
        int server_fd, new_socket, valread;
        struct sockaddr_in address;
        int opt = 1;
        int addrlen = sizeof(address);
        char buffer[1024] = {0};
        char *hello = "Hello from server";
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
        {
            printf("socket failed\n");
            exit(EXIT_FAILURE);
        }
        printf("server_fd = %d\n",server_fd);
        if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
                                  &opt, sizeof(opt)))
        {
            printf("setsockopt\n");
            exit(EXIT_FAILURE);
     }

     int state = 1;
        if(setsockopt(server_fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)))
        {
            printf("TCP CORK\n");
        }
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        address.sin_port = htons( PORT );
        // Forcefully attaching socket to the port 8080
        if (bind(server_fd, (struct sockaddr *)&address,
                     sizeof(address))<0)
        {
            printf("bind failed\n");
            exit(EXIT_FAILURE);
        }
        if (listen(server_fd, 3) < 0)
        {
            printf("listen\n");
            exit(EXIT_FAILURE);
        }
        accept1(server_fd,address,addrlen);
        return 0;





    }

this is the file that contains linux socket accept function call accept.c

    #include "headers/accept.h"
    #define BUFFER 1024+1
    int check_if_t(char *buffer,int valread,int *f)
    {
        int i=0;
        while(i<BUFFER)
        {
            printf("%c",*(buffer+i));
            if(*(buffer+i)=='\r')
            {
                    *f=1;
                printf("***************************************************************************\n");

                return i;
            }
            i++;
        }
        return i;

    }

    void* handle_request(void *arg)
    {

        //printf("\n");
        struct connection *con_obj=malloc(sizeof(struct connection));


        if(con_obj!=NULL)
        {
            //con_obj->server_fd=(int*)arg;
            con_obj=(struct connection *)arg;

            //printf("new_socket get thread : %d\n",*(con_obj->new_socket));
            char *str_array=NULL;
            str_array=malloc(BUFFER+1);
            str_array[0]='\0';
            char buffer[BUFFER] = {0};
            int x=0;
            int check_tp=0;
            int j=0;
            while(x<BUFFER)
            {
                int valread=read(*(con_obj->new_socket),&buffer[x],BUFFER);;
            //  printf("Socket Buffer read valread = %d \n %.*s",(valread-1),(valread-1),buffer);
                int f=0;

                check_tp=check_if_t(buffer,(BUFFER),&f);
                //printf("check_tp = %d\n",check_tp);

                if(f==1)
                {

                    strncpy(&str_array[j],&buffer[j],check_tp);
                    j=j+check_tp;
                    str_array[j]='\0';
                    //printf("--->\n");
                    //printf("j = %d\n",j);
                    //printf("%.*s\n",j,str_array);
                    break;
                }
                if(str_array!=NULL)
                strncpy(&str_array[j],&buffer[j],check_tp);
                j=j+check_tp;

                x=x+check_tp;

            }
            x=0;


            if(str_array!=NULL)
            {
                if(strlen(str_array)>0)
                    {

                        //printf("printing first line = %s | length = %zu j = %d\n ",str_array,strlen(str_array),j);
                        pthread_t thread_process;
                        con_obj->request_line=malloc(sizeof(char)*j);
                        if(con_obj->request_line==NULL)
                        {
                            while(1)
                            {
                                    printf("handle_request -- heap problem at line 80: con_obj->request_line=malloc(sizeof(char)*j); \n");
                            }
                        }

                        if(j>=BUFFER)
                        {
                            free(con_obj->request_line);
                          printf("check line 89 handle_request\n");
                            //exit(0);
                            j=BUFFER-1;
                            con_obj->request_line[j]='\0';
                            close(*(con_obj->new_socket));
                            free(con_obj);
                            return (void*)0;



                        }
                        memcpy((con_obj->request_line),str_array,sizeof(char)*j);
                        con_obj->request_line_size=j;
                        pthread_create(&thread_process, NULL, process, (void *)con_obj);


                    }
                else{
                    //printf("str_array length not found = %zu\n",strlen(str_array));
                }
                free(str_array);
                //

            }
        }
        else
        {
            printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
            printf("No space for malloc on heap\n");
        }
        return (void*)0;

    }

    void accept1(int server_fd,struct sockaddr_in address,int addrlen)
    {


        while(1)
        {
            int new_socket;
            if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
            (socklen_t*)&addrlen))<0)
            {
                perror("accept");
                exit(EXIT_FAILURE);
            }

            //printf("new_socket just created = %d\n",new_socket);
            pthread_t thread_get;

            struct connection *con_obj=malloc(sizeof(struct connection));
            if(con_obj!=NULL)
            {
                con_obj->new_socket=&new_socket;
                con_obj->address=address;
                //printf("%d hello\n",*(con_obj->new_socket));


                pthread_create(&thread_get, NULL, handle_request, (void *)con_obj);
            }
            else
            {
                printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
                printf("No space for malloc on heap\n");

            }
            int state = 0;
            if(setsockopt(server_fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)))
            {
                printf("TCP CORK\n");
            }

            state = 1;
            if(setsockopt(server_fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)))
            {
                printf("TCP CORK\n");
            }
            //printf("After Thread creation\n");
        }

    }
    

this is process.c

    #include <stdio.h>
    #include "headers/accept.h"

    //fetch null terminated string values
    // void get_dirs_file(char *path,   char **dirs,char **file)
    // {
    //  printf("getting directories and file for path = %s \n",path);
    //
    // }

    int handle_process(struct connection *con_obj,char **path,char **request_type)
    {
        int i=0;
        request_type[0]='\0';
        int method=0;

        while(i<(con_obj->request_line_size))
        {
            //printf("%c",(con_obj->request_line[i]));
            if(strncmp(&(con_obj->request_line[i]),"GET",3)==0)
            {

                method=3;
                *request_type=malloc(sizeof(char)*4);
                strncpy(*request_type,"GET",sizeof(char)*4);
                char *temp=con_obj->request_line+4;
                if(strncmp(temp,"/ ",2)==0)
                {
                    *path=malloc(sizeof(char)*strlen("html9.html"));
                    strncpy(*path,"html9.html",strlen("html9.html")+1);


                    break;
                }
                if(strncmp(temp,"/add?",sizeof(char)*5)==0)
                {
                    int j=0;
                    while(j<strlen(temp))
                    {
                        if(temp[j]==' ')
                        {
                            break;
                        }
                        j++;
                    }
                    *path =malloc(sizeof(char)*j+1);
                    strncpy(*path,temp,sizeof(char)*j);

                    (*(path+j))='\0';
                    //tm((&path));
                    //printf("hello line now -------> %s\n",*path);

                    break;


                }
                if(strncmp(temp,"/css/",sizeof(char)*5)==0)
                {
                    int j=0;

                    while(j<strlen(temp))
                    {
                        if(temp[j]==' ')
                        {
                            break;
                        }

                        j++;
                    }
                    *path=malloc(sizeof(char)*j+1);
                    strncpy(*path,temp,sizeof(char)*j);
                    (*(path+j))='\0';
                    //printf("hello line now -------> %s\n",*path);
                }           //return strlen("GET");
            }
            i++;
        }
        printf("hello line now -------> path = %s request_type = %s\n",*path,*request_type);
        if(method==0)
        return 0;
        return strlen(*request_type);
    }


    void *process(void *p)
    {   //printf("hello in process thread\n");
        int r=0;
        struct connection *con_obj=malloc(sizeof(struct connection));
        if (con_obj != NULL)
        {
            printf("\n\n");
            printf("------------------------------------------------------------------------------------\n");
            con_obj=(struct connection *)p;
            printf("In process thread | new_socket = %d\n",*(con_obj->new_socket));

            printf("j = %d | request_line = %s\n",(con_obj->request_line_size),(con_obj->request_line));
            char *path=NULL;
            char*request_type=NULL;

            //path will be allocated and get null ['\0'] terminated in handle_process
            printf("before handle_process\n");
            int request_type_size=handle_process(con_obj,&path,&request_type);
            if(request_type_size==0)
            {
                //different request_type
                close(*(con_obj->new_socket));
                if(path!=NULL)
                free(path);
                if(request_type!=NULL)
                free(request_type);
                free(con_obj);
                return (void*)0;
            }
        printf("after handle_process\n" );
            if(path!=NULL)
            {
                printf("in handle_process  Path Len = %zu Request Path = '%s'\n",strlen(path),path);
            }

            if(request_type!=NULL)
            {
                printf("in handle_process  Type Len = %zu Request Type = '%s'\n",strlen(request_type),request_type);
            }
            if(path!=NULL && request_type!=NULL)
            {
                if(strncmp(request_type,"GET",request_type_size)==0)
                {
                    printf("inside GET in process func\n");
                    char *dirs;
                    char *file;
                    file=malloc(sizeof(char)*strlen(path));

        // get_dirs_file: returns '/ ' for TYPE_MAIN | 'style9.css' for TYPE_CSS | 'title=&price=' for TYPE_QUERY
                    r=  get_dirs_file(&path[0],file);//get null terminated string values
                    printf("\n\npath to follow -- = 'path = %s' 'file = %s'\n\n",path,file);
                    if(r == TYPE_MAIN)
                    {
                        respond_main(con_obj,RESPONSE_MAIN_HTML);
                    }
                    else if(r== TYPE_CSS)
                    {
                        respond_main(con_obj,RESPONSE_MAIN_CSS);

                    }
                    else if(r=TYPE_QUERY)
                    {
                        respond_main(con_obj,RESPONSE_MIAN_QUERY);
                    }
                    free(file);
                }
            }

            close(*(con_obj->new_socket));
            free(con_obj);

            printf("------------------------------------------------------------------------------------\n");
        }
        else
        {
            printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
            printf("No space for malloc on heap\n");
        }

        return (void *)0;
    }

this is file.c

    #include <fcntl.h>
    #include  <errno.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include "headers/accept.h"
    #include <sys/types.h>




    #include <stdlib.h>



    #include <errno.h>

    #include <unistd.h>
    #include <fcntl.h>
    #include <time.h>
    int get_file_type(char *path,char *file)
    {
      int i=0;
      printf("%zu \n",strlen(path));
      while(i<strlen(path))
      {
        if(path[i]=='?')
        {
          //query string
          int j=0;
          strcpy(file,&path[i+1]);
          file[strlen(path)]='\0';

          return TYPE_QUERY;
        }
        if(strncmp(&path[i],"html9.html",strlen(path))==0)
        {

          strcpy(file,"/ ");
          return TYPE_MAIN;
        }
        if(strncmp(&path[i],".css",4)==0)
        {
          int j=strlen(path);
          while(j>0)
          {
        if(path[j]=='/')
        {
          strcpy(&file[0],&path[j]);
          break;
        }

        j--;
          }
        //  file[j]='\0';
          return TYPE_CSS;
        }
        i++;

      }
      return 0;

    }
    /*<-whether found return 1; else 0;*/
    int getfile_data(char *path,char *file)
    {


      int ret=get_file_type(path,file);
      printf("request found !!! %s\n",file);
      if(ret==TYPE_MAIN)
      {
        printf("responding with main page\n" );
        return TYPE_MAIN;

      }
      else if(ret ==TYPE_CSS)
      {
        printf("responding with css\n");
        return TYPE_CSS;
      }
      else if(ret ==TYPE_QUERY)
      {
        printf("finally saving data and redirecting user to main with message\n" );
        return TYPE_QUERY;
      }
      else{
        return 0;
      }




      //strncpy(**file,"fwd",4);

    }

    int get_dirs_file( char *path,char *file)
    {
      printf("in get_dirs_file");
      int ret=getfile_data(path,file);
      return ret;


    }

this is listhell.c

    //for fullfilling user requests
    #include <stdio.h>
    #include "headers/accept.h"
    int respond_main(struct connection *con_obj,int response_type)
    {
      char *html_header = "HTTP/1.1 200 Okay\r\nContent-Type: text/html; charset=ISO-8859-4 \r\n\r\n";
      char *css_header = "HTTP/1.1 200 Okay\r\nContent-Type: text/css\r\n\r\n";
      char *data_saved_message="HTTP/1.1 200 Okay\r\nContent-Type: text/html; charset=ISO-8859-4 \r\n\r\n<html><body>add posted. will appear in 24 hours on listhell <a href=\"http://listhell.com/\">go to main page</a></body></html>";
      if(response_type==RESPONSE_MAIN_HTML)
      {
        struct stat sb_html;
        send(*(con_obj->new_socket) , html_header , strlen(html_header) , 0 );
        sleep(10);
        int fd_in_html=open("/home/user/Desktop/lh/html9.html",O_RDONLY);
        const char* filename_html="/home/user/Desktop/lh/html9.html";
        if(fd_in_html!=-1)
        {
          if (stat(filename_html, &sb_html) == -1)
            {
                printf("stat error %d\n",errno);

            }
          int x3=sendfile(*(con_obj->new_socket),fd_in_html,0,sb_html.st_size);
          close(fd_in_html);

          return HTTP_OK_200;

      }


      }
      else if(response_type==RESPONSE_MAIN_CSS)
      {
        struct stat sb_css;
        int fd_in_css=open("/home/user/Desktop/lh/css/style9.css",O_RDONLY);
        if(fd_in_css!=-1)
        {
          const char* filename_css="/home/user/Desktop/lh/css/style9.css";
          if (stat(filename_css, &sb_css) == -1)
                {
                    printf("stat error %d\n",errno);
                    exit(EXIT_FAILURE);
                }
          sendfile(*(con_obj->new_socket),fd_in_css,0,sb_css.st_size);
          close(fd_in_css);
          return HTTP_OK_200;


        }

      }
      else if(response_type==RESPONSE_MIAN_QUERY)
      {
        //
          send(*(con_obj->new_socket) , data_saved_message , strlen(data_saved_message) , 0 );
          //send(*(con_obj->new_socket) , data_saved_message , 200 , 0 );
          //send(*(con_obj->new_socket) , data_saved_message , 200 , 0 );
        //data_saved_message
        printf("data saved\n");
        return HTTP_OK_200;

      }
      return 0;
    }

    


    

This is sendfile.c

    #include <stdio.h>
    #include "headers/accept.h"

    ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
    {
        off_t orig;

        if (offset != NULL) {

        /* Save current file offset and set offset to value in '*offset' */

        orig = lseek(in_fd, 0, SEEK_CUR);

        if (orig == -1)
            return -1;
        if (lseek(in_fd, *offset, SEEK_SET) == -1)
            return -1;
        }

        size_t totSent = 0;

        while (count > 0) {
        size_t toRead = min(BUF_SIZE, count);

        char buf[BUF_SIZE];
        ssize_t numRead = read(in_fd, buf, toRead);
        if (numRead == -1)
            return -1;
        if (numRead == 0)
            break;                      /* EOF */

        ssize_t numSent = write(out_fd, buf, numRead);
        if (numSent == -1)
            return -1;
        if (numSent == 0)               /* Should never happen */
            printf("fatal: should never happen");
            //fatal("sendfile: write() transferred 0 bytes");

        count -= numSent;
        totSent += numSent;
        }

        if (offset != NULL) {

        /* Return updated file offset in '*offset', and reset the file offset
           to the value it had when we were called. */

        *offset = lseek(in_fd, 0, SEEK_CUR);
        if (*offset == -1)
            return -1;
        if (lseek(in_fd, orig, SEEK_SET) == -1)
            return -1;
        }

        return totSent;
    }

accept.h

    #ifndef SERVER
    #define SERVER

    #include <unistd.h>
    #include <stdio.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <pthread.h>
    #include <malloc.h>

    #include <errno.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <netinet/tcp.h>
    #include <time.h>

    #define PORT 80
    void accept1(int server_fd,struct sockaddr_in address,int addrlen);

    struct connection
    {
        int *new_socket;
        int type;
        struct sockaddr_in address;
        char *request_line;
        int request_line_size;
        int server_fd;

    };

    struct lh
    {
        int adid;
        char title[250];
        float price;
        char city[75];
        char state[125];
        char country[125];


    };
    void *handle_request(void *arg);
    void *process(void *p);
    int handle_process(struct connection *con_obj,char **path,char **request_type);


    int get_dirs_file( char *path,char *file);
    int respond_main(struct connection *con_obj,int response_type);
    ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

    //request type

    #define TYPE_MAIN 2

    #define TYPE_CSS 3

    #define TYPE_QUERY 4


    //reply type

    #define RESPONSE_MAIN_HTML 2

    #define RESPONSE_MAIN_CSS 3

    #define RESPONSE_MIAN_QUERY 4

    //

    #define BUF_SIZE 20000

    #define SEEK_SET 0
    #define SEEK_CUR 1
    #define SEEK_END 2


    //HTTP RESPONSE

    #define HTTP_OK_200 200



    #endif
    
    
    

makefile

    HEADERS = headers/

    lh: accept.o server.o process.o file.o listhell.o
        cc -g -pthread -o lh accept.o server.o process.o file.o listhell.o
        
    listhell.o: $(HEADERS)accept.h listhell.c
        cc -g -c -pthread listhell.c
        
    process.o : $(HEADERS)accept.h process.c
        cc -g -c -pthread process.c

    file.o : $(HEADERS)accept.h file.c
        cc -g -c -pthread file.c

    server.o : $(HEADERS)accept.h server.c
        cc -g -c -pthread server.c

    accept.o : $(HEADERS)accept.h accept.c
        cc -g -c -pthread accept.c


    clean :
        rm lh accept.o server.o process.o file.o listhell.o
    
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Print errors to stderr

Error messages should be sent to stderr. This is especially important if regular messages are sent to stdout and the standard output is redirected to a file or a pipe. On Linux, I recommend you use the err() function to report errors, like so:

if ((server_fd = socket(...)) == -1)
    err(EXIT_FAILURE, "failed to open socket");

You can also print warnings using warn().

Incorrect checks of return values

If socket() fails, it will return -1, not 0. In fact, 0 is a valid file descriptor. Note that most POSIX functions return -1 on error if they return an int, and NULL if they return a pointer.

Consider supporting IPv6

Your program only supports IPv4 at the moment. I strongly recommend you also make it work for IPv6. It is not that hard. On Linux, you can get away with creating an IPv6 socket and ensuring the IPV6_V6ONLY socket option is set to zero. The socket will then accept both IPv4 and IPv6 connections.

accept1() only needs to know the file descriptor

There is no need to pass address to accept1(). You can just declare a local struct sockaddr variable inside accept1().

Remove unused variables

You declared several variables at the top of main() that are never used in that function. Your compiler should be warning you about unused variables (and if not, be sure to enable warnings). Just remove them.

Useless use of TCP_CORK on the listening socket

The listening socket does not receive or transmit any data, so TCP_CORK is not doing anything there. Instead, you should set TCP_CORK on the file descriptor you got from accept().

Remove unnecessary variables from struct connection

There are a lot of variables in struct connection that are never used or should not be in there. The only thing handle_request() needs is the file descriptor of the socket it is handling. Since handle_request() only handles a single request and then immediately exits, there is no need for it to create yet another thread to call process(), it could just call process() directly. And if it calls it directly, it can pass any number of variables as regular function arguments, no need to store it in con_obj.

Variables not used at all are address and server_fd.

Thread management

You create threads with pthread_create(), but you never clean them up. Threads should either be joined using pthread_join() or detached using pthread_detach(). If you don't do either the resources used by the threads will not be cleaned up, and at some point you won't be able to create new threads anymore.

Also think about what happens if someone opens thousands of connections to your server per second. This will spawn thousands of threads that all want to use the CPU. This can create lots of issues. I recommend implementing some way to limit how many threads can run at the same time. The best way is to implement a thread pool and limit the number of threads to roughly the number of CPU cores of your machine. You can have the main thread call accept() in a loop and add struct connection object to a task queue that the workers will pick up, or alternatively have the workers call accept() themselves (but see this StackOverflow question).

Use array notation where appropriate

I see you write *(buffer+i) in serveral places, but you should just use array notation instead: buffer[i].

Memory leak

In handle_requests(), you call malloc() at the start, but you never actually use that memory, because you overwrite con_obj with arg right afterwards. Just remove that call to malloc().

Reading lines from a socket

You correctly read from the socket in a loop, however the string copying code you wrote is rather buggy; in particular if there was no \r in the first read(), you call read() again but now you can write past the end of buffer. The copying between buffer and str_array seems unnecessary as well. Instead of doing all this low-level stuff, consider using fdopen() to create a FILE handle from a file descriptor, and then you can use getdelim() to read a whole line at once, and not have to worry about allocating memory for buffers at all.

Consider using a HTTP server library

As you have probably realized, writing a HTTP server, even a simple one, is quite a lot of work. There are many libraries that can help you write a HTTP server without having to reimplement the socket I/O and request parsing yourself, like libmicrohttpd or Mongoose.

\$\endgroup\$
4
  • \$\begingroup\$ did I accept ur answer some time ago? \$\endgroup\$
    – user786
    Commented Oct 19, 2021 at 7:30
  • \$\begingroup\$ What about resource leaks. Can u please check that \$\endgroup\$
    – user786
    Commented Oct 19, 2021 at 13:56
  • \$\begingroup\$ is Mongoose a C++ library or C library? Is there any documentation for it? \$\endgroup\$
    – user786
    Commented Oct 24, 2021 at 10:14
  • \$\begingroup\$ @user786 Yes and yes. Just read the README.md that is shown when you follow the link. \$\endgroup\$
    – G. Sliepen
    Commented Oct 24, 2021 at 10:30
1
\$\begingroup\$

In this part:

struct connection *con_obj=malloc(sizeof(struct connection));
if(con_obj!=NULL)

You have checked if malloc succeeded, but then you print a warning and continue on. Consider using assert to test:

struct connection *con_obj=malloc(sizeof(struct connection));
assert(con_obj != NULL);

Why are you sleeping for 10 seconds?

sleep(10);

Here you are allocating one byte less than you need, and do not check if malloc was successfull:

*path=malloc(sizeof(char)*strlen("html9.html"));
strncpy(*path,"html9.html",strlen("html9.html")+1);

Corrected code;

size_t length = strlen("html9.html") + 1;
*path = (char*)malloc(sizeof(char) * length);
assert(*path);
strncpy(*path, "html9.html", length);

There is still a lot to go through, but I have to go and mow the lawn =)

\$\endgroup\$
2
  • \$\begingroup\$ did I accept your answer some time ago \$\endgroup\$
    – user786
    Commented Oct 19, 2021 at 7:30
  • \$\begingroup\$ @user786, nope and i think G Sliepen's answer is much better =) \$\endgroup\$
    – jdt
    Commented Oct 19, 2021 at 12:22

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.