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 "http-server.h" |
#include "http-server.h" |
33 |
#include "instrument-js.h" |
#include "instrument-js.h" |
66 |
static const char ** no_instrument; |
static const char ** no_instrument; |
67 |
static size_t num_no_instrument = 0; |
static size_t num_no_instrument = 0; |
68 |
|
|
69 |
|
#ifdef __MINGW32__ |
70 |
|
CRITICAL_SECTION javascript_mutex; |
71 |
|
CRITICAL_SECTION source_cache_mutex; |
72 |
|
#define LOCK EnterCriticalSection |
73 |
|
#define UNLOCK LeaveCriticalSection |
74 |
|
#else |
75 |
pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutex_t javascript_mutex = PTHREAD_MUTEX_INITIALIZER; |
76 |
pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
pthread_mutex_t source_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
77 |
|
#define LOCK pthread_mutex_lock |
78 |
|
#define UNLOCK pthread_mutex_unlock |
79 |
|
#endif |
80 |
|
|
81 |
static Stream * find_cached_source(const char * url) { |
static Stream * find_cached_source(const char * url) { |
82 |
Stream * result = NULL; |
Stream * result = NULL; |
83 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
84 |
for (SourceCache * p = source_cache; p != NULL; p = p->next) { |
for (SourceCache * p = source_cache; p != NULL; p = p->next) { |
85 |
if (strcmp(url, p->url) == 0) { |
if (strcmp(url, p->url) == 0) { |
86 |
result = p->source; |
result = p->source; |
87 |
break; |
break; |
88 |
} |
} |
89 |
} |
} |
90 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
91 |
return result; |
return result; |
92 |
} |
} |
93 |
|
|
95 |
SourceCache * new_source_cache = xmalloc(sizeof(SourceCache)); |
SourceCache * new_source_cache = xmalloc(sizeof(SourceCache)); |
96 |
new_source_cache->url = xstrdup(url); |
new_source_cache->url = xstrdup(url); |
97 |
new_source_cache->source = source; |
new_source_cache->source = source; |
98 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
99 |
new_source_cache->next = source_cache; |
new_source_cache->next = source_cache; |
100 |
source_cache = new_source_cache; |
source_cache = new_source_cache; |
101 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
102 |
} |
} |
103 |
|
|
104 |
static int get(const char * url, Stream * stream) __attribute__((warn_unused_result)); |
static int get(const char * url, Stream * stream) __attribute__((warn_unused_result)); |
349 |
return true; |
return true; |
350 |
} |
} |
351 |
|
|
352 |
static int merge(Coverage * coverage, const char * path, size_t size) __attribute__((warn_unused_result)); |
static int merge(Coverage * coverage, FILE * f) __attribute__((warn_unused_result)); |
353 |
|
|
354 |
static int merge(Coverage * coverage, const char * path, size_t size) { |
static int merge(Coverage * coverage, FILE * f) { |
355 |
FILE * f = fopen(path, "r"); |
Stream * stream = Stream_new(0); |
356 |
if (f == NULL) { |
Stream_write_file_contents(stream, f); |
|
return -1; |
|
|
} |
|
|
uint8_t * buffer = xmalloc(size); |
|
|
if (fread(buffer, 1, size, f) != size) { |
|
|
fclose(f); |
|
|
free(buffer); |
|
|
return -1; |
|
|
} |
|
|
fclose(f); |
|
357 |
|
|
358 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
359 |
int result = jscoverage_parse_json(coverage, buffer, size); |
int result = jscoverage_parse_json(coverage, stream->data, stream->length); |
360 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
361 |
|
|
362 |
free(buffer); |
Stream_delete(stream); |
363 |
return result; |
return result; |
364 |
} |
} |
365 |
|
|
446 |
/* check that the path begins with / */ |
/* check that the path begins with / */ |
447 |
if (file_coverage->id[0] == '/') { |
if (file_coverage->id[0] == '/') { |
448 |
char * source_path = make_path(document_root, file_coverage->id + 1); |
char * source_path = make_path(document_root, file_coverage->id + 1); |
449 |
FILE * source_file = fopen(source_path, "r"); |
FILE * source_file = fopen(source_path, "rb"); |
450 |
free(source_path); |
free(source_path); |
451 |
if (source_file == NULL) { |
if (source_file == NULL) { |
452 |
fputs("\"\"", f); |
fputs("\"\"", f); |
513 |
} |
} |
514 |
|
|
515 |
Coverage * coverage = Coverage_new(); |
Coverage * coverage = Coverage_new(); |
516 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
517 |
int result = jscoverage_parse_json(coverage, json->data, json->length); |
int result = jscoverage_parse_json(coverage, json->data, json->length); |
518 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
519 |
Stream_delete(json); |
Stream_delete(json); |
520 |
|
|
521 |
if (result != 0) { |
if (result != 0) { |
526 |
|
|
527 |
mkdir_if_necessary(report_directory); |
mkdir_if_necessary(report_directory); |
528 |
char * path = make_path(report_directory, "jscoverage.json"); |
char * path = make_path(report_directory, "jscoverage.json"); |
529 |
|
|
530 |
|
/* check if the JSON file exists */ |
531 |
struct stat buf; |
struct stat buf; |
532 |
if (stat(path, &buf) == 0) { |
if (stat(path, &buf) == 0) { |
533 |
/* it exists: merge */ |
/* it exists: merge */ |
534 |
result = merge(coverage, path, buf.st_size); |
FILE * f = fopen(path, "r"); |
535 |
|
if (f == NULL) { |
536 |
|
result = 1; |
537 |
|
} |
538 |
|
else { |
539 |
|
result = merge(coverage, f); |
540 |
|
if (fclose(f) == EOF) { |
541 |
|
result = 1; |
542 |
|
} |
543 |
|
} |
544 |
if (result != 0) { |
if (result != 0) { |
545 |
free(path); |
free(path); |
546 |
Coverage_delete(coverage); |
Coverage_delete(coverage); |
617 |
} |
} |
618 |
|
|
619 |
static void instrument_js(const char * id, Stream * input_stream, Stream * output_stream) { |
static void instrument_js(const char * id, Stream * input_stream, Stream * output_stream) { |
620 |
pthread_mutex_lock(&javascript_mutex); |
LOCK(&javascript_mutex); |
621 |
jscoverage_instrument_js(id, input_stream, output_stream); |
jscoverage_instrument_js(id, input_stream, output_stream); |
622 |
pthread_mutex_unlock(&javascript_mutex); |
UNLOCK(&javascript_mutex); |
623 |
|
|
624 |
const struct Resource * resource = get_resource("report.js"); |
const struct Resource * resource = get_resource("report.js"); |
625 |
Stream_write(output_stream, resource->data, resource->length); |
Stream_write(output_stream, resource->data, resource->length); |
827 |
} |
} |
828 |
|
|
829 |
filesystem_path = make_path(document_root, abs_path + 1); |
filesystem_path = make_path(document_root, abs_path + 1); |
830 |
|
size_t filesystem_path_length = strlen(filesystem_path); |
831 |
|
if (filesystem_path_length > 0 && filesystem_path[filesystem_path_length - 1] == '/') { |
832 |
|
/* stat on Windows doesn't work with trailing slash */ |
833 |
|
filesystem_path[filesystem_path_length - 1] = '\0'; |
834 |
|
} |
835 |
|
|
836 |
struct stat buf; |
struct stat buf; |
837 |
if (stat(filesystem_path, &buf) == -1) { |
if (stat(filesystem_path, &buf) == -1) { |
840 |
} |
} |
841 |
|
|
842 |
if (S_ISDIR(buf.st_mode)) { |
if (S_ISDIR(buf.st_mode)) { |
843 |
if (filesystem_path[strlen(filesystem_path) - 1] != '/') { |
if (abs_path[strlen(abs_path) - 1] != '/') { |
844 |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
const char * request_uri = HTTPExchange_get_request_uri(exchange); |
845 |
char * uri = xmalloc(strlen(request_uri) + 2); |
char * uri = xmalloc(strlen(request_uri) + 2); |
846 |
strcpy(uri, request_uri); |
strcpy(uri, request_uri); |
875 |
closedir(d); |
closedir(d); |
876 |
} |
} |
877 |
else if (S_ISREG(buf.st_mode)) { |
else if (S_ISREG(buf.st_mode)) { |
878 |
FILE * f = fopen(filesystem_path, "r"); |
FILE * f = fopen(filesystem_path, "rb"); |
879 |
if (f == NULL) { |
if (f == NULL) { |
880 |
send_response(exchange, 404, "Not found\n"); |
send_response(exchange, 404, "Not found\n"); |
881 |
goto done; |
goto done; |
883 |
|
|
884 |
const char * content_type = get_content_type(filesystem_path); |
const char * content_type = get_content_type(filesystem_path); |
885 |
HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type); |
HTTPExchange_set_response_header(exchange, HTTP_CONTENT_TYPE, content_type); |
886 |
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)) { |
|
887 |
Stream * input_stream = Stream_new(0); |
Stream * input_stream = Stream_new(0); |
888 |
Stream * output_stream = Stream_new(0); |
Stream * output_stream = Stream_new(0); |
889 |
|
|
890 |
Stream_write_file_contents(input_stream, f); |
Stream_write_file_contents(input_stream, f); |
891 |
|
|
892 |
instrument_js(request_uri, input_stream, output_stream); |
instrument_js(abs_path, input_stream, output_stream); |
893 |
|
|
894 |
if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) { |
if (HTTPExchange_write_response(exchange, output_stream->data, output_stream->length) != 0) { |
895 |
HTTPServer_log_err("Warning: error writing to client\n"); |
HTTPServer_log_err("Warning: error writing to client\n"); |
1038 |
|
|
1039 |
/* is this a shutdown? */ |
/* is this a shutdown? */ |
1040 |
if (shutdown) { |
if (shutdown) { |
1041 |
|
#ifdef __MINGW32__ |
1042 |
|
WSADATA data; |
1043 |
|
if (WSAStartup(MAKEWORD(1, 1), &data) != 0) { |
1044 |
|
fatal("could not start Winsock"); |
1045 |
|
} |
1046 |
|
#endif |
1047 |
|
|
1048 |
/* INADDR_LOOPBACK */ |
/* INADDR_LOOPBACK */ |
1049 |
HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port); |
HTTPConnection * connection = HTTPConnection_new_client("127.0.0.1", numeric_port); |
1050 |
if (connection == NULL) { |
if (connection == NULL) { |
1074 |
|
|
1075 |
jscoverage_init(); |
jscoverage_init(); |
1076 |
|
|
1077 |
|
#ifndef __MINGW32__ |
1078 |
/* handle broken pipe */ |
/* handle broken pipe */ |
1079 |
signal(SIGPIPE, SIG_IGN); |
signal(SIGPIPE, SIG_IGN); |
1080 |
|
#endif |
1081 |
|
|
1082 |
|
#ifdef __MINGW32__ |
1083 |
|
InitializeCriticalSection(&javascript_mutex); |
1084 |
|
InitializeCriticalSection(&source_cache_mutex); |
1085 |
|
#endif |
1086 |
|
|
1087 |
if (verbose) { |
if (verbose) { |
1088 |
printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port); |
printf("Starting HTTP server on %s:%lu\n", ip_address, numeric_port); |
1089 |
|
fflush(stdout); |
1090 |
} |
} |
1091 |
HTTPServer_run(ip_address, (uint16_t) numeric_port, handler); |
HTTPServer_run(ip_address, (uint16_t) numeric_port, handler); |
1092 |
if (verbose) { |
if (verbose) { |
1093 |
printf("Stopping HTTP server\n"); |
printf("Stopping HTTP server\n"); |
1094 |
|
fflush(stdout); |
1095 |
} |
} |
1096 |
|
|
1097 |
jscoverage_cleanup(); |
jscoverage_cleanup(); |
1098 |
|
|
1099 |
free(no_instrument); |
free(no_instrument); |
1100 |
|
|
1101 |
pthread_mutex_lock(&source_cache_mutex); |
LOCK(&source_cache_mutex); |
1102 |
while (source_cache != NULL) { |
while (source_cache != NULL) { |
1103 |
SourceCache * p = source_cache; |
SourceCache * p = source_cache; |
1104 |
source_cache = source_cache->next; |
source_cache = source_cache->next; |
1106 |
Stream_delete(p->source); |
Stream_delete(p->source); |
1107 |
free(p); |
free(p); |
1108 |
} |
} |
1109 |
pthread_mutex_unlock(&source_cache_mutex); |
UNLOCK(&source_cache_mutex); |
1110 |
|
|
1111 |
return 0; |
return 0; |
1112 |
} |
} |