3232 }
3333 </ style >
3434 < script type ="module ">
35+ let conn ;
36+ let buckets = new Map ( ) ;
37+
3538 function toHex ( v ) {
3639 if ( ! v ) return '' ;
3740 return v . reduce ( ( s , x ) => s + x . toString ( 16 ) . padStart ( 2 , '0' ) , '' )
282285 }
283286
284287 async function getBucket ( ) {
285- if ( ! transport ) {
286- throw 'transport not connected' ;
287- }
288+ if ( ! conn ) throw 'WebTransport not connected' ;
289+
288290 const obj = getFormValues ( '#settings' ) ;
289291 let identifier = '' ;
290- let conn = obj . connection ;
291- if ( conn === 'mem' ) {
292+ let proto = obj . proto ;
293+ if ( proto === 'mem' ) {
292294 identifier = '' ;
293- } else if ( conn === 'nats' ) {
295+ } else if ( proto === 'nats' ) {
294296 const addr = obj [ 'nats-addr' ] ;
295297 if ( ! addr ) throw 'NATS.io server address must be set' ;
296298 identifier = 'wrpc+nats://' + addr ;
300302
301303 const bucket = obj [ 'nats-bucket' ] ;
302304 if ( bucket ) identifier = identifier + ';' + bucket ;
303- } else if ( conn === 'redis' ) {
305+ } else if ( proto === 'redis' ) {
304306 const url = obj [ 'redis-url' ] ;
305307 if ( ! url ) throw 'Redis URL must be set' ;
306308 identifier = url ;
307- } else if ( conn === 'quic' ) {
309+ } else if ( proto === 'quic' ) {
308310 const addr = obj [ 'quic-addr' ] ;
309311 if ( ! addr ) throw 'QUIC address must be set' ;
310312 identifier = 'wrpc+quic://' + addr ;
311313
312314 const bucket = obj [ 'quic-bucket' ] ;
313315 if ( bucket ) identifier = identifier + ';' + bucket ;
314- } else if ( conn === 'tcp' ) {
316+ } else if ( proto === 'tcp' ) {
315317 const addr = obj [ 'tcp-addr' ] ;
316318 if ( ! addr ) throw 'TCP address must be set' ;
317319 identifier = 'wrpc+tcp://' + addr ;
318320
319321 const bucket = obj [ 'tcp-bucket' ] ;
320322 if ( bucket ) identifier = identifier + ';' + bucket ;
321- } else if ( conn === 'unix' ) {
323+ } else if ( proto === 'unix' ) {
322324 const path = obj [ 'unix-path' ] ;
323325 if ( ! path ) throw 'Unix Domain Socket path must be set' ;
324326 identifier = 'wrpc+unix://' + path ;
325327
326328 const bucket = obj [ 'unix-bucket' ]
327329 if ( bucket ) identifier = identifier + ';' + bucket ;
328- } else if ( conn === 'web' ) {
330+ } else if ( proto === 'web' ) {
329331 const addr = obj [ 'web-addr' ] ;
330332 if ( ! addr ) throw 'WebTransport address must be set' ;
331333 identifier = 'wrpc+web://' + addr ;
332334
333335 const bucket = obj [ 'web-bucket' ] ;
334336 if ( bucket ) identifier = identifier + ';' + bucket ;
335337 } else {
336- throw 'transport not supported yet' ;
338+ throw 'selected wRPC transport not supported yet' ;
337339 }
338340
339341 if ( identifier . length > 127 ) throw 'this example currently does not support identifiers longer than 127 bytes - open a PR!' ;
340342
341- // TODO: cache the bucket by URL (it should not persist across server restarts)
343+ const bucket = buckets . get ( identifier ) ;
344+ if ( bucket ) return bucket ;
342345
343346 dbg . debug ( 'creating `open` stream...' ) ;
344- const stream = await transport . createBidirectionalStream ( ) ;
347+ const stream = await conn . createBidirectionalStream ( ) ;
345348 return await wasiKeyvalueStoreOpen (
346349 stream . writable . getWriter ( ) ,
347350 stream . readable . getReader ( ) ,
348351 identifier ,
349352 ) . then ( bucket => {
350353 const bucketName = uuidString ( bucket ) ;
351354 dbg . log ( `opened bucket ${ bucketName } ` ) ;
355+ buckets . set ( identifier , bucket ) ;
352356 return bucket ;
353357 } , err => {
354358 throw 'failed to open bucket: ' + err ;
360364 const getValue = document . querySelector ( '#get input[name="get-value"]' ) ;
361365 if ( getValue ) getValue . value = null ;
362366
363- if ( ! transport ) throw 'transport not connected' ;
367+ if ( ! conn ) throw 'WebTransport not connected' ;
368+
364369 const bucket = await getBucket ( ) ;
365370 if ( ! bucket ) throw 'failed to get bucket' ;
366371
371376 if ( key . length > 127 ) throw 'this example currently does not support keys longer than 127 bytes - open a PR!' ;
372377
373378 dbg . debug ( 'creating `get` stream...' ) ;
374- const stream = await transport . createBidirectionalStream ( ) ;
379+ const stream = await conn . createBidirectionalStream ( ) ;
375380 await wasiKeyvalueStoreBucketGet (
376381 stream . writable . getWriter ( ) ,
377382 stream . readable . getReader ( ) ,
392397 }
393398
394399 async function handleSet ( ) {
395- if ( ! transport ) {
396- dbg . error ( 'transport not connected' )
397- return
398- }
400+ if ( ! conn ) throw 'WebTransport not connected' ;
401+
399402 const bucket = await getBucket ( ) ;
400- if ( bucket == null ) {
401- return
402- }
403+ if ( ! bucket ) throw 'failed to get bucket' ;
404+
403405 const obj = getFormValues ( '#set' ) ;
404406
405407 const key = obj [ 'set-key' ] ;
416418 if ( valueBuf . length > 127 ) throw 'this example currently does not support values longer than 127 bytes - open a PR!' ;
417419
418420 dbg . debug ( 'creating `set` stream...' ) ;
419- const stream = await transport . createBidirectionalStream ( ) ;
421+ const stream = await conn . createBidirectionalStream ( ) ;
420422 await wasiKeyvalueStoreBucketSet (
421423 stream . writable . getWriter ( ) ,
422424 stream . readable . getReader ( ) ,
431433 } ) ;
432434 }
433435
434- async function connect ( ) {
435- const { PORT , CERT_DIGEST } = await import ( './consts.js' ) ;
436- let transport ;
437-
438- dbg . log ( 'connecting to wRPC over WebTransport on `' + PORT + '`...' ) ;
439- try {
440- transport = new WebTransport ( 'https://localhost:' + PORT , {
441- serverCertificateHashes : [
442- {
443- algorithm : 'sha-256' ,
444- value : CERT_DIGEST . buffer
445- }
446- ]
447- } ) ;
448- } catch ( e ) {
449- dbg . error ( 'failed to connect: ' + e ) ;
450- return null ;
451- }
452-
453- dbg . debug ( 'waiting for WebTransport readiness...' ) ;
454- try {
455- await transport . ready ;
456- } catch ( e ) {
457- throw 'failed to await WebTransport readiness: ' + e ;
458- return null ;
459- }
460- dbg . info ( 'WebTransport connection established' ) ;
461- return transport ;
462- }
463-
464436 const dbg = ( ( ) => {
465437 const output = document . querySelector ( '#message-output' ) ;
466438 const className = {
496468 // @ts -check
497469 function initUI ( ) {
498470 function updateTemplate ( ) {
499- const option = connectionDropdown ?. value ?? 'mem'
471+ const option = protoDropdown ?. value ?? 'mem'
500472 const defaultTemplate = document . querySelector ( '.form-fields[data-option=default]' ) ;
501473 const templateOutput = document . querySelector ( '#template-output' ) ;
502474 const template = document . querySelector ( `.form-fields[data-option=${ option } ]` ) ?? defaultTemplate ;
503475 if ( templateOutput && template ) templateOutput . innerHTML = template . innerHTML ;
504476 }
505477
506478 /** @type {HTMLSelectElement | null } */
507- const connectionDropdown = document . querySelector ( '#connection ' ) ;
508- connectionDropdown ?. addEventListener ( 'change' , updateTemplate ) ;
479+ const protoDropdown = document . querySelector ( '#proto ' ) ;
480+ protoDropdown ?. addEventListener ( 'change' , updateTemplate ) ;
509481 updateTemplate ( ) ;
510482
511483 /** @type {HTMLFormElement | null } */
538510
539511 initUI ( ) ;
540512
541- let transport = await connect ( ) . catch ( err => dbg . error ( 'failed to connect to server: ' + err ) ) ;
542- // TODO: Reconnect on failure
513+ const { PORT , CERT_DIGEST } = await import ( './consts.js' ) ;
514+ for ( ; ; ) {
515+ dbg . log ( 'connecting to wRPC over WebTransport on `' + PORT + '`...' ) ;
516+ let c ;
517+ try {
518+ c = new WebTransport ( 'https://localhost:' + PORT , {
519+ serverCertificateHashes : [
520+ {
521+ algorithm : 'sha-256' ,
522+ value : CERT_DIGEST . buffer
523+ }
524+ ]
525+ } ) ;
526+ } catch ( err ) {
527+ dbg . error ( 'failed to establish WebTransport connection: ' + err ) ;
528+ await new Promise ( r => setTimeout ( r , 1000 ) ) ;
529+ continue ;
530+ }
531+
532+ dbg . debug ( 'waiting for WebTransport readiness...' ) ;
533+ try {
534+ await c . ready ;
535+ } catch ( err ) {
536+ dbg . error ( 'failed to await WebTransport readiness: ' + err ) ;
537+ await new Promise ( r => setTimeout ( r , 1000 ) ) ;
538+ continue ;
539+ }
540+
541+ conn = c ;
542+ dbg . info ( 'WebTransport connection established' ) ;
543+ try {
544+ const { closeCode, reason} = await conn . closed ;
545+ dbg . log ( `WebTransport connection closed with code '${ closeCode } ': ${ reason } ` ) ;
546+ } catch ( err ) {
547+ dbg . error ( 'WebTransport connection failed: ' + err ) ;
548+ }
549+ conn = null ;
550+ }
551+
543552 </ script >
544553</ head >
545554
@@ -550,7 +559,7 @@ <h1 class="title"><code>wasi:keyvalue</code></h1>
550559 </ div >
551560 < div class ="column is-narrow ">
552561 < div class ="select ">
553- < select id ="connection " form ="settings " name ="connection ">
562+ < select id ="proto " form ="settings " name ="proto ">
554563 < option value ="mem "> In-memory</ option >
555564 < option value ="redis "> Redis</ option >
556565 < option value ="nats "> wRPC/NATS.io</ option >
0 commit comments