Newer
Older
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx,getprivate_failure,WHERE);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
if((opt = getSocketOptionByName(JS_GetStringBytes(JS_ValueToString(cx,argv[0])),&level)) == -1) {
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
if(opt == SO_LINGER) {
vp=&linger;
len=sizeof(linger);
}
if(getsockopt(p->sock, level, opt, vp, &len)==0) {
if(opt == SO_LINGER) {
if(linger.l_onoff==TRUE)
val = linger.l_linger;
else
val = 0;
}
JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(val));
p->last_error=ERROR_VALUE;
dbprintf(TRUE, p, "error %d getting option %d"
,ERROR_VALUE,opt);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_setsockopt(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
private_t* p;
LINGER linger;
void* vp=&val;
socklen_t len=sizeof(val);
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx,getprivate_failure,WHERE);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
opt = getSocketOptionByName(JS_GetStringBytes(JS_ValueToString(cx,argv[0])),&level);
JS_RESUMEREQUEST(cx, rc);
JS_ValueToInt32(cx,argv[1],&val);
rc=JS_SUSPENDREQUEST(cx);
if(opt == SO_LINGER) {
if(val) {
linger.l_onoff = TRUE;
linger.l_linger = (ushort)val;
} else {
ZERO_VAR(linger);
}
vp=&linger;
len=sizeof(linger);
}
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(
setsockopt(p->sock, level, opt, vp, len)==0));
p->last_error=ERROR_VALUE;
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
js_ioctlsocket(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
int32 arg=0;
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx,getprivate_failure,WHERE);
if(argv[0]!=JSVAL_VOID)
JS_ValueToInt32(cx,argv[0],&cmd);
if(argc>1 && argv[1]!=JSVAL_VOID)
JS_ValueToInt32(cx,argv[1],&arg);
rc=JS_SUSPENDREQUEST(cx);
if(ioctlsocket(p->sock,cmd,(ulong*)&arg)==0) {
JS_RESUMEREQUEST(cx, rc);
JS_SET_RVAL(cx, arglist,INT_TO_JSVAL(arg));
JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
JS_RESUMEREQUEST(cx, rc);
p->last_error=ERROR_VALUE;
return(JS_TRUE);
}
static JSBool
js_poll(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
private_t* p;
BOOL poll_for_write=FALSE;
fd_set socket_set;
fd_set* rd_set=NULL;
fd_set* wr_set=NULL;
uintN argn;
int result;
struct timeval tv = {0, 0};
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx,getprivate_failure,WHERE);
return(JS_FALSE);
if(p->sock==INVALID_SOCKET) {
dbprintf(TRUE, p, "INVALID SOCKET in call to poll");
JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
return(JS_TRUE);
}
for(argn=0;argn<argc;argn++) {
if(JSVAL_IS_BOOLEAN(argv[argn]))
poll_for_write=JSVAL_TO_BOOLEAN(argv[argn]);
else if(JSVAL_IS_NUMBER(argv[argn]))
js_timeval(cx,argv[argn],&tv);
}
rc=JS_SUSPENDREQUEST(cx);
FD_ZERO(&socket_set);
FD_SET(p->sock,&socket_set);
if(poll_for_write)
wr_set=&socket_set;
else
rd_set=&socket_set;
result = select(p->sock+1,rd_set,wr_set,NULL,&tv);
p->last_error=ERROR_VALUE;
dbprintf(FALSE, p, "poll: select returned %d (errno %d)"
,result,p->last_error);
JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(result));
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
/* Socket Object Properites */
enum {
SOCK_PROP_LAST_ERROR
,SOCK_PROP_IS_CONNECTED
,SOCK_PROP_IS_WRITEABLE
,SOCK_PROP_DATA_WAITING
,SOCK_PROP_NREAD
,SOCK_PROP_LOCAL_IP
,SOCK_PROP_LOCAL_PORT
,SOCK_PROP_REMOTE_IP
,SOCK_PROP_REMOTE_PORT
,SOCK_PROP_TYPE
,SOCK_PROP_NETWORK_ORDER
#ifdef BUILD_JSDOCS
static char* socket_prop_desc[] = {
"error status for the last socket operation that failed - <small>READ ONLY</small>"
,"<i>true</i> if socket is in a connected state - <small>READ ONLY</small>"
,"<i>true</i> if socket can accept written data - Setting to false will shutdown the write end of the socket."
,"<i>true</i> if data is waiting to be read from socket - <small>READ ONLY</small>"
,"number of bytes waiting to be read - <small>READ ONLY</small>"
,"use non-blocking operation (default is <i>false</i>)"
,"local IP address (string in dotted-decimal format)"
,"local TCP or UDP port number"
,"remote IP address (string in dotted-decimal format)"
,"remote TCP or UDP port number"
,"socket type, <tt>SOCK_STREAM</tt> (TCP) or <tt>SOCK_DGRAM</tt> (UDP)"
,"<i>true</i> if binary data is to be sent in Network Byte Order (big end first), default is <i>true</i>"
/* statically-defined properties: */
,"array of socket option names supported by the current platform"
static JSBool js_socket_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
jsval idval;
private_t* p;
BOOL b;
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
// Prototype access
return(JS_TRUE);
JS_IdToValue(cx, id, &idval);
tiny = JSVAL_TO_INT(idval);
rc=JS_SUSPENDREQUEST(cx);
dbprintf(FALSE, p, "setting property %d",tiny);
JS_RESUMEREQUEST(cx, rc);
switch(tiny) {
case SOCK_PROP_DEBUG:
JS_ValueToBoolean(cx,*vp,&(p->debug));
JS_ValueToInt32(cx,*vp,(int32*)&(p->sock));
p->is_connected=TRUE;
break;
case SOCK_PROP_LAST_ERROR:
JS_ValueToInt32(cx,*vp,(int32*)&(p->last_error));
case SOCK_PROP_NONBLOCKING:
JS_ValueToBoolean(cx,*vp,&(p->nonblocking));
rc=JS_SUSPENDREQUEST(cx);
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
JS_RESUMEREQUEST(cx, rc);
case SOCK_PROP_NETWORK_ORDER:
JS_ValueToBoolean(cx,*vp,&(p->network_byte_order));
break;
case SOCK_PROP_IS_WRITEABLE:
JS_ValueToBoolean(cx,*vp,&b);
if(!b)
shutdown(p->sock,SHUT_WR);
break;
static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
jsval idval;
ulong cnt;
BOOL rd;
BOOL wr;
private_t* p;
SOCKADDR_IN addr;
socklen_t len=sizeof(addr);
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
// Protoype access
return(JS_TRUE);
JS_IdToValue(cx, id, &idval);
tiny = JSVAL_TO_INT(idval);
rc=JS_SUSPENDREQUEST(cx);
*vp = INT_TO_JSVAL(p->last_error);
if(!p->is_connected)
*vp = JSVAL_FALSE;
else
*vp = BOOLEAN_TO_JSVAL(socket_check(p->sock,NULL,NULL,0));
case SOCK_PROP_IS_WRITEABLE:
socket_check(p->sock,NULL,&wr,0);
*vp = BOOLEAN_TO_JSVAL(wr);
break;
case SOCK_PROP_DATA_WAITING:
socket_check(p->sock,&rd,NULL,0);
*vp = BOOLEAN_TO_JSVAL(rd);
break;
case SOCK_PROP_NREAD:
cnt=0;
if(ioctlsocket(p->sock, FIONREAD, &cnt)==0) {
*vp=DOUBLE_TO_JSVAL((double)cnt);
*vp = INT_TO_JSVAL(p->debug);
*vp = INT_TO_JSVAL(p->sock);
case SOCK_PROP_NONBLOCKING:
*vp = BOOLEAN_TO_JSVAL(p->nonblocking);
break;
case SOCK_PROP_LOCAL_IP:
if(p->sock != INVALID_SOCKET) {
if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0)
return(JS_FALSE);
JS_RESUMEREQUEST(cx, rc);
if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
rc=JS_SUSPENDREQUEST(cx);
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_LOCAL_PORT:
if(p->sock != INVALID_SOCKET) {
if(getsockname(p->sock, (struct sockaddr *)&addr,&len)!=0)
return(JS_FALSE);
JS_RESUMEREQUEST(cx, rc);
if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL)
return(JS_FALSE);
*vp = INT_TO_JSVAL(ntohs(addr.sin_port));
rc=JS_SUSPENDREQUEST(cx);
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_REMOTE_IP:
if(p->is_connected) {
JS_RESUMEREQUEST(cx, rc);
if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(p->remote_addr.sin_addr)))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
rc=JS_SUSPENDREQUEST(cx);
else
*vp=JSVAL_VOID;
break;
case SOCK_PROP_REMOTE_PORT:
if(p->is_connected)
*vp = INT_TO_JSVAL(ntohs(p->remote_addr.sin_port));
else
*vp=JSVAL_VOID;
case SOCK_PROP_TYPE:
*vp = INT_TO_JSVAL(p->type);
break;
case SOCK_PROP_NETWORK_ORDER:
*vp = BOOLEAN_TO_JSVAL(p->network_byte_order);
break;
JS_RESUMEREQUEST(cx, rc);
#define SOCK_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
static jsSyncPropertySpec js_socket_properties[] = {
/* name ,tinyid ,flags, ver */
{ "error" ,SOCK_PROP_LAST_ERROR ,SOCK_PROP_FLAGS, 311 },
{ "last_error" ,SOCK_PROP_LAST_ERROR ,JSPROP_READONLY, 310 }, /* alias */
{ "is_connected" ,SOCK_PROP_IS_CONNECTED ,SOCK_PROP_FLAGS, 310 },
{ "is_writeable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_ENUMERATE, 311 },
{ "is_writable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_ENUMERATE, 312 }, /* alias */
{ "data_waiting" ,SOCK_PROP_DATA_WAITING ,SOCK_PROP_FLAGS, 310 },
{ "nread" ,SOCK_PROP_NREAD ,SOCK_PROP_FLAGS, 310 },
{ "debug" ,SOCK_PROP_DEBUG ,JSPROP_ENUMERATE, 310 },
{ "descriptor" ,SOCK_PROP_DESCRIPTOR ,JSPROP_ENUMERATE, 310 },
{ "nonblocking" ,SOCK_PROP_NONBLOCKING ,JSPROP_ENUMERATE, 310 },
{ "local_ip_address" ,SOCK_PROP_LOCAL_IP ,SOCK_PROP_FLAGS, 310 },
{ "local_port" ,SOCK_PROP_LOCAL_PORT ,SOCK_PROP_FLAGS, 310 },
{ "remote_ip_address" ,SOCK_PROP_REMOTE_IP ,SOCK_PROP_FLAGS, 310 },
{ "remote_port" ,SOCK_PROP_REMOTE_PORT ,SOCK_PROP_FLAGS, 310 },
{ "type" ,SOCK_PROP_TYPE ,SOCK_PROP_FLAGS, 310 },
{ "network_byte_order",SOCK_PROP_NETWORK_ORDER,JSPROP_ENUMERATE, 311 },
static jsSyncMethodSpec js_socket_functions[] = {
,JSDOCSTR("close (shutdown) the socket immediately")
,310
},
{"bind", js_bind, 0, JSTYPE_BOOLEAN, JSDOCSTR("[port] [,ip_address]")
,JSDOCSTR("bind socket to a TCP or UDP <i>port</i> (number or service name), "
"optionally specifying a network interface (via <i>ip_address</i>)")
,311
},
{"connect", js_connect, 2, JSTYPE_BOOLEAN, JSDOCSTR("host, port [,timeout=<tt>10.0</tt>]")
,JSDOCSTR("connect to a remote port (number or service name) on the specified host (IP address or host name)"
", default <i>timeout</i> value is <i>10.0</i> (seconds)")
,311
},
{"listen", js_listen, 0, JSTYPE_BOOLEAN, JSDOCSTR("")
,JSDOCSTR("place socket in a state to listen for incoming connections (use before an accept)")
,310
},
{"accept", js_accept, 0, JSTYPE_OBJECT, JSDOCSTR("")
,JSDOCSTR("accept an incoming connection, returns a new <i>Socket</i> object representing the new connection")
,310
},
{"send", js_send, 1, JSTYPE_BOOLEAN, JSDOCSTR("data")
,310
},
{"sendto", js_sendto, 3, JSTYPE_BOOLEAN, JSDOCSTR("data, address, port")
,JSDOCSTR("send data to a specific host (IP address or host name) and port (number or service name), for UDP sockets")
,310
},
{"sendfile", js_sendfile, 1, JSTYPE_BOOLEAN, JSDOCSTR("path/filename")
,JSDOCSTR("send an entire file over the socket")
,310
},
{"writeBin", js_sendbin, 1, JSTYPE_ALIAS },
{"sendBin", js_sendbin, 1, JSTYPE_BOOLEAN, JSDOCSTR("value [,bytes=<tt>4</tt>]")
,JSDOCSTR("send a binary integer over the socket, default number of bytes is 4 (32-bits)")
,311
{"recv", js_recv, 1, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>]")
,JSDOCSTR("receive a string, default maxlen is 512 characters (AKA read)")
,310
},
{"peek", js_peek, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>]")
,JSDOCSTR("receive a string, default maxlen is 512 characters, leaves string in receive buffer")
,310
},
{"readline", js_recvline, 0, JSTYPE_ALIAS },
{"readln", js_recvline, 0, JSTYPE_ALIAS },
{"recvline", js_recvline, 0, JSTYPE_STRING, JSDOCSTR("[maxlen=<tt>512</tt>] [,timeout=<tt>30.0</tt>]")
,JSDOCSTR("receive a line-feed terminated string, default maxlen is 512 characters, default timeout is 30 seconds (AKA readline and readln)")
,310
{"recvfrom", js_recvfrom, 0, JSTYPE_OBJECT, JSDOCSTR("[binary=<tt>false</tt>] [,maxlen=<tt>512</tt> or int_size=<tt>4</tt>]")
,JSDOCSTR("receive data (string or integer) from a socket (typically UDP)"
"<p>returns object with <i>ip_address</i> and <i>port</i> of sender along with <i>data</i> properties"
"<p><i>binary</i> defaults to <i>false</i>, <i>maxlen</i> defaults to 512 chars, <i>int_size</i> defaults to 4 bytes (32-bits)")
,311
},
{"readBin", js_recvbin, 0, JSTYPE_ALIAS },
{"recvBin", js_recvbin, 0, JSTYPE_NUMBER, JSDOCSTR("[bytes=<tt>4</tt>]")
,JSDOCSTR("receive a binary integer from the socket, default number of bytes is 4 (32-bits)")
,311
},
{"getoption", js_getsockopt, 1, JSTYPE_NUMBER, JSDOCSTR("option")
,JSDOCSTR("get socket option value, option may be socket option name "
"(see <tt>sockopts</tt> in <tt>sockdefs.js</tt>) or number")
,310
},
{"setoption", js_setsockopt, 2, JSTYPE_BOOLEAN, JSDOCSTR("option, value")
,JSDOCSTR("set socket option value, option may be socket option name "
"(see <tt>sockopts</tt> in <tt>sockdefs.js</tt>) or number")
,310
},
{"ioctl", js_ioctlsocket, 1, JSTYPE_NUMBER, JSDOCSTR("command [,argument=<tt>0</tt>]")
,JSDOCSTR("send socket IOCTL (advanced)")
,310
},
{"poll", js_poll, 1, JSTYPE_NUMBER, JSDOCSTR("[timeout=<tt>0</tt>] [,write=<tt>false</tt>]")
,JSDOCSTR("poll socket for read or write ability (default is <i>read</i>), "
"default timeout value is 0.0 seconds (immediate timeout)")
,310
},
static JSBool js_socket_resolve(JSContext *cx, JSObject *obj, jsid id)
if(id != JSID_VOID && id != JSID_EMPTY) {
jsval idval;
JS_IdToValue(cx, id, &idval);
name=JS_GetStringBytes(JSVAL_TO_STRING(idval));
}
return(js_SyncResolve(cx, obj, name, js_socket_properties, js_socket_functions, NULL, 0));
}
static JSBool js_socket_enumerate(JSContext *cx, JSObject *obj)
{
}
static JSClass js_socket_class = {
"Socket" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
,JS_PropertyStub /* delProperty */
,js_socket_get /* getProperty */
,js_socket_set /* setProperty */
,js_socket_enumerate /* enumerate */
,js_socket_resolve /* resolve */
,JS_ConvertStub /* convert */
,js_finalize_socket /* finalize */
};
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
static BOOL js_DefineSocketOptionsArray(JSContext *cx, JSObject *obj, int type)
{
size_t i;
size_t count=0;
jsval val;
JSObject* array;
socket_option_t* options;
if((options=getSocketOptionList())==NULL)
return(FALSE);
if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
return(FALSE);
if(!JS_DefineProperty(cx, obj, "option_list", OBJECT_TO_JSVAL(array)
, NULL, NULL, JSPROP_ENUMERATE))
return(FALSE);
for(i=0; options[i].name!=NULL; i++) {
if(options[i].type && options[i].type!=type)
continue;
val=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,options[i].name));
JS_SetElement(cx, array, count++, &val);
}
return(TRUE);
}
/* Socket Constructor (creates socket descriptor) */
static JSBool
js_socket_constructor(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
int32 type=SOCK_STREAM; /* default = TCP */
uintN i;
private_t* p;
char* protocol=NULL;
obj=JS_NewObject(cx, &js_socket_class, NULL, NULL);
JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
for(i=0;i<argc;i++) {
if(JSVAL_IS_NUMBER(argv[i]))
JS_ValueToInt32(cx,argv[i],&type);
else if(protocol==NULL)
protocol=JS_GetStringBytes(JS_ValueToString(cx,argv[i]));
}
if((p=(private_t*)malloc(sizeof(private_t)))==NULL) {
JS_ReportError(cx,"malloc failed");
return(JS_FALSE);
}
memset(p,0,sizeof(private_t));
if((p->sock=open_socket(type,protocol))==INVALID_SOCKET) {
JS_ReportError(cx,"open_socket failed with error %d",ERROR_VALUE);
return(JS_FALSE);
}
p->type = type;
p->network_byte_order = TRUE;
if(!JS_SetPrivate(cx, obj, p)) {
JS_ReportError(cx,"JS_SetPrivate failed");
return(JS_FALSE);
}
if(!js_DefineSocketOptionsArray(cx, obj, type))
return(JS_FALSE);
#ifdef BUILD_JSDOCS
js_DescribeSyncObject(cx,obj,"Class used for TCP/IP socket communications",310);
js_DescribeSyncConstructor(cx,obj,"To create a new Socket object: "
"<tt>load('sockdefs.js'); var s = new Socket(<i>type</i>, <i>protocol</i>)</tt><br>"
"where <i>type</i> = <tt>SOCK_STREAM</tt> for TCP (default) or <tt>SOCK_DGRAM</tt> for UDP<br>"
"and <i>protocol</i> (optional) = the name of the protocol or service the socket is to be used for"
);
js_CreateArrayOfStrings(cx, obj, "_property_desc_list", socket_prop_desc, JSPROP_READONLY);
#endif
dbprintf(FALSE, p, "object constructed");
return(JS_TRUE);
}
JSObject* DLLCALL js_CreateSocketClass(JSContext* cx, JSObject* parent)
{
JSObject* sockobj;
sockobj = JS_InitClass(cx, parent, NULL
,&js_socket_class
,js_socket_constructor
,0 /* number of constructor args */
,NULL /* props, specified in constructor */
,NULL /* funcs, specified in constructor */
JSObject* DLLCALL js_CreateSocketObject(JSContext* cx, JSObject* parent, char *name, SOCKET sock)
{
JSObject* obj;
private_t* p;
int type=SOCK_STREAM;
socklen_t len;
obj = JS_DefineObject(cx, parent, name, &js_socket_class, NULL
,JSPROP_ENUMERATE|JSPROP_READONLY);
len = sizeof(type);
getsockopt(sock,SOL_SOCKET,SO_TYPE,(void*)&type,&len);
if(!js_DefineSocketOptionsArray(cx, obj, type))
return(NULL);
if((p=(private_t*)malloc(sizeof(private_t)))==NULL)
return(NULL);
memset(p,0,sizeof(private_t));
p->sock = sock;
p->external = TRUE;
p->network_byte_order = TRUE;
len=sizeof(p->remote_addr);
if(getpeername(p->sock, (struct sockaddr *)&p->remote_addr,&len)==0)
p->is_connected=TRUE;
if(!JS_SetPrivate(cx, obj, p)) {
dbprintf(TRUE, p, "JS_SetPrivate failed");
dbprintf(FALSE, p, "object created");