/[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 189 by siliconforks, Tue Sep 23 03:48:37 2008 UTC revision 478 by siliconforks, Thu Oct 8 20:34:59 2009 UTC
# Line 1  Line 1 
1  /*  /*
2      jscoverage-server.c - JSCoverage server main routine      jscoverage-server.c - JSCoverage server main routine
3      Copyright (C) 2008 siliconforks.com      Copyright (C) 2008, 2009 siliconforks.com
4    
5      This program is free software; you can redistribute it and/or modify      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      it under the terms of the GNU General Public License as published by
# 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 242  Line 243 
243      return c - '0';      return c - '0';
244    }    }
245    else if ('A' <= c && c <= 'F') {    else if ('A' <= c && c <= 'F') {
246      return c - 'A';      return c - 'A' + 10;
247    }    }
248    else if ('a' <= c && c <= 'f') {    else if ('a' <= c && c <= 'f') {
249      return c - 'a';      return c - 'a' + 10;
250    }    }
251    else {    else {
252      return 0;      return 0;
# Line 443  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 512  Line 516 
516      else {      else {
517        /* check that the path begins with / */        /* check that the path begins with / */
518        if (file_coverage->id[0] == '/') {        if (file_coverage->id[0] == '/') {
519          char * source_path = make_path(document_root, file_coverage->id + 1);          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          FILE * source_file = fopen(source_path, "rb");          FILE * source_file = fopen(source_path, "rb");
529          free(source_path);          free(source_path);
530          if (source_file == NULL) {          if (source_file == NULL) {
# Line 559  Line 571 
571      }      }
572      fputc(']', f);      fputc(']', f);
573    }    }
574    done:
575    fputc('}', f);    fputc('}', f);
576  }  }
577    
# Line 566  Line 579 
579    
580  static int write_json(Coverage * coverage, const char * path) {  static int write_json(Coverage * coverage, const char * path) {
581    /* write the JSON */    /* write the JSON */
582    FILE * f = fopen(path, "w");    FILE * f = fopen(path, "wb");
583    if (f == NULL) {    if (f == NULL) {
584      return -1;      return -1;
585    }    }
# Line 630  Line 643 
643      struct stat buf;      struct stat buf;
644      if (stat(path, &buf) == 0) {      if (stat(path, &buf) == 0) {
645        /* it exists: merge */        /* it exists: merge */
646        FILE * f = fopen(path, "r");        FILE * f = fopen(path, "rb");
647        if (f == NULL) {        if (f == NULL) {
648          result = 1;          result = 1;
649        }        }
# Line 788  Line 801 
801    
802    /* create a new exchange */    /* create a new exchange */
803    server_exchange = HTTPExchange_new(server_connection);    server_exchange = HTTPExchange_new(server_connection);
804    
805    HTTPExchange_set_method(server_exchange, HTTPExchange_get_method(client_exchange));    HTTPExchange_set_method(server_exchange, HTTPExchange_get_method(client_exchange));
806    HTTPExchange_set_request_uri(server_exchange, HTTPExchange_get_request_uri(client_exchange));  
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    for (const HTTPHeader * h = HTTPExchange_get_request_headers(client_exchange); h != NULL; h = h->next) {    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) {      if (strcasecmp(h->name, HTTP_TRAILER) == 0 || strcasecmp(h->name, HTTP_TRANSFER_ENCODING) == 0) {
828        /* do nothing: we want to keep this header */        /* do nothing: we want to keep this header */
# Line 852  Line 885 
885      free(encoding);      free(encoding);
886      Stream_delete(input_stream);      Stream_delete(input_stream);
887      if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {      if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
888        free(characters);        send_response(client_exchange, 500, "Encoding not supported\n");
       send_response(client_exchange, 502, "Encoding not supported\n");  
889        goto done;        goto done;
890      }      }
891      else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {      else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
       free(characters);  
892        send_response(client_exchange, 502, "Error decoding response\n");        send_response(client_exchange, 502, "Error decoding response\n");
893        goto done;        goto done;
894      }      }
# Line 870  Line 901 
901        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) {
902          continue;          continue;
903        }        }
904          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        HTTPExchange_add_response_header(client_exchange, h->name, h->value);        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));      add_via_header(HTTPExchange_get_response_message(client_exchange), HTTPExchange_get_response_http_version(server_exchange));
# Line 933  Line 968 
968    /* add the `Server' response-header (RFC 2616 14.38, 3.8) */    /* add the `Server' response-header (RFC 2616 14.38, 3.8) */
969    HTTPExchange_add_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION);    HTTPExchange_add_response_header(exchange, HTTP_SERVER, "jscoverage-server/" VERSION);
970    
971      char * decoded_path = NULL;
972    char * filesystem_path = NULL;    char * filesystem_path = NULL;
973    
974    const char * abs_path = HTTPExchange_get_abs_path(exchange);    const char * abs_path = HTTPExchange_get_abs_path(exchange);
975    assert(*abs_path != '\0');    assert(*abs_path != '\0');
976    
977    if (str_starts_with(abs_path, "/jscoverage")) {    decoded_path = decode_uri_component(abs_path);
978    
979      if (str_starts_with(decoded_path, "/jscoverage")) {
980      handle_jscoverage_request(exchange);      handle_jscoverage_request(exchange);
981      goto done;      goto done;
982    }    }
983    
984    if (strstr(abs_path, "..") != NULL) {    if (strstr(decoded_path, "..") != NULL) {
985      send_response(exchange, 403, "Forbidden\n");      send_response(exchange, 403, "Forbidden\n");
986      goto done;      goto done;
987    }    }
988    
989    filesystem_path = make_path(document_root, abs_path + 1);    filesystem_path = make_path(document_root, decoded_path + 1);
990    size_t filesystem_path_length = strlen(filesystem_path);    size_t filesystem_path_length = strlen(filesystem_path);
991    if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') {    if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') {
992      /* stat on Windows doesn't work with trailing slash */      /* stat on Windows doesn't work with trailing slash */
# Line 1003  Line 1041 
1041        goto done;        goto done;
1042      }      }
1043    
1044        /*
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      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);  
1057      if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) {      if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) {
1058          HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, "text/javascript; charset=ISO-8859-1");
1059    
1060        Stream * input_stream = Stream_new(0);        Stream * input_stream = Stream_new(0);
1061        Stream_write_file_contents(input_stream, f);        Stream_write_file_contents(input_stream, f);
1062    
# Line 1015  Line 1066 
1066        Stream_delete(input_stream);        Stream_delete(input_stream);
1067    
1068        if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {        if (result == JSCOVERAGE_ERROR_ENCODING_NOT_SUPPORTED) {
         free(characters);  
1069          send_response(exchange, 500, "Encoding not supported\n");          send_response(exchange, 500, "Encoding not supported\n");
1070          goto done;          goto done;
1071        }        }
1072        else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {        else if (result == JSCOVERAGE_ERROR_INVALID_BYTE_SEQUENCE) {
         free(characters);  
1073          send_response(exchange, 500, "Error decoding JavaScript file\n");          send_response(exchange, 500, "Error decoding JavaScript file\n");
1074          goto done;          goto done;
1075        }        }
# Line 1035  Line 1084 
1084        Stream_delete(output_stream);        Stream_delete(output_stream);
1085      }      }
1086      else {      else {
1087          /* 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        char buffer[8192];        char buffer[8192];
1099        size_t bytes_read;        size_t bytes_read;
1100        while ((bytes_read = fread(buffer, 1, 8192, f)) > 0) {        while ((bytes_read = fread(buffer, 1, 8192, f)) > 0) {
# Line 1052  Line 1112 
1112    
1113  done:  done:
1114    free(filesystem_path);    free(filesystem_path);
1115      free(decoded_path);
1116  }  }
1117    
1118  static void handler(HTTPExchange * exchange) {  static void handler(HTTPExchange * exchange) {
# Line 1082  Line 1143 
1143        exit(EXIT_SUCCESS);        exit(EXIT_SUCCESS);
1144      }      }
1145      else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {      else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
1146        printf("jscoverage-server %s\n", VERSION);        version();
       exit(EXIT_SUCCESS);  
1147      }      }
1148      else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {      else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
1149        verbose = 1;        verbose = 1;
# Line 1092  Line 1152 
1152      else if (strcmp(argv[i], "--report-dir") == 0) {      else if (strcmp(argv[i], "--report-dir") == 0) {
1153        i++;        i++;
1154        if (i == argc) {        if (i == argc) {
1155          fatal("--report-dir: option requires an argument");          fatal_command_line("--report-dir: option requires an argument");
1156        }        }
1157        report_directory = argv[i];        report_directory = argv[i];
1158      }      }
# Line 1103  Line 1163 
1163      else if (strcmp(argv[i], "--document-root") == 0) {      else if (strcmp(argv[i], "--document-root") == 0) {
1164        i++;        i++;
1165        if (i == argc) {        if (i == argc) {
1166          fatal("--document-root: option requires an argument");          fatal_command_line("--document-root: option requires an argument");
1167        }        }
1168        document_root = argv[i];        document_root = argv[i];
1169      }      }
# Line 1114  Line 1174 
1174      else if (strcmp(argv[i], "--encoding") == 0) {      else if (strcmp(argv[i], "--encoding") == 0) {
1175        i++;        i++;
1176        if (i == argc) {        if (i == argc) {
1177          fatal("--encoding: option requires an argument");          fatal_command_line("--encoding: option requires an argument");
1178        }        }
1179        jscoverage_encoding = argv[i];        jscoverage_encoding = argv[i];
1180          specified_encoding = jscoverage_encoding;
1181      }      }
1182      else if (strncmp(argv[i], "--encoding=", 11) == 0) {      else if (strncmp(argv[i], "--encoding=", 11) == 0) {
1183        jscoverage_encoding = argv[i] + 11;        jscoverage_encoding = argv[i] + 11;
1184          specified_encoding = jscoverage_encoding;
1185      }      }
1186    
1187      else if (strcmp(argv[i], "--ip-address") == 0) {      else if (strcmp(argv[i], "--ip-address") == 0) {
1188        i++;        i++;
1189        if (i == argc) {        if (i == argc) {
1190          fatal("--ip-address: option requires an argument");          fatal_command_line("--ip-address: option requires an argument");
1191        }        }
1192        ip_address = argv[i];        ip_address = argv[i];
1193      }      }
# Line 1133  Line 1195 
1195        ip_address = argv[i] + 13;        ip_address = argv[i] + 13;
1196      }      }
1197    
1198        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      else if (strcmp(argv[i], "--no-highlight") == 0) {      else if (strcmp(argv[i], "--no-highlight") == 0) {
1210        jscoverage_highlight = false;        jscoverage_highlight = false;
1211      }      }
# Line 1140  Line 1213 
1213      else if (strcmp(argv[i], "--no-instrument") == 0) {      else if (strcmp(argv[i], "--no-instrument") == 0) {
1214        i++;        i++;
1215        if (i == argc) {        if (i == argc) {
1216          fatal("--no-instrument: option requires an argument");          fatal_command_line("--no-instrument: option requires an argument");
1217        }        }
1218        no_instrument[num_no_instrument] = argv[i];        no_instrument[num_no_instrument] = argv[i];
1219        num_no_instrument++;        num_no_instrument++;
# Line 1153  Line 1226 
1226      else if (strcmp(argv[i], "--port") == 0) {      else if (strcmp(argv[i], "--port") == 0) {
1227        i++;        i++;
1228        if (i == argc) {        if (i == argc) {
1229          fatal("--port: option requires an argument");          fatal_command_line("--port: option requires an argument");
1230        }        }
1231        port = argv[i];        port = argv[i];
1232      }      }
# Line 1170  Line 1243 
1243      }      }
1244    
1245      else if (strncmp(argv[i], "-", 1) == 0) {      else if (strncmp(argv[i], "-", 1) == 0) {
1246        fatal("unrecognized option `%s'", argv[i]);        fatal_command_line("unrecognized option `%s'", argv[i]);
1247      }      }
1248      else {      else {
1249        fatal("too many arguments");        fatal_command_line("too many arguments");
1250      }      }
1251    }    }
1252    
# Line 1181  Line 1254 
1254    char * end;    char * end;
1255    unsigned long numeric_port = strtoul(port, &end, 10);    unsigned long numeric_port = strtoul(port, &end, 10);
1256    if (*end != '\0') {    if (*end != '\0') {
1257      fatal("--port: option must be an integer");      fatal_command_line("--port: option must be an integer");
1258    }    }
1259    if (numeric_port > UINT16_MAX) {    if (numeric_port > UINT16_MAX) {
1260      fatal("--port: option must be 16 bits");      fatal_command_line("--port: option must be 16 bits");
1261      }
1262    
1263      /* 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    /* is this a shutdown? */    /* is this a shutdown? */

Legend:
Removed from v.189  
changed lines
  Added in v.478

  ViewVC Help
Powered by ViewVC 1.1.24