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

Diff of /trunk/jscoverage-server.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 179 by siliconforks, Sun Sep 21 18:35:21 2008 UTC revision 339 by siliconforks, Fri Oct 24 16:13:57 2008 UTC
# Line 38  Line 38 
38  #include "stream.h"  #include "stream.h"
39  #include "util.h"  #include "util.h"
40    
41    static const char * specified_encoding = NULL;
42  const char * jscoverage_encoding = "ISO-8859-1";  const char * jscoverage_encoding = "ISO-8859-1";
43  bool jscoverage_highlight = true;  bool jscoverage_highlight = true;
44    
# Line 237  Line 238 
238    return result;    return result;
239  }  }
240    
241    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        return c - 'A' + 10;
247      }
248      else if ('a' <= c && c <= 'f') {
249        return c - 'a' + 10;
250      }
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  static const char * get_entity(char c) {  static const char * get_entity(char c) {
280    switch(c) {    switch(c) {
281    case '<':    case '<':
# Line 405  Line 444 
444      case '\t':      case '\t':
445        fputs("\\t", f);        fputs("\\t", f);
446        break;        break;
447        /* IE doesn't support this */
448        /*
449      case '\v':      case '\v':
450        fputs("\\v", f);        fputs("\\v", f);
451        break;        break;
452        */
453      case '"':      case '"':
454        fputs("\\\"", f);        fputs("\\\"", f);
455        break;        break;
# Line 528  Line 570 
570    
571  static int write_json(Coverage * coverage, const char * path) {  static int write_json(Coverage * coverage, const char * path) {
572    /* write the JSON */    /* write the JSON */
573    FILE * f = fopen(path, "w");    FILE * f = fopen(path, "wb");
574    if (f == NULL) {    if (f == NULL) {
575      return -1;      return -1;
576    }    }
# Line 576  Line 618 
618      }      }
619    
620      mkdir_if_necessary(report_directory);      mkdir_if_necessary(report_directory);
621      char * path = make_path(report_directory, "jscoverage.json");      char * current_report_directory;
622        if (str_starts_with(abs_path, "/jscoverage-store/") && abs_path[18] != '\0') {
623          char * dir = decode_uri_component(abs_path + 18);
624          current_report_directory = make_path(report_directory, dir);
625          free(dir);
626        }
627        else {
628          current_report_directory = xstrdup(report_directory);
629        }
630        mkdir_if_necessary(current_report_directory);
631        char * path = make_path(current_report_directory, "jscoverage.json");
632    
633      /* check if the JSON file exists */      /* check if the JSON file exists */
634      struct stat buf;      struct stat buf;
635      if (stat(path, &buf) == 0) {      if (stat(path, &buf) == 0) {
636        /* it exists: merge */        /* it exists: merge */
637        FILE * f = fopen(path, "r");        FILE * f = fopen(path, "rb");
638        if (f == NULL) {        if (f == NULL) {
639          result = 1;          result = 1;
640        }        }
# Line 593  Line 645 
645          }          }
646        }        }
647        if (result != 0) {        if (result != 0) {
648            free(current_report_directory);
649          free(path);          free(path);
650          Coverage_delete(coverage);          Coverage_delete(coverage);
651          send_response(exchange, 500, "Could not merge with existing coverage data\n");          send_response(exchange, 500, "Could not merge with existing coverage data\n");
# Line 604  Line 657 
657      free(path);      free(path);
658      Coverage_delete(coverage);      Coverage_delete(coverage);
659      if (result != 0) {      if (result != 0) {
660          free(current_report_directory);
661        send_response(exchange, 500, "Could not write coverage data\n");        send_response(exchange, 500, "Could not write coverage data\n");
662        return;        return;
663      }      }
664    
665      /* copy other files */      /* copy other files */
666      jscoverage_copy_resources(report_directory);      jscoverage_copy_resources(current_report_directory);
667      path = make_path(report_directory, "jscoverage.js");      path = make_path(current_report_directory, "jscoverage.js");
668        free(current_report_directory);
669      FILE * f = fopen(path, "ab");      FILE * f = fopen(path, "ab");
670      free(path);      free(path);
671      if (f == NULL) {      if (f == NULL) {
# Line 668  Line 723 
723  }  }
724    
725  static void instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output_stream) {  static void instrument_js(const char * id, const uint16_t * characters, size_t num_characters, Stream * output_stream) {
726      const struct Resource * resource = get_resource("report.js");
727      Stream_write(output_stream, resource->data, resource->length);
728    
729    LOCK(&javascript_mutex);    LOCK(&javascript_mutex);
730    jscoverage_instrument_js(id, characters, num_characters, output_stream);    jscoverage_instrument_js(id, characters, num_characters, output_stream);
731    UNLOCK(&javascript_mutex);    UNLOCK(&javascript_mutex);
   
   const struct Resource * resource = get_resource("report.js");  
   Stream_write(output_stream, resource->data, resource->length);  
732  }  }
733    
734  static bool is_hop_by_hop_header(const char * h) {  static bool is_hop_by_hop_header(const char * h) {
# Line 801  Line 856 
856      free(encoding);      free(encoding);
857      Stream_delete(input_stream);      Stream_delete(input_stream);
858      if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {      if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
859        free(characters);        send_response(client_exchange, 500, "Encoding not supported\n");
       send_response(client_exchange, 502, "Encoding not supported\n");  
860        goto done;        goto done;
861      }      }
862      else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {      else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
       free(characters);  
863        send_response(client_exchange, 502, "Error decoding response\n");        send_response(client_exchange, 502, "Error decoding response\n");
864        goto done;        goto done;
865      }      }
# Line 819  Line 872 
872        if (is_hop_by_hop_header(h->name) || strcasecmp(h->name, HTTP_CONTENT_LENGTH) == 0) {        if (is_hop_by_hop_header(h->name) || strcasecmp(h->name, HTTP_CONTENT_LENGTH) == 0) {
873          continue;          continue;
874        }        }
875          else if (strcasecmp(h->name, HTTP_CONTENT_TYPE) == 0) {
876            HTTPExchange_add_response_header(client_exchange, HTTP_CONTENT_TYPE, "text/javascript; charset=ISO-8859-1");
877            continue;
878          }
879        HTTPExchange_add_response_header(client_exchange, h->name, h->value);        HTTPExchange_add_response_header(client_exchange, h->name, h->value);
880      }      }
881      add_via_header(HTTPExchange_get_response_message(client_exchange), HTTPExchange_get_response_http_version(server_exchange));      add_via_header(HTTPExchange_get_response_message(client_exchange), HTTPExchange_get_response_http_version(server_exchange));
# Line 882  Line 939 
939    /* add the `Server' response-header (RFC 2616 14.38, 3.8) */    /* add the `Server' response-header (RFC 2616 14.38, 3.8) */
940    HTTPExchange_add_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION);    HTTPExchange_add_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION);
941    
942      char * decoded_path = NULL;
943    char * filesystem_path = NULL;    char * filesystem_path = NULL;
944    
945    const char * abs_path = HTTPExchange_get_abs_path(exchange);    const char * abs_path = HTTPExchange_get_abs_path(exchange);
946    assert(*abs_path != '\0');    assert(*abs_path != '\0');
947    
948    if (str_starts_with(abs_path, "/jscoverage")) {    decoded_path = decode_uri_component(abs_path);
949    
950      if (str_starts_with(decoded_path, "/jscoverage")) {
951      handle_jscoverage_request(exchange);      handle_jscoverage_request(exchange);
952      goto done;      goto done;
953    }    }
954    
955    if (strstr(abs_path, "..") != NULL) {    if (strstr(decoded_path, "..") != NULL) {
956      send_response(exchange, 403, "Forbidden\n");      send_response(exchange, 403, "Forbidden\n");
957      goto done;      goto done;
958    }    }
959    
960    filesystem_path = make_path(document_root, abs_path + 1);    filesystem_path = make_path(document_root, decoded_path + 1);
961    size_t filesystem_path_length = strlen(filesystem_path);    size_t filesystem_path_length = strlen(filesystem_path);
962    if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') {    if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') {
963      /* stat on Windows doesn't work with trailing slash */      /* stat on Windows doesn't work with trailing slash */
# Line 952  Line 1012 
1012        goto done;        goto done;
1013      }      }
1014    
1015        /*
1016        When do we send a charset with Content-Type?
1017        if Content-Type is "text" or "application"
1018          if instrumented JavaScript
1019            use Content-Type: application/javascript; charset=ISO-8859-1
1020          else if --encoding is given
1021            use that encoding
1022          else
1023            send no charset
1024        else
1025          send no charset
1026        */
1027      const char * content_type = get_content_type(filesystem_path);      const char * content_type = get_content_type(filesystem_path);
     HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type);  
1028      if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) {      if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) {
1029          HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, "text/javascript; charset=ISO-8859-1");
1030    
1031        Stream * input_stream = Stream_new(0);        Stream * input_stream = Stream_new(0);
1032        Stream_write_file_contents(input_stream, f);        Stream_write_file_contents(input_stream, f);
1033    
# Line 964  Line 1037 
1037        Stream_delete(input_stream);        Stream_delete(input_stream);
1038    
1039        if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {        if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
         free(characters);  
1040          send_response(exchange, 500, "Encoding not supported\n");          send_response(exchange, 500, "Encoding not supported\n");
1041          goto done;          goto done;
1042        }        }
1043        else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {        else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
         free(characters);  
1044          send_response(exchange, 500, "Error decoding JavaScript file\n");          send_response(exchange, 500, "Error decoding JavaScript file\n");
1045          goto done;          goto done;
1046        }        }
# Line 984  Line 1055 
1055        Stream_delete(output_stream);        Stream_delete(output_stream);
1056      }      }
1057      else {      else {
1058          /* send the Content-Type with charset if necessary */
1059          if (specified_encoding != NULL && (str_starts_with(content_type, "text/") || str_starts_with(content_type, "application/"))) {
1060            char * content_type_with_charset = NULL;
1061            xasprintf(&content_type_with_charset, "%s; charset=%s", content_type, specified_encoding);
1062            HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type_with_charset);
1063            free(content_type_with_charset);
1064          }
1065          else {
1066            HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type);
1067          }
1068    
1069        char buffer[8192];        char buffer[8192];
1070        size_t bytes_read;        size_t bytes_read;
1071        while ((bytes_read = fread(buffer, 1, 8192, f)) > 0) {        while ((bytes_read = fread(buffer, 1, 8192, f)) > 0) {
# Line 1001  Line 1083 
1083    
1084  done:  done:
1085    free(filesystem_path);    free(filesystem_path);
1086      free(decoded_path);
1087  }  }
1088    
1089  static void handler(HTTPExchange * exchange) {  static void handler(HTTPExchange * exchange) {
# Line 1031  Line 1114 
1114        exit(EXIT_SUCCESS);        exit(EXIT_SUCCESS);
1115      }      }
1116      else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {      else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
1117        printf("jscoverage-server %s\n", VERSION);        version();
       exit(EXIT_SUCCESS);  
1118      }      }
1119      else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {      else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
1120        verbose = 1;        verbose = 1;
# Line 1041  Line 1123 
1123      else if (strcmp(argv[i], "--report-dir") == 0) {      else if (strcmp(argv[i], "--report-dir") == 0) {
1124        i++;        i++;
1125        if (i == argc) {        if (i == argc) {
1126          fatal("--report-dir: option requires an argument");          fatal_command_line("--report-dir: option requires an argument");
1127        }        }
1128        report_directory = argv[i];        report_directory = argv[i];
1129      }      }
# Line 1052  Line 1134 
1134      else if (strcmp(argv[i], "--document-root") == 0) {      else if (strcmp(argv[i], "--document-root") == 0) {
1135        i++;        i++;
1136        if (i == argc) {        if (i == argc) {
1137          fatal("--document-root: option requires an argument");          fatal_command_line("--document-root: option requires an argument");
1138        }        }
1139        document_root = argv[i];        document_root = argv[i];
1140      }      }
# Line 1063  Line 1145 
1145      else if (strcmp(argv[i], "--encoding") == 0) {      else if (strcmp(argv[i], "--encoding") == 0) {
1146        i++;        i++;
1147        if (i == argc) {        if (i == argc) {
1148          fatal("--encoding: option requires an argument");          fatal_command_line("--encoding: option requires an argument");
1149        }        }
1150        jscoverage_encoding = argv[i];        jscoverage_encoding = argv[i];
1151          specified_encoding = jscoverage_encoding;
1152      }      }
1153      else if (strncmp(argv[i], "--encoding=", 11) == 0) {      else if (strncmp(argv[i], "--encoding=", 11) == 0) {
1154        jscoverage_encoding = argv[i] + 11;        jscoverage_encoding = argv[i] + 11;
1155          specified_encoding = jscoverage_encoding;
1156      }      }
1157    
1158      else if (strcmp(argv[i], "--ip-address") == 0) {      else if (strcmp(argv[i], "--ip-address") == 0) {
1159        i++;        i++;
1160        if (i == argc) {        if (i == argc) {
1161          fatal("--ip-address: option requires an argument");          fatal_command_line("--ip-address: option requires an argument");
1162        }        }
1163        ip_address = argv[i];        ip_address = argv[i];
1164      }      }
# Line 1082  Line 1166 
1166        ip_address = argv[i] + 13;        ip_address = argv[i] + 13;
1167      }      }
1168    
1169        else if (strcmp(argv[i], "--js-version") == 0) {
1170          i++;
1171          if (i == argc) {
1172            fatal_command_line("--js-version: option requires an argument");
1173          }
1174          jscoverage_set_js_version(argv[i]);
1175        }
1176        else if (strncmp(argv[i], "--js-version=", 13) == 0) {
1177          jscoverage_set_js_version(argv[i] + 13);
1178        }
1179    
1180      else if (strcmp(argv[i], "--no-highlight") == 0) {      else if (strcmp(argv[i], "--no-highlight") == 0) {
1181        jscoverage_highlight = false;        jscoverage_highlight = false;
1182      }      }
# Line 1089  Line 1184 
1184      else if (strcmp(argv[i], "--no-instrument") == 0) {      else if (strcmp(argv[i], "--no-instrument") == 0) {
1185        i++;        i++;
1186        if (i == argc) {        if (i == argc) {
1187          fatal("--no-instrument: option requires an argument");          fatal_command_line("--no-instrument: option requires an argument");
1188        }        }
1189        no_instrument[num_no_instrument] = argv[i];        no_instrument[num_no_instrument] = argv[i];
1190        num_no_instrument++;        num_no_instrument++;
# Line 1102  Line 1197 
1197      else if (strcmp(argv[i], "--port") == 0) {      else if (strcmp(argv[i], "--port") == 0) {
1198        i++;        i++;
1199        if (i == argc) {        if (i == argc) {
1200          fatal("--port: option requires an argument");          fatal_command_line("--port: option requires an argument");
1201        }        }
1202        port = argv[i];        port = argv[i];
1203      }      }
# Line 1119  Line 1214 
1214      }      }
1215    
1216      else if (strncmp(argv[i], "-", 1) == 0) {      else if (strncmp(argv[i], "-", 1) == 0) {
1217        fatal("unrecognized option `%s'", argv[i]);        fatal_command_line("unrecognized option `%s'", argv[i]);
1218      }      }
1219      else {      else {
1220        fatal("too many arguments");        fatal_command_line("too many arguments");
1221      }      }
1222    }    }
1223    
# Line 1130  Line 1225 
1225    char * end;    char * end;
1226    unsigned long numeric_port = strtoul(port, &end, 10);    unsigned long numeric_port = strtoul(port, &end, 10);
1227    if (*end != '\0') {    if (*end != '\0') {
1228      fatal("--port: option must be an integer");      fatal_command_line("--port: option must be an integer");
1229    }    }
1230    if (numeric_port > UINT16_MAX) {    if (numeric_port > UINT16_MAX) {
1231      fatal("--port: option must be 16 bits");      fatal_command_line("--port: option must be 16 bits");
1232    }    }
1233    
1234    /* is this a shutdown? */    /* is this a shutdown? */

Legend:
Removed from v.179  
changed lines
  Added in v.339

  ViewVC Help
Powered by ViewVC 1.1.24