Update.
This commit is contained in:
parent
264712dcbc
commit
5edaf69915
29
Makefile
29
Makefile
@ -1,6 +1,6 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
.PHONY: build tests clean debug sync-sidebar buildmanual wasm wasm-clean install-emscripten
|
||||
.PHONY: build tests clean debug sync-sidebar buildmanual wasm wasm-clean install-emscripten coverage valgrind clean-coverage
|
||||
|
||||
build:
|
||||
cd projects/make && $(MAKE) -f wren_cli.make -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
@ -26,6 +26,14 @@ install:
|
||||
clean:
|
||||
cd projects/make && $(MAKE) -f wren_cli.make clean
|
||||
|
||||
publish:
|
||||
git add src
|
||||
git add example
|
||||
git add manual_src
|
||||
git add test
|
||||
git commit -a -m "Update."
|
||||
git push
|
||||
|
||||
wasm-clean:
|
||||
cd projects/make.wasm && $(MAKE) -f wren_wasm.make clean
|
||||
|
||||
@ -50,3 +58,22 @@ wasm:
|
||||
|
||||
buildmanual:
|
||||
python3 util/build_manual.py
|
||||
|
||||
coverage:
|
||||
cd projects/make && $(MAKE) -f wren_cli.make config=coverage_64bit clean
|
||||
cd projects/make && $(MAKE) -f wren_cli.make config=coverage_64bit -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
python3 util/test.py --suffix=_cov
|
||||
@echo "Generating coverage report..."
|
||||
lcov --capture --directory projects/make/obj/64bit/Coverage --output-file coverage.info --ignore-errors mismatch,inconsistent
|
||||
lcov --remove coverage.info '/usr/*' '*/deps/*' --output-file coverage.info --ignore-errors unused
|
||||
genhtml coverage.info --output-directory coverage_report
|
||||
@echo "Coverage report: coverage_report/index.html"
|
||||
|
||||
valgrind:
|
||||
cd projects/make && $(MAKE) -f wren_cli.make config=debug_64bit -j $$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
python3 util/test.py --suffix=_d --valgrind
|
||||
|
||||
clean-coverage:
|
||||
rm -rf coverage.info coverage_report
|
||||
find projects/make -name "*.gcda" -delete
|
||||
find projects/make -name "*.gcno" -delete
|
||||
|
||||
@ -87,6 +87,15 @@ ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g -std=c99
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g
|
||||
ALL_LDFLAGS += $(LDFLAGS)
|
||||
|
||||
else ifeq ($(config),coverage_64bit)
|
||||
TARGETDIR = ../../bin
|
||||
TARGET = $(TARGETDIR)/wren_cli_cov
|
||||
OBJDIR = obj/64bit/Coverage
|
||||
DEFINES += -DDEBUG -DWREN_PROFILE_ENABLED -D_GNU_SOURCE
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g --coverage -O0 -std=c99
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -g --coverage -O0
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64 --coverage
|
||||
|
||||
else
|
||||
$(error "invalid configuration $(config)")
|
||||
endif
|
||||
|
||||
@ -50,6 +50,7 @@ extern void bytesXorMask(WrenVM* vm);
|
||||
extern void bytesConcat(WrenVM* vm);
|
||||
extern void bytesSlice(WrenVM* vm);
|
||||
extern void bytesLength(WrenVM* vm);
|
||||
extern void bytesIndexOf(WrenVM* vm);
|
||||
extern void cryptoRandomBytes(WrenVM* vm);
|
||||
extern void cryptoMd5(WrenVM* vm);
|
||||
extern void cryptoSha1(WrenVM* vm);
|
||||
@ -158,6 +159,8 @@ extern void serverAllocate(WrenVM* vm);
|
||||
extern void serverFinalize(void* data);
|
||||
extern void serverBind(WrenVM* vm);
|
||||
extern void serverAccept(WrenVM* vm);
|
||||
extern void serverAcceptBatch(WrenVM* vm);
|
||||
extern void serverPendingCount(WrenVM* vm);
|
||||
extern void serverClose(WrenVM* vm);
|
||||
extern void signalTrap(WrenVM* vm);
|
||||
extern void signalIgnore(WrenVM* vm);
|
||||
@ -389,6 +392,8 @@ static ModuleRegistry modules[] =
|
||||
STATIC_METHOD("concat(_,_)", bytesConcat)
|
||||
STATIC_METHOD("slice(_,_,_)", bytesSlice)
|
||||
STATIC_METHOD("length(_)", bytesLength)
|
||||
STATIC_METHOD("indexOf(_,_)", bytesIndexOf)
|
||||
STATIC_METHOD("indexOf(_,_,_)", bytesIndexOf)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(crypto)
|
||||
@ -563,6 +568,8 @@ static ModuleRegistry modules[] =
|
||||
FINALIZE(serverFinalize)
|
||||
METHOD("bind_(_,_)", serverBind)
|
||||
METHOD("accept_(_)", serverAccept)
|
||||
METHOD("acceptBatch_(_)", serverAcceptBatch)
|
||||
METHOD("pendingCount_", serverPendingCount)
|
||||
METHOD("close_()", serverClose)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
|
||||
@ -178,3 +178,27 @@ void bytesLength(WrenVM* vm) {
|
||||
wrenGetSlotBytes(vm, 1, &length);
|
||||
wrenSetSlotDouble(vm, 0, (double)length);
|
||||
}
|
||||
|
||||
void bytesIndexOf(WrenVM* vm) {
|
||||
int haystackLen = 0;
|
||||
const char* haystack = wrenGetSlotBytes(vm, 1, &haystackLen);
|
||||
int needleLen = 0;
|
||||
const char* needle = wrenGetSlotBytes(vm, 2, &needleLen);
|
||||
int start = 0;
|
||||
if (wrenGetSlotCount(vm) > 3 && wrenGetSlotType(vm, 3) == WREN_TYPE_NUM) {
|
||||
start = (int)wrenGetSlotDouble(vm, 3);
|
||||
}
|
||||
|
||||
if (needleLen == 0 || haystackLen == 0 || start < 0 || start >= haystackLen) {
|
||||
wrenSetSlotDouble(vm, 0, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = start; i <= haystackLen - needleLen; i++) {
|
||||
if (memcmp(haystack + i, needle, needleLen) == 0) {
|
||||
wrenSetSlotDouble(vm, 0, (double)i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wrenSetSlotDouble(vm, 0, -1);
|
||||
}
|
||||
|
||||
@ -11,5 +11,6 @@ void bytesXorMask(WrenVM* vm);
|
||||
void bytesConcat(WrenVM* vm);
|
||||
void bytesSlice(WrenVM* vm);
|
||||
void bytesLength(WrenVM* vm);
|
||||
void bytesIndexOf(WrenVM* vm);
|
||||
|
||||
#endif
|
||||
|
||||
2
src/module/bytes.wren
vendored
2
src/module/bytes.wren
vendored
@ -7,4 +7,6 @@ class Bytes {
|
||||
foreign static concat(a, b)
|
||||
foreign static slice(data, start, end)
|
||||
foreign static length(data)
|
||||
foreign static indexOf(haystack, needle)
|
||||
foreign static indexOf(haystack, needle, start)
|
||||
}
|
||||
|
||||
@ -11,4 +11,6 @@ static const char* bytesModuleSource =
|
||||
" foreign static concat(a, b)\n"
|
||||
" foreign static slice(data, start, end)\n"
|
||||
" foreign static length(data)\n"
|
||||
" foreign static indexOf(haystack, needle)\n"
|
||||
" foreign static indexOf(haystack, needle, start)\n"
|
||||
"}\n";
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "uv.h"
|
||||
|
||||
@ -736,15 +737,36 @@ static void stdinFileTimerCallback(uv_timer_t* handle)
|
||||
stdinOnData = wrenMakeCallHandle(vm, "onData_(_)");
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
ssize_t numRead = read(stdinDescriptor, buffer, sizeof(buffer));
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotHandle(vm, 0, stdinClass);
|
||||
wrenSetSlotNull(vm, 1);
|
||||
WrenInterpretResult result = wrenCall(vm, stdinOnData);
|
||||
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
if (numRead > 0)
|
||||
{
|
||||
uv_stop(getLoop());
|
||||
setExitCode(70);
|
||||
wrenSetSlotBytes(vm, 1, buffer, (size_t)numRead);
|
||||
WrenInterpretResult result = wrenCall(vm, stdinOnData);
|
||||
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
{
|
||||
uv_stop(getLoop());
|
||||
setExitCode(70);
|
||||
return;
|
||||
}
|
||||
|
||||
uv_timer_start(&stdinFileTimer, stdinFileTimerCallback, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
wrenSetSlotNull(vm, 1);
|
||||
WrenInterpretResult result = wrenCall(vm, stdinOnData);
|
||||
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
{
|
||||
uv_stop(getLoop());
|
||||
setExitCode(70);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,11 +26,14 @@ typedef struct SocketListNode {
|
||||
struct SocketListNode* next;
|
||||
} SocketListNode;
|
||||
|
||||
#define MAX_PENDING_CONNECTIONS 512
|
||||
|
||||
typedef struct {
|
||||
uv_tcp_t* handle;
|
||||
WrenHandle* acceptFiber; // Fiber waiting for accept()
|
||||
WrenHandle* acceptFiber;
|
||||
SocketListNode* pendingHead;
|
||||
SocketListNode* pendingTail;
|
||||
int pendingCount;
|
||||
} ServerData;
|
||||
|
||||
static WrenHandle* socketClassHandle = NULL;
|
||||
@ -278,6 +281,7 @@ void serverAllocate(WrenVM* vm)
|
||||
data->acceptFiber = NULL;
|
||||
data->pendingHead = NULL;
|
||||
data->pendingTail = NULL;
|
||||
data->pendingCount = 0;
|
||||
}
|
||||
|
||||
void serverFinalize(void* data)
|
||||
@ -313,27 +317,27 @@ void netShutdown()
|
||||
static void connectionCallback(uv_stream_t* server, int status)
|
||||
{
|
||||
ServerData* data = (ServerData*)server->data;
|
||||
|
||||
|
||||
if (status < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
|
||||
uv_tcp_init(getLoop(), client);
|
||||
|
||||
|
||||
if (uv_accept(server, (uv_stream_t*)client) == 0) {
|
||||
if (data->acceptFiber) {
|
||||
WrenHandle* fiber = data->acceptFiber;
|
||||
data->acceptFiber = NULL;
|
||||
|
||||
|
||||
WrenVM* vm = getVM();
|
||||
wrenEnsureSlots(vm, 3);
|
||||
|
||||
|
||||
if (socketClassHandle == NULL) {
|
||||
fprintf(stderr, "FATAL: socketClassHandle NULL in connectionCallback\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
wrenSetSlotHandle(vm, 1, socketClassHandle);
|
||||
wrenSetSlotNewForeign(vm, 2, 1, sizeof(SocketData));
|
||||
SocketData* sockData = (SocketData*)wrenGetSlotForeign(vm, 2);
|
||||
@ -348,6 +352,10 @@ static void connectionCallback(uv_stream_t* server, int status)
|
||||
schedulerResume(fiber, true);
|
||||
schedulerFinishResume();
|
||||
} else {
|
||||
if (data->pendingCount >= MAX_PENDING_CONNECTIONS) {
|
||||
uv_close((uv_handle_t*)client, closeCallback);
|
||||
return;
|
||||
}
|
||||
SocketListNode* node = (SocketListNode*)malloc(sizeof(SocketListNode));
|
||||
node->handle = client;
|
||||
node->next = NULL;
|
||||
@ -358,6 +366,7 @@ static void connectionCallback(uv_stream_t* server, int status)
|
||||
data->pendingHead = node;
|
||||
data->pendingTail = node;
|
||||
}
|
||||
data->pendingCount++;
|
||||
}
|
||||
} else {
|
||||
uv_close((uv_handle_t*)client, closeCallback);
|
||||
@ -390,18 +399,19 @@ void serverAccept(WrenVM* vm)
|
||||
SocketListNode* node = data->pendingHead;
|
||||
data->pendingHead = node->next;
|
||||
if (data->pendingHead == NULL) data->pendingTail = NULL;
|
||||
|
||||
data->pendingCount--;
|
||||
|
||||
uv_tcp_t* client = node->handle;
|
||||
free(node);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
if (socketClassHandle == NULL) {
|
||||
fprintf(stderr, "FATAL: socketClassHandle NULL in serverAccept\n");
|
||||
exit(1);
|
||||
}
|
||||
wrenSetSlotHandle(vm, 2, socketClassHandle);
|
||||
wrenSetSlotNewForeign(vm, 0, 2, sizeof(SocketData));
|
||||
|
||||
|
||||
SocketData* sockData = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
sockData->handle = client;
|
||||
client->data = sockData;
|
||||
@ -417,6 +427,50 @@ void serverAccept(WrenVM* vm)
|
||||
}
|
||||
}
|
||||
|
||||
void serverAcceptBatch(WrenVM* vm)
|
||||
{
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
int maxCount = (int)wrenGetSlotDouble(vm, 1);
|
||||
|
||||
wrenEnsureSlots(vm, 4);
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
|
||||
int count = 0;
|
||||
while (data->pendingHead && count < maxCount) {
|
||||
SocketListNode* node = data->pendingHead;
|
||||
data->pendingHead = node->next;
|
||||
if (data->pendingHead == NULL) data->pendingTail = NULL;
|
||||
data->pendingCount--;
|
||||
|
||||
if (socketClassHandle == NULL) {
|
||||
uv_close((uv_handle_t*)node->handle, closeCallback);
|
||||
free(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
wrenSetSlotHandle(vm, 2, socketClassHandle);
|
||||
wrenSetSlotNewForeign(vm, 1, 2, sizeof(SocketData));
|
||||
SocketData* sockData = (SocketData*)wrenGetSlotForeign(vm, 1);
|
||||
sockData->handle = node->handle;
|
||||
node->handle->data = sockData;
|
||||
sockData->fiber = NULL;
|
||||
sockData->connectReq = NULL;
|
||||
sockData->writeReq = NULL;
|
||||
sockData->readBuffer = NULL;
|
||||
sockData->readBufferSize = 0;
|
||||
|
||||
free(node);
|
||||
wrenInsertInList(vm, 0, -1, 1);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
void serverPendingCount(WrenVM* vm)
|
||||
{
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
wrenSetSlotDouble(vm, 0, (double)data->pendingCount);
|
||||
}
|
||||
|
||||
void serverClose(WrenVM* vm)
|
||||
{
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
|
||||
@ -19,6 +19,8 @@ void serverAllocate(WrenVM* vm);
|
||||
void serverFinalize(void* data);
|
||||
void serverBind(WrenVM* vm);
|
||||
void serverAccept(WrenVM* vm);
|
||||
void serverAcceptBatch(WrenVM* vm);
|
||||
void serverPendingCount(WrenVM* vm);
|
||||
void serverClose(WrenVM* vm);
|
||||
|
||||
#endif
|
||||
|
||||
12
src/module/net.wren
vendored
12
src/module/net.wren
vendored
@ -40,7 +40,7 @@ foreign class Server {
|
||||
static bind(host, port) {
|
||||
if (!(host is String)) Fiber.abort("Host must be a string.")
|
||||
if (!(port is Num)) Fiber.abort("Port must be a number.")
|
||||
|
||||
|
||||
var server = Server.new()
|
||||
server.bind_(host, port)
|
||||
return server
|
||||
@ -51,12 +51,20 @@ foreign class Server {
|
||||
if (socket != null) return socket
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
|
||||
acceptBatch(maxCount) {
|
||||
return acceptBatch_(maxCount)
|
||||
}
|
||||
|
||||
pendingCount { pendingCount_ }
|
||||
|
||||
close() {
|
||||
close_()
|
||||
}
|
||||
|
||||
foreign bind_(host, port)
|
||||
foreign accept_(fiber)
|
||||
foreign acceptBatch_(maxCount)
|
||||
foreign pendingCount_
|
||||
foreign close_()
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ static const char* netModuleSource =
|
||||
" static bind(host, port) {\n"
|
||||
" if (!(host is String)) Fiber.abort(\"Host must be a string.\")\n"
|
||||
" if (!(port is Num)) Fiber.abort(\"Port must be a number.\")\n"
|
||||
" \n"
|
||||
"\n"
|
||||
" var server = Server.new()\n"
|
||||
" server.bind_(host, port)\n"
|
||||
" return server\n"
|
||||
@ -55,12 +55,20 @@ static const char* netModuleSource =
|
||||
" if (socket != null) return socket\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
" \n"
|
||||
"\n"
|
||||
" acceptBatch(maxCount) {\n"
|
||||
" return acceptBatch_(maxCount)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" pendingCount { pendingCount_ }\n"
|
||||
"\n"
|
||||
" close() {\n"
|
||||
" close_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign bind_(host, port)\n"
|
||||
" foreign accept_(fiber)\n"
|
||||
" foreign acceptBatch_(maxCount)\n"
|
||||
" foreign pendingCount_\n"
|
||||
" foreign close_()\n"
|
||||
"}\n";
|
||||
|
||||
@ -135,7 +135,9 @@ void regexAllocate(WrenVM* vm) {
|
||||
char* processed = preprocessPattern(pattern, data->dotall);
|
||||
if (!processed) {
|
||||
free(data->pattern);
|
||||
data->pattern = NULL;
|
||||
free(data->flags);
|
||||
data->flags = NULL;
|
||||
wrenSetSlotString(vm, 0, "Failed to preprocess pattern");
|
||||
wrenAbortFiber(vm, 0);
|
||||
return;
|
||||
@ -148,7 +150,9 @@ void regexAllocate(WrenVM* vm) {
|
||||
char errbuf[256];
|
||||
regerror(err, &data->compiled, errbuf, sizeof(errbuf));
|
||||
free(data->pattern);
|
||||
data->pattern = NULL;
|
||||
free(data->flags);
|
||||
data->flags = NULL;
|
||||
wrenSetSlotString(vm, 0, errbuf);
|
||||
wrenAbortFiber(vm, 0);
|
||||
return;
|
||||
|
||||
2
src/module/scheduler.wren
vendored
2
src/module/scheduler.wren
vendored
@ -45,7 +45,7 @@ class Scheduler {
|
||||
}
|
||||
var fiber = __scheduled[__scheduleIndex]
|
||||
__scheduleIndex = __scheduleIndex + 1
|
||||
if (__scheduleIndex > 64 && __scheduleIndex > __scheduled.count / 2) {
|
||||
if (__scheduleIndex > 32 && __scheduleIndex > __scheduled.count / 4) {
|
||||
__scheduled = __scheduled[__scheduleIndex..-1]
|
||||
__scheduleIndex = 0
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ static const char* schedulerModuleSource =
|
||||
" }\n"
|
||||
" var fiber = __scheduled[__scheduleIndex]\n"
|
||||
" __scheduleIndex = __scheduleIndex + 1\n"
|
||||
" if (__scheduleIndex > 64 && __scheduleIndex > __scheduled.count / 2) {\n"
|
||||
" if (__scheduleIndex > 32 && __scheduleIndex > __scheduled.count / 4) {\n"
|
||||
" __scheduled = __scheduled[__scheduleIndex..-1]\n"
|
||||
" __scheduleIndex = 0\n"
|
||||
" }\n"
|
||||
|
||||
@ -468,6 +468,13 @@ void popenAllocate(WrenVM* vm) {
|
||||
}
|
||||
|
||||
void popenFinalize(void* userData) {
|
||||
PopenData* data = (PopenData*)userData;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (data->streams[i].buffer != NULL) {
|
||||
free(data->streams[i].buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void popenPid(WrenVM* vm) {
|
||||
|
||||
102
src/module/web.wren
vendored
102
src/module/web.wren
vendored
@ -40,6 +40,7 @@ class Request {
|
||||
headers { _headers }
|
||||
body { _body }
|
||||
params { _params }
|
||||
params=(value) { _params = value }
|
||||
socket { _socket }
|
||||
|
||||
header(name) {
|
||||
@ -71,43 +72,92 @@ class Request {
|
||||
var boundary = extractBoundary_(contentType)
|
||||
if (boundary == null) return result
|
||||
|
||||
var parts = _body.split("--" + boundary)
|
||||
for (part in parts) {
|
||||
var trimmed = part.trim()
|
||||
if (trimmed.count == 0 || trimmed == "--") continue
|
||||
var delimBytes = ("--" + boundary)
|
||||
var bodyLen = Bytes.length(_body)
|
||||
var pos = Bytes.indexOf(_body, delimBytes, 0)
|
||||
if (pos < 0) return result
|
||||
|
||||
var headerEnd = trimmed.indexOf("\r\n\r\n")
|
||||
while (pos >= 0) {
|
||||
pos = pos + Bytes.length(delimBytes)
|
||||
if (pos >= bodyLen) break
|
||||
|
||||
var nextByte = Bytes.slice(_body, pos, pos + 2)
|
||||
if (nextByte == "--") break
|
||||
|
||||
if (Bytes.slice(_body, pos, pos + 2) == "\r\n") {
|
||||
pos = pos + 2
|
||||
} else if (Bytes.slice(_body, pos, pos + 1) == "\n") {
|
||||
pos = pos + 1
|
||||
}
|
||||
|
||||
var nextPos = Bytes.indexOf(_body, delimBytes, pos)
|
||||
if (nextPos < 0) nextPos = bodyLen
|
||||
|
||||
var partData = Bytes.slice(_body, pos, nextPos)
|
||||
var partLen = Bytes.length(partData)
|
||||
|
||||
var headerEnd = Bytes.indexOf(partData, "\r\n\r\n", 0)
|
||||
var delimLen = 4
|
||||
if (headerEnd < 0) {
|
||||
headerEnd = trimmed.indexOf("\n\n")
|
||||
headerEnd = Bytes.indexOf(partData, "\n\n", 0)
|
||||
delimLen = 2
|
||||
}
|
||||
if (headerEnd < 0) continue
|
||||
|
||||
var headers = trimmed[0...headerEnd]
|
||||
var content = trimmed[headerEnd + delimLen..-1]
|
||||
if (content.endsWith("\r\n")) {
|
||||
content = content[0...-2]
|
||||
} else if (content.endsWith("\n")) {
|
||||
content = content[0...-1]
|
||||
if (headerEnd < 0) {
|
||||
pos = nextPos
|
||||
continue
|
||||
}
|
||||
|
||||
var headerBytes = Bytes.slice(partData, 0, headerEnd)
|
||||
var headers = ""
|
||||
for (b in Bytes.toList(headerBytes)) {
|
||||
headers = headers + String.fromByte(b)
|
||||
}
|
||||
|
||||
var contentStart = headerEnd + delimLen
|
||||
var contentEnd = partLen
|
||||
var trailCheck = Bytes.slice(partData, partLen - 2, partLen)
|
||||
if (trailCheck == "\r\n") {
|
||||
contentEnd = partLen - 2
|
||||
} else {
|
||||
trailCheck = Bytes.slice(partData, partLen - 1, partLen)
|
||||
if (trailCheck == "\n") {
|
||||
contentEnd = partLen - 1
|
||||
}
|
||||
}
|
||||
var content = Bytes.slice(partData, contentStart, contentEnd)
|
||||
|
||||
var disposition = extractHeader_(headers, "Content-Disposition")
|
||||
if (disposition == null) continue
|
||||
if (disposition == null) {
|
||||
pos = nextPos
|
||||
continue
|
||||
}
|
||||
|
||||
var name = extractDispositionParam_(disposition, "name")
|
||||
if (name == null) continue
|
||||
if (name == null) {
|
||||
pos = nextPos
|
||||
continue
|
||||
}
|
||||
|
||||
var filename = extractDispositionParam_(disposition, "filename")
|
||||
if (filename != null) {
|
||||
var contentStr = ""
|
||||
for (b in Bytes.toList(content)) {
|
||||
contentStr = contentStr + String.fromByte(b)
|
||||
}
|
||||
result[name] = {
|
||||
"filename": filename,
|
||||
"content": content,
|
||||
"content": contentStr,
|
||||
"content_type": extractHeader_(headers, "Content-Type") || "application/octet-stream"
|
||||
}
|
||||
} else {
|
||||
result[name] = content
|
||||
var contentStr = ""
|
||||
for (b in Bytes.toList(content)) {
|
||||
contentStr = contentStr + String.fromByte(b)
|
||||
}
|
||||
result[name] = contentStr
|
||||
}
|
||||
|
||||
pos = nextPos
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -840,6 +890,12 @@ class Application {
|
||||
Scheduler.add {
|
||||
self.handleConnectionSafe_(socket)
|
||||
}
|
||||
var batch = server.acceptBatch(15)
|
||||
for (s in batch) {
|
||||
Scheduler.add {
|
||||
self.handleConnectionSafe_(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,8 +920,15 @@ class Application {
|
||||
var headersComplete = false
|
||||
var headerBytes = 0
|
||||
var headerStr = null
|
||||
var maxReadIterations = 1000
|
||||
var readIterations = 0
|
||||
|
||||
while (true) {
|
||||
readIterations = readIterations + 1
|
||||
if (readIterations > maxReadIterations) {
|
||||
socket.close()
|
||||
return
|
||||
}
|
||||
var chunk = socket.read()
|
||||
if (chunk == null || chunk.bytes.count == 0) {
|
||||
socket.close()
|
||||
@ -960,8 +1023,7 @@ class Application {
|
||||
if (response == null) {
|
||||
var route = _router.match(method, path)
|
||||
if (route != null) {
|
||||
request = Request.new_(method, path, query, headers, body, route["params"], socket)
|
||||
loadSession_(request)
|
||||
request.params = route["params"]
|
||||
for (mw in _middleware) {
|
||||
var result = mw.call(request)
|
||||
if (result != null) {
|
||||
|
||||
@ -44,6 +44,7 @@ static const char* webModuleSource =
|
||||
" headers { _headers }\n"
|
||||
" body { _body }\n"
|
||||
" params { _params }\n"
|
||||
" params=(value) { _params = value }\n"
|
||||
" socket { _socket }\n"
|
||||
"\n"
|
||||
" header(name) {\n"
|
||||
@ -75,43 +76,92 @@ static const char* webModuleSource =
|
||||
" var boundary = extractBoundary_(contentType)\n"
|
||||
" if (boundary == null) return result\n"
|
||||
"\n"
|
||||
" var parts = _body.split(\"--\" + boundary)\n"
|
||||
" for (part in parts) {\n"
|
||||
" var trimmed = part.trim()\n"
|
||||
" if (trimmed.count == 0 || trimmed == \"--\") continue\n"
|
||||
" var delimBytes = (\"--\" + boundary)\n"
|
||||
" var bodyLen = Bytes.length(_body)\n"
|
||||
" var pos = Bytes.indexOf(_body, delimBytes, 0)\n"
|
||||
" if (pos < 0) return result\n"
|
||||
"\n"
|
||||
" var headerEnd = trimmed.indexOf(\"\\r\\n\\r\\n\")\n"
|
||||
" while (pos >= 0) {\n"
|
||||
" pos = pos + Bytes.length(delimBytes)\n"
|
||||
" if (pos >= bodyLen) break\n"
|
||||
"\n"
|
||||
" var nextByte = Bytes.slice(_body, pos, pos + 2)\n"
|
||||
" if (nextByte == \"--\") break\n"
|
||||
"\n"
|
||||
" if (Bytes.slice(_body, pos, pos + 2) == \"\\r\\n\") {\n"
|
||||
" pos = pos + 2\n"
|
||||
" } else if (Bytes.slice(_body, pos, pos + 1) == \"\\n\") {\n"
|
||||
" pos = pos + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var nextPos = Bytes.indexOf(_body, delimBytes, pos)\n"
|
||||
" if (nextPos < 0) nextPos = bodyLen\n"
|
||||
"\n"
|
||||
" var partData = Bytes.slice(_body, pos, nextPos)\n"
|
||||
" var partLen = Bytes.length(partData)\n"
|
||||
"\n"
|
||||
" var headerEnd = Bytes.indexOf(partData, \"\\r\\n\\r\\n\", 0)\n"
|
||||
" var delimLen = 4\n"
|
||||
" if (headerEnd < 0) {\n"
|
||||
" headerEnd = trimmed.indexOf(\"\\n\\n\")\n"
|
||||
" headerEnd = Bytes.indexOf(partData, \"\\n\\n\", 0)\n"
|
||||
" delimLen = 2\n"
|
||||
" }\n"
|
||||
" if (headerEnd < 0) continue\n"
|
||||
"\n"
|
||||
" var headers = trimmed[0...headerEnd]\n"
|
||||
" var content = trimmed[headerEnd + delimLen..-1]\n"
|
||||
" if (content.endsWith(\"\\r\\n\")) {\n"
|
||||
" content = content[0...-2]\n"
|
||||
" } else if (content.endsWith(\"\\n\")) {\n"
|
||||
" content = content[0...-1]\n"
|
||||
" if (headerEnd < 0) {\n"
|
||||
" pos = nextPos\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var headerBytes = Bytes.slice(partData, 0, headerEnd)\n"
|
||||
" var headers = \"\"\n"
|
||||
" for (b in Bytes.toList(headerBytes)) {\n"
|
||||
" headers = headers + String.fromByte(b)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var contentStart = headerEnd + delimLen\n"
|
||||
" var contentEnd = partLen\n"
|
||||
" var trailCheck = Bytes.slice(partData, partLen - 2, partLen)\n"
|
||||
" if (trailCheck == \"\\r\\n\") {\n"
|
||||
" contentEnd = partLen - 2\n"
|
||||
" } else {\n"
|
||||
" trailCheck = Bytes.slice(partData, partLen - 1, partLen)\n"
|
||||
" if (trailCheck == \"\\n\") {\n"
|
||||
" contentEnd = partLen - 1\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" var content = Bytes.slice(partData, contentStart, contentEnd)\n"
|
||||
"\n"
|
||||
" var disposition = extractHeader_(headers, \"Content-Disposition\")\n"
|
||||
" if (disposition == null) continue\n"
|
||||
" if (disposition == null) {\n"
|
||||
" pos = nextPos\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var name = extractDispositionParam_(disposition, \"name\")\n"
|
||||
" if (name == null) continue\n"
|
||||
" if (name == null) {\n"
|
||||
" pos = nextPos\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var filename = extractDispositionParam_(disposition, \"filename\")\n"
|
||||
" if (filename != null) {\n"
|
||||
" var contentStr = \"\"\n"
|
||||
" for (b in Bytes.toList(content)) {\n"
|
||||
" contentStr = contentStr + String.fromByte(b)\n"
|
||||
" }\n"
|
||||
" result[name] = {\n"
|
||||
" \"filename\": filename,\n"
|
||||
" \"content\": content,\n"
|
||||
" \"content\": contentStr,\n"
|
||||
" \"content_type\": extractHeader_(headers, \"Content-Type\") || \"application/octet-stream\"\n"
|
||||
" }\n"
|
||||
" } else {\n"
|
||||
" result[name] = content\n"
|
||||
" var contentStr = \"\"\n"
|
||||
" for (b in Bytes.toList(content)) {\n"
|
||||
" contentStr = contentStr + String.fromByte(b)\n"
|
||||
" }\n"
|
||||
" result[name] = contentStr\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" pos = nextPos\n"
|
||||
" }\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
@ -844,6 +894,12 @@ static const char* webModuleSource =
|
||||
" Scheduler.add {\n"
|
||||
" self.handleConnectionSafe_(socket)\n"
|
||||
" }\n"
|
||||
" var batch = server.acceptBatch(15)\n"
|
||||
" for (s in batch) {\n"
|
||||
" Scheduler.add {\n"
|
||||
" self.handleConnectionSafe_(s)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
@ -868,8 +924,15 @@ static const char* webModuleSource =
|
||||
" var headersComplete = false\n"
|
||||
" var headerBytes = 0\n"
|
||||
" var headerStr = null\n"
|
||||
" var maxReadIterations = 1000\n"
|
||||
" var readIterations = 0\n"
|
||||
"\n"
|
||||
" while (true) {\n"
|
||||
" readIterations = readIterations + 1\n"
|
||||
" if (readIterations > maxReadIterations) {\n"
|
||||
" socket.close()\n"
|
||||
" return\n"
|
||||
" }\n"
|
||||
" var chunk = socket.read()\n"
|
||||
" if (chunk == null || chunk.bytes.count == 0) {\n"
|
||||
" socket.close()\n"
|
||||
@ -964,8 +1027,7 @@ static const char* webModuleSource =
|
||||
" if (response == null) {\n"
|
||||
" var route = _router.match(method, path)\n"
|
||||
" if (route != null) {\n"
|
||||
" request = Request.new_(method, path, query, headers, body, route[\"params\"], socket)\n"
|
||||
" loadSession_(request)\n"
|
||||
" request.params = route[\"params\"]\n"
|
||||
" for (mw in _middleware) {\n"
|
||||
" var result = mw.call(request)\n"
|
||||
" if (result != null) {\n"
|
||||
|
||||
3
test/io/stdin/read_byte_eof.wren
vendored
3
test/io/stdin/read_byte_eof.wren
vendored
@ -1,4 +1,5 @@
|
||||
// skip:
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "io" for Stdin
|
||||
|
||||
Stdin.readLine() // stdin: one line
|
||||
|
||||
3
test/io/stdin/read_line.wren
vendored
3
test/io/stdin/read_line.wren
vendored
@ -1,4 +1,5 @@
|
||||
// skip:
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "io" for Stdin
|
||||
|
||||
System.write("> ")
|
||||
|
||||
3
test/io/stdin/read_line_eof.wren
vendored
3
test/io/stdin/read_line_eof.wren
vendored
@ -1,4 +1,5 @@
|
||||
// skip:
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "io" for Stdin
|
||||
|
||||
Stdin.readLine() // stdin: one line
|
||||
|
||||
3
test/regex/error_invalid_pattern.wren
vendored
3
test/regex/error_invalid_pattern.wren
vendored
@ -1,6 +1,5 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
// skip: double free bug in C implementation when handling invalid regex patterns
|
||||
|
||||
import "regex" for Regex, Match
|
||||
|
||||
var re = Regex.new("[unclosed")
|
||||
var re = Regex.new("[unclosed") // expect runtime error: Unmatched [, [^, [:, [., or [=
|
||||
|
||||
1
test/web/multipart_binary.wren
vendored
1
test/web/multipart_binary.wren
vendored
@ -1,5 +1,4 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
// skip: multipart parser has pre-existing issues with null bytes and high-byte values
|
||||
|
||||
import "web" for Request
|
||||
|
||||
|
||||
28
util/test.py
28
util/test.py
@ -27,6 +27,8 @@ parser.add_argument('--suffix', default='')
|
||||
parser.add_argument('suite', nargs='?')
|
||||
parser.add_argument('--skip-tests', action='store_true')
|
||||
parser.add_argument('--skip-examples', action='store_true')
|
||||
parser.add_argument('--valgrind', action='store_true',
|
||||
help='Run tests under valgrind')
|
||||
|
||||
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
@ -150,10 +152,15 @@ class Test:
|
||||
return True
|
||||
|
||||
|
||||
def run(self, app, type):
|
||||
# Invoke wren and run the test.
|
||||
def run(self, app, type, valgrind=False):
|
||||
test_arg = self.path
|
||||
proc = Popen([app, test_arg], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
if valgrind:
|
||||
cmd = ['valgrind', '--leak-check=full', '--error-exitcode=99',
|
||||
'--suppressions=' + join(dirname(realpath(__file__)), 'valgrind.supp'),
|
||||
app, test_arg]
|
||||
else:
|
||||
cmd = [app, test_arg]
|
||||
proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
|
||||
# If a test takes longer than five seconds, kill it.
|
||||
#
|
||||
@ -173,12 +180,14 @@ class Test:
|
||||
if timed_out[0]:
|
||||
self.fail("Timed out.")
|
||||
else:
|
||||
self.validate(type == "example", proc.returncode, out, err)
|
||||
if valgrind and proc.returncode == 99:
|
||||
self.fail("Valgrind detected memory errors")
|
||||
self.validate(type == "example", proc.returncode, out, err, valgrind)
|
||||
finally:
|
||||
timer.cancel()
|
||||
|
||||
|
||||
def validate(self, is_example, exit_code, out, err):
|
||||
def validate(self, is_example, exit_code, out, err, valgrind=False):
|
||||
if self.compile_errors and self.runtime_error_message:
|
||||
self.fail("Test error: Cannot expect both compile and runtime errors.")
|
||||
return
|
||||
@ -189,6 +198,13 @@ class Test:
|
||||
except:
|
||||
self.fail('Error decoding output.')
|
||||
|
||||
if valgrind:
|
||||
err_lines = []
|
||||
for line in err.split('\n'):
|
||||
if not line.startswith('==') and not line.startswith('--'):
|
||||
err_lines.append(line)
|
||||
err = '\n'.join(err_lines)
|
||||
|
||||
error_lines = err.split('\n')
|
||||
|
||||
# Validate that an expected runtime error occurred.
|
||||
@ -376,7 +392,7 @@ def run_script(app, path, type):
|
||||
# It's a skipped or non-test file.
|
||||
return
|
||||
|
||||
test.run(app, type)
|
||||
test.run(app, type, args.valgrind)
|
||||
|
||||
# Display the results.
|
||||
if len(test.failures) == 0:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user