diff --git a/makefile b/makefile index b0715b2..a48a39b 100644 --- a/makefile +++ b/makefile @@ -23,6 +23,7 @@ test:nasal @ ./nasal -op -t -d test/fib.nas @ ./nasal -op -e test/filesystem.nas @ ./nasal -op -e -d test/hexdump.nas + @ ./nasal -op -c test/httptest.nas @ ./nasal -op -e test/json.nas @ ./nasal -op -e test/leetcode1319.nas @ ./nasal -op -e -d test/lexer.nas diff --git a/module/libsock.nas b/module/libsock.nas index 573f30f..c7fb8df 100644 --- a/module/libsock.nas +++ b/module/libsock.nas @@ -2,6 +2,16 @@ var socket=func(){ var lib=dylib.dlopen("./module/libnasock"~(os.platform()=="windows"?".dll":".so")); var sock=dylib.dlsym(lib,"nas_socket"); var closesocket=dylib.dlsym(lib,"nas_closesocket"); + var shutdown=dylib.dlsym(lib,"nas_shutdown"); + var bind=dylib.dlsym(lib,"nas_bind"); + var listen=dylib.dlsym(lib,"nas_listen"); + var connect=dylib.dlsym(lib,"nas_connect"); + var accept=dylib.dlsym(lib,"nas_accept"); + var send=dylib.dlsym(lib,"nas_send"); + var sendto=dylib.dlsym(lib,"nas_sendto"); + var recv=dylib.dlsym(lib,"nas_recv"); + var recvfrom=dylib.dlsym(lib,"nas_recvfrom"); + var errno=dylib.dlsym(lib,"nas_errno"); var call=dylib.dlcall; return { AF_UNSPEC:0,AF_UNIX:1,AF_INET:2,AF_IMPLINK:3, @@ -27,11 +37,49 @@ var socket=func(){ IPPORT_EFSSERVER:520,IPPORT_BIFFUDP:512,IPPORT_WHOSERVER:513,IPPORT_ROUTESERVER:520, IPPORT_RESERVED:1024, + SHUT_RD :0x00, + SHUT_WR :0x01, + SHUT_RDWR:0x02, + + MSG_OOB:0x1, + MSG_PEEK:0x2, + MSG_DONTROUTE:0x4, + socket:func(af,type,proto){ return call(sock,af,type,proto); }, closesocket:func(sd){ return call(closesocket,sd); + }, + shutdown: func(sd,how){ + return call(shutdown,sd,how); + }, + bind: func(sd,ip,port){ + return call(bind,sd,ip,port); + }, + listen: func(sd,backlog){ + return call(listen,sd,backlog); + }, + connect: func(sd,hostname,port){ + return call(connect,sd,hostname,port); + }, + accept: func(sd){ + return call(accept,sd); + }, + send: func(sd,buff,flags=0){ + return call(send,sd,buff,flags); + }, + sendto: func(sd,hostname,port,buff,flags=0){ + return call(sendto,sd,hostname,port,buff,flags); + }, + recv: func(sd,len,flags=0){ + return call(recv,sd,len,flags); + }, + recvfrom: func(sd,len,flags=0){ + return call(recvfrom,sd,len,flags); + }, + errno: func(){ + return call(errno); } }; }(); \ No newline at end of file diff --git a/module/makefile b/module/makefile index 88a6474..7983ecb 100644 --- a/module/makefile +++ b/module/makefile @@ -25,7 +25,7 @@ libnasock.dll: nasocket.cpp clean: rm *.o *.so *.dll *.dylib -all: libfib.so libkey.so +all: libfib.so libkey.so libnasock.so @ echo "build done" -mingw-all: libfib.dll libkey.dll +mingw-all: libfib.dll libkey.dll libnasock.dll @ echo "build done" \ No newline at end of file diff --git a/module/nasocket.cpp b/module/nasocket.cpp index f69a984..46d8dba 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -1,4 +1,5 @@ #include "../nasal.h" +#include #ifdef _WIN32 #include @@ -18,12 +19,13 @@ public: static WSAmanager win; #else +#include #include +#include +#include #endif extern "C" nasal_ref nas_socket(std::vector& args,nasal_gc& gc){ - if(args.size()!=3) - return builtin_err("socket","invalid arguments"); if(args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num) return builtin_err("socket","\"af\", \"type\", \"protocol\" should be number"); int sd=socket(args[0].num(),args[1].num(),args[2].num()); @@ -31,8 +33,6 @@ extern "C" nasal_ref nas_socket(std::vector& args,nasal_gc& gc){ } extern "C" nasal_ref nas_closesocket(std::vector& args,nasal_gc& gc){ - if(args.size()!=1) - return builtin_err("closesocket","invalid arguments"); if(args[0].type!=vm_num) return builtin_err("closesocket","\"\" should be number"); #ifdef _WIN32 @@ -40,4 +40,162 @@ extern "C" nasal_ref nas_closesocket(std::vector& args,nasal_gc& gc){ #else return {vm_num,(double)close(args[0].num())}; #endif +} + +extern "C" nasal_ref nas_shutdown(std::vector& args,nasal_gc& gc){ + //shutdown(); + if(args[0].type!=vm_num) + return builtin_err("shutdown","\"sd\" must be a number"); + if(args[1].type!=vm_num) + return builtin_err("shutdown","\"how\" must be a number"); + return {vm_num,(double)shutdown(args[0].num(),args[1].num())}; +} + +extern "C" nasal_ref nas_bind(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("bind","\"sd\" muse be a number"); + if(args[1].type!=vm_str) + return builtin_err("bind","\"ip\" should be a string including an ip with correct format"); + if(args[2].type!=vm_num) + return builtin_err("bind","\"port\" must be a number"); + sockaddr_in server; + memset(&server,0,sizeof(sockaddr_in)); + server.sin_family=AF_INET; + server.sin_addr.s_addr=inet_addr(args[1].str().c_str()); + server.sin_port=htons(args[2].num()); + return {vm_num,(double)bind(args[0].num(),(sockaddr*)&server,sizeof(server))}; +} + +extern "C" nasal_ref nas_listen(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("listen","\"sd\" must be a number"); + if(args[1].type!=vm_num) + return builtin_err("listen","\"backlog\" must be a number"); + return{vm_num,(double)listen(args[0].num(),args[1].num())}; +} + +extern "C" nasal_ref nas_connect(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("connect","\"sd\" must be a number"); + if(args[1].type!=vm_str) + return builtin_err("connect","\"hostname\" must be a string"); + if(args[2].type!=vm_num) + return builtin_err("connect","\"port\" must be a number"); + sockaddr_in addr; + memset(&addr,0,sizeof(sockaddr_in)); + addr.sin_family=AF_INET; + addr.sin_port=htons(args[2].num()); + hostent* entry=gethostbyname(args[1].str().c_str()); + memcpy(&addr.sin_addr,entry->h_addr,entry->h_length); + return {vm_num,(double)connect(args[0].num(),(sockaddr*)&addr,sizeof(sockaddr_in))}; +} + +extern "C" nasal_ref nas_accept(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("accept","\"sd\" must be a number"); + sockaddr_in client; + int socklen=sizeof(sockaddr_in); +#ifdef _WIN32 + int client_sd=accept(args[0].num(),(sockaddr*)&client,&socklen); +#else + int client_sd=accept(args[0].num(),(sockaddr*)&client,(socklen_t*)&socklen); +#endif + if(gc.top+1>=gc.canary) + return builtin_err("accept","expand temporary space error:stackoverflow"); + (++gc.top)[0]=gc.alloc(vm_hash); + auto& hash=gc.top[0].hash().elems; + hash["sd"]={vm_num,(double)client_sd}; + hash["ip"]=gc.alloc(vm_str); + hash["ip"].str()=inet_ntoa(client.sin_addr); + --gc.top; + return gc.top[1]; +} + +extern "C" nasal_ref nas_send(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("send","\"sd\" must be a number"); + if(args[1].type!=vm_str) + return builtin_err("send","\"buff\" must be a string"); + if(args[2].type!=vm_num) + return builtin_err("send","\"flags\" muse be a number"); + return {vm_num,(double)send(args[0].num(),args[1].str().c_str(),args[1].str().length(),args[2].num())}; +} + +extern "C" nasal_ref nas_sendto(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("sendto","\"sd\" must be a number"); + if(args[1].type!=vm_str) + return builtin_err("sendto","\"hostname\" must be a string"); + if(args[2].type!=vm_num) + return builtin_err("sendto","\"port\" must be a number"); + if(args[3].type!=vm_str) + return builtin_err("sendto","\"buff\" must be a string"); + if(args[4].type!=vm_num) + return builtin_err("sendto","\"flags\" must be a number"); + sockaddr_in addr; + memset(&addr,0,sizeof(sockaddr_in)); + addr.sin_family=AF_INET; + addr.sin_port=htons(args[2].num()); + hostent* entry=gethostbyname(args[1].str().c_str()); + memcpy(&addr.sin_addr,entry->h_addr,entry->h_length); + return {vm_num,(double)sendto(args[0].num(),args[3].str().c_str(),args[3].str().length(),args[4].num(),(sockaddr*)&addr,sizeof(sockaddr_in))}; +} + +extern "C" nasal_ref nas_recv(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("recv","\"sd\" must be a number"); + if(args[1].type!=vm_num) + return builtin_err("recv","\"len\" must be a number"); + if(args[1].num()<=0 || args[1].num()>16*1024*1024) + return builtin_err("recv","\"len\" out of range"); + if(args[2].type!=vm_num) + return builtin_err("recv","\"flags\" muse be a number"); + if(gc.top+1>=gc.canary) + return builtin_err("recv","expand temporary space error:stackoverflow"); + (++gc.top)[0]=gc.alloc(vm_hash); + auto& hash=gc.top[0].hash().elems; + hash["str"]=gc.alloc(vm_str); + char* buf=new char[(int)args[1].num()]; + hash["size"]={vm_num,(double)recv(args[0].num(),buf,args[1].num(),args[2].num())}; + hash["str"].str()=buf; + delete[] buf; + --gc.top; + return gc.top[1]; +} + +extern "C" nasal_ref nas_recvfrom(std::vector& args,nasal_gc& gc){ + if(args[0].type!=vm_num) + return builtin_err("recvfrom","\"sd\" must be a number"); + if(args[1].type!=vm_num) + return builtin_err("recvfrom","\"len\" must be a number"); + if(args[1].num()<=0 || args[1].num()>16*1024*1024) + return builtin_err("recvfrom","\"len\" out of range"); + if(args[2].type!=vm_num) + return builtin_err("recvfrom","\"flags\" muse be a number"); + if(gc.top+1>=gc.canary) + return builtin_err("recvfrom","expand temporary space error:stackoverflow"); + sockaddr_in addr; + int socklen=sizeof(sockaddr_in); + (++gc.top)[0]=gc.alloc(vm_hash); + auto& hash=gc.top[0].hash().elems; + hash["str"]=gc.alloc(vm_str); + char* buf=new char[(int)args[1].num()+1]; +#ifdef _WIN32 + hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,&socklen)}; +#else + hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,(socklen_t*)&socklen)}; +#endif + buf[(int)hash["size"].num()]=0; + hash["str"].str()=buf; + delete[] buf; + hash["fromip"]=gc.alloc(vm_str); + hash["fromip"].str()=inet_ntoa(addr.sin_addr); + --gc.top; + return gc.top[1]; +} + +extern "C" nasal_ref nas_errno(std::vector& args,nasal_gc& gc){ + nasal_ref res=gc.alloc(vm_str); + res.str()=strerror(errno); + return res; } \ No newline at end of file diff --git a/test/calc.nas b/test/calc.nas index 93323eb..d118415 100644 --- a/test/calc.nas +++ b/test/calc.nas @@ -44,6 +44,7 @@ var testfile=[ "test/fib.nas ", "test/filesystem.nas ", "test/hexdump.nas ", + "test/httptest.nas ", "test/json.nas ", "test/leetcode1319.nas ", "test/lexer.nas ", @@ -73,8 +74,10 @@ var testfile=[ var module=[ "module/fib.cpp ", "module/keyboard.cpp ", + "module/nasocket.cpp ", "module/libfib.nas ", - "module/libkey.nas " + "module/libkey.nas ", + "module/libsock.nas " ]; var getname=func(s){ diff --git a/test/httptest.nas b/test/httptest.nas new file mode 100644 index 0000000..4933f4b --- /dev/null +++ b/test/httptest.nas @@ -0,0 +1,34 @@ +import("module/libsock.nas"); + +var sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP); +socket.bind(sd,"127.0.0.1",8080); +socket.listen(sd,1); + +var client=socket.accept(sd); +println("request ip: ",client.ip); +var rv=socket.recv(client.sd,1024); +println(rv.str); +socket.send(client.sd,"Http/1.1 200 OK + + + + nasal-http-test-web + + + + Hello, this is a simple HTML document just for test.
+ This simple http server is written in nasal.
+ Nasal is an ECMAscript-like programming language that used in Flightgear.
+ This language is designed by Andy Ross.
+ + The interpreter is totally rewritten by ValKmjolnir using C++(-std=c++11) without reusing the code in Andy Ross's nasal interpreter.
+ But we really appreciate that Andy created this amazing programming language and his interpreter project.
+ + Now this project uses MIT license(2021/5/4). Edit it if you want, use this project to learn or create more interesting things(But don't forget me XD). +
+ + + +"); +socket.closesocket(client.sd); +socket.closesocket(sd); \ No newline at end of file diff --git a/test/md5compare.nas b/test/md5compare.nas index 9f64c98..b32295b 100644 --- a/test/md5compare.nas +++ b/test/md5compare.nas @@ -75,6 +75,7 @@ var filechecksum=func(){ "./test/fib.nas ", "./test/filesystem.nas ", "./test/hexdump.nas ", + "./test/httptest.nas ", "./test/json.nas ", "./test/leetcode1319.nas ", "./test/lexer.nas ",