initial web server skeleton
This commit is contained in:
26
concurrency-webserver/src/Makefile
Normal file
26
concurrency-webserver/src/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
# An admittedly primitive Makefile
|
||||
# To compile, type "make" or make "all"
|
||||
# To remove files, type "make clean"
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall
|
||||
OBJS = wserver.o wclient.o request.o io_helper.o
|
||||
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
all: wserver wclient spin.cgi
|
||||
|
||||
wserver: wserver.o request.o io_helper.o
|
||||
$(CC) $(CFLAGS) -o wserver wserver.o request.o io_helper.o
|
||||
|
||||
wclient: wclient.o io_helper.o
|
||||
$(CC) $(CFLAGS) -o wclient wclient.o io_helper.o
|
||||
|
||||
spin.cgi: spin.c
|
||||
$(CC) $(CFLAGS) -o spin.cgi spin.c
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
-rm -f $(OBJS) wserver wclient spin.cgi
|
||||
83
concurrency-webserver/src/io_helper.c
Normal file
83
concurrency-webserver/src/io_helper.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "io_helper.h"
|
||||
|
||||
ssize_t readline(int fd, void *buf, size_t maxlen) {
|
||||
char c;
|
||||
char *bufp = buf;
|
||||
int n;
|
||||
for (n = 0; n < maxlen - 1; n++) { // leave room at end for '\0'
|
||||
int rc;
|
||||
if ((rc = read_or_die(fd, &c, 1)) == 1) {
|
||||
*bufp++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
} else if (rc == 0) {
|
||||
if (n == 1)
|
||||
return 0; /* EOF, no data read */
|
||||
else
|
||||
break; /* EOF, some data was read */
|
||||
} else
|
||||
return -1; /* error */
|
||||
}
|
||||
*bufp = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
int open_client_fd(char *hostname, int port) {
|
||||
int client_fd;
|
||||
struct hostent *hp;
|
||||
struct sockaddr_in server_addr;
|
||||
|
||||
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
return -1;
|
||||
|
||||
// Fill in the server's IP address and port
|
||||
if ((hp = gethostbyname(hostname)) == NULL)
|
||||
return -2; // check h_errno for cause of error
|
||||
bzero((char *) &server_addr, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
bcopy((char *) hp->h_addr,
|
||||
(char *) &server_addr.sin_addr.s_addr, hp->h_length);
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
// Establish a connection with the server
|
||||
if (connect(client_fd, (sockaddr_t *) &server_addr, sizeof(server_addr)) < 0)
|
||||
return -1;
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
int open_listen_fd(int port) {
|
||||
// Create a socket descriptor
|
||||
int listen_fd;
|
||||
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
fprintf(stderr, "socket() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Eliminates "Address already in use" error from bind
|
||||
int optval = 1;
|
||||
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int)) < 0) {
|
||||
fprintf(stderr, "setsockopt() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Listen_fd will be an endpoint for all requests to port on any IP address for this host
|
||||
struct sockaddr_in server_addr;
|
||||
bzero((char *) &server_addr, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server_addr.sin_port = htons((unsigned short) port);
|
||||
if (bind(listen_fd, (sockaddr_t *) &server_addr, sizeof(server_addr)) < 0) {
|
||||
fprintf(stderr, "bind() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make it a listening socket ready to accept connection requests
|
||||
if (listen(listen_fd, 1024) < 0) {
|
||||
fprintf(stderr, "listen() failed\n");
|
||||
return -1;
|
||||
}
|
||||
return listen_fd;
|
||||
}
|
||||
|
||||
|
||||
95
concurrency-webserver/src/io_helper.h
Normal file
95
concurrency-webserver/src/io_helper.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef __IO_HELPER__
|
||||
#define __IO_HELPER__
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct sockaddr sockaddr_t;
|
||||
|
||||
// useful here: gcc statement expressions
|
||||
// http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
|
||||
// macro ({ ...; x; }) returns value 'x' for caller
|
||||
// e.g., macro 'fork_or_die()' below returns 'pid' value
|
||||
#define fork_or_die() \
|
||||
({ pid_t pid = fork(); assert(pid >= 0); pid; })
|
||||
#define execve_or_die(filename, argv, envp) \
|
||||
assert(execve(filename, argv, envp) == 0);
|
||||
#define wait_or_die(status) \
|
||||
({ pid_t pid = wait(status); assert(pid >= 0); pid; })
|
||||
#define gethostname_or_die(name, len) \
|
||||
({ int rc = gethostname(name, len); assert(rc == 0); rc; })
|
||||
#define setenv_or_die(name, value, overwrite) \
|
||||
({ int rc = setenv(name, value, overwrite); assert(rc == 0); rc; })
|
||||
#define chdir_or_die(path) \
|
||||
assert(chdir(path) == 0);
|
||||
#define open_or_die(pathname, flags, mode) \
|
||||
({ int rc = open(pathname, flags, mode); assert(rc >= 0); rc; })
|
||||
#define read_or_die(fd, buf, count) \
|
||||
({ ssize_t rc = read(fd, buf, count); assert(rc >= 0); rc; })
|
||||
#define write_or_die(fd, buf, count) \
|
||||
({ ssize_t rc = write(fd, buf, count); assert(rc >= 0); rc; })
|
||||
#define lseek_or_die(fd, offset, whence) \
|
||||
({ off_t rc = lseek(fd, offset, whence); assert(rc >= 0); rc; })
|
||||
#define close_or_die(fd) \
|
||||
assert(close(fd) == 0);
|
||||
#define select_or_die(n, readfds, writefds, exceptfds, timeout) \
|
||||
({ int rc = select(n, readfds, writefds, exceptfds, timeout); assert(rc >= 0); rc; })
|
||||
#define dup2_or_die(fd1, fd2) \
|
||||
({ int rc = dup2(fd1, fd2); assert(rc >= 0); rc; })
|
||||
#define stat_or_die(filename, buf) \
|
||||
assert(stat(filename, buf) >= 0);
|
||||
#define fstat_or_die(fd, buf) \
|
||||
{ assert(fstat(fd, buf) >= 0); }
|
||||
#define mmap_or_die(addr, len, prot, flags, fd, offset) \
|
||||
({ void *ptr = mmap(addr, len, prot, flags, fd, offset); assert(ptr != (void *) -1); ptr; })
|
||||
#define munmap_or_die(start, length) \
|
||||
assert(munmap(start, length) >= 0);
|
||||
#define socket_or_die(domain, type, protocol) \
|
||||
({ int rc = socket(domain, type, protocol); assert(rc >= 0); rc; })
|
||||
#define setsockopt_or_die(s, level, optname, optval, optlen) \
|
||||
{ assert(setsockopt(s, level, optname, optval, optlen) >= 0); }
|
||||
#define bind_or_die(sockfd, my_addr, addrlen) \
|
||||
{ assert(bind(sockfd, my_addr, addrlen) >= 0); }
|
||||
#define listen_or_die(s, backlog) \
|
||||
{ assert(listen(s, backlog) >= 0); }
|
||||
#define accept_or_die(s, addr, addrlen) \
|
||||
({ int rc = accept(s, addr, addrlen); assert(rc >= 0); rc; })
|
||||
#define connect_or_die(sockfd, serv_addr, addrlen) \
|
||||
{ assert(connect(sockfd, serv_addr, addrlen) >= 0); }
|
||||
#define gethostbyname_or_die(name) \
|
||||
({ struct hostent *p = gethostbyname(name); assert(p != NULL); p; })
|
||||
#define gethostbyaddr_or_die(addr, len, type) \
|
||||
({ struct hostent *p = gethostbyaddr(addr, len, type); assert(p != NULL); p; })
|
||||
|
||||
// client/server helper functions
|
||||
ssize_t readline(int fd, void *buf, size_t maxlen);
|
||||
int open_client_fd(char *hostname, int portno);
|
||||
int open_listen_fd(int portno);
|
||||
|
||||
// wrappers for above
|
||||
#define readline_or_die(fd, buf, maxlen) \
|
||||
({ ssize_t rc = readline(fd, buf, maxlen); assert(rc >= 0); rc; })
|
||||
#define open_client_fd_or_die(hostname, port) \
|
||||
({ int rc = open_client_fd(hostname, port); assert(rc >= 0); rc; })
|
||||
#define open_listen_fd_or_die(port) \
|
||||
({ int rc = open_listen_fd(port); assert(rc >= 0); rc; })
|
||||
|
||||
#endif // __IO_HELPER__
|
||||
180
concurrency-webserver/src/request.c
Normal file
180
concurrency-webserver/src/request.c
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "io_helper.h"
|
||||
#include "request.h"
|
||||
|
||||
//
|
||||
// Some of this code stolen from Bryant/O'Halloran
|
||||
// Hopefully this is not a problem ... :)
|
||||
//
|
||||
|
||||
#define MAXBUF (8192)
|
||||
|
||||
void request_error(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) {
|
||||
char buf[MAXBUF], body[MAXBUF];
|
||||
|
||||
// Create the body of error message first (have to know its length for header)
|
||||
sprintf(body, ""
|
||||
"<!doctype html>\r\n"
|
||||
"<head>\r\n"
|
||||
" <title>OSTEP WebServer Error</title>\r\n"
|
||||
"</head>\r\n"
|
||||
"<body>\r\n"
|
||||
" <h2>%s: %s</h2>\r\n"
|
||||
" <p>%s: %s</p>\r\n"
|
||||
"</body>\r\n"
|
||||
"</html>\r\n", errnum, shortmsg, longmsg, cause);
|
||||
|
||||
// Write out the header information for this response
|
||||
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
|
||||
write_or_die(fd, buf, strlen(buf));
|
||||
|
||||
sprintf(buf, "Content-Type: text/html\r\n");
|
||||
write_or_die(fd, buf, strlen(buf));
|
||||
|
||||
sprintf(buf, "Content-Length: %lu\r\n\r\n", strlen(body));
|
||||
write_or_die(fd, buf, strlen(buf));
|
||||
|
||||
// Write out the body last
|
||||
write_or_die(fd, body, strlen(body));
|
||||
}
|
||||
|
||||
//
|
||||
// Reads and discards everything up to an empty text line
|
||||
//
|
||||
void request_read_headers(int fd) {
|
||||
char buf[MAXBUF];
|
||||
|
||||
readline_or_die(fd, buf, MAXBUF);
|
||||
while (strcmp(buf, "\r\n")) {
|
||||
readline_or_die(fd, buf, MAXBUF);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Return 1 if static, 0 if dynamic content
|
||||
// Calculates filename (and cgiargs, for dynamic) from uri
|
||||
//
|
||||
int request_parse_uri(char *uri, char *filename, char *cgiargs) {
|
||||
char *ptr;
|
||||
|
||||
if (!strstr(uri, "cgi")) {
|
||||
// static
|
||||
strcpy(cgiargs, "");
|
||||
sprintf(filename, ".%s", uri);
|
||||
if (uri[strlen(uri)-1] == '/') {
|
||||
strcat(filename, "index.html");
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
// dynamic
|
||||
ptr = index(uri, '?');
|
||||
if (ptr) {
|
||||
strcpy(cgiargs, ptr+1);
|
||||
*ptr = '\0';
|
||||
} else {
|
||||
strcpy(cgiargs, "");
|
||||
}
|
||||
sprintf(filename, ".%s", uri);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Fills in the filetype given the filename
|
||||
//
|
||||
void request_get_filetype(char *filename, char *filetype) {
|
||||
if (strstr(filename, ".html"))
|
||||
strcpy(filetype, "text/html");
|
||||
else if (strstr(filename, ".gif"))
|
||||
strcpy(filetype, "image/gif");
|
||||
else if (strstr(filename, ".jpg"))
|
||||
strcpy(filetype, "image/jpeg");
|
||||
else
|
||||
strcpy(filetype, "text/plain");
|
||||
}
|
||||
|
||||
void request_serve_dynamic(int fd, char *filename, char *cgiargs) {
|
||||
char buf[MAXBUF], *argv[] = { NULL };
|
||||
|
||||
// The server does only a little bit of the header.
|
||||
// The CGI script has to finish writing out the header.
|
||||
sprintf(buf, ""
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Server: OSTEP WebServer\r\n");
|
||||
|
||||
write_or_die(fd, buf, strlen(buf));
|
||||
|
||||
if (fork_or_die() == 0) { // child
|
||||
setenv_or_die("QUERY_STRING", cgiargs, 1); // args to cgi go here
|
||||
dup2_or_die(fd, STDOUT_FILENO); // make cgi writes go to socket (not screen)
|
||||
extern char **environ; // defined by libc
|
||||
execve_or_die(filename, argv, environ);
|
||||
} else {
|
||||
wait_or_die(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void request_serve_static(int fd, char *filename, int filesize) {
|
||||
int srcfd;
|
||||
char *srcp, filetype[MAXBUF], buf[MAXBUF];
|
||||
|
||||
request_get_filetype(filename, filetype);
|
||||
srcfd = open_or_die(filename, O_RDONLY, 0);
|
||||
|
||||
// Rather than call read() to read the file into memory,
|
||||
// which would require that we allocate a buffer, we memory-map the file
|
||||
srcp = mmap_or_die(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
|
||||
close_or_die(srcfd);
|
||||
|
||||
// put together response
|
||||
sprintf(buf, ""
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Server: OSTEP WebServer\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: %s\r\n\r\n",
|
||||
filesize, filetype);
|
||||
|
||||
write_or_die(fd, buf, strlen(buf));
|
||||
|
||||
// Writes out to the client socket the memory-mapped file
|
||||
write_or_die(fd, srcp, filesize);
|
||||
munmap_or_die(srcp, filesize);
|
||||
}
|
||||
|
||||
// handle a request
|
||||
void request_handle(int fd) {
|
||||
int is_static;
|
||||
struct stat sbuf;
|
||||
char buf[MAXBUF], method[MAXBUF], uri[MAXBUF], version[MAXBUF];
|
||||
char filename[MAXBUF], cgiargs[MAXBUF];
|
||||
|
||||
readline_or_die(fd, buf, MAXBUF);
|
||||
sscanf(buf, "%s %s %s", method, uri, version);
|
||||
printf("method:%s uri:%s version:%s\n", method, uri, version);
|
||||
|
||||
if (strcasecmp(method, "GET")) {
|
||||
request_error(fd, method, "501", "Not Implemented", "server does not implement this method");
|
||||
return;
|
||||
}
|
||||
request_read_headers(fd);
|
||||
|
||||
is_static = request_parse_uri(uri, filename, cgiargs);
|
||||
if (stat(filename, &sbuf) < 0) {
|
||||
request_error(fd, filename, "404", "Not found", "server could not find this file");
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_static) {
|
||||
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
|
||||
request_error(fd, filename, "403", "Forbidden", "server could not read this file");
|
||||
return;
|
||||
}
|
||||
request_serve_static(fd, filename, sbuf.st_size);
|
||||
} else {
|
||||
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
|
||||
request_error(fd, filename, "403", "Forbidden", "server could not run this CGI program");
|
||||
return;
|
||||
}
|
||||
request_serve_dynamic(fd, filename, cgiargs);
|
||||
}
|
||||
}
|
||||
5
concurrency-webserver/src/request.h
Normal file
5
concurrency-webserver/src/request.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef __REQUEST_H__
|
||||
|
||||
void request_handle(int fd);
|
||||
|
||||
#endif // __REQUEST_H__
|
||||
52
concurrency-webserver/src/spin.c
Normal file
52
concurrency-webserver/src/spin.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAXBUF (8192)
|
||||
|
||||
//
|
||||
// This program is intended to help you test your web server.
|
||||
// You can use it to test that you are correctly having multiple threads
|
||||
// handling http requests.
|
||||
//
|
||||
|
||||
double get_seconds() {
|
||||
struct timeval t;
|
||||
int rc = gettimeofday(&t, NULL);
|
||||
assert(rc == 0);
|
||||
return (double) ((double)t.tv_sec + (double)t.tv_usec / 1e6);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Extract arguments
|
||||
double spin_for = 0.0;
|
||||
char *buf;
|
||||
if ((buf = getenv("QUERY_STRING")) != NULL) {
|
||||
// just expecting a single number
|
||||
spin_for = (double) atoi(buf);
|
||||
}
|
||||
|
||||
double t1 = get_seconds();
|
||||
while ((get_seconds() - t1) < spin_for)
|
||||
sleep(1);
|
||||
double t2 = get_seconds();
|
||||
|
||||
/* Make the response body */
|
||||
char content[MAXBUF];
|
||||
sprintf(content, "<p>Welcome to the CGI program (%s)</p>\r\n", buf);
|
||||
sprintf(content, "%s<p>My only purpose is to waste time on the server!</p>\r\n", content);
|
||||
sprintf(content, "%s<p>I spun for %.2f seconds</p>\r\n", content, t2 - t1);
|
||||
|
||||
/* Generate the HTTP response */
|
||||
printf("Content-length: %lu\r\n", strlen(content));
|
||||
printf("Content-type: text/html\r\n\r\n");
|
||||
printf("%s", content);
|
||||
fflush(stdout);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
92
concurrency-webserver/src/wclient.c
Normal file
92
concurrency-webserver/src/wclient.c
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// client.c: A very, very primitive HTTP client.
|
||||
//
|
||||
// To run, try:
|
||||
// client hostname portnumber filename
|
||||
//
|
||||
// Sends one HTTP request to the specified HTTP server.
|
||||
// Prints out the HTTP response.
|
||||
//
|
||||
// For testing your server, you will want to modify this client.
|
||||
// For example:
|
||||
// You may want to make this multi-threaded so that you can
|
||||
// send many requests simultaneously to the server.
|
||||
//
|
||||
// You may also want to be able to request different URIs;
|
||||
// you may want to get more URIs from the command line
|
||||
// or read the list from a file.
|
||||
//
|
||||
// When we test your server, we will be using modifications to this client.
|
||||
//
|
||||
|
||||
#include "io_helper.h"
|
||||
|
||||
#define MAXBUF (8192)
|
||||
|
||||
//
|
||||
// Send an HTTP request for the specified file
|
||||
//
|
||||
void client_send(int fd, char *filename) {
|
||||
char buf[MAXBUF];
|
||||
char hostname[MAXBUF];
|
||||
|
||||
gethostname_or_die(hostname, MAXBUF);
|
||||
|
||||
/* Form and send the HTTP request */
|
||||
sprintf(buf, "GET %s HTTP/1.1\n", filename);
|
||||
sprintf(buf, "%shost: %s\n\r\n", buf, hostname);
|
||||
write_or_die(fd, buf, strlen(buf));
|
||||
}
|
||||
|
||||
//
|
||||
// Read the HTTP response and print it out
|
||||
//
|
||||
void client_print(int fd) {
|
||||
char buf[MAXBUF];
|
||||
int n;
|
||||
|
||||
// Read and display the HTTP Header
|
||||
n = readline_or_die(fd, buf, MAXBUF);
|
||||
while (strcmp(buf, "\r\n") && (n > 0)) {
|
||||
printf("Header: %s", buf);
|
||||
n = readline_or_die(fd, buf, MAXBUF);
|
||||
|
||||
// If you want to look for certain HTTP tags...
|
||||
// int length = 0;
|
||||
//if (sscanf(buf, "Content-Length: %d ", &length) == 1) {
|
||||
// printf("Length = %d\n", length);
|
||||
//}
|
||||
}
|
||||
|
||||
// Read and display the HTTP Body
|
||||
n = readline_or_die(fd, buf, MAXBUF);
|
||||
while (n > 0) {
|
||||
printf("%s", buf);
|
||||
n = readline_or_die(fd, buf, MAXBUF);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *host, *filename;
|
||||
int port;
|
||||
int clientfd;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Usage: %s <host> <port> <filename>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
host = argv[1];
|
||||
port = atoi(argv[2]);
|
||||
filename = argv[3];
|
||||
|
||||
/* Open a single connection to the specified host and port */
|
||||
clientfd = open_client_fd_or_die(host, port);
|
||||
|
||||
client_send(clientfd, filename);
|
||||
client_print(clientfd);
|
||||
|
||||
close_or_die(clientfd);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
47
concurrency-webserver/src/wserver.c
Normal file
47
concurrency-webserver/src/wserver.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include "request.h"
|
||||
#include "io_helper.h"
|
||||
|
||||
char default_root[] = ".";
|
||||
|
||||
//
|
||||
// ./wserver [-d <basedir>] [-p <portnum>]
|
||||
//
|
||||
int main(int argc, char *argv[]) {
|
||||
int c;
|
||||
char *root_dir = default_root;
|
||||
int port = 10000;
|
||||
|
||||
while ((c = getopt(argc, argv, "d:p:")) != -1)
|
||||
switch (c) {
|
||||
case 'd':
|
||||
root_dir = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "usage: wserver [-d basedir] [-p port]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// run out of this directory
|
||||
chdir_or_die(root_dir);
|
||||
|
||||
// now, get to work
|
||||
int listen_fd = open_listen_fd_or_die(port);
|
||||
while (1) {
|
||||
struct sockaddr_in client_addr;
|
||||
int client_len = sizeof(client_addr);
|
||||
int conn_fd = accept_or_die(listen_fd, (sockaddr_t *) &client_addr, (socklen_t *) &client_len);
|
||||
request_handle(conn_fd);
|
||||
close_or_die(conn_fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user