25 |
#include <string.h> |
#include <string.h> |
26 |
|
|
27 |
#include <dirent.h> |
#include <dirent.h> |
28 |
|
#ifdef HAVE_PTHREAD_H |
29 |
#include <pthread.h> |
#include <pthread.h> |
30 |
|
#endif |
31 |
|
|
32 |
|
#include "global.h" |
33 |
#include "http-server.h" |
#include "http-server.h" |
34 |
#include "instrument-js.h" |
#include "instrument-js.h" |
35 |
#include "resource-manager.h" |
#include "resource-manager.h" |
36 |
#include "stream.h" |
#include "stream.h" |
37 |
#include "util.h" |
#include "util.h" |
38 |
|
|
39 |
|
const char * jscoverage_encoding = "ISO-8859-1"; |
40 |
|
|
41 |
typedef struct SourceCache { |
typedef struct SourceCache { |
42 |
char * url; |
char * url; |
43 |
Stream * source; |
Stream * source; |
69 |
static const char ** no_instrument; |
static const char ** no_instrument; |
70 |
static size_t num_no_instrument = 0; |
static size_t num_no_instrument = 0; |
71 |
|
|
72 |
|
#ifdef __MINGW32__ |
73 |
|
CRITICAL_SECTION javascript_mutex; |
74 |
|
CRITICAL_SECTION source_cache_mutex; |
75 |
|
#define LOCK EnterCriticalSection |
76 |
|
#define UNLOCK LeaveCriticalSection |
77 |
|
#else |
78 |
pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER; |
79 |
pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
80 |
|
#define LOCK pthread_mutex_lock |
81 |
|
#define UNLOCK pthread_mutex_unlock |
82 |
|
#endif |
83 |
|
|
84 |
static Stream * find_cached_source(const char * url) { |
static Stream * find_cached_source(const char * url) { |
85 |
Stream * result = NULL; |
Stream * result = NULL; |
86 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
87 |
for (SourceCache * p = source_cache; p != NULL; p = p->next) { |
for (SourceCache * p = source_cache; p != NULL; p = p->next) { |
88 |
if (strcmp(url, p->url) == 0) { |
if (strcmp(url, p->url) == 0) { |
89 |
result = p->source; |
result = p->source; |
90 |
break; |
break; |
91 |
} |
} |
92 |
} |
} |
93 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
94 |
return result; |
return result; |
95 |
} |
} |
96 |
|
|
98 |
SourceCache * new_source_cache = xmalloc(sizeof(SourceCache)); |
SourceCache * new_source_cache = xmalloc(sizeof(SourceCache)); |
99 |
new_source_cache->url = xstrdup(url); |
new_source_cache->url = xstrdup(url); |
100 |
new_source_cache->source = source; |
new_source_cache->source = source; |
101 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
102 |
new_source_cache->next = source_cache; |
new_source_cache->next = source_cache; |
103 |
source_cache = new_source_cache; |
source_cache = new_source_cache; |
104 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
105 |
} |
} |
106 |
|
|
107 |
static int get(const char * url, Stream * stream) __attribute__((warn_unused_result)); |
static int get(const char * url, Stream * stream) __attribute__((warn_unused_result)); |
358 |
Stream * stream = Stream_new(0); |
Stream * stream = Stream_new(0); |
359 |
Stream_write_file_contents(stream, f); |
Stream_write_file_contents(stream, f); |
360 |
|
|
361 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
362 |
int result = jscoverage_parse_json(coverage, stream->data, stream->length); |
int result = jscoverage_parse_json(coverage, stream->data, stream->length); |
363 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
364 |
|
|
365 |
Stream_delete(stream); |
Stream_delete(stream); |
366 |
return result; |
return result; |
449 |
/* check that the path begins with / */ |
/* check that the path begins with / */ |
450 |
if (file_coverage->id[0] == '/') { |
if (file_coverage->id[0] == '/') { |
451 |
char * source_path = make_path(document_root, file_coverage->id + 1); |
char * source_path = make_path(document_root, file_coverage->id + 1); |
452 |
FILE * source_file = fopen(source_path, "r"); |
FILE * source_file = fopen(source_path, "rb"); |
453 |
free(source_path); |
free(source_path); |
454 |
if (source_file == NULL) { |
if (source_file == NULL) { |
455 |
fputs("\"\"", f); |
fputs("\"\"", f); |
516 |
} |
} |
517 |
|
|
518 |
Coverage * coverage = Coverage_new(); |
Coverage * coverage = Coverage_new(); |
519 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
520 |
int result = jscoverage_parse_json(coverage, json->data, json->length); |
int result = jscoverage_parse_json(coverage, json->data, json->length); |
521 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
522 |
Stream_delete(json); |
Stream_delete(json); |
523 |
|
|
524 |
if (result != 0) { |
if (result != 0) { |
529 |
|
|
530 |
mkdir_if_necessary(report_directory); |
mkdir_if_necessary(report_directory); |
531 |
char * path = make_path(report_directory, "jscoverage.json"); |
char * path = make_path(report_directory, "jscoverage.json"); |
532 |
FILE * f = fopen(path, "r"); |
|
533 |
if (f != NULL) { |
/* check if the JSON file exists */ |
534 |
|
struct stat buf; |
535 |
|
if (stat(path, &buf) == 0) { |
536 |
/* it exists: merge */ |
/* it exists: merge */ |
537 |
result = merge(coverage, f); |
FILE * f = fopen(path, "r"); |
538 |
if (fclose(f) == EOF) { |
if (f == NULL) { |
539 |
result = 1; |
result = 1; |
540 |
} |
} |
541 |
|
else { |
542 |
|
result = merge(coverage, f); |
543 |
|
if (fclose(f) == EOF) { |
544 |
|
result = 1; |
545 |
|
} |
546 |
|
} |
547 |
if (result != 0) { |
if (result != 0) { |
548 |
free(path); |
free(path); |
549 |
Coverage_delete(coverage); |
Coverage_delete(coverage); |
563 |
/* copy other files */ |
/* copy other files */ |
564 |
jscoverage_copy_resources(report_directory); |
jscoverage_copy_resources(report_directory); |
565 |
path = make_path(report_directory, "jscoverage.js"); |
path = make_path(report_directory, "jscoverage.js"); |
566 |
f = fopen(path, "ab"); |
FILE * f = fopen(path, "ab"); |
567 |
free(path); |
free(path); |
568 |
if (f == NULL) { |
if (f == NULL) { |
569 |
send_response(exchange, 500, "Could not write to file: jscoverage.js\n"); |
send_response(exchange, 500, "Could not write to file: jscoverage.js\n"); |
619 |
} |
} |
620 |
} |
} |
621 |
|
|
622 |
static void instrument_js(const char * id, Stream * input_stream, Stream * output_stream) { |
static void instrument_js(const char * id, const char * encoding, Stream * input_stream, Stream * output_stream) { |
623 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
624 |
jscoverage_instrument_js(id, input_stream, output_stream); |
jscoverage_instrument_js(id, encoding, input_stream, output_stream); |
625 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
626 |
|
|
627 |
const struct Resource * resource = get_resource("report.js"); |
const struct Resource * resource = get_resource("report.js"); |
628 |
Stream_write(output_stream, resource->data, resource->length); |
Stream_write(output_stream, resource->data, resource->length); |
744 |
|
|
745 |
const char * request_uri = HTTPExchange_get_request_uri(client_exchange); |
const char * request_uri = HTTPExchange_get_request_uri(client_exchange); |
746 |
Stream * output_stream = Stream_new(0); |
Stream * output_stream = Stream_new(0); |
747 |
instrument_js(request_uri, input_stream, output_stream); |
instrument_js(request_uri, jscoverage_encoding, input_stream, output_stream); |
748 |
|
|
749 |
/* send the headers to the client */ |
/* send the headers to the client */ |
750 |
for (const HTTPHeader * h = HTTPExchange_get_response_headers(server_exchange); h != NULL; h = h->next) { |
for (const HTTPHeader * h = HTTPExchange_get_response_headers(server_exchange); h != NULL; h = h->next) { |
830 |
} |
} |
831 |
|
|
832 |
filesystem_path = make_path(document_root, abs_path + 1); |
filesystem_path = make_path(document_root, abs_path + 1); |
833 |
|
size_t filesystem_path_length = strlen(filesystem_path); |
834 |
|
if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') { |
835 |
|
/* stat on Windows doesn't work with trailing slash */ |
836 |
|
filesystem_path[filesystem_path_length - 1] = '\0'; |
837 |
|
} |
838 |
|
|
839 |
struct stat buf; |
struct stat buf; |
840 |
if (stat(filesystem_path, &buf) == -1) { |
if (stat(filesystem_path, &buf) == -1) { |
843 |
} |
} |
844 |
|
|
845 |
if (S_ISDIR(buf.st_mode)) { |
if (S_ISDIR(buf.st_mode)) { |
846 |
if (filesystem_path[strlen(filesystem_path) - 1] != '/') { |
if (abs_path[strlen(abs_path) - 1] != '/') { |
847 |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
848 |
char * uri = xmalloc(strlen(request_uri) + 2); |
char * uri = xmalloc(strlen(request_uri) + 2); |
849 |
strcpy(uri, request_uri); |
strcpy(uri, request_uri); |
878 |
closedir(d); |
closedir(d); |
879 |
} |
} |
880 |
else if (S_ISREG(buf.st_mode)) { |
else if (S_ISREG(buf.st_mode)) { |
881 |
FILE * f = fopen(filesystem_path, "r"); |
FILE * f = fopen(filesystem_path, "rb"); |
882 |
if (f == NULL) { |
if (f == NULL) { |
883 |
send_response(exchange, 404, "Not found\n"); |
send_response(exchange, 404, "Not found\n"); |
884 |
goto done; |
goto done; |
886 |
|
|
887 |
const char * content_type = get_content_type(filesystem_path); |
const char * content_type = get_content_type(filesystem_path); |
888 |
HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type); |
HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type); |
889 |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(abs_path)) { |
|
if (strcmp(content_type, "text/javascript") == 0 && ! is_no_instrument(request_uri)) { |
|
890 |
Stream * input_stream = Stream_new(0); |
Stream * input_stream = Stream_new(0); |
891 |
Stream * output_stream = Stream_new(0); |
Stream * output_stream = Stream_new(0); |
892 |
|
|
893 |
Stream_write_file_contents(input_stream, f); |
Stream_write_file_contents(input_stream, f); |
894 |
|
|
895 |
instrument_js(request_uri, input_stream, output_stream); |
instrument_js(abs_path, jscoverage_encoding, input_stream, output_stream); |
896 |
|
|
897 |
if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) { |
if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) { |
898 |
HTTPServer_log_err("Warning: error writing to client\n"); |
HTTPServer_log_err("Warning: error writing to client\n"); |
978 |
document_root = argv[i] + 16; |
document_root = argv[i] + 16; |
979 |
} |
} |
980 |
|
|
981 |
|
else if (strcmp(argv[i], "--encoding") == 0) { |
982 |
|
i++; |
983 |
|
if (i == argc) { |
984 |
|
fatal("--encoding: option requires an argument"); |
985 |
|
} |
986 |
|
jscoverage_encoding = argv[i]; |
987 |
|
} |
988 |
|
else if (strncmp(argv[i], "--encoding=", 11) == 0) { |
989 |
|
jscoverage_encoding = argv[i] + 11; |
990 |
|
} |
991 |
|
|
992 |
else if (strcmp(argv[i], "--ip-address") == 0) { |
else if (strcmp(argv[i], "--ip-address") == 0) { |
993 |
i++; |
i++; |
994 |
if (i == argc) { |
if (i == argc) { |
1052 |
|
|
1053 |
/* is this a shutdown? */ |
/* is this a shutdown? */ |
1054 |
if (shutdown) { |
if (shutdown) { |
1055 |
|
#ifdef __MINGW32__ |
1056 |
|
WSADATA data; |
1057 |
|
if (WSAStartup(MAKEWORD(1, 1), &data) != 0) { |
1058 |
|
fatal("could not start Winsock"); |
1059 |
|
} |
1060 |
|
#endif |
1061 |
|
|
1062 |
/* INADDR_LOOPBACK */ |
/* INADDR_LOOPBACK */ |
1063 |
HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port); |
HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port); |
1064 |
if (connection == NULL) { |
if (connection == NULL) { |
1088 |
|
|
1089 |
jscoverage_init(); |
jscoverage_init(); |
1090 |
|
|
1091 |
|
#ifndef __MINGW32__ |
1092 |
/* handle broken pipe */ |
/* handle broken pipe */ |
1093 |
signal(SIGPIPE, SIG_IGN); |
signal(SIGPIPE, SIG_IGN); |
1094 |
|
#endif |
1095 |
|
|
1096 |
|
#ifdef __MINGW32__ |
1097 |
|
InitializeCriticalSection(&javascript_mutex); |
1098 |
|
InitializeCriticalSection(&source_cache_mutex); |
1099 |
|
#endif |
1100 |
|
|
1101 |
if (verbose) { |
if (verbose) { |
1102 |
printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port); |
printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port); |
1103 |
|
fflush(stdout); |
1104 |
} |
} |
1105 |
HTTPServer_run(ip_address, (uint16_t) numeric_port, handler); |
HTTPServer_run(ip_address, (uint16_t) numeric_port, handler); |
1106 |
if (verbose) { |
if (verbose) { |
1107 |
printf("Stopping HTTP server\n"); |
printf("Stopping HTTP server\n"); |
1108 |
|
fflush(stdout); |
1109 |
} |
} |
1110 |
|
|
1111 |
jscoverage_cleanup(); |
jscoverage_cleanup(); |
1112 |
|
|
1113 |
free(no_instrument); |
free(no_instrument); |
1114 |
|
|
1115 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
1116 |
while (source_cache != NULL) { |
while (source_cache != NULL) { |
1117 |
SourceCache * p = source_cache; |
SourceCache * p = source_cache; |
1118 |
source_cache = source_cache->next; |
source_cache = source_cache->next; |
1120 |
Stream_delete(p->source); |
Stream_delete(p->source); |
1121 |
free(p); |
free(p); |
1122 |
} |
} |
1123 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
1124 |
|
|
1125 |
return 0; |
return 0; |
1126 |
} |
} |