#include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #define SERVER_DEBUG 0 #define MY_PORT 48080 #define MY_PORT_2 58080 #define GUPPY_IP "152.2.128.185" #define MAX_REQUEST_SIZE 1000 #define FILE_CHUNK_SIZE 500 int MAX_REQUESTS = 1; int QUEUE_LENGTH = 10; int FAKE_ENTITY_SIZE = 100; int handle_connection(int the_socket); int main(int argc, char * argv[]) { int my_socket, new_socket; // conceptually socket descriptors int returncode, addrlen; // utility variables struct sockaddr_in my_addr; struct sockaddr_in their_addr; char buffer[MAX_REQUEST_SIZE + 1]; // Zeroth, initialize buffer and do args printf("Welcome to Steve Matuszek's HTTP server for COMP 243.\n"); printf("I'm not great at threads in C, so hit control-C to exit.\n\n"); printf("Server starting up.\n"); int i; for(i=0; i 1) { MAX_REQUESTS = atoi(argv[1]); printf("Max requests from each client: %d\n", MAX_REQUESTS); } if (argc > 2) { QUEUE_LENGTH = atoi(argv[2]); printf("Allowing up to %d clients to connect at once\n", QUEUE_LENGTH); } if (argc > 3) { FAKE_ENTITY_SIZE = atoi(argv[3]); printf("Sending out fake entities of size %d bytes\n", FAKE_ENTITY_SIZE); } // First, create an Internet socket my_socket = socket(AF_INET, SOCK_STREAM, 0); if (my_socket < 0) { printf("Server: error %d creating socket\n", errno); perror(" "); exit(1); } if (SERVER_DEBUG) printf("Server: my_socket's file descriptor is %d\n", my_socket); // Bind it to a port, chosen for us by using 0, or not my_addr.sin_family = AF_INET; // fine my_addr.sin_port = htons(MY_PORT); // no, don't my_addr.sin_addr.s_addr = INADDR_ANY; // use mine bzero(&(my_addr.sin_zero), 8); // zero the rest addrlen = sizeof(struct sockaddr); returncode = bind(my_socket, (struct sockaddr *) &my_addr, addrlen); if (returncode < 0) { printf("Server: error %d binding name\n", errno); perror(" "); printf("Going to pick a different port.\n"); my_addr.sin_port = htons(MY_PORT_2); // Try the other one returncode = bind(my_socket, (struct sockaddr *) &my_addr, addrlen); if (returncode < 0) { printf("Server: error %d binding name\n", errno); perror(" "); printf("Giving up.\n"); exit(1); } else { printf("NOTE!: Started instead on port %d\n", my_addr.sin_port); } } else { printf("Started on port %d\n", my_addr.sin_port); } // Have it listen returncode = listen(my_socket, QUEUE_LENGTH); if (returncode < 0) { printf("Server: error %d listening\n", errno); perror(" "); exit(1); } else if (1 || SERVER_DEBUG) printf("Server listening...\n"); // Ignore child process termination signal(SIGCHLD, SIG_IGN); // Go into the infinite loop waiting for connections. while (1) { new_socket = accept(my_socket, (struct sockaddr *) &their_addr, &addrlen); if (new_socket < 0) { printf("Server: error %d accepting\n", errno); perror(" "); exit(1); } else if (SERVER_DEBUG) printf("Connection accepted...\n"); // Here we fork when accept()ing a connect()ion. // Have a pretty good handle on this at this point. if (fork() == 0) // Then we're the child... { close(my_socket); if (SERVER_DEBUG) printf(" ...Child started\n"); returncode = handle_connection(new_socket); if (returncode < 0) { printf("Server: error handling connection\n", errno); exit(1); } if (SERVER_DEBUG) printf("Done handling connection.\n"); exit(0); } else { // printf("Forking...\n"); } } // Clean up. close(my_socket); } // This function does the work of handling the connection. // It's up to it to fulfill ALL the requests, so I only have to // change this function. // // Note that we get to skip all the file opening stuff. int handle_connection(int the_socket) { char buffer[MAX_REQUEST_SIZE+1]; int returncode, i; struct sockaddr_in peer_addr; int addrlen; char *peer_name; char *message; int messagelength; int bytes_sent; int status = 500; int requests_fulfilled = 0; // All this stuff does is tell me who the connection's from. addrlen = sizeof(struct sockaddr); returncode = getpeername(the_socket, (struct sockaddr *) &peer_addr, &addrlen); if (returncode < 0) { printf("Server: error %d getting peer name\n", errno); perror(" "); } else if (1 || SERVER_DEBUG) { peer_name = inet_ntoa(peer_addr.sin_addr); printf("Connection from %s\n", peer_name); } // Now, actually listen for a message from the client. int filesize; char * fake_chunk = (char *) malloc ((FAKE_ENTITY_SIZE + 1) * sizeof(char)); while (requests_fulfilled < MAX_REQUESTS) { requests_fulfilled++; // Do this first just in case if (SERVER_DEBUG) printf("Waiting for connection...\n"); returncode = recv(the_socket, buffer, MAX_REQUEST_SIZE, 0); if (returncode < 0) { printf("Server: error %d receiving message\n", errno); perror(" "); return(-1); } else if (returncode == 0) { printf("Socket closed by other side...\n"); break; } if (SERVER_DEBUG) { printf("Message received (and ignored) from socket was:\n"); buffer[returncode] = '\0'; printf("%s\n", buffer); } allocate_and_fill_http_response((char **) &message, 200, FAKE_ENTITY_SIZE); messagelength = strlen(message); if (SERVER_DEBUG) printf("Response is \n----------\n%s\n------------\n", message); bytes_sent = send(the_socket, message, messagelength, 0); if (bytes_sent < 0) { printf("Server: error %d sending header\n", errno); perror(" "); exit(1); } // Next, send the FAKE content -- just a bunch of characters -- heh. filesize = FAKE_ENTITY_SIZE; while(filesize > 0) { for (i=0; (i