Newer
Older
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);
}
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);
*rval = INT_TO_JSVAL(result);
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 - <small>READ ONLY</small>"
,"<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, jsval id, jsval *vp)
{
private_t* p;
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx,getprivate_failure,WHERE);
return(JS_FALSE);
tiny = JSVAL_TO_INT(id);
dbprintf(FALSE, p, "setting property %d",tiny);
switch(tiny) {
case SOCK_PROP_DEBUG:
JS_ValueToBoolean(cx,*vp,&(p->debug));
JS_ValueToInt32(cx,*vp,(int32*)&(p->sock));
break;
case SOCK_PROP_LAST_ERROR:
JS_ValueToInt32(cx,*vp,(int32*)&(p->last_error));
case SOCK_PROP_NONBLOCKING:
JS_ValueToBoolean(cx,*vp,&(p->nonblocking));
ioctlsocket(p->sock,FIONBIO,(ulong*)&(p->nonblocking));
case SOCK_PROP_NETWORK_ORDER:
JS_ValueToBoolean(cx,*vp,&(p->network_byte_order));
break;
return(JS_TRUE);
}
static JSBool js_socket_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
jsint tiny;
ulong cnt;
BOOL rd;
BOOL wr;
private_t* p;
socklen_t addr_len;
SOCKADDR_IN addr;
if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx,getprivate_failure,WHERE);
return(JS_FALSE);
*vp = INT_TO_JSVAL(p->last_error);
if(!p->is_connected && !p->external)
*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 = 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:
addr_len = sizeof(addr);
if(getsockname(p->sock, (struct sockaddr *)&addr,&addr_len)!=0) {
p->last_error=ERROR_VALUE;
*vp = JSVAL_VOID;
} else {
if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
}
break;
case SOCK_PROP_LOCAL_PORT:
addr_len = sizeof(addr);
if(getsockname(p->sock, (struct sockaddr *)&addr,&addr_len)!=0) {
p->last_error=ERROR_VALUE;
} else
*vp = INT_TO_JSVAL(ntohs(addr.sin_port));
break;
case SOCK_PROP_REMOTE_IP:
addr_len = sizeof(addr);
if(getpeername(p->sock, (struct sockaddr *)&addr,&addr_len)!=0) {
p->last_error=ERROR_VALUE;
*vp = JSVAL_VOID;
} else {
if((js_str=JS_NewStringCopyZ(cx,inet_ntoa(addr.sin_addr)))==NULL)
return(JS_FALSE);
*vp = STRING_TO_JSVAL(js_str);
}
break;
case SOCK_PROP_REMOTE_PORT:
addr_len = sizeof(addr);
if(getpeername(p->sock, (struct sockaddr *)&addr,&addr_len)!=0) {
p->last_error=ERROR_VALUE;
} else
*vp = INT_TO_JSVAL(ntohs(addr.sin_port));
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;
#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 ,SOCK_PROP_FLAGS, 311 },
{ "is_writable" ,SOCK_PROP_IS_WRITEABLE ,JSPROP_READONLY, 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
},
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
static JSBool js_socket_resolve(JSContext *cx, JSObject *obj, jsval id)
{
char* name=NULL;
if(id != JSVAL_NULL)
name=JS_GetStringBytes(JSVAL_TO_STRING(id));
return(js_SyncResolve(cx, obj, name, js_socket_properties, js_socket_functions, NULL, 0));
}
static JSBool js_socket_enumerate(JSContext *cx, JSObject *obj)
{
return(js_socket_resolve(cx, obj, JSVAL_NULL));
}
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 */
};
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
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, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
int32 type=SOCK_STREAM; /* default = TCP */
uintN i;
private_t* p;
char* protocol=NULL;
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;
if(!JS_SetPrivate(cx, obj, p)) {
dbprintf(TRUE, p, "JS_SetPrivate failed");
dbprintf(FALSE, p, "object created");