1
1
package url
2
2
3
3
import (
4
- "math"
5
- "net/url"
6
- "reflect"
7
- "strconv"
8
- "strings"
9
-
10
- "golang.org/x/net/idna"
11
-
12
4
"github.com/dop251/goja"
13
5
"github.com/dop251/goja_nodejs/require"
14
6
)
15
7
16
8
const ModuleName = "url"
17
9
18
- var (
19
- reflectTypeURL = reflect .TypeOf ((* url .URL )(nil ))
20
- reflectTypeInt = reflect .TypeOf (0 )
21
- )
22
-
23
- func isDefaultURLPort (protocol string , port int ) bool {
24
- switch port {
25
- case 21 :
26
- if protocol == "ftp" {
27
- return true
28
- }
29
- case 80 :
30
- if protocol == "http" || protocol == "ws" {
31
- return true
32
- }
33
- case 443 :
34
- if protocol == "https" || protocol == "wss" {
35
- return true
36
- }
37
- }
38
- return false
39
- }
40
-
41
- func isSpecialProtocol (protocol string ) bool {
42
- switch protocol {
43
- case "ftp" , "file" , "http" , "https" , "ws" , "wss" :
44
- return true
45
- }
46
- return false
47
- }
48
-
49
- func clearURLPort (u * url.URL ) {
50
- u .Host = u .Hostname ()
51
- }
52
-
53
- func valueToURLPort (v goja.Value ) (portNum int , empty bool ) {
54
- portNum = - 1
55
- if et := v .ExportType (); et == reflectTypeInt {
56
- if num := v .ToInteger (); num >= 0 && num <= math .MaxUint16 {
57
- portNum = int (num )
58
- }
59
- } else {
60
- s := v .String ()
61
- if s == "" {
62
- return 0 , true
63
- }
64
- for i := 0 ; i < len (s ); i ++ {
65
- if c := s [i ]; c >= '0' && c <= '9' {
66
- if portNum == - 1 {
67
- portNum = 0
68
- }
69
- portNum = portNum * 10 + int (c - '0' )
70
- if portNum > math .MaxUint16 {
71
- portNum = - 1
72
- break
73
- }
74
- } else {
75
- break
76
- }
77
- }
78
- }
79
- return
80
- }
81
-
82
- func setURLPort (u * url.URL , v goja.Value ) {
83
- if u .Scheme == "file" {
84
- return
85
- }
86
- portNum , empty := valueToURLPort (v )
87
- if empty {
88
- clearURLPort (u )
89
- return
90
- }
91
- if portNum == - 1 {
92
- return
93
- }
94
- if isDefaultURLPort (u .Scheme , portNum ) {
95
- clearURLPort (u )
96
- } else {
97
- u .Host = u .Hostname () + ":" + strconv .Itoa (portNum )
98
- }
99
- }
100
-
101
- func toURL (r * goja.Runtime , v goja.Value ) * url.URL {
10
+ func toURL (r * goja.Runtime , v goja.Value ) * nodeURL {
102
11
if v .ExportType () == reflectTypeURL {
103
- if u := v .Export ().(* url. URL ); u != nil {
12
+ if u := v .Export ().(* nodeURL ); u != nil {
104
13
return u
105
14
}
106
15
}
107
16
panic (r .NewTypeError ("Expected URL" ))
108
17
}
109
18
110
- func defineURLAccessorProp (r * goja.Runtime , p * goja.Object , name string , getter func (* url. URL ) interface {}, setter func (* url. URL , goja.Value )) {
19
+ func defineURLAccessorProp (r * goja.Runtime , p * goja.Object , name string , getter func (* nodeURL ) interface {}, setter func (* nodeURL , goja.Value )) {
111
20
var getterVal , setterVal goja.Value
112
21
if getter != nil {
113
22
getterVal = r .ToValue (func (call goja.FunctionCall ) goja.Value {
@@ -123,240 +32,15 @@ func defineURLAccessorProp(r *goja.Runtime, p *goja.Object, name string, getter
123
32
p .DefineAccessorProperty (name , getterVal , setterVal , goja .FLAG_FALSE , goja .FLAG_TRUE )
124
33
}
125
34
126
- func createURLPrototype (r * goja.Runtime ) * goja.Object {
127
- p := r .NewObject ()
128
-
129
- // host
130
- defineURLAccessorProp (r , p , "host" , func (u * url.URL ) interface {} {
131
- return u .Host
132
- }, func (u * url.URL , arg goja.Value ) {
133
- host := arg .String ()
134
- if _ , err := url .ParseRequestURI (u .Scheme + "://" + host ); err == nil {
135
- u .Host = host
136
- fixURL (r , u )
137
- }
138
- })
139
-
140
- // hash
141
- defineURLAccessorProp (r , p , "hash" , func (u * url.URL ) interface {} {
142
- if u .Fragment != "" {
143
- return "#" + u .EscapedFragment ()
144
- }
145
- return ""
146
- }, func (u * url.URL , arg goja.Value ) {
147
- h := arg .String ()
148
- if len (h ) > 0 && h [0 ] == '#' {
149
- h = h [1 :]
150
- }
151
- u .Fragment = h
152
- })
153
-
154
- // hostname
155
- defineURLAccessorProp (r , p , "hostname" , func (u * url.URL ) interface {} {
156
- return strings .Split (u .Host , ":" )[0 ]
157
- }, func (u * url.URL , arg goja.Value ) {
158
- h := arg .String ()
159
- if strings .IndexByte (h , ':' ) >= 0 {
160
- return
161
- }
162
- if _ , err := url .ParseRequestURI (u .Scheme + "://" + h ); err == nil {
163
- if port := u .Port (); port != "" {
164
- u .Host = h + ":" + port
165
- } else {
166
- u .Host = h
167
- }
168
- fixURL (r , u )
169
- }
170
- })
171
-
172
- // href
173
- defineURLAccessorProp (r , p , "href" , func (u * url.URL ) interface {} {
174
- return u .String ()
175
- }, func (u * url.URL , arg goja.Value ) {
176
- url := parseURL (r , arg .String (), true )
177
- * u = * url
178
- })
179
-
180
- // pathname
181
- defineURLAccessorProp (r , p , "pathname" , func (u * url.URL ) interface {} {
182
- return u .EscapedPath ()
183
- }, func (u * url.URL , arg goja.Value ) {
184
- p := arg .String ()
185
- if _ , err := url .Parse (p ); err == nil {
186
- switch u .Scheme {
187
- case "https" , "http" , "ftp" , "ws" , "wss" :
188
- if ! strings .HasPrefix (p , "/" ) {
189
- p = "/" + p
190
- }
191
- }
192
- u .Path = p
193
- }
194
- })
195
-
196
- // origin
197
- defineURLAccessorProp (r , p , "origin" , func (u * url.URL ) interface {} {
198
- return u .Scheme + "://" + u .Hostname ()
199
- }, nil )
200
-
201
- // password
202
- defineURLAccessorProp (r , p , "password" , func (u * url.URL ) interface {} {
203
- p , _ := u .User .Password ()
204
- return p
205
- }, func (u * url.URL , arg goja.Value ) {
206
- user := u .User
207
- u .User = url .UserPassword (user .Username (), arg .String ())
208
- })
209
-
210
- // username
211
- defineURLAccessorProp (r , p , "username" , func (u * url.URL ) interface {} {
212
- return u .User .Username ()
213
- }, func (u * url.URL , arg goja.Value ) {
214
- p , has := u .User .Password ()
215
- if ! has {
216
- u .User = url .User (arg .String ())
217
- } else {
218
- u .User = url .UserPassword (arg .String (), p )
219
- }
220
- })
221
-
222
- // port
223
- defineURLAccessorProp (r , p , "port" , func (u * url.URL ) interface {} {
224
- return u .Port ()
225
- }, func (u * url.URL , arg goja.Value ) {
226
- setURLPort (u , arg )
227
- })
228
-
229
- // protocol
230
- defineURLAccessorProp (r , p , "protocol" , func (u * url.URL ) interface {} {
231
- return u .Scheme + ":"
232
- }, func (u * url.URL , arg goja.Value ) {
233
- s := arg .String ()
234
- pos := strings .IndexByte (s , ':' )
235
- if pos >= 0 {
236
- s = s [:pos ]
237
- }
238
- s = strings .ToLower (s )
239
- if isSpecialProtocol (u .Scheme ) == isSpecialProtocol (s ) {
240
- if _ , err := url .ParseRequestURI (s + "://" + u .Host ); err == nil {
241
- u .Scheme = s
242
- }
243
- }
244
- })
245
-
246
- // Search
247
- defineURLAccessorProp (r , p , "search" , func (u * url.URL ) interface {} {
248
- if u .RawQuery != "" {
249
- return "?" + u .RawQuery
250
- }
251
- return ""
252
- }, func (u * url.URL , arg goja.Value ) {
253
- u .RawQuery = arg .String ()
254
- fixRawQuery (u )
255
- })
256
-
257
- p .Set ("toString" , r .ToValue (func (call goja.FunctionCall ) goja.Value {
258
- return r .ToValue (toURL (r , call .This ).String ())
259
- }))
260
-
261
- p .Set ("toJSON" , r .ToValue (func (call goja.FunctionCall ) goja.Value {
262
- return r .ToValue (toURL (r , call .This ).String ())
263
- }))
264
-
265
- return p
266
- }
267
-
268
- const (
269
- URLNotAbsolute = "URL is not absolute"
270
- InvalidURL = "Invalid URL"
271
- InvalidBaseURL = "Invalid base URL"
272
- InvalidHostname = "Invalid hostname"
273
- )
274
-
275
- func newInvalidURLError (r * goja.Runtime , msg , input string ) * goja.Object {
276
- // when node's error module is added this should return a NodeError
277
- o := r .NewTypeError (msg )
278
- o .Set ("input" , r .ToValue (input ))
279
- return o
280
- }
281
-
282
- func fixRawQuery (u * url.URL ) {
283
- if u .RawQuery != "" {
284
- var u1 url.URL
285
- u1 .Fragment = u .RawQuery
286
- u .RawQuery = u1 .EscapedFragment ()
287
- }
288
- }
289
-
290
- func fixURL (r * goja.Runtime , u * url.URL ) {
291
- switch u .Scheme {
292
- case "https" , "http" , "ftp" , "wss" , "ws" :
293
- if u .Path == "" {
294
- u .Path = "/"
295
- }
296
- hostname := u .Hostname ()
297
- lh := strings .ToLower (hostname )
298
- ch , err := idna .Punycode .ToASCII (lh )
299
- if err != nil {
300
- panic (newInvalidURLError (r , InvalidHostname , lh ))
301
- }
302
- if ch != hostname {
303
- if port := u .Port (); port != "" {
304
- u .Host = ch + ":" + port
305
- } else {
306
- u .Host = ch
307
- }
308
- }
309
- fixRawQuery (u )
310
- }
311
- }
312
-
313
- func parseURL (r * goja.Runtime , s string , isBase bool ) * url.URL {
314
- u , err := url .Parse (s )
315
- if err != nil {
316
- if isBase {
317
- panic (newInvalidURLError (r , InvalidBaseURL , s ))
318
- } else {
319
- panic (newInvalidURLError (r , InvalidURL , s ))
320
- }
321
- }
322
- if isBase && ! u .IsAbs () {
323
- panic (newInvalidURLError (r , URLNotAbsolute , s ))
324
- }
325
- if portStr := u .Port (); portStr != "" {
326
- if port , err := strconv .Atoi (portStr ); err != nil || isDefaultURLPort (u .Scheme , port ) {
327
- clearURLPort (u )
328
- }
329
- }
330
- fixURL (r , u )
331
- return u
332
- }
333
-
334
- func createURLConstructor (r * goja.Runtime ) goja.Value {
335
- f := r .ToValue (func (call goja.ConstructorCall ) * goja.Object {
336
- var u * url.URL
337
- if baseArg := call .Argument (1 ); ! goja .IsUndefined (baseArg ) {
338
- base := parseURL (r , baseArg .String (), true )
339
- ref := parseURL (r , call .Arguments [0 ].String (), false )
340
- u = base .ResolveReference (ref )
341
- } else {
342
- u = parseURL (r , call .Argument (0 ).String (), true )
343
- }
344
- res := r .ToValue (u ).(* goja.Object )
345
- res .SetPrototype (call .This .Prototype ())
346
- return res
347
- }).(* goja.Object )
348
-
349
- f .Set ("prototype" , createURLPrototype (r ))
350
- return f
351
- }
352
-
353
35
func Require (runtime * goja.Runtime , module * goja.Object ) {
354
36
exports := module .Get ("exports" ).(* goja.Object )
355
37
exports .Set ("URL" , createURLConstructor (runtime ))
38
+ exports .Set ("URLSearchParams" , createURLSearchParamsConstructor (runtime ))
356
39
}
357
40
358
41
func Enable (runtime * goja.Runtime ) {
359
42
runtime .Set ("URL" , require .Require (runtime , ModuleName ).ToObject (runtime ).Get ("URL" ))
43
+ runtime .Set ("URLSearchParams" , require .Require (runtime , ModuleName ).ToObject (runtime ).Get ("URLSearchParams" ))
360
44
}
361
45
362
46
func init () {
0 commit comments