@@ -368,6 +368,199 @@ describe('RawBytesFetch', () => {
368368 ) ;
369369 expect ( decodedRequestBody ) . toEqual ( encodedBodyBytes ) ;
370370 } ) ;
371+
372+ it ( 'parseHttpRequest should handle a path and query string' , async ( ) => {
373+ const requestBytes = `GET /core/version-check/1.7/?channel=beta HTTP/1.1\r\nHost: playground.internal\r\n\r\n` ;
374+ const request = await RawBytesFetch . parseHttpRequest (
375+ new ReadableStream ( {
376+ start ( controller ) {
377+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
378+ controller . close ( ) ;
379+ } ,
380+ } ) ,
381+ 'playground.internal' ,
382+ 'http'
383+ ) ;
384+ expect ( request . url ) . toEqual (
385+ 'http://playground.internal/core/version-check/1.7/?channel=beta'
386+ ) ;
387+ } ) ;
388+
389+ it ( 'parseHttpRequest should handle a simple path without query string' , async ( ) => {
390+ const requestBytes = `GET /api/users HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
391+ const request = await RawBytesFetch . parseHttpRequest (
392+ new ReadableStream ( {
393+ start ( controller ) {
394+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
395+ controller . close ( ) ;
396+ } ,
397+ } ) ,
398+ 'example.com' ,
399+ 'http'
400+ ) ;
401+ expect ( request . url ) . toEqual ( 'http://example.com/api/users' ) ;
402+ } ) ;
403+
404+ it ( 'parseHttpRequest should handle root path' , async ( ) => {
405+ const requestBytes = `GET / HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
406+ const request = await RawBytesFetch . parseHttpRequest (
407+ new ReadableStream ( {
408+ start ( controller ) {
409+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
410+ controller . close ( ) ;
411+ } ,
412+ } ) ,
413+ 'example.com' ,
414+ 'https'
415+ ) ;
416+ expect ( request . url ) . toEqual ( 'https://example.com/' ) ;
417+ } ) ;
418+
419+ it ( 'parseHttpRequest should handle multiple query parameters' , async ( ) => {
420+ const requestBytes = `GET /search?q=test&page=2&sort=asc HTTP/1.1\r\nHost: api.example.com\r\n\r\n` ;
421+ const request = await RawBytesFetch . parseHttpRequest (
422+ new ReadableStream ( {
423+ start ( controller ) {
424+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
425+ controller . close ( ) ;
426+ } ,
427+ } ) ,
428+ 'api.example.com' ,
429+ 'https'
430+ ) ;
431+ expect ( request . url ) . toEqual (
432+ 'https://api.example.com/search?q=test&page=2&sort=asc'
433+ ) ;
434+ } ) ;
435+
436+ it ( 'parseHttpRequest should handle path with trailing slash' , async ( ) => {
437+ const requestBytes = `GET /api/users/ HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
438+ const request = await RawBytesFetch . parseHttpRequest (
439+ new ReadableStream ( {
440+ start ( controller ) {
441+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
442+ controller . close ( ) ;
443+ } ,
444+ } ) ,
445+ 'example.com' ,
446+ 'http'
447+ ) ;
448+ expect ( request . url ) . toEqual ( 'http://example.com/api/users/' ) ;
449+ } ) ;
450+
451+ it ( 'parseHttpRequest should handle nested paths' , async ( ) => {
452+ const requestBytes = `GET /api/v1/users/123/posts/456 HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
453+ const request = await RawBytesFetch . parseHttpRequest (
454+ new ReadableStream ( {
455+ start ( controller ) {
456+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
457+ controller . close ( ) ;
458+ } ,
459+ } ) ,
460+ 'example.com' ,
461+ 'https'
462+ ) ;
463+ expect ( request . url ) . toEqual (
464+ 'https://example.com/api/v1/users/123/posts/456'
465+ ) ;
466+ } ) ;
467+
468+ it ( 'parseHttpRequest should handle URL-encoded characters in path' , async ( ) => {
469+ const requestBytes = `GET /search/hello%20world HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
470+ const request = await RawBytesFetch . parseHttpRequest (
471+ new ReadableStream ( {
472+ start ( controller ) {
473+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
474+ controller . close ( ) ;
475+ } ,
476+ } ) ,
477+ 'example.com' ,
478+ 'http'
479+ ) ;
480+ expect ( request . url ) . toEqual ( 'http://example.com/search/hello%20world' ) ;
481+ } ) ;
482+
483+ it ( 'parseHttpRequest should handle special characters in query string' , async ( ) => {
484+ const requestBytes = `GET /search?q=hello+world&filter=a%26b HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
485+ const request = await RawBytesFetch . parseHttpRequest (
486+ new ReadableStream ( {
487+ start ( controller ) {
488+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
489+ controller . close ( ) ;
490+ } ,
491+ } ) ,
492+ 'example.com' ,
493+ 'http'
494+ ) ;
495+ expect ( request . url ) . toEqual (
496+ 'http://example.com/search?q=hello+world&filter=a%26b'
497+ ) ;
498+ } ) ;
499+
500+ it ( 'parseHttpRequest should handle empty query parameter values' , async ( ) => {
501+ const requestBytes = `GET /api?key1=&key2=value2 HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
502+ const request = await RawBytesFetch . parseHttpRequest (
503+ new ReadableStream ( {
504+ start ( controller ) {
505+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
506+ controller . close ( ) ;
507+ } ,
508+ } ) ,
509+ 'example.com' ,
510+ 'http'
511+ ) ;
512+ expect ( request . url ) . toEqual ( 'http://example.com/api?key1=&key2=value2' ) ;
513+ } ) ;
514+
515+ it ( 'parseHttpRequest should handle path with hash fragment' , async ( ) => {
516+ // Note: Hash fragments are typically not sent in HTTP requests,
517+ // but if they are, the URL constructor should handle them
518+ const requestBytes = `GET /page#section HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
519+ const request = await RawBytesFetch . parseHttpRequest (
520+ new ReadableStream ( {
521+ start ( controller ) {
522+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
523+ controller . close ( ) ;
524+ } ,
525+ } ) ,
526+ 'example.com' ,
527+ 'http'
528+ ) ;
529+ expect ( request . url ) . toEqual ( 'http://example.com/page#section' ) ;
530+ } ) ;
531+
532+ it ( 'parseHttpRequest should handle path with query and hash' , async ( ) => {
533+ const requestBytes = `GET /page?param=value#section HTTP/1.1\r\nHost: example.com\r\n\r\n` ;
534+ const request = await RawBytesFetch . parseHttpRequest (
535+ new ReadableStream ( {
536+ start ( controller ) {
537+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
538+ controller . close ( ) ;
539+ } ,
540+ } ) ,
541+ 'example.com' ,
542+ 'http'
543+ ) ;
544+ expect ( request . url ) . toEqual (
545+ 'http://example.com/page?param=value#section'
546+ ) ;
547+ } ) ;
548+
549+ it ( 'parseHttpRequest should preserve Host header over default host' , async ( ) => {
550+ const requestBytes = `GET /api HTTP/1.1\r\nHost: custom.host.com\r\n\r\n` ;
551+ const request = await RawBytesFetch . parseHttpRequest (
552+ new ReadableStream ( {
553+ start ( controller ) {
554+ controller . enqueue ( new TextEncoder ( ) . encode ( requestBytes ) ) ;
555+ controller . close ( ) ;
556+ } ,
557+ } ) ,
558+ 'default.host.com' , // Different from Host header
559+ 'https'
560+ ) ;
561+ // Should use the Host header, not the default host parameter
562+ expect ( request . url ) . toEqual ( 'https://custom.host.com/api' ) ;
563+ } ) ;
371564} ) ;
372565
373566type MakeRequestOptions = {
0 commit comments