/[jscoverage]/trunk/jscoverage-server.c
ViewVC logotype

Annotation of /trunk/jscoverage-server.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 505 - (hide annotations)
Sat Jan 2 04:55:54 2010 UTC (9 years, 11 months ago) by siliconforks
File MIME type: text/plain
File size: 41122 byte(s)
Update copyright year.

1 siliconforks 116 /*
2     jscoverage-server.c - JSCoverage server main routine
3 siliconforks 505 Copyright (C) 2008, 2009, 2010 siliconforks.com
4 siliconforks 116
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9    
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13     GNU General Public License for more details.
14    
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18     */
19    
20     #include <config.h>
21    
22     #include <assert.h>
23     #include <ctype.h>
24     #include <signal.h>
25 siliconforks 179 #include <stdint.h>
26 siliconforks 116 #include <string.h>
27    
28     #include <dirent.h>
29 siliconforks 125 #ifdef HAVE_PTHREAD_H
30 siliconforks 116 #include <pthread.h>
31 siliconforks 125 #endif
32 siliconforks 116
33 siliconforks 179 #include "encoding.h"
34 siliconforks 174 #include "global.h"
35 siliconforks 116 #include "http-server.h"
36     #include "instrument-js.h"
37     #include "resource-manager.h"
38     #include "stream.h"
39     #include "util.h"
40    
41 siliconforks 278 static const char * specified_encoding = NULL;
42 siliconforks 174 const char * jscoverage_encoding = "ISO-8859-1";
43 siliconforks 179 bool jscoverage_highlight = true;
44 siliconforks 174
45 siliconforks 116 typedef struct SourceCache {
46     char * url;
47 siliconforks 179 uint16_t * characters;
48     size_t num_characters;
49 siliconforks 116 struct SourceCache * next;
50     } SourceCache;
51    
52     static SourceCache * source_cache = NULL;
53    
54     static const struct {
55     const char * extension;
56     const char * mime_type;
57     } mime_types[] = {
58     {".gif", "image/gif"},
59     {".jpg", "image/jpeg"},
60     {".jpeg", "image/jpeg"},
61     {".png", "image/png"},
62     {".css", "text/css"},
63     {".html", "text/html"},
64     {".htm", "text/html"},
65     {".js", "text/javascript"},
66     {".txt", "text/plain"},
67     {".xml", "application/xml"},
68     };
69    
70     static bool verbose = false;
71     static const char * report_directory = "jscoverage-report";
72     static const char * document_root = ".";
73     static bool proxy = false;
74     static const char ** no_instrument;
75     static size_t num_no_instrument = 0;
76    
77 siliconforks 125 #ifdef __MINGW32__
78     CRITICAL_SECTION javascript_mutex;
79     CRITICAL_SECTION source_cache_mutex;
80     #define LOCK EnterCriticalSection
81     #define UNLOCK LeaveCriticalSection
82     #else
83 siliconforks 116 pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER;
84     pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
85 siliconforks 125 #define LOCK pthread_mutex_lock
86     #define UNLOCK pthread_mutex_unlock
87     #endif
88 siliconforks 116
89 siliconforks 179 static const SourceCache * find_cached_source(const char * url) {
90     SourceCache * result = NULL;
91 siliconforks 125 LOCK(&source_cache_mutex);
92 siliconforks 116 for (SourceCache * p = source_cache; p != NULL; p = p->next) {
93     if (strcmp(url, p->url) == 0) {
94 siliconforks 179 result = p;
95 siliconforks 116 break;
96     }
97     }
98 siliconforks 125 UNLOCK(&source_cache_mutex);
99 siliconforks 116 return result;
100     }
101    
102 siliconforks 179 static void add_cached_source(const char * url, uint16_t * characters, size_t num_characters) {
103 siliconforks 116 SourceCache * new_source_cache = xmalloc(sizeof(SourceCache));
104     new_source_cache->url = xstrdup(url);
105 siliconforks 179 new_source_cache->characters = characters;
106     new_source_cache->num_characters = num_characters;
107 siliconforks 125 LOCK(&source_cache_mutex);
108 siliconforks 116 new_source_cache->next = source_cache;
109     source_cache = new_source_cache;
110 siliconforks 125 UNLOCK(&source_cache_mutex);
111 siliconforks 116 }
112    
113 siliconforks 179 static int get(const char * url, uint16_t ** characters, size_t * num_characters) __attribute__((warn_unused_result));
114 siliconforks 116
115 siliconforks 179 static int get(const char * url, uint16_t ** characters, size_t * num_characters) {
116 siliconforks 116 char * host = NULL;
117     uint16_t port;
118     char * abs_path = NULL;
119     char * query = NULL;
120     HTTPConnection * connection = NULL;
121     HTTPExchange * exchange = NULL;
122 siliconforks 179 Stream * stream = NULL;
123 siliconforks 116
124     int result = URL_parse(url, &host, &port, &abs_path, &query);
125     if (result != 0) {
126     goto done;
127     }
128    
129     connection = HTTPConnection_new_client(host, port);
130     if (connection == NULL) {
131     result = -1;
132     goto done;
133     }
134    
135     exchange = HTTPExchange_new(connection);
136     HTTPExchange_set_request_uri(exchange, url);
137     result = HTTPExchange_write_request_headers(exchange);
138     if (result != 0) {
139     goto done;
140     }
141    
142     result = HTTPExchange_read_response_headers(exchange);
143     if (result != 0) {
144     goto done;
145     }
146    
147 siliconforks 179 stream = Stream_new(0);
148 siliconforks 116 result = HTTPExchange_read_entire_response_entity_body(exchange, stream);
149     if (result != 0) {
150     goto done;
151     }
152 siliconforks 179 char * encoding = HTTPMessage_get_charset(HTTPExchange_get_response_message(exchange));
153     if (encoding == NULL) {
154     encoding = xstrdup(jscoverage_encoding);
155     }
156     result = jscoverage_bytes_to_characters(encoding, stream->data, stream->length, characters, num_characters);
157     free(encoding);
158     if (result != 0) {
159     goto done;
160     }
161 siliconforks 116
162     result = 0;
163    
164     done:
165 siliconforks 179 if (stream != NULL) {
166     Stream_delete(stream);
167     }
168 siliconforks 116 if (exchange != NULL) {
169     HTTPExchange_delete(exchange);
170     }
171     if (connection != NULL) {
172     if (HTTPConnection_delete(connection) != 0) {
173     HTTPServer_log_err("Warning: error closing connection after retrieving URL: %s\n", url);
174     }
175     }
176     free(host);
177     free(abs_path);
178     free(query);
179     return result;
180     }
181    
182     static void send_response(HTTPExchange * exchange, uint16_t status_code, const char * html) {
183     HTTPExchange_set_status_code(exchange, status_code);
184     if (HTTPExchange_write_response(exchange, html, strlen(html)) != 0) {
185     HTTPServer_log_err("Warning: error writing to client\n");
186     }
187     }
188    
189     /*
190     RFC 2396, Appendix A: we are checking for `pchar'
191     */
192     static bool is_escaped(char c) {
193     /* `pchar' */
194     if (strchr(":@&=+$,", c) != NULL) {
195     return false;
196     }
197    
198     if (isalnum((unsigned char) c)) {
199     return false;
200     }
201    
202     /* `mark' */
203     if (strchr("-_.!~*'()", c) != NULL) {
204     return false;
205     }
206    
207     return true;
208     }
209    
210     static char * encode_uri_component(const char * s) {
211     size_t length = 0;
212     for (const char * p = s; *p != '\0'; p++) {
213     if (is_escaped(*p)) {
214     length = addst(length, 3);
215     }
216     else {
217     length = addst(length, 1);
218     }
219     }
220    
221     length = addst(length, 1);
222     char * result = xmalloc(length);
223     size_t i = 0;
224     for (const char * p = s; *p != '\0'; p++) {
225     if (is_escaped(*p)) {
226     result[i] = '%';
227     i++;
228     snprintf(result + i, 3, "%02X", *p);
229     i += 2;
230     }
231     else {
232     result[i] = *p;
233     i++;
234     }
235     }
236     result[i] = '\0';
237    
238     return result;
239     }
240    
241 siliconforks 189 static unsigned int hex_value(char c) {
242     if ('0' <= c && c <= '9') {
243     return c - '0';
244     }
245     else if ('A' <= c && c <= 'F') {
246 siliconforks 253 return c - 'A' + 10;
247 siliconforks 189 }
248     else if ('a' <= c && c <= 'f') {
249 siliconforks 253 return c - 'a' + 10;
250 siliconforks 189 }
251     else {
252     return 0;
253     }
254     }
255    
256     static char * decode_uri_component(const char * s) {
257     size_t length = strlen(s);
258     char * result = xmalloc(length + 1);
259     char * p = result;
260     while (*s != '\0') {
261     if (*s == '%') {
262     if (s[1] == '\0' || s[2] == '\0') {
263     *p = '\0';
264     return result;
265     }
266     *p = hex_value(s[1]) * 16 + hex_value(s[2]);
267     s += 2;
268     }
269     else {
270     *p = *s;
271     }
272     p++;
273     s++;
274     }
275     *p = '\0';
276     return result;
277     }
278    
279 siliconforks 116 static const char * get_entity(char c) {
280     switch(c) {
281     case '<':
282     return "&lt;";
283     case '>':
284     return "&gt;";
285     case '&':
286     return "&amp;";
287     case '\'':
288     return "&apos;";
289     case '"':
290     return "&quot;";
291     default:
292     return NULL;
293     }
294     }
295    
296     static char * encode_html(const char * s) {
297     size_t length = 0;
298     for (const char * p = s; *p != '\0'; p++) {
299     const char * entity = get_entity(*p);
300     if (entity == NULL) {
301     length = addst(length, 1);
302     }
303     else {
304     length = addst(length, strlen(entity));
305     }
306     }
307    
308     length = addst(length, 1);
309     char * result = xmalloc(length);
310     size_t i = 0;
311     for (const char * p = s; *p != '\0'; p++) {
312     const char * entity = get_entity(*p);
313     if (entity == NULL) {
314     result[i] = *p;
315     i++;
316     }
317     else {
318     strcpy(result + i, entity);
319     i += strlen(entity);
320     }
321     }
322     result[i] = '\0';
323    
324     return result;
325     }
326    
327     static const char * get_content_type(const char * path) {
328     char * last_dot = strrchr(path, '.');
329     if (last_dot == NULL) {
330     return "application/octet-stream";
331     }
332     for (size_t i = 0; i < sizeof(mime_types) / sizeof(mime_types[0]); i++) {
333     if (strcmp(last_dot, mime_types[i].extension) == 0) {
334     return mime_types[i].mime_type;
335     }
336     }
337     return "application/octet-stream";
338     }
339    
340     /**
341     Checks whether a URI is on the no-instrument list.
342     @param uri the HTTP "Request-URI"; must not be NULL, and must not be a zero-length string
343     @return true if the URI is on the no-instrument list, false otherwise
344     */
345     static bool is_no_instrument(const char * uri) {
346     assert(*uri != '\0');
347    
348     for (size_t i = 0; i < num_no_instrument; i++) {
349     if (str_starts_with(uri, no_instrument[i])) {
350     return true;
351     }
352    
353     /*
354     For a local URL, accept "/foo/bar" and "foo/bar" on the no-instrument list.
355     */
356     if (! proxy && str_starts_with(uri + 1, no_instrument[i])) {
357     return true;
358     }
359     }
360    
361     return false;
362     }
363    
364     static bool is_javascript(HTTPExchange * exchange) {
365     const char * header = HTTPExchange_find_response_header(exchange, HTTP_CONTENT_TYPE);
366     if (header == NULL) {
367     /* guess based on extension */
368     return str_ends_with(HTTPExchange_get_request_uri(exchange), ".js");
369     }
370     else {
371     char * semicolon = strchr(header, ';');
372     char * content_type;
373     if (semicolon == NULL) {
374     content_type = xstrdup(header);
375     }
376     else {
377     content_type = xstrndup(header, semicolon - header);
378     }
379     /* RFC 4329 */
380     bool result = strcmp(content_type, "text/javascript") == 0 ||
381     strcmp(content_type, "text/ecmascript") == 0 ||
382     strcmp(content_type, "text/javascript1.0") == 0 ||
383     strcmp(content_type, "text/javascript1.1") == 0 ||
384     strcmp(content_type, "text/javascript1.2") == 0 ||
385     strcmp(content_type, "text/javascript1.3") == 0 ||
386     strcmp(content_type, "text/javascript1.4") == 0 ||
387     strcmp(content_type, "text/javascript1.5") == 0 ||
388     strcmp(content_type, "text/jscript") == 0 ||
389     strcmp(content_type, "text/livescript") == 0 ||
390     strcmp(content_type, "text/x-javascript") == 0 ||
391     strcmp(content_type, "text/x-ecmascript") == 0 ||
392     strcmp(content_type, "application/x-javascript") == 0 ||
393     strcmp(content_type, "application/x-ecmascript") == 0 ||
394     strcmp(content_type, "application/javascript") == 0 ||
395     strcmp(content_type, "application/ecmascript") == 0;
396     free(content_type);
397     return result;
398     }
399     }
400    
401 siliconforks 480 static bool should_instrument_request(HTTPExchange * exchange, const char * uri) {
402 siliconforks 116 if (! is_javascript(exchange)) {
403     return false;
404     }
405    
406 siliconforks 480 if (is_no_instrument(uri)) {
407 siliconforks 116 return false;
408     }
409    
410     return true;
411     }
412    
413 siliconforks 119 static int merge(Coverage * coverage, FILE * f) __attribute__((warn_unused_result));
414 siliconforks 116
415 siliconforks 119 static int merge(Coverage * coverage, FILE * f) {
416     Stream * stream = Stream_new(0);
417     Stream_write_file_contents(stream, f);
418 siliconforks 116
419 siliconforks 125 LOCK(&javascript_mutex);
420 siliconforks 119 int result = jscoverage_parse_json(coverage, stream->data, stream->length);
421 siliconforks 125 UNLOCK(&javascript_mutex);
422 siliconforks 116
423 siliconforks 119 Stream_delete(stream);
424 siliconforks 116 return result;
425     }
426    
427     static void write_js_quoted_string(FILE * f, char * data, size_t length) {
428     putc('"', f);
429     for (size_t i = 0; i < length; i++) {
430     char c = data[i];
431     switch (c) {
432     case '\b':
433     fputs("\\b", f);
434     break;
435     case '\f':
436     fputs("\\f", f);
437     break;
438     case '\n':
439     fputs("\\n", f);
440     break;
441     case '\r':
442     fputs("\\r", f);
443     break;
444     case '\t':
445     fputs("\\t", f);
446     break;
447 siliconforks 317 /* IE doesn't support this */
448     /*
449 siliconforks 116 case '\v':
450     fputs("\\v", f);
451     break;
452 siliconforks 317 */
453 siliconforks 116 case '"':
454     fputs("\\\"", f);
455     break;
456     case '\\':
457     fputs("\\\\", f);
458     break;
459     default:
460     putc(c, f);
461     break;
462     }
463     }
464     putc('"', f);
465     }
466    
467 siliconforks 179 static void write_source(const char * id, const uint16_t * characters, size_t num_characters, FILE * f) {
468     Stream * output = Stream_new(num_characters);
469     jscoverage_write_source(id, characters, num_characters, output);
470     fwrite(output->data, 1, output->length, f);
471     Stream_delete(output);
472     }
473    
474 siliconforks 116 static void write_json_for_file(const FileCoverage * file_coverage, int i, void * p) {
475     FILE * f = p;
476    
477     if (i > 0) {
478     putc(',', f);
479     }
480    
481     write_js_quoted_string(f, file_coverage->id, strlen(file_coverage->id));
482    
483     fputs(":{\"coverage\":[", f);
484 siliconforks 179 for (uint32_t i = 0; i < file_coverage->num_coverage_lines; i++) {
485 siliconforks 116 if (i > 0) {
486     putc(',', f);
487     }
488 siliconforks 179 int timesExecuted = file_coverage->coverage_lines[i];
489 siliconforks 116 if (timesExecuted < 0) {
490     fputs("null", f);
491     }
492     else {
493     fprintf(f, "%d", timesExecuted);
494     }
495     }
496     fputs("],\"source\":", f);
497 siliconforks 179 if (file_coverage->source_lines == NULL) {
498 siliconforks 116 if (proxy) {
499 siliconforks 179 const SourceCache * cached = find_cached_source(file_coverage->id);
500     if (cached == NULL) {
501     uint16_t * characters;
502     size_t num_characters;
503     if (get(file_coverage->id, &characters, &num_characters) == 0) {
504     write_source(file_coverage->id, characters, num_characters, f);
505     add_cached_source(file_coverage->id, characters, num_characters);
506 siliconforks 116 }
507     else {
508 siliconforks 179 fputs("[]", f);
509 siliconforks 116 HTTPServer_log_err("Warning: cannot retrieve URL: %s\n", file_coverage->id);
510     }
511     }
512     else {
513 siliconforks 179 write_source(file_coverage->id, cached->characters, cached->num_characters, f);
514 siliconforks 116 }
515     }
516     else {
517     /* check that the path begins with / */
518     if (file_coverage->id[0] == '/') {
519 siliconforks 447 char * decoded_path = decode_uri_component(file_coverage->id);
520     if (strstr(decoded_path, "..") != NULL) {
521     free(decoded_path);
522     fputs("[]", f);
523     HTTPServer_log_err("Warning: invalid source path: %s\n", file_coverage->id);
524     goto done;
525     }
526     char * source_path = make_path(document_root, decoded_path + 1);
527     free(decoded_path);
528 siliconforks 125 FILE * source_file = fopen(source_path, "rb");
529 siliconforks 116 free(source_path);
530     if (source_file == NULL) {
531 siliconforks 179 fputs("[]", f);
532 siliconforks 116 HTTPServer_log_err("Warning: cannot open file: %s\n", file_coverage->id);
533     }
534     else {
535     Stream * stream = Stream_new(0);
536     Stream_write_file_contents(stream, source_file);
537     fclose(source_file);
538 siliconforks 179 uint16_t * characters;
539     size_t num_characters;
540     int result = jscoverage_bytes_to_characters(jscoverage_encoding, stream->data, stream->length, &characters, &num_characters);
541 siliconforks 116 Stream_delete(stream);
542 siliconforks 179 if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
543     fputs("[]", f);
544     HTTPServer_log_err("Warning: encoding %s not supported\n", jscoverage_encoding);
545     }
546     else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
547     fputs("[]", f);
548     HTTPServer_log_err("Warning: error decoding %s in file %s\n", jscoverage_encoding, file_coverage->id);
549     }
550     else {
551     write_source(file_coverage->id, characters, num_characters, f);
552     free(characters);
553     }
554 siliconforks 116 }
555     }
556     else {
557     /* path does not begin with / */
558 siliconforks 179 fputs("[]", f);
559 siliconforks 116 HTTPServer_log_err("Warning: invalid source path: %s\n", file_coverage->id);
560     }
561     }
562     }
563     else {
564 siliconforks 179 fputc('[', f);
565     for (uint32_t i = 0; i < file_coverage->num_source_lines; i++) {
566     if (i > 0) {
567     fputc(',', f);
568     }
569     char * source_line = file_coverage->source_lines[i];
570     write_js_quoted_string(f, source_line, strlen(source_line));
571     }
572     fputc(']', f);
573 siliconforks 116 }
574 siliconforks 447 done:
575 siliconforks 116 fputc('}', f);
576     }
577    
578     static int write_json(Coverage * coverage, const char * path) __attribute__((warn_unused_result));
579    
580     static int write_json(Coverage * coverage, const char * path) {
581     /* write the JSON */
582 siliconforks 241 FILE * f = fopen(path, "wb");
583 siliconforks 116 if (f == NULL) {
584     return -1;
585     }
586     putc('{', f);
587     Coverage_foreach_file(coverage, write_json_for_file, f);
588     putc('}', f);
589     if (fclose(f) == EOF) {
590     return -1;
591     }
592     return 0;
593     }
594    
595     static void handle_jscoverage_request(HTTPExchange * exchange) {
596     /* set the `Server' response-header (RFC 2616 14.38, 3.8) */
597     HTTPExchange_set_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION);
598    
599     const char * abs_path = HTTPExchange_get_abs_path(exchange);
600     assert(*abs_path != '\0');
601     if (str_starts_with(abs_path, "/jscoverage-store")) {
602     if (strcmp(HTTPExchange_get_method(exchange), "POST") != 0) {
603     HTTPExchange_set_response_header(exchange, HTTP_ALLOW, "POST");
604     send_response(exchange, 405, "Method not allowed\n");
605     return;
606     }
607    
608     Stream * json = Stream_new(0);
609    
610     /* read the POST body */
611     if (HTTPExchange_read_entire_request_entity_body(exchange, json) != 0) {
612     Stream_delete(json);
613     send_response(exchange, 400, "Could not read request body\n");
614     return;
615     }
616    
617     Coverage * coverage = Coverage_new();
618 siliconforks 125 LOCK(&javascript_mutex);
619 siliconforks 116 int result = jscoverage_parse_json(coverage, json->data, json->length);
620 siliconforks 125 UNLOCK(&javascript_mutex);
621 siliconforks 116 Stream_delete(json);
622    
623     if (result != 0) {
624     Coverage_delete(coverage);
625     send_response(exchange, 400, "Could not parse coverage data\n");
626     return;
627     }
628    
629     mkdir_if_necessary(report_directory);
630 siliconforks 189 char * current_report_directory;
631     if (str_starts_with(abs_path, "/jscoverage-store/") && abs_path[18] != '\0') {
632     char * dir = decode_uri_component(abs_path + 18);
633     current_report_directory = make_path(report_directory, dir);
634     free(dir);
635     }
636     else {
637     current_report_directory = xstrdup(report_directory);
638     }
639     mkdir_if_necessary(current_report_directory);
640     char * path = make_path(current_report_directory, "jscoverage.json");
641 siliconforks 133
642     /* check if the JSON file exists */
643     struct stat buf;
644     if (stat(path, &buf) == 0) {
645 siliconforks 116 /* it exists: merge */
646 siliconforks 241 FILE * f = fopen(path, "rb");
647 siliconforks 133 if (f == NULL) {
648 siliconforks 119 result = 1;
649     }
650 siliconforks 133 else {
651     result = merge(coverage, f);
652     if (fclose(f) == EOF) {
653     result = 1;
654     }
655     }
656 siliconforks 116 if (result != 0) {
657 siliconforks 189 free(current_report_directory);
658 siliconforks 116 free(path);
659     Coverage_delete(coverage);
660     send_response(exchange, 500, "Could not merge with existing coverage data\n");
661     return;
662     }
663     }
664    
665     result = write_json(coverage, path);
666     free(path);
667     Coverage_delete(coverage);
668     if (result != 0) {
669 siliconforks 189 free(current_report_directory);
670 siliconforks 116 send_response(exchange, 500, "Could not write coverage data\n");
671     return;
672     }
673    
674     /* copy other files */
675 siliconforks 189 jscoverage_copy_resources(current_report_directory);
676     path = make_path(current_report_directory, "jscoverage.js");
677     free(current_report_directory);
678 siliconforks 133 FILE * f = fopen(path, "ab");
679 siliconforks 116 free(path);
680     if (f == NULL) {
681     send_response(exchange, 500, "Could not write to file: jscoverage.js\n");
682     return;
683     }
684     fputs("jscoverage_isReport = true;\r\n", f);
685     if (fclose(f) == EOF) {
686     send_response(exchange, 500, "Could not write to file: jscoverage.js\n");
687     return;
688     }
689    
690     send_response(exchange, 200, "Coverage data stored\n");
691     }
692     else if (str_starts_with(abs_path, "/jscoverage-shutdown")) {
693     if (strcmp(HTTPExchange_get_method(exchange), "POST") != 0) {
694     HTTPExchange_set_response_header(exchange, HTTP_ALLOW, "POST");
695     send_response(exchange, 405, "Method not allowed\n");
696     return;
697     }
698    
699     /* allow only from localhost */
700     struct sockaddr_in client;
701     if (HTTPExchange_get_peer(exchange, &client) != 0) {
702     send_response(exchange, 500, "Cannot get client address\n");
703     return;
704     }
705     if (client.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
706     send_response(exchange, 403, "This operation can be performed only by localhost\n");
707     return;
708     }
709    
710     send_response(exchange, 200, "The server will now shut down\n");
711     HTTPServer_shutdown();
712     }
713     else {
714     const char * path = abs_path + 1;
715     const struct Resource * resource = get_resource(path);
716     if (resource == NULL) {
717     send_response(exchange, 404, "Not found\n");
718     return;
719     }
720     HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, get_content_type(path));
721     if (HTTPExchange_write_response(exchange, resource->data, resource->length) != 0) {
722     HTTPServer_log_err("Warning: error writing to client\n");
723     return;
724     }
725     if (strcmp(abs_path, "/jscoverage.js") == 0) {
726 siliconforks 300 const char * s = "jscoverage_isServer = true;\r\n";
727 siliconforks 116 if (HTTPExchange_write_response(exchange, s, strlen(s)) != 0) {
728     HTTPServer_log_err("Warning: error writing to client\n");
729     }
730     }
731     }
732     }
733    
734 siliconforks 179 static void instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output_stream) {
735 siliconforks 189 const struct Resource * resource = get_resource("report.js");
736     Stream_write(output_stream, resource->data, resource->length);
737    
738 siliconforks 125 LOCK(&javascript_mutex);
739 siliconforks 179 jscoverage_instrument_js(id, characters, num_characters, output_stream);
740 siliconforks 125 UNLOCK(&javascript_mutex);
741 siliconforks 116 }
742    
743     static bool is_hop_by_hop_header(const char * h) {
744     /* hop-by-hop headers (RFC 2616 13.5.1) */
745     return strcasecmp(h, HTTP_CONNECTION) == 0 ||
746     strcasecmp(h, "Keep-Alive") == 0 ||
747     strcasecmp(h, HTTP_PROXY_AUTHENTICATE) == 0 ||
748     strcasecmp(h, HTTP_PROXY_AUTHORIZATION) == 0 ||
749     strcasecmp(h, HTTP_TE) == 0 ||
750     strcasecmp(h, HTTP_TRAILER) == 0 ||
751     strcasecmp(h, HTTP_TRANSFER_ENCODING) == 0 ||
752     strcasecmp(h, HTTP_UPGRADE) == 0;
753     }
754    
755     static void add_via_header(HTTPMessage * message, const char * version) {
756     char * value;
757     xasprintf(&value, "%s jscoverage-server", version);
758     HTTPMessage_add_header(message, HTTP_VIA, value);
759     free(value);
760     }
761    
762     static int copy_http_message_body(HTTPMessage * from, HTTPMessage * to) __attribute__((warn_unused_result));
763    
764     static int copy_http_message_body(HTTPMessage * from, HTTPMessage * to) {
765     uint8_t * buffer[8192];
766     for (;;) {
767     size_t bytes_read;
768     int result = HTTPMessage_read_message_body(from, buffer, 8192, &bytes_read);
769     if (result != 0) {
770     return result;
771     }
772     if (bytes_read == 0) {
773     return 0;
774     }
775     result = HTTPMessage_write(to, buffer, bytes_read);
776     if (result != 0) {
777     return result;
778     }
779     }
780     }
781    
782     static void handle_proxy_request(HTTPExchange * client_exchange) {
783     HTTPConnection * server_connection = NULL;
784     HTTPExchange * server_exchange = NULL;
785    
786     const char * abs_path = HTTPExchange_get_abs_path(client_exchange);
787     if (str_starts_with(abs_path, "/jscoverage")) {
788     handle_jscoverage_request(client_exchange);
789     return;
790     }
791    
792     const char * host = HTTPExchange_get_host(client_exchange);
793     uint16_t port = HTTPExchange_get_port(client_exchange);
794    
795     /* create a new connection */
796     server_connection = HTTPConnection_new_client(host, port);
797     if (server_connection == NULL) {
798     send_response(client_exchange, 504, "Could not connect to server\n");
799     goto done;
800     }
801    
802     /* create a new exchange */
803     server_exchange = HTTPExchange_new(server_connection);
804 siliconforks 478
805 siliconforks 116 HTTPExchange_set_method(server_exchange, HTTPExchange_get_method(client_exchange));
806 siliconforks 478
807     /* don't send full URI to origin server - just send abs_path and query */
808     const char * query = HTTPExchange_get_query(client_exchange);
809     char * origin_server_request_uri;
810     if (query == NULL) {
811     origin_server_request_uri = xstrdup(abs_path);
812     }
813     else {
814     size_t abs_path_length = strlen(abs_path);
815     size_t query_length = strlen(query);
816     size_t origin_server_request_uri_length = addst(abs_path_length, query_length);
817     origin_server_request_uri_length = addst(origin_server_request_uri_length, 2);
818     origin_server_request_uri = xmalloc(origin_server_request_uri_length);
819     strcpy(origin_server_request_uri, abs_path);
820     origin_server_request_uri[abs_path_length] = '?';
821     strcpy(origin_server_request_uri + abs_path_length + 1, query);
822     }
823     HTTPExchange_set_request_uri(server_exchange, origin_server_request_uri);
824     free(origin_server_request_uri);
825    
826 siliconforks 116 for (const HTTPHeader * h = HTTPExchange_get_request_headers(client_exchange); h != NULL; h = h->next) {
827     if (strcasecmp(h->name, HTTP_TRAILER) == 0 || strcasecmp(h->name, HTTP_TRANSFER_ENCODING) == 0) {
828     /* do nothing: we want to keep this header */
829     }
830     else if (is_hop_by_hop_header(h->name) ||
831     strcasecmp(h->name, HTTP_ACCEPT_ENCODING) == 0 ||
832     strcasecmp(h->name, HTTP_RANGE) == 0) {
833     continue;
834     }
835     HTTPExchange_add_request_header(server_exchange, h->name, h->value);
836     }
837     add_via_header(HTTPExchange_get_request_message(server_exchange), HTTPExchange_get_request_http_version(client_exchange));
838    
839     /* send the request */
840     if (HTTPExchange_write_request_headers(server_exchange) != 0) {
841     send_response(client_exchange, 502, "Could not write to server\n");
842     goto done;
843     }
844    
845     /* handle POST or PUT */
846     if (HTTPExchange_request_has_body(client_exchange)) {
847     HTTPMessage * client_request = HTTPExchange_get_request_message(client_exchange);
848     HTTPMessage * server_request = HTTPExchange_get_request_message(server_exchange);
849     if (copy_http_message_body(client_request, server_request) != 0) {
850     send_response(client_exchange, 400, "Error copying request body from client to server\n");
851     goto done;
852     }
853     }
854    
855     if (HTTPExchange_flush_request(server_exchange) != 0) {
856     send_response(client_exchange, 502, "Could not write to server\n");
857     goto done;
858     }
859    
860     /* receive the response */
861     if (HTTPExchange_read_response_headers(server_exchange) != 0) {
862     send_response(client_exchange, 502, "Could not read headers from server\n");
863     goto done;
864     }
865    
866     HTTPExchange_set_status_code(client_exchange, HTTPExchange_get_status_code(server_exchange));
867    
868 siliconforks 480 if (HTTPExchange_response_has_body(server_exchange) && should_instrument_request(server_exchange, HTTPExchange_get_request_uri(client_exchange))) {
869 siliconforks 116 /* needs instrumentation */
870     Stream * input_stream = Stream_new(0);
871     if (HTTPExchange_read_entire_response_entity_body(server_exchange, input_stream) != 0) {
872     Stream_delete(input_stream);
873     send_response(client_exchange, 502, "Could not read body from server\n");
874     goto done;
875     }
876    
877     const char * request_uri = HTTPExchange_get_request_uri(client_exchange);
878 siliconforks 177 char * encoding = HTTPMessage_get_charset(HTTPExchange_get_response_message(server_exchange));
879     if (encoding == NULL) {
880 siliconforks 179 encoding = xstrdup(jscoverage_encoding);
881 siliconforks 177 }
882 siliconforks 179 uint16_t * characters;
883     size_t num_characters;
884     int result = jscoverage_bytes_to_characters(encoding, input_stream->data, input_stream->length, &characters, &num_characters);
885     free(encoding);
886     Stream_delete(input_stream);
887     if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
888 siliconforks 312 send_response(client_exchange, 500, "Encoding not supported\n");
889 siliconforks 179 goto done;
890 siliconforks 177 }
891 siliconforks 179 else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
892     send_response(client_exchange, 502, "Error decoding response\n");
893     goto done;
894     }
895 siliconforks 116
896 siliconforks 179 Stream * output_stream = Stream_new(0);
897     instrument_js(request_uri, characters, num_characters, output_stream);
898    
899 siliconforks 116 /* send the headers to the client */
900     for (const HTTPHeader * h = HTTPExchange_get_response_headers(server_exchange); h != NULL; h = h->next) {
901     if (is_hop_by_hop_header(h->name) || strcasecmp(h->name, HTTP_CONTENT_LENGTH) == 0) {
902     continue;
903     }
904 siliconforks 278 else if (strcasecmp(h->name, HTTP_CONTENT_TYPE) == 0) {
905     HTTPExchange_add_response_header(client_exchange, HTTP_CONTENT_TYPE, "text/javascript; charset=ISO-8859-1");
906     continue;
907     }
908 siliconforks 116 HTTPExchange_add_response_header(client_exchange, h->name, h->value);
909     }
910     add_via_header(HTTPExchange_get_response_message(client_exchange), HTTPExchange_get_response_http_version(server_exchange));
911     HTTPExchange_set_response_content_length(client_exchange, output_stream->length);
912    
913     /* send the instrumented code to the client */
914     if (HTTPExchange_write_response(client_exchange, output_stream->data, output_stream->length) != 0) {
915     HTTPServer_log_err("Warning: error writing to client\n");
916     }
917    
918 siliconforks 179 /* characters go on the cache */
919 siliconforks 116 /*
920 siliconforks 179 free(characters);
921 siliconforks 116 */
922     Stream_delete(output_stream);
923 siliconforks 179 add_cached_source(request_uri, characters, num_characters);
924 siliconforks 116 }
925     else {
926     /* does not need instrumentation */
927    
928     /* send the headers to the client */
929     for (const HTTPHeader * h = HTTPExchange_get_response_headers(server_exchange); h != NULL; h = h->next) {
930     if (strcasecmp(h->name, HTTP_TRAILER) == 0 || strcasecmp(h->name, HTTP_TRANSFER_ENCODING) == 0) {
931     /* do nothing: we want to keep this header */
932     }
933     else if (is_hop_by_hop_header(h->name)) {
934     continue;
935     }
936     HTTPExchange_add_response_header(client_exchange, h->name, h->value);
937     }
938     add_via_header(HTTPExchange_get_response_message(client_exchange), HTTPExchange_get_response_http_version(server_exchange));
939    
940     if (HTTPExchange_write_response_headers(client_exchange) != 0) {
941     HTTPServer_log_err("Warning: error writing to client\n");
942     goto done;
943     }
944    
945     if (HTTPExchange_response_has_body(server_exchange)) {
946     /* read the body from the server and send it to the client */
947     HTTPMessage * client_response = HTTPExchange_get_response_message(client_exchange);
948     HTTPMessage * server_response = HTTPExchange_get_response_message(server_exchange);
949     if (copy_http_message_body(server_response, client_response) != 0) {
950     HTTPServer_log_err("Warning: error copying response body from server to client\n");
951     goto done;
952     }
953     }
954     }
955    
956     done:
957     if (server_exchange != NULL) {
958     HTTPExchange_delete(server_exchange);
959     }
960     if (server_connection != NULL) {
961     if (HTTPConnection_delete(server_connection) != 0) {
962     HTTPServer_log_err("Warning: error closing connection to server\n");
963     }
964     }
965     }
966    
967     static void handle_local_request(HTTPExchange * exchange) {
968     /* add the `Server' response-header (RFC 2616 14.38, 3.8) */
969     HTTPExchange_add_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION);
970    
971 siliconforks 253 char * decoded_path = NULL;
972 siliconforks 116 char * filesystem_path = NULL;
973    
974     const char * abs_path = HTTPExchange_get_abs_path(exchange);
975     assert(*abs_path != '\0');
976    
977 siliconforks 253 decoded_path = decode_uri_component(abs_path);
978    
979     if (str_starts_with(decoded_path, "/jscoverage")) {
980 siliconforks 116 handle_jscoverage_request(exchange);
981     goto done;
982     }
983    
984 siliconforks 253 if (strstr(decoded_path, "..") != NULL) {
985 siliconforks 116 send_response(exchange, 403, "Forbidden\n");
986     goto done;
987     }
988    
989 siliconforks 253 filesystem_path = make_path(document_root, decoded_path + 1);
990 siliconforks 125 size_t filesystem_path_length = strlen(filesystem_path);
991     if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') {
992     /* stat on Windows doesn't work with trailing slash */
993     filesystem_path[filesystem_path_length - 1] = '\0';
994     }
995 siliconforks 116
996     struct stat buf;
997     if (stat(filesystem_path, &buf) == -1) {
998     send_response(exchange, 404, "Not found\n");
999     goto done;
1000     }
1001    
1002     if (S_ISDIR(buf.st_mode)) {
1003 siliconforks 125 if (abs_path[strlen(abs_path) - 1] != '/') {
1004 siliconforks 116 const char * request_uri = HTTPExchange_get_request_uri(exchange);
1005     char * uri = xmalloc(strlen(request_uri) + 2);
1006     strcpy(uri, request_uri);
1007     strcat(uri, "/");
1008     HTTPExchange_add_response_header(exchange, "Location", uri);
1009     free(uri);
1010     send_response(exchange, 301, "Moved permanently\n");
1011     goto done;
1012     }
1013    
1014     DIR * d = opendir(filesystem_path);
1015     if (d == NULL) {
1016     send_response(exchange, 404, "Not found\n");
1017     goto done;
1018     }
1019    
1020     struct dirent * entry;
1021     while ((entry = readdir(d)) != NULL) {
1022     char * href = encode_uri_component(entry->d_name);
1023     char * html_href = encode_html(href);
1024     char * link = encode_html(entry->d_name);
1025     char * directory_entry;
1026     xasprintf(&directory_entry, "<a href=\"%s\">%s</a><br>\n", html_href, link);
1027     if (HTTPExchange_write_response(exchange, directory_entry, strlen(directory_entry)) != 0) {
1028     HTTPServer_log_err("Warning: error writing to client\n");
1029     }
1030     free(directory_entry);
1031     free(href);
1032     free(html_href);
1033     free(link);
1034     }
1035     closedir(d);
1036     }
1037     else if (S_ISREG(buf.st_mode)) {
1038 siliconforks 125 FILE * f = fopen(filesystem_path, "rb");
1039 siliconforks 116 if (f == NULL) {
1040     send_response(exchange, 404, "Not found\n");
1041     goto done;
1042     }
1043    
1044 siliconforks 278 /*
1045     When do we send a charset with Content-Type?
1046     if Content-Type is "text" or "application"
1047     if instrumented JavaScript
1048     use Content-Type: application/javascript; charset=ISO-8859-1
1049     else if --encoding is given
1050     use that encoding
1051     else
1052     send no charset
1053     else
1054     send no charset
1055     */
1056 siliconforks 116 const char * content_type = get_content_type(filesystem_path);
1057 siliconforks 146 if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) {
1058 siliconforks 278 HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, "text/javascript; charset=ISO-8859-1");
1059    
1060 siliconforks 116 Stream * input_stream = Stream_new(0);
1061     Stream_write_file_contents(input_stream, f);
1062    
1063 siliconforks 179 uint16_t * characters;
1064     size_t num_characters;
1065     int result = jscoverage_bytes_to_characters(jscoverage_encoding, input_stream->data, input_stream->length, &characters, &num_characters);
1066     Stream_delete(input_stream);
1067 siliconforks 116
1068 siliconforks 179 if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
1069     send_response(exchange, 500, "Encoding not supported\n");
1070     goto done;
1071     }
1072     else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
1073     send_response(exchange, 500, "Error decoding JavaScript file\n");
1074     goto done;
1075     }
1076    
1077     Stream * output_stream = Stream_new(0);
1078     instrument_js(abs_path, characters, num_characters, output_stream);
1079     free(characters);
1080    
1081 siliconforks 116 if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) {
1082     HTTPServer_log_err("Warning: error writing to client\n");
1083     }
1084     Stream_delete(output_stream);
1085     }
1086     else {
1087 siliconforks 278 /* send the Content-Type with charset if necessary */
1088     if (specified_encoding != NULL && (str_starts_with(content_type, "text/") || str_starts_with(content_type, "application/"))) {
1089     char * content_type_with_charset = NULL;
1090     xasprintf(&content_type_with_charset, "%s; charset=%s", content_type, specified_encoding);
1091     HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type_with_charset);
1092     free(content_type_with_charset);
1093     }
1094     else {
1095     HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type);
1096     }
1097    
1098 siliconforks 116 char buffer[8192];
1099     size_t bytes_read;
1100     while ((bytes_read = fread(buffer, 1, 8192, f)) > 0) {
1101     if (HTTPExchange_write_response(exchange, buffer, bytes_read) != 0) {
1102     HTTPServer_log_err("Warning: error writing to client\n");
1103     }
1104     }
1105     }
1106     fclose(f);
1107     }
1108     else {
1109     send_response(exchange, 404, "Not found\n");
1110     goto done;
1111     }
1112    
1113     done:
1114     free(filesystem_path);
1115 siliconforks 253 free(decoded_path);
1116 siliconforks 116 }
1117    
1118     static void handler(HTTPExchange * exchange) {
1119     if (verbose) {
1120     HTTPServer_log_out("%s", HTTPExchange_get_request_line(exchange));
1121     }
1122    
1123     if (proxy) {
1124     handle_proxy_request(exchange);
1125     }
1126     else {
1127     handle_local_request(exchange);
1128     }
1129     }
1130    
1131     int main(int argc, char ** argv) {
1132     program = "jscoverage-server";
1133    
1134     const char * ip_address = "127.0.0.1";
1135     const char * port = "8080";
1136     int shutdown = 0;
1137    
1138     no_instrument = xnew(const char *, argc - 1);
1139    
1140     for (int i = 1; i < argc; i++) {
1141     if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1142     copy_resource_to_stream("jscoverage-server-help.txt", stdout);
1143     exit(EXIT_SUCCESS);
1144     }
1145     else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
1146 siliconforks 311 version();
1147 siliconforks 116 }
1148     else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
1149     verbose = 1;
1150     }
1151    
1152     else if (strcmp(argv[i], "--report-dir") == 0) {
1153     i++;
1154     if (i == argc) {
1155 siliconforks 191 fatal_command_line("--report-dir: option requires an argument");
1156 siliconforks 116 }
1157     report_directory = argv[i];
1158     }
1159     else if (strncmp(argv[i], "--report-dir=", 13) == 0) {
1160     report_directory = argv[i] + 13;
1161     }
1162    
1163     else if (strcmp(argv[i], "--document-root") == 0) {
1164     i++;
1165     if (i == argc) {
1166 siliconforks 191 fatal_command_line("--document-root: option requires an argument");
1167 siliconforks 116 }
1168     document_root = argv[i];
1169     }
1170     else if (strncmp(argv[i], "--document-root=", 16) == 0) {
1171     document_root = argv[i] + 16;
1172     }
1173    
1174 siliconforks 174 else if (strcmp(argv[i], "--encoding") == 0) {
1175     i++;
1176     if (i == argc) {
1177 siliconforks 191 fatal_command_line("--encoding: option requires an argument");
1178 siliconforks 174 }
1179     jscoverage_encoding = argv[i];
1180 siliconforks 278 specified_encoding = jscoverage_encoding;
1181 siliconforks 174 }
1182     else if (strncmp(argv[i], "--encoding=", 11) == 0) {
1183     jscoverage_encoding = argv[i] + 11;
1184 siliconforks 278 specified_encoding = jscoverage_encoding;
1185 siliconforks 174 }
1186    
1187 siliconforks 116 else if (strcmp(argv[i], "--ip-address") == 0) {
1188     i++;
1189     if (i == argc) {
1190 siliconforks 191 fatal_command_line("--ip-address: option requires an argument");
1191 siliconforks 116 }
1192     ip_address = argv[i];
1193     }
1194     else if (strncmp(argv[i], "--ip-address=", 13) == 0) {
1195     ip_address = argv[i] + 13;
1196     }
1197    
1198 siliconforks 339 else if (strcmp(argv[i], "--js-version") == 0) {
1199     i++;
1200     if (i == argc) {
1201     fatal_command_line("--js-version: option requires an argument");
1202     }
1203     jscoverage_set_js_version(argv[i]);
1204     }
1205     else if (strncmp(argv[i], "--js-version=", 13) == 0) {
1206     jscoverage_set_js_version(argv[i] + 13);
1207     }
1208    
1209 siliconforks 179 else if (strcmp(argv[i], "--no-highlight") == 0) {
1210     jscoverage_highlight = false;
1211     }
1212    
1213 siliconforks 116 else if (strcmp(argv[i], "--no-instrument") == 0) {
1214     i++;
1215     if (i == argc) {
1216 siliconforks 191 fatal_command_line("--no-instrument: option requires an argument");
1217 siliconforks 116 }
1218     no_instrument[num_no_instrument] = argv[i];
1219     num_no_instrument++;
1220     }
1221     else if (strncmp(argv[i], "--no-instrument=", 16) == 0) {
1222     no_instrument[num_no_instrument] = argv[i] + 16;
1223     num_no_instrument++;
1224     }
1225    
1226     else if (strcmp(argv[i], "--port") == 0) {
1227     i++;
1228     if (i == argc) {
1229 siliconforks 191 fatal_command_line("--port: option requires an argument");
1230 siliconforks 116 }
1231     port = argv[i];
1232     }
1233     else if (strncmp(argv[i], "--port=", 7) == 0) {
1234     port = argv[i] + 7;
1235     }
1236    
1237     else if (strcmp(argv[i], "--proxy") == 0) {
1238     proxy = 1;
1239     }
1240    
1241     else if (strcmp(argv[i], "--shutdown") == 0) {
1242     shutdown = 1;
1243     }
1244    
1245     else if (strncmp(argv[i], "-", 1) == 0) {
1246 siliconforks 191 fatal_command_line("unrecognized option `%s'", argv[i]);
1247 siliconforks 116 }
1248     else {
1249 siliconforks 191 fatal_command_line("too many arguments");
1250 siliconforks 116 }
1251     }
1252    
1253     /* check the port */
1254     char * end;
1255     unsigned long numeric_port = strtoul(port, &end, 10);
1256     if (*end != '\0') {
1257 siliconforks 191 fatal_command_line("--port: option must be an integer");
1258 siliconforks 116 }
1259     if (numeric_port > UINT16_MAX) {
1260 siliconforks 191 fatal_command_line("--port: option must be 16 bits");
1261 siliconforks 116 }
1262    
1263 siliconforks 475 /* check the document root exists and is a directory */
1264     struct stat buf;
1265     xstat(document_root, &buf);
1266     if (! S_ISDIR(buf.st_mode)) {
1267     fatal_command_line("--document-root: option must be a directory");
1268     }
1269    
1270 siliconforks 116 /* is this a shutdown? */
1271     if (shutdown) {
1272 siliconforks 125 #ifdef __MINGW32__
1273     WSADATA data;
1274     if (WSAStartup(MAKEWORD(1, 1), &data) != 0) {
1275 siliconforks 134 fatal("could not start Winsock");
1276 siliconforks 125 }
1277     #endif
1278    
1279 siliconforks 116 /* INADDR_LOOPBACK */
1280     HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port);
1281     if (connection == NULL) {
1282     fatal("could not connect to server");
1283     }
1284     HTTPExchange * exchange = HTTPExchange_new(connection);
1285     HTTPExchange_set_method(exchange, "POST");
1286     HTTPExchange_set_request_uri(exchange, "/jscoverage-shutdown");
1287     if (HTTPExchange_write_request_headers(exchange) != 0) {
1288     fatal("could not write request headers to server");
1289     }
1290     if (HTTPExchange_read_response_headers(exchange) != 0) {
1291     fatal("could not read response headers from server");
1292     }
1293     Stream * stream = Stream_new(0);
1294     if (HTTPExchange_read_entire_response_entity_body(exchange, stream) != 0) {
1295     fatal("could not read response body from server");
1296     }
1297     fwrite(stream->data, 1, stream->length, stdout);
1298     Stream_delete(stream);
1299     HTTPExchange_delete(exchange);
1300     if (HTTPConnection_delete(connection) != 0) {
1301     fatal("could not close connection with server");
1302     }
1303     exit(EXIT_SUCCESS);
1304     }
1305    
1306     jscoverage_init();
1307    
1308 siliconforks 125 #ifndef __MINGW32__
1309 siliconforks 116 /* handle broken pipe */
1310     signal(SIGPIPE, SIG_IGN);
1311 siliconforks 125 #endif
1312 siliconforks 116
1313 siliconforks 125 #ifdef __MINGW32__
1314     InitializeCriticalSection(&javascript_mutex);
1315     InitializeCriticalSection(&source_cache_mutex);
1316     #endif
1317    
1318 siliconforks 116 if (verbose) {
1319     printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port);
1320 siliconforks 125 fflush(stdout);
1321 siliconforks 116 }
1322     HTTPServer_run(ip_address, (uint16_t) numeric_port, handler);
1323     if (verbose) {
1324     printf("Stopping HTTP server\n");
1325 siliconforks 125 fflush(stdout);
1326 siliconforks 116 }
1327    
1328     jscoverage_cleanup();
1329    
1330     free(no_instrument);
1331    
1332 siliconforks 125 LOCK(&source_cache_mutex);
1333 siliconforks 116 while (source_cache != NULL) {
1334     SourceCache * p = source_cache;
1335     source_cache = source_cache->next;
1336     free(p->url);
1337 siliconforks 179 free(p->characters);
1338 siliconforks 116 free(p);
1339     }
1340 siliconforks 125 UNLOCK(&source_cache_mutex);
1341 siliconforks 116
1342     return 0;
1343     }

  ViewVC Help
Powered by ViewVC 1.1.24