/* Single-threaded server by Pieter Suurmond, februari 2, 2003. No copyrights. Updated november 14, 2003 (). Compile with a UNIX C compiler, for example GNU: 'gcc stserver.c -o stserver'. Startup './stserver' and watch 'http://localhost:1234/BLOOP/' in a webbrowser. You can shutdown the server with something like 'http://localhost:1234/Q'. */ #include #include #include #include #include #include #include /* Thanks to Sam Roig Torrubiano for fix: */ /* bind() and accept() need . */ #include #include #include typedef struct /* Using names like CGI environment variables. */ { char *REQUEST_METHOD, *SCRIPT_NAME, *SERVER_PROTOCOL, /* What the client's requested. */ *remains; } RQ; /*----------------------------------------------------------------*/ int parseInput(char* in, RQ* out) /* Arguments may not be NULL. */ { out->REQUEST_METHOD = in; in = strchr(in, ' '); if (!in) return -1; *in++ = (char)0; out->SCRIPT_NAME = in; in = strchr(in, ' '); if (!in) return -2; *in++ = (char)0; if ('/' != out->SCRIPT_NAME[0]) /* SCRIPT_NAME must start with '/' */ return -3; /* (so at least one char there). */ if (strchr(".,/\\", out->SCRIPT_NAME[1])) /* Name may not start with dot, slash... */ return -4; /* True if SCRIPT_NAME[1] == '\0'. */ out->SERVER_PROTOCOL = in; while (*in) { if ((*in == 10) || (*in == 13)) { *in++ = (char)0; out->remains = in; if (strcasecmp("HTTP/1.0", out->SERVER_PROTOCOL) && strcasecmp("HTTP/1.1", out->SERVER_PROTOCOL)) return -5; return 0; } in++; } return -6; } /*---------------------------------------------------------------------------------*/ int main(void) { /* Configuration: */ int port = 1234; /* port number for server. */ FILE* diag = stdout; /* diag may not be NULL. */ long in_size = 4096; long out_size = 16384; char* software = "Single-threaded server 0.05"; int n, num, sockfd, new_fd, fd_size, one = 1, up = 1; struct sockaddr_in addr, remote_addr; unsigned char *in_buff, *out_buff; char *tt, *msg = "?", *bye = ""; time_t t; RQ request; time(&t); tt = ctime(&t); fprintf(diag, "%s started on %s", software, tt); /*------------------------------------------------------------- Memory alloc: */ in_buff = (unsigned char*)malloc(in_size * sizeof(unsigned char)); out_buff = (unsigned char*)malloc(out_size * sizeof(unsigned char)); if (!(in_buff && out_buff)) { msg = "Not enough memory!"; goto finish; } /*------------------------------------------------------------ Create socket: */ sockfd = socket(PF_INET, SOCK_STREAM, 0); /* Or is 'AF_INET' better? */ if (sockfd == -1) { msg = "Socket error!"; goto finish; } /* Alternative for 'perror("socket()");'. */ addr.sin_family = PF_INET; addr.sin_port = htons(port); memset(&addr.sin_zero, 0, 8); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(int)) < 0) { msg = "Setsockopt error!"; goto finish; } if (bind(sockfd, &addr, sizeof(struct sockaddr)) < 0) { msg = "Bind error!"; goto finish; } if (listen(sockfd, 5) < 0) { msg = "Listen error!"; goto finish; } /*------------------------------------------------ Service incoming requests: */ fprintf(diag, "Listening on port %d\n\n", port); while (up) /* Main loop. */ { fd_size = sizeof(struct sockaddr_in); new_fd = accept(sockfd, &remote_addr, &fd_size); if (new_fd < 0) { fprintf(diag, "Accept error!\n"); continue; /* But don't quit. */ } num = recv(new_fd, in_buff, in_size-1, 0); /* One less for trailing '\0'. */ if (num < 0) { close(new_fd); msg = "Recv error!"; goto finish; } #if 0 if (num >= in_size) /* Should never occur. */ { close(new_fd); msg = "Weird recv error!"; goto finish; } #endif in_buff[num] = (char)0; /* Make it a C-string. */ num = parseInput(in_buff, &request); if (num) { fprintf(diag, "Bad request: parseInput() = %d.\n", num); n = sprintf(out_buff, "\ HTTP/1.0 400 Bad request\nServer: %s\nContent-type: text/plain\n\ \n\ parseInput() = %d\n", software, num); } else if (strcasecmp(request.REQUEST_METHOD, "GET")) /* Block anything else. */ { fprintf(diag, "Request method not allowed: %s.\n", request.REQUEST_METHOD); n = sprintf(out_buff, "\ %s 401 Unauthorized\nServer: %s\nContent-type: text/plain\n\ \n\ Not allowed REQUEST_METHOD: %s\n", request.SERVER_PROTOCOL, software, request.REQUEST_METHOD); } else /* SCRIPT_NAME[0] == '/' */ { if ((request.SCRIPT_NAME[1] == 'q') || (request.SCRIPT_NAME[1] == 'Q')) { bye = ", YOU ARE KILLING ME"; up = 0; /* Leave while loop agter saying goodbye. */ } /* Output complete header: */ n = sprintf(out_buff, "\ %s 200 OK\nServer: %s\nContent-type: text/plain\n\ \n\ Hi%s!\n\ I am %s.\n\ You sent me this:\n\n\ REQUEST_METHOD = %s\n\ SCRIPT_NAME = %s\n\ SERVER_PROTOCOL = %s\n\ remains = \n%s\n", request.SERVER_PROTOCOL, software, bye, software, request.REQUEST_METHOD, request.SCRIPT_NAME, request.SERVER_PROTOCOL, request.remains); } num = send(new_fd, out_buff, n, 0); if (n != num) fprintf(diag, "Could only send %d out of %d chars!\n", num, n); else fprintf(diag, "Served ok.\n"); close(new_fd); } msg = "Graceful shutdown."; finish: fprintf(diag, "%s\r\n", msg); if (out_buff) free(out_buff); if (in_buff) free(in_buff); return 0; }