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

Annotation of /trunk/jscoverage-server.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 583 - (hide annotations)
Sat Sep 11 20:20:14 2010 UTC (8 years, 10 months ago) by siliconforks
File MIME type: text/plain
File size: 41723 byte(s)
Allow shutdown of server even if --ip-address was specified as a non-loopback address, as long as client has same address.

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

  ViewVC Help
Powered by ViewVC 1.1.24