Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * tinychat.c - [Starting code for] a web-based chat server.
- *
- * Based on:
- * tiny.c - A simple, iterative HTTP/1.0 Web server that uses the
- * GET method to serve static and dynamic content.
- * Tiny Web server
- * Dave O'Hallaron
- * Carnegie Mellon University
- */
- #include "csapp.h"
- #include "dictionary.h"
- #include "more_string.h"
- #include <unistd.h>
- void doit(int fd);
- dictionary_t *read_requesthdrs(rio_t *rp);
- void read_postquery(rio_t *rp, dictionary_t *headers, dictionary_t *d);
- void parse_query(const char *uri, dictionary_t *d);
- void serve_form(int fd, const char *pre_content);
- void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
- static void print_stringdictionary(dictionary_t *d);
- static char *ok_header(size_t len, const char *content_type);
- void welcome_screen(int);
- void server_chat_form(int fd, char* conversation, char* topic, char* user);
- void send_conversation(char* conversation, int fd);
- void send_ok_response(int fd, char* entry);
- /* thread pool header*/
- typedef struct {
- int *buf; /* Buffer array */
- int n; /* Maximum number of slots */
- int front; /* buf[(front+1)%n] is first item */
- int rear; /* buf[rear%n] is last item */
- sem_t mutex; /* Protects accesses to buf */
- sem_t slots; /* Counts available slots */
- sem_t items; /* Counts available items */
- } sbuf_t;
- void sbuf_init(sbuf_t *sp, int n);
- void sbuf_insert(sbuf_t *sp, int item);
- int sbuf_remove(sbuf_t *sp);
- void *worker(void*);
- dictionary_t *conversations;
- typedef struct convo {
- char* entry;
- struct convo *next;
- } convo;
- const char *html_header = "<html><head><script>\r\n\
- function checkEmpty(form) {\r\n\
- if(!form.content.value) {\r\n\
- location.reload();\r\n\
- return false;\r\n\
- }\r\n\
- return true;\r\n\
- }\r\n\
- window.onload = function(){var convoBox = document.getElementById('convo');convoBox.scrollTop = convoBox.scrollHeight;};\
- </script><style>body{position:relative;width:100%;}body > * {margin: auto;width: 500px;}#convo{max-height:300px;min-height:150px;overflow:scroll;border:solid pink;border-radius:5%;padding:0px 20px;}</style></head><body>";
- char *html_footer = "</body></html>";
- sbuf_t fd_queue;
- sem_t convo_lock;
- int main(int argc, char **argv) {
- int listenfd, connfd, i;
- char hostname[MAXLINE], port[MAXLINE];
- socklen_t clientlen;
- struct sockaddr_storage clientaddr;
- pthread_t thread;
- /* Check command line args */
- if (argc != 2) {
- fprintf(stderr, "usage: %s <port>\n", argv[0]);
- exit(1);
- }
- listenfd = Open_listenfd(argv[1]);
- /* Don't kill the server if there's an error, because
- we want to survive errors due to a client. But we
- do want to report errors. */
- exit_on_error(0);
- /* Also, don't stop on broken connections: */
- Signal(SIGPIPE, SIG_IGN);
- /*keep track of all conversations*/
- conversations = make_dictionary(COMPARE_CASE_SENS, free);
- Sem_init(&convo_lock, 0,1);
- /*init queue */
- long NUM_OF_THREADS = sysconf(_SC_NPROCESSORS_ONLN);
- sbuf_init(&fd_queue, NUM_OF_THREADS);
- printf("Starting %ld threads\n", NUM_OF_THREADS);
- for (i = 0; i < NUM_OF_THREADS; ++i) {
- Pthread_create(&thread, NULL, worker, NULL);
- Pthread_detach(thread);
- }
- while (1) {
- clientlen = sizeof(clientaddr);
- connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
- if (connfd >= 0) {
- Getnameinfo((SA *) &clientaddr, clientlen, hostname, MAXLINE,
- port, MAXLINE, 0);
- // printf("Accepted connection from (%s, %s)\n", hostname, port);
- sbuf_insert(&fd_queue, connfd);
- }
- }
- sbuf_remove(&fd_queue);
- Sem_destroy(&convo_lock);
- free_dictionary(conversations);
- }
- void* worker(void* args) {
- int connfd;
- while(1) {
- connfd = sbuf_remove(&fd_queue);
- doit(connfd);
- Close(connfd);
- }
- }
- /*
- * doit - handle one HTTP request/response transaction
- */
- void doit(int fd) {
- char buf[MAXLINE], *method, *uri, *version, *status, *desc;
- char *updated_convo, *topic, *conversation, *entry, *name, *content;
- char *host, *port;
- char *temp;
- size_t bytes;
- rio_t rio;
- dictionary_t *headers, *query;
- /* Read request line and headers */
- Rio_readinitb(&rio, fd);
- if (Rio_readlineb(&rio, buf, MAXLINE) <= 0) {
- return;
- }
- printf("readline: %s", buf); //i wrote this
- if (!parse_request_line(buf, &method, &uri, &version)) {
- clienterror(fd, method, "400", "Bad Request",
- "TinyChat did not recognize the request");
- } else {
- if (strcasecmp(version, "HTTP/1.0")
- && strcasecmp(version, "HTTP/1.1")) {
- clienterror(fd, version, "501", "Not Implemented",
- "TinyChat does not implement that version");
- } else if (strcasecmp(method, "GET")
- && strcasecmp(method, "POST")) {
- clienterror(fd, method, "501", "Not Implemented",
- "TinyChat does not implement that method");
- } else {
- /* Parse all query arguments into a dictionary */
- /* The start code sends back a text-field form: */
- headers = read_requesthdrs(&rio);
- query = make_dictionary(COMPARE_CASE_SENS, free);
- parse_uriquery(uri, query);
- if (!strcasecmp(method, "POST")) {
- read_postquery(&rio, headers, query);
- }
- /* For debugging, print the dictionary */
- // print_stringdictionary(query);
- /* route -- this is me!*/
- if(!strcasecmp(uri, "/")) {
- serve_form(fd, "Welcome to TinyChat");
- } else if(!strcasecmp(uri, "/chat")) {
- //request
- topic = dictionary_get(query, "topic");
- if(topic == NULL) {
- clienterror(fd, method, "403", "Not authenticated",
- "I don't think I know who you are");
- return;
- }
- /* lock here */
- P(&convo_lock);
- conversation = dictionary_get(conversations, topic);
- if(conversation == NULL) {
- conversation = malloc(1);
- conversation[0] = 0;
- dictionary_set(conversations, topic, conversation);
- }
- /* only add an entry if there is content*/
- if(dictionary_get(query, "content") != NULL && strlen(dictionary_get(query, "content")) > 0) {
- entry = append_strings(dictionary_get(query, "name"),": ", dictionary_get(query, "content"),
- "\r\n", NULL);
- updated_convo = append_strings(conversation, entry, NULL);
- dictionary_set(conversations, topic, updated_convo);
- free(entry);
- } else {
- updated_convo = conversation;
- }
- V(&convo_lock);
- /*unlock here */
- server_chat_form(fd, updated_convo, topic, dictionary_get(query,"name"));
- } else if (starts_with("/conversation", uri)) {
- topic = dictionary_get(query, "topic");
- /*lock */
- P(&convo_lock);
- conversation = dictionary_get(conversations, topic);
- conversation = conversation ? conversation : ""; // if null, make empty string
- send_conversation(conversation,fd);
- V(&convo_lock);
- /*unlock*/
- } else if(starts_with("/say", uri)) {
- topic = dictionary_get(query, "topic");
- name = dictionary_get(query, "user");
- content = dictionary_get(query, "content");
- if(content == NULL) {
- content = "";
- }
- /*lock*/
- P(&convo_lock);
- conversation = dictionary_get(conversations, topic);
- conversation = conversation ? conversation : ""; // if the conversation doesn't exist, create it.
- entry = append_strings(name, ": ", content, "\r\n", NULL);
- dictionary_set(conversations, topic, append_strings(conversation, entry, NULL));
- V(&convo_lock);
- /*unlock*/
- send_ok_response(fd, entry);
- free(entry);
- } else if (starts_with("/import", uri)) {
- //connect to server
- host = dictionary_get(query, "host");
- port = dictionary_get(query, "port");
- topic = dictionary_get(query, "topic");
- if(!host || !port) {
- clienterror(fd, method, "400", "Bad Request - no host or port",
- "TinyChat did not recognize the request");
- return;
- }
- //connect to host and port.
- int otherfd = Open_clientfd(host, port);
- if (otherfd < 0) { // bad server / port.
- clienterror(otherfd, method, "400", "Bad Request - invalid host or port",
- "TinyChat did not recognize the request");
- return;
- }
- char * encoded_topic = query_encode(topic);
- char *request = append_strings("GET /conversation?topic=",encoded_topic," HTTP/1.0\r\n\r\n",NULL);
- free(encoded_topic);
- Rio_writen(otherfd, request, strlen(request));
- Rio_readinitb(&rio, otherfd);
- if (Rio_readlineb(&rio, buf, MAXLINE) <= 0) {
- clienterror(otherfd, method, "500", "Failed to read response",
- "Internal server error");
- return;
- }
- free(version);
- if(!parse_request_line(buf, &version, &status, &desc)) {
- //parse failed
- clienterror(otherfd, method, "403", "There was an error parsing the request line.",
- "Bad request");
- } else if(!strcmp(status, "200")) {
- /*lock here*/
- P(&convo_lock);
- // printf("we get here, right?\n");
- conversation = dictionary_get(conversations, topic);
- updated_convo = conversation ? conversation : "";
- while((bytes = Rio_readn(otherfd, buf, MAXLINE)) == MAXLINE) {
- temp = strndup(buf, MAXLINE);
- printf("We have too many bytes!\n");
- updated_convo = append_strings(updated_convo, temp, NULL);
- printf("updated: %s", updated_convo);
- free(temp);
- memset(buf, 0, MAXLINE);
- }
- buf[bytes] = 0;
- dictionary_set(conversations, topic, append_strings(updated_convo, buf, NULL));
- V(&convo_lock);
- send_ok_response(fd, buf);
- /*unlock here*/
- } else {
- clienterror(otherfd, method, "401", "The response had a non 200 error code.",
- "Bad request");
- }
- Close(otherfd);
- free(status);
- free(desc);
- }/* end */
- /* Clean up */
- free_dictionary(query);
- free_dictionary(headers);
- }
- /* Clean up status line */
- free(method);
- free(uri);
- free(version);
- }
- }
- void send_ok_response(int fd, char* entry) {
- char *header, *body;
- body = append_strings("Successfully added ", entry, "\r\n", NULL);
- size_t bod_len = strlen(body);
- header = ok_header(bod_len, "text/plain; charset=utf-8");
- Rio_writen(fd, header, strlen(header));
- Rio_writen(fd, body, bod_len);
- free(header);
- free(body);
- }
- void send_conversation(char* conversation, int fd) {
- char *header;
- size_t len;
- len = strlen(conversation);
- header = ok_header(len, "text/plain; charset=utf-8");
- Rio_writen(fd, header, strlen(header)); // send header
- Rio_writen(fd, conversation, len); // send body
- free(header);
- }
- void server_chat_form(int fd, char* conversation, char* topic, char* user) {
- size_t len;
- char *body, *header;
- body = append_strings(html_header,"<h3 style=text-align:center>",entity_encode(topic),"</h3>"
- "<div class='conversation' id='convo'><pre>", entity_encode(conversation), "</pre></div>",
- "\r\n<form action='chat' method=\"post\" onsubmit='checkEmpty(this)'",
- " enctype=\"application/x-www-form-urlencoded\"",
- " accept-charset=\"UTF-8\">\r\n",
- "<input type='hidden' name=\"topic\" value='", entity_encode(topic), "'><br>",
- "<input type='hidden' name=\"name\" value='", entity_encode(user), "'><br>",
- "<label for='content'>Content: </label>"
- "<input type=\"text\" name=\"content\" autofocus><br>",
- "<input type=\"submit\" value=\"Send\" >",
- "</form>",html_footer,
- NULL);
- len = strlen(body);
- /* Send response headers to client */
- header = ok_header(len, "text/html; charset=utf-8");
- Rio_writen(fd, header, strlen(header));
- // printf("Response headers:\n");
- // printf("%s", header);
- free(header);
- /* Send response body to client */
- Rio_writen(fd, body, len);
- free(body);
- }
- /*
- * read_requesthdrs - read HTTP request headers
- */
- dictionary_t *read_requesthdrs(rio_t *rp)
- {
- char buf[MAXLINE];
- dictionary_t *d = make_dictionary(COMPARE_CASE_INSENS, free);
- Rio_readlineb(rp, buf, MAXLINE);
- // printf("%s", buf);
- while(strcmp(buf, "\r\n")) {
- Rio_readlineb(rp, buf, MAXLINE);
- // printf("%s", buf);
- parse_header_line(buf, d);
- }
- return d;
- }
- void read_postquery(rio_t *rp, dictionary_t *headers, dictionary_t *dest)
- {
- char *len_str, *type, *buffer;
- int len;
- len_str = dictionary_get(headers, "Content-Length");
- len = (len_str ? atoi(len_str) : 0);
- type = dictionary_get(headers, "Content-Type");
- buffer = malloc(len+1);
- Rio_readnb(rp, buffer, len);
- buffer[len] = 0;
- if (!strcasecmp(type, "application/x-www-form-urlencoded")) {
- parse_query(buffer, dest);
- }
- free(buffer);
- }
- static char *ok_header(size_t len, const char *content_type) {
- char *len_str, *header;
- header = append_strings("HTTP/1.0 200 OK\r\n",
- "Server: TinyChat Web Server\r\n",
- "Connection: close\r\n",
- "Content-length: ", len_str = to_string(len), "\r\n",
- "Content-type: ", content_type, "\r\n\r\n",
- NULL);
- free(len_str);
- return header;
- }
- /*
- * serve_form - sends a form to a client
- */
- void serve_form(int fd, const char *pre_content) {
- size_t len;
- char *body, *header;
- body = append_strings(html_header,
- "<p>Welcome to TinyChat</p>",
- "\r\n<form action='chat' method=\"post\" onsubmit='checkEmpty(this)'" ,
- " enctype=\"application/x-www-form-urlencoded\"",
- " accept-charset=\"UTF-8\">\r\n",
- "<label for='topic'>Topic: </label>"
- "<input type=\"text\" name=\"topic\"><br>",
- "<label for='name'>Name: </label>"
- "<input type=\"text\" name=\"name\"><br>",
- "<input type=\"submit\" value=\"Send\" >",
- "</form>",html_footer,
- NULL);
- len = strlen(body);
- /* Send response headers to client */
- header = ok_header(len, "text/html; charset=utf-8");
- Rio_writen(fd, header, strlen(header));
- // printf("Response headers:\n");
- // printf("%s", header);
- free(header);
- /* Send response body to client */
- Rio_writen(fd, body, len);
- free(body);
- }
- void welcome_screen(int fd) {
- /* char *body, *header;
- size_t len;
- body = append_strings("<html><body>\r\n",
- "<p>Welcome to TinyChat</p>",
- "\r\n<form action=\"reply\" method=\"post\"",
- " enctype=\"application/x-www-form-urlencoded\"",
- " accept-charset=\"UTF-8\">\r\n",
- "<input type=\"text\" name=\"content\">\r\n",
- "<input type=\"text\" name=\"user\">\r\n",
- "<input type=\"submit\" value=\"Send\">\r\n",
- "</form></body></html>",
- NULL);
- len = strlen(body); */
- }
- /*
- * clienterror - returns an error message to the client
- */
- void clienterror(int fd, char *cause, char *errnum,
- char *shortmsg, char *longmsg) {
- size_t len;
- char *header, *body, *len_str;
- body = append_strings("<html><title>Tiny Error</title>",
- "<body bgcolor=""ffffff"">\r\n",
- errnum, " ", shortmsg,
- "<p>", longmsg, ": ", cause,
- "<hr><em>The Tiny Web server</em>\r\n",
- NULL);
- len = strlen(body);
- /* Print the HTTP response */
- header = append_strings("HTTP/1.0 ", errnum, " ", shortmsg,
- "Content-type: text/html; charset=utf-8\r\n",
- "Content-length: ", len_str = to_string(len), "\r\n\r\n",
- NULL);
- free(len_str);
- Rio_writen(fd, header, strlen(header));
- Rio_writen(fd, body, len);
- free(header);
- free(body);
- }
- static void print_stringdictionary(dictionary_t *d)
- {
- // int i, count;
- //
- // count = dictionary_count(d);
- // for (i = 0; i < count; i++) {
- // printf("%s=%s\n",
- // dictionary_key(d, i),
- // (const char *)dictionary_value(d, i));
- // }
- // printf("\n");
- }
- /* threadpool */
- void sbuf_init(sbuf_t *sp, int n) {
- sp->buf = Calloc(n, sizeof(int));
- sp->n = n; /* max of n items */
- sp->front = sp->rear = 0; /* empty iff front == rear */
- Sem_init(&sp->mutex, 0, 1); /* for locking */
- Sem_init(&sp->slots, 0, n); /* initially n empty slots */
- Sem_init(&sp->items, 0, 0); /* initially zero data items */
- }
- void sbuf_insert(sbuf_t *sp, int item) {
- P(&sp->slots); /* wait for available slot */
- P(&sp->mutex); /* lock */
- sp->buf[(++sp->rear)%(sp->n)] = item;
- V(&sp->mutex); /* unlock */
- V(&sp->items); /* announce available item */
- }
- int sbuf_remove(sbuf_t *sp) {
- int item;
- P(&sp->items); /* wait for available item */
- P(&sp->mutex); /* lock */
- item = sp->buf[(++sp->front)%(sp->n)];
- V(&sp->mutex); /* unlock */
- V(&sp->slots); /* announce available slot */
- return item;
- }
Add Comment
Please, Sign In to add comment