1
1
/**
2
- * Provides classes for working with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [ws](https://github.com/websockets/ws).
2
+ * Provides classes for working with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), [ws](https://github.com/websockets/ws), and [SockJS](http://sockjs.org ).
3
3
*
4
4
* The model is based on the EventEmitter model, and there is therefore a
5
5
* data-flow step from where a WebSocket event is sent to where the message
@@ -18,26 +18,64 @@ import javascript
18
18
*/
19
19
private string channelName ( ) { result = "message" }
20
20
21
+ /**
22
+ * The names of the libraries modelled in this file.
23
+ */
24
+ private module LibraryNames {
25
+ string sockjs ( ) { result = "SockJS" }
26
+
27
+ string websocket ( ) { result = "WebSocket" }
28
+
29
+ string ws ( ) { result = "ws" }
30
+
31
+ class LibraryName extends string {
32
+ LibraryName ( ) { this = sockjs ( ) or this = websocket ( ) or this = ws ( ) }
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Holds if the websocket library named `client` can send a message to the library named `server`.
38
+ * Both `client` and `server` are library names defined in `LibraryNames`.
39
+ */
40
+ private predicate areLibrariesCompatible (
41
+ LibraryNames:: LibraryName client , LibraryNames:: LibraryName server
42
+ ) {
43
+ // sockjs is a WebSocket emulating library, but not actually an implementation of WebSockets.
44
+ client = LibraryNames:: sockjs ( ) and server = LibraryNames:: sockjs ( )
45
+ or
46
+ server = LibraryNames:: ws ( ) and
47
+ ( client = LibraryNames:: ws ( ) or client = LibraryNames:: websocket ( ) )
48
+ }
49
+
21
50
/**
22
51
* Provides classes that model WebSockets clients.
23
52
*/
24
53
module ClientWebSocket {
54
+ private import LibraryNames
55
+
25
56
/**
26
57
* A class that can be used to instantiate a WebSocket instance.
27
58
*/
28
59
class SocketClass extends DataFlow:: SourceNode {
29
- boolean isNode ;
60
+ LibraryName library ; // the name of the WebSocket library. Can be one of the libraries defined in `LibraryNames`.
30
61
31
62
SocketClass ( ) {
32
- this = DataFlow:: globalVarRef ( "WebSocket" ) and isNode = false
63
+ this = DataFlow:: globalVarRef ( "WebSocket" ) and library = websocket ( )
64
+ or
65
+ this = DataFlow:: moduleImport ( "ws" ) and library = ws ( )
33
66
or
34
- this = DataFlow:: moduleImport ( "ws" ) and isNode = true
67
+ // the sockjs-client library:https://www.npmjs.com/package/sockjs-client
68
+ library = sockjs ( ) and
69
+ (
70
+ this = DataFlow:: moduleImport ( "sockjs-client" ) or
71
+ this = DataFlow:: globalVarRef ( "SockJS" )
72
+ )
35
73
}
36
74
37
75
/**
38
- * Holds if this class is an import of the "ws" module .
76
+ * Gets the WebSocket library name .
39
77
*/
40
- predicate isNode ( ) { isNode = true }
78
+ LibraryName getLibrary ( ) { result = library }
41
79
}
42
80
43
81
/**
@@ -49,11 +87,9 @@ module ClientWebSocket {
49
87
ClientSocket ( ) { this = socketClass .getAnInstantiation ( ) }
50
88
51
89
/**
52
- * Holds if this ClientSocket is created from the "ws" module.
53
- *
54
- * The predicate is used to differentiate where the behavior of the "ws" module differs from the native WebSocket in browsers.
90
+ * Gets the WebSocket library name.
55
91
*/
56
- predicate isNode ( ) { socketClass .isNode ( ) }
92
+ LibraryName getLibrary ( ) { result = socketClass .getLibrary ( ) }
57
93
}
58
94
59
95
/**
@@ -68,7 +104,10 @@ module ClientWebSocket {
68
104
69
105
override DataFlow:: Node getSentItem ( int i ) { i = 0 and result = this .getArgument ( 0 ) }
70
106
71
- override ServerWebSocket:: ReceiveNode getAReceiver ( ) { any ( ) }
107
+ override ServerWebSocket:: ReceiveNode getAReceiver ( ) {
108
+ areLibrariesCompatible ( emitter .getLibrary ( ) ,
109
+ result .getEmitter ( ) .( ServerWebSocket:: ServerSocket ) .getLibrary ( ) )
110
+ }
72
111
}
73
112
74
113
/**
@@ -116,7 +155,7 @@ module ClientWebSocket {
116
155
*/
117
156
private class WSReceiveNode extends ClientWebSocket:: ReceiveNode {
118
157
WSReceiveNode ( ) {
119
- emitter .isNode ( ) and
158
+ emitter .getLibrary ( ) = ws ( ) and
120
159
this = getAMessageHandler ( emitter , EventEmitter:: on ( ) )
121
160
}
122
161
@@ -128,21 +167,38 @@ module ClientWebSocket {
128
167
* Provides classes that model WebSocket servers.
129
168
*/
130
169
module ServerWebSocket {
170
+ private import LibraryNames
171
+
172
+ /**
173
+ * Gets a server created by a library named `library`.
174
+ */
175
+ DataFlow:: SourceNode getAServer ( LibraryName library ) {
176
+ library = ws ( ) and
177
+ result = DataFlow:: moduleImport ( "ws" ) .getAConstructorInvocation ( "Server" )
178
+ or
179
+ library = sockjs ( ) and
180
+ result = DataFlow:: moduleImport ( "sockjs" ) .getAMemberCall ( "createServer" )
181
+ }
182
+
131
183
/**
132
184
* A server WebSocket instance.
133
185
*/
134
186
class ServerSocket extends EventEmitter:: Range , DataFlow:: SourceNode {
187
+ LibraryName library ;
188
+
135
189
ServerSocket ( ) {
136
190
exists ( DataFlow:: CallNode onCall |
137
- onCall =
138
- DataFlow:: moduleImport ( "ws" )
139
- .getAConstructorInvocation ( "Server" )
140
- .getAMemberCall ( EventEmitter:: on ( ) ) and
191
+ onCall = getAServer ( library ) .getAMemberCall ( EventEmitter:: on ( ) ) and
141
192
onCall .getArgument ( 0 ) .mayHaveStringValue ( "connection" )
142
193
|
143
194
this = onCall .getCallback ( 1 ) .getParameter ( 0 )
144
195
)
145
196
}
197
+
198
+ /**
199
+ * Gets the name of the library that created this server socket.
200
+ */
201
+ LibraryName getLibrary ( ) { result = library }
146
202
}
147
203
148
204
/**
@@ -151,7 +207,13 @@ module ServerWebSocket {
151
207
class SendNode extends EventDispatch:: Range , DataFlow:: CallNode {
152
208
override ServerSocket emitter ;
153
209
154
- SendNode ( ) { this = emitter .getAMemberCall ( "send" ) }
210
+ SendNode ( ) {
211
+ emitter .getLibrary ( ) = ws ( ) and
212
+ this = emitter .getAMemberCall ( "send" )
213
+ or
214
+ emitter .getLibrary ( ) = sockjs ( ) and
215
+ this = emitter .getAMemberCall ( "write" )
216
+ }
155
217
156
218
override string getChannel ( ) { result = channelName ( ) }
157
219
@@ -160,7 +222,10 @@ module ServerWebSocket {
160
222
result = getArgument ( 0 )
161
223
}
162
224
163
- override ClientWebSocket:: ReceiveNode getAReceiver ( ) { any ( ) }
225
+ override ClientWebSocket:: ReceiveNode getAReceiver ( ) {
226
+ areLibrariesCompatible ( result .getEmitter ( ) .( ClientWebSocket:: ClientSocket ) .getLibrary ( ) ,
227
+ emitter .getLibrary ( ) )
228
+ }
164
229
}
165
230
166
231
/**
@@ -170,8 +235,14 @@ module ServerWebSocket {
170
235
override ServerSocket emitter ;
171
236
172
237
ReceiveNode ( ) {
173
- this = emitter .getAMemberCall ( EventEmitter:: on ( ) ) and
174
- this .getArgument ( 0 ) .mayHaveStringValue ( "message" )
238
+ exists ( string eventName |
239
+ emitter .getLibrary ( ) = ws ( ) and eventName = "message"
240
+ or
241
+ emitter .getLibrary ( ) = sockjs ( ) and eventName = "data"
242
+ |
243
+ this = emitter .getAMemberCall ( EventEmitter:: on ( ) ) and
244
+ this .getArgument ( 0 ) .mayHaveStringValue ( eventName )
245
+ )
175
246
}
176
247
177
248
override string getChannel ( ) { result = channelName ( ) }
0 commit comments