Guest User

Untitled

a guest
Dec 8th, 2016
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.39 KB | None | 0 0
  1. /*
  2. * tinychat.c - [Starting code for] a web-based chat server.
  3. *
  4. * Based on:
  5. * tiny.c - A simple, iterative HTTP/1.0 Web server that uses the
  6. * GET method to serve static and dynamic content.
  7. * Tiny Web server
  8. * Dave O'Hallaron
  9. * Carnegie Mellon University
  10. */
  11. #include "csapp.h"
  12. #include "dictionary.h"
  13. #include "more_string.h"
  14. #include <unistd.h>
  15.  
  16. void doit(int fd);
  17. dictionary_t *read_requesthdrs(rio_t *rp);
  18. void read_postquery(rio_t *rp, dictionary_t *headers, dictionary_t *d);
  19. void parse_query(const char *uri, dictionary_t *d);
  20. void serve_form(int fd, const char *pre_content);
  21. void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
  22. static void print_stringdictionary(dictionary_t *d);
  23. static char *ok_header(size_t len, const char *content_type);
  24. void welcome_screen(int);
  25.  
  26. void server_chat_form(int fd, char* conversation, char* topic, char* user);
  27. void send_conversation(char* conversation, int fd);
  28. void send_ok_response(int fd, char* entry);
  29.  
  30. /* thread pool header*/
  31.  
  32. typedef struct {
  33. int *buf; /* Buffer array */
  34. int n; /* Maximum number of slots */
  35. int front; /* buf[(front+1)%n] is first item */
  36. int rear; /* buf[rear%n] is last item */
  37. sem_t mutex; /* Protects accesses to buf */
  38. sem_t slots; /* Counts available slots */
  39. sem_t items; /* Counts available items */
  40. } sbuf_t;
  41.  
  42. void sbuf_init(sbuf_t *sp, int n);
  43. void sbuf_insert(sbuf_t *sp, int item);
  44. int sbuf_remove(sbuf_t *sp);
  45.  
  46. void *worker(void*);
  47.  
  48. dictionary_t *conversations;
  49.  
  50. typedef struct convo {
  51. char* entry;
  52. struct convo *next;
  53. } convo;
  54.  
  55. const char *html_header = "<html><head><script>\r\n\
  56. function checkEmpty(form) {\r\n\
  57. if(!form.content.value) {\r\n\
  58. location.reload();\r\n\
  59. return false;\r\n\
  60. }\r\n\
  61. return true;\r\n\
  62. }\r\n\
  63. window.onload = function(){var convoBox = document.getElementById('convo');convoBox.scrollTop = convoBox.scrollHeight;};\
  64. </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>";
  65.  
  66. char *html_footer = "</body></html>";
  67. sbuf_t fd_queue;
  68. sem_t convo_lock;
  69.  
  70. int main(int argc, char **argv) {
  71. int listenfd, connfd, i;
  72. char hostname[MAXLINE], port[MAXLINE];
  73. socklen_t clientlen;
  74. struct sockaddr_storage clientaddr;
  75. pthread_t thread;
  76.  
  77. /* Check command line args */
  78. if (argc != 2) {
  79. fprintf(stderr, "usage: %s <port>\n", argv[0]);
  80. exit(1);
  81. }
  82.  
  83.  
  84. listenfd = Open_listenfd(argv[1]);
  85.  
  86. /* Don't kill the server if there's an error, because
  87. we want to survive errors due to a client. But we
  88. do want to report errors. */
  89. exit_on_error(0);
  90.  
  91. /* Also, don't stop on broken connections: */
  92. Signal(SIGPIPE, SIG_IGN);
  93.  
  94. /*keep track of all conversations*/
  95. conversations = make_dictionary(COMPARE_CASE_SENS, free);
  96. Sem_init(&convo_lock, 0,1);
  97.  
  98. /*init queue */
  99. long NUM_OF_THREADS = sysconf(_SC_NPROCESSORS_ONLN);
  100. sbuf_init(&fd_queue, NUM_OF_THREADS);
  101.  
  102. printf("Starting %ld threads\n", NUM_OF_THREADS);
  103.  
  104. for (i = 0; i < NUM_OF_THREADS; ++i) {
  105. Pthread_create(&thread, NULL, worker, NULL);
  106. Pthread_detach(thread);
  107. }
  108.  
  109. while (1) {
  110. clientlen = sizeof(clientaddr);
  111. connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
  112. if (connfd >= 0) {
  113. Getnameinfo((SA *) &clientaddr, clientlen, hostname, MAXLINE,
  114. port, MAXLINE, 0);
  115. // printf("Accepted connection from (%s, %s)\n", hostname, port);
  116. sbuf_insert(&fd_queue, connfd);
  117. }
  118. }
  119.  
  120. sbuf_remove(&fd_queue);
  121. Sem_destroy(&convo_lock);
  122. free_dictionary(conversations);
  123. }
  124.  
  125. void* worker(void* args) {
  126. int connfd;
  127. while(1) {
  128. connfd = sbuf_remove(&fd_queue);
  129. doit(connfd);
  130. Close(connfd);
  131. }
  132. }
  133.  
  134. /*
  135. * doit - handle one HTTP request/response transaction
  136. */
  137. void doit(int fd) {
  138. char buf[MAXLINE], *method, *uri, *version, *status, *desc;
  139. char *updated_convo, *topic, *conversation, *entry, *name, *content;
  140. char *host, *port;
  141. char *temp;
  142.  
  143.  
  144. size_t bytes;
  145. rio_t rio;
  146. dictionary_t *headers, *query;
  147.  
  148. /* Read request line and headers */
  149. Rio_readinitb(&rio, fd);
  150. if (Rio_readlineb(&rio, buf, MAXLINE) <= 0) {
  151. return;
  152. }
  153. printf("readline: %s", buf); //i wrote this
  154.  
  155. if (!parse_request_line(buf, &method, &uri, &version)) {
  156. clienterror(fd, method, "400", "Bad Request",
  157. "TinyChat did not recognize the request");
  158. } else {
  159. if (strcasecmp(version, "HTTP/1.0")
  160. && strcasecmp(version, "HTTP/1.1")) {
  161. clienterror(fd, version, "501", "Not Implemented",
  162. "TinyChat does not implement that version");
  163. } else if (strcasecmp(method, "GET")
  164. && strcasecmp(method, "POST")) {
  165. clienterror(fd, method, "501", "Not Implemented",
  166. "TinyChat does not implement that method");
  167. } else {
  168. /* Parse all query arguments into a dictionary */
  169. /* The start code sends back a text-field form: */
  170. headers = read_requesthdrs(&rio);
  171. query = make_dictionary(COMPARE_CASE_SENS, free);
  172. parse_uriquery(uri, query);
  173. if (!strcasecmp(method, "POST")) {
  174. read_postquery(&rio, headers, query);
  175. }
  176.  
  177. /* For debugging, print the dictionary */
  178. // print_stringdictionary(query);
  179.  
  180. /* route -- this is me!*/
  181. if(!strcasecmp(uri, "/")) {
  182. serve_form(fd, "Welcome to TinyChat");
  183. } else if(!strcasecmp(uri, "/chat")) {
  184. //request
  185. topic = dictionary_get(query, "topic");
  186. if(topic == NULL) {
  187. clienterror(fd, method, "403", "Not authenticated",
  188. "I don't think I know who you are");
  189. return;
  190. }
  191. /* lock here */
  192. P(&convo_lock);
  193. conversation = dictionary_get(conversations, topic);
  194.  
  195. if(conversation == NULL) {
  196. conversation = malloc(1);
  197. conversation[0] = 0;
  198. dictionary_set(conversations, topic, conversation);
  199. }
  200.  
  201. /* only add an entry if there is content*/
  202. if(dictionary_get(query, "content") != NULL && strlen(dictionary_get(query, "content")) > 0) {
  203. entry = append_strings(dictionary_get(query, "name"),": ", dictionary_get(query, "content"),
  204. "\r\n", NULL);
  205. updated_convo = append_strings(conversation, entry, NULL);
  206. dictionary_set(conversations, topic, updated_convo);
  207. free(entry);
  208. } else {
  209. updated_convo = conversation;
  210. }
  211. V(&convo_lock);
  212. /*unlock here */
  213.  
  214. server_chat_form(fd, updated_convo, topic, dictionary_get(query,"name"));
  215. } else if (starts_with("/conversation", uri)) {
  216. topic = dictionary_get(query, "topic");
  217. /*lock */
  218. P(&convo_lock);
  219. conversation = dictionary_get(conversations, topic);
  220. conversation = conversation ? conversation : ""; // if null, make empty string
  221. send_conversation(conversation,fd);
  222. V(&convo_lock);
  223. /*unlock*/
  224. } else if(starts_with("/say", uri)) {
  225. topic = dictionary_get(query, "topic");
  226. name = dictionary_get(query, "user");
  227. content = dictionary_get(query, "content");
  228. if(content == NULL) {
  229. content = "";
  230. }
  231. /*lock*/
  232. P(&convo_lock);
  233. conversation = dictionary_get(conversations, topic);
  234. conversation = conversation ? conversation : ""; // if the conversation doesn't exist, create it.
  235.  
  236. entry = append_strings(name, ": ", content, "\r\n", NULL);
  237. dictionary_set(conversations, topic, append_strings(conversation, entry, NULL));
  238. V(&convo_lock);
  239. /*unlock*/
  240. send_ok_response(fd, entry);
  241. free(entry);
  242. } else if (starts_with("/import", uri)) {
  243. //connect to server
  244. host = dictionary_get(query, "host");
  245. port = dictionary_get(query, "port");
  246. topic = dictionary_get(query, "topic");
  247.  
  248. if(!host || !port) {
  249. clienterror(fd, method, "400", "Bad Request - no host or port",
  250. "TinyChat did not recognize the request");
  251. return;
  252. }
  253. //connect to host and port.
  254. int otherfd = Open_clientfd(host, port);
  255. if (otherfd < 0) { // bad server / port.
  256. clienterror(otherfd, method, "400", "Bad Request - invalid host or port",
  257. "TinyChat did not recognize the request");
  258. return;
  259. }
  260.  
  261. char * encoded_topic = query_encode(topic);
  262. char *request = append_strings("GET /conversation?topic=",encoded_topic," HTTP/1.0\r\n\r\n",NULL);
  263. free(encoded_topic);
  264. Rio_writen(otherfd, request, strlen(request));
  265. Rio_readinitb(&rio, otherfd);
  266. if (Rio_readlineb(&rio, buf, MAXLINE) <= 0) {
  267. clienterror(otherfd, method, "500", "Failed to read response",
  268. "Internal server error");
  269. return;
  270. }
  271.  
  272. free(version);
  273. if(!parse_request_line(buf, &version, &status, &desc)) {
  274. //parse failed
  275. clienterror(otherfd, method, "403", "There was an error parsing the request line.",
  276. "Bad request");
  277. } else if(!strcmp(status, "200")) {
  278. /*lock here*/
  279. P(&convo_lock);
  280. // printf("we get here, right?\n");
  281. conversation = dictionary_get(conversations, topic);
  282. updated_convo = conversation ? conversation : "";
  283. while((bytes = Rio_readn(otherfd, buf, MAXLINE)) == MAXLINE) {
  284. temp = strndup(buf, MAXLINE);
  285. printf("We have too many bytes!\n");
  286. updated_convo = append_strings(updated_convo, temp, NULL);
  287. printf("updated: %s", updated_convo);
  288. free(temp);
  289. memset(buf, 0, MAXLINE);
  290. }
  291. buf[bytes] = 0;
  292. dictionary_set(conversations, topic, append_strings(updated_convo, buf, NULL));
  293. V(&convo_lock);
  294. send_ok_response(fd, buf);
  295. /*unlock here*/
  296. } else {
  297. clienterror(otherfd, method, "401", "The response had a non 200 error code.",
  298. "Bad request");
  299. }
  300.  
  301. Close(otherfd);
  302. free(status);
  303. free(desc);
  304. }/* end */
  305.  
  306. /* Clean up */
  307. free_dictionary(query);
  308. free_dictionary(headers);
  309. }
  310.  
  311. /* Clean up status line */
  312. free(method);
  313. free(uri);
  314. free(version);
  315. }
  316. }
  317.  
  318. void send_ok_response(int fd, char* entry) {
  319. char *header, *body;
  320. body = append_strings("Successfully added ", entry, "\r\n", NULL);
  321. size_t bod_len = strlen(body);
  322.  
  323. header = ok_header(bod_len, "text/plain; charset=utf-8");
  324. Rio_writen(fd, header, strlen(header));
  325. Rio_writen(fd, body, bod_len);
  326. free(header);
  327. free(body);
  328. }
  329.  
  330. void send_conversation(char* conversation, int fd) {
  331. char *header;
  332. size_t len;
  333. len = strlen(conversation);
  334. header = ok_header(len, "text/plain; charset=utf-8");
  335. Rio_writen(fd, header, strlen(header)); // send header
  336. Rio_writen(fd, conversation, len); // send body
  337.  
  338. free(header);
  339. }
  340.  
  341. void server_chat_form(int fd, char* conversation, char* topic, char* user) {
  342. size_t len;
  343. char *body, *header;
  344.  
  345. body = append_strings(html_header,"<h3 style=text-align:center>",entity_encode(topic),"</h3>"
  346. "<div class='conversation' id='convo'><pre>", entity_encode(conversation), "</pre></div>",
  347. "\r\n<form action='chat' method=\"post\" onsubmit='checkEmpty(this)'",
  348. " enctype=\"application/x-www-form-urlencoded\"",
  349. " accept-charset=\"UTF-8\">\r\n",
  350. "<input type='hidden' name=\"topic\" value='", entity_encode(topic), "'><br>",
  351. "<input type='hidden' name=\"name\" value='", entity_encode(user), "'><br>",
  352. "<label for='content'>Content: </label>"
  353. "<input type=\"text\" name=\"content\" autofocus><br>",
  354. "<input type=\"submit\" value=\"Send\" >",
  355. "</form>",html_footer,
  356. NULL);
  357.  
  358. len = strlen(body);
  359.  
  360. /* Send response headers to client */
  361. header = ok_header(len, "text/html; charset=utf-8");
  362. Rio_writen(fd, header, strlen(header));
  363. // printf("Response headers:\n");
  364. // printf("%s", header);
  365.  
  366. free(header);
  367.  
  368. /* Send response body to client */
  369. Rio_writen(fd, body, len);
  370.  
  371. free(body);
  372. }
  373.  
  374. /*
  375. * read_requesthdrs - read HTTP request headers
  376. */
  377. dictionary_t *read_requesthdrs(rio_t *rp)
  378. {
  379. char buf[MAXLINE];
  380. dictionary_t *d = make_dictionary(COMPARE_CASE_INSENS, free);
  381.  
  382. Rio_readlineb(rp, buf, MAXLINE);
  383. // printf("%s", buf);
  384. while(strcmp(buf, "\r\n")) {
  385. Rio_readlineb(rp, buf, MAXLINE);
  386. // printf("%s", buf);
  387. parse_header_line(buf, d);
  388. }
  389.  
  390. return d;
  391. }
  392.  
  393. void read_postquery(rio_t *rp, dictionary_t *headers, dictionary_t *dest)
  394. {
  395. char *len_str, *type, *buffer;
  396. int len;
  397.  
  398. len_str = dictionary_get(headers, "Content-Length");
  399. len = (len_str ? atoi(len_str) : 0);
  400.  
  401. type = dictionary_get(headers, "Content-Type");
  402.  
  403. buffer = malloc(len+1);
  404. Rio_readnb(rp, buffer, len);
  405. buffer[len] = 0;
  406.  
  407. if (!strcasecmp(type, "application/x-www-form-urlencoded")) {
  408. parse_query(buffer, dest);
  409. }
  410.  
  411. free(buffer);
  412. }
  413.  
  414. static char *ok_header(size_t len, const char *content_type) {
  415. char *len_str, *header;
  416.  
  417. header = append_strings("HTTP/1.0 200 OK\r\n",
  418. "Server: TinyChat Web Server\r\n",
  419. "Connection: close\r\n",
  420. "Content-length: ", len_str = to_string(len), "\r\n",
  421. "Content-type: ", content_type, "\r\n\r\n",
  422. NULL);
  423. free(len_str);
  424.  
  425. return header;
  426. }
  427.  
  428. /*
  429. * serve_form - sends a form to a client
  430. */
  431. void serve_form(int fd, const char *pre_content) {
  432. size_t len;
  433. char *body, *header;
  434.  
  435. body = append_strings(html_header,
  436. "<p>Welcome to TinyChat</p>",
  437. "\r\n<form action='chat' method=\"post\" onsubmit='checkEmpty(this)'" ,
  438. " enctype=\"application/x-www-form-urlencoded\"",
  439. " accept-charset=\"UTF-8\">\r\n",
  440. "<label for='topic'>Topic: </label>"
  441. "<input type=\"text\" name=\"topic\"><br>",
  442. "<label for='name'>Name: </label>"
  443. "<input type=\"text\" name=\"name\"><br>",
  444. "<input type=\"submit\" value=\"Send\" >",
  445. "</form>",html_footer,
  446. NULL);
  447.  
  448. len = strlen(body);
  449.  
  450. /* Send response headers to client */
  451. header = ok_header(len, "text/html; charset=utf-8");
  452. Rio_writen(fd, header, strlen(header));
  453. // printf("Response headers:\n");
  454. // printf("%s", header);
  455.  
  456. free(header);
  457.  
  458. /* Send response body to client */
  459. Rio_writen(fd, body, len);
  460.  
  461. free(body);
  462. }
  463.  
  464. void welcome_screen(int fd) {
  465. /* char *body, *header;
  466. size_t len;
  467. body = append_strings("<html><body>\r\n",
  468. "<p>Welcome to TinyChat</p>",
  469. "\r\n<form action=\"reply\" method=\"post\"",
  470. " enctype=\"application/x-www-form-urlencoded\"",
  471. " accept-charset=\"UTF-8\">\r\n",
  472. "<input type=\"text\" name=\"content\">\r\n",
  473. "<input type=\"text\" name=\"user\">\r\n",
  474. "<input type=\"submit\" value=\"Send\">\r\n",
  475. "</form></body></html>",
  476. NULL);
  477. len = strlen(body); */
  478. }
  479.  
  480.  
  481. /*
  482. * clienterror - returns an error message to the client
  483. */
  484. void clienterror(int fd, char *cause, char *errnum,
  485. char *shortmsg, char *longmsg) {
  486. size_t len;
  487. char *header, *body, *len_str;
  488.  
  489. body = append_strings("<html><title>Tiny Error</title>",
  490. "<body bgcolor=""ffffff"">\r\n",
  491. errnum, " ", shortmsg,
  492. "<p>", longmsg, ": ", cause,
  493. "<hr><em>The Tiny Web server</em>\r\n",
  494. NULL);
  495. len = strlen(body);
  496.  
  497. /* Print the HTTP response */
  498. header = append_strings("HTTP/1.0 ", errnum, " ", shortmsg,
  499. "Content-type: text/html; charset=utf-8\r\n",
  500. "Content-length: ", len_str = to_string(len), "\r\n\r\n",
  501. NULL);
  502. free(len_str);
  503.  
  504. Rio_writen(fd, header, strlen(header));
  505. Rio_writen(fd, body, len);
  506.  
  507. free(header);
  508. free(body);
  509. }
  510.  
  511. static void print_stringdictionary(dictionary_t *d)
  512. {
  513. // int i, count;
  514. //
  515. // count = dictionary_count(d);
  516. // for (i = 0; i < count; i++) {
  517. // printf("%s=%s\n",
  518. // dictionary_key(d, i),
  519. // (const char *)dictionary_value(d, i));
  520. // }
  521. // printf("\n");
  522. }
  523.  
  524.  
  525. /* threadpool */
  526.  
  527.  
  528.  
  529. void sbuf_init(sbuf_t *sp, int n) {
  530. sp->buf = Calloc(n, sizeof(int));
  531. sp->n = n; /* max of n items */
  532. sp->front = sp->rear = 0; /* empty iff front == rear */
  533. Sem_init(&sp->mutex, 0, 1); /* for locking */
  534. Sem_init(&sp->slots, 0, n); /* initially n empty slots */
  535. Sem_init(&sp->items, 0, 0); /* initially zero data items */
  536. }
  537.  
  538. void sbuf_insert(sbuf_t *sp, int item) {
  539. P(&sp->slots); /* wait for available slot */
  540. P(&sp->mutex); /* lock */
  541. sp->buf[(++sp->rear)%(sp->n)] = item;
  542. V(&sp->mutex); /* unlock */
  543. V(&sp->items); /* announce available item */
  544. }
  545.  
  546. int sbuf_remove(sbuf_t *sp) {
  547. int item;
  548. P(&sp->items); /* wait for available item */
  549. P(&sp->mutex); /* lock */
  550. item = sp->buf[(++sp->front)%(sp->n)];
  551. V(&sp->mutex); /* unlock */
  552. V(&sp->slots); /* announce available slot */
  553. return item;
  554. }
Add Comment
Please, Sign In to add comment