@@ -816,6 +816,141 @@ func TestRedirects(t *testing.T) {
816816 do (http .MethodHead )
817817 })
818818
819+ t .Run ("_redirects file redirect preserves both static and dynamic query parameters" , func (t * testing.T ) {
820+ t .Parallel ()
821+
822+ backend , root := newMockBackend (t , "redirects-query-params.car" )
823+ backend .namesys ["/ipns/example.org" ] = newMockNamesysItem (path .FromCid (root ), 0 )
824+
825+ ts := newTestServerWithConfig (t , backend , Config {
826+ NoDNSLink : false ,
827+ PublicGateways : map [string ]* PublicGateway {
828+ "example.org" : {
829+ UseSubdomains : true ,
830+ DeserializedResponses : true ,
831+ },
832+ },
833+ DeserializedResponses : true ,
834+ })
835+
836+ // Request for missing page with some dynamic parameters
837+ missingPageURL := ts .URL + "/source1/source-file?dynamic-query1=dynamic-value1&dynamic-query2=dynamic-value2"
838+
839+ // Expected response is redirect URL that preserves both static and dynamic parameters
840+ expectedTargetURL := "/target-file?dynamic-query1=dynamic-value1&dynamic-query2=dynamic-value2&static-query1=static-val1&static-query2=static-val2"
841+
842+ do := func (method string ) {
843+ // Make initial request to non-existing page that should return redirect that contains both query params from original request AND target URL in _redirects file.
844+ req := mustNewRequest (t , method , missingPageURL , nil )
845+ req .Header .Add ("Accept" , "text/html" )
846+ req .Host = "example.org"
847+
848+ res := mustDoWithoutRedirect (t , req )
849+ defer res .Body .Close ()
850+
851+ // Status code should match 301 from _redirects catch-all rule
852+ require .Equal (t , http .StatusMovedPermanently , res .StatusCode )
853+
854+ // Check Redirect target contains all query parameters
855+ redirectURL := res .Header .Get ("Location" )
856+ require .Equal (t , expectedTargetURL , redirectURL )
857+
858+ }
859+
860+ do (http .MethodGet )
861+ do (http .MethodHead )
862+ })
863+
864+ t .Run ("_redirects file redirect supports named placeholders as query parameters" , func (t * testing.T ) {
865+ t .Parallel ()
866+
867+ backend , root := newMockBackend (t , "redirects-query-params.car" )
868+ backend .namesys ["/ipns/example.org" ] = newMockNamesysItem (path .FromCid (root ), 0 )
869+
870+ ts := newTestServerWithConfig (t , backend , Config {
871+ NoDNSLink : false ,
872+ PublicGateways : map [string ]* PublicGateway {
873+ "example.org" : {
874+ UseSubdomains : true ,
875+ DeserializedResponses : true ,
876+ },
877+ },
878+ DeserializedResponses : true ,
879+ })
880+
881+ // Request for missing page under path that has named placeholders and some dynamic parameters
882+ missingPageURL := ts .URL + "/source2/codeA/nameA?dynamic-query1=dynamic-value1&dynamic-query2=dynamic-value2"
883+
884+ // Expected response is redirect URL that preserves both named and dynamic parameters
885+ expectedTargetURL := "/target-file?code=codeA&dynamic-query1=dynamic-value1&dynamic-query2=dynamic-value2&name=nameA"
886+
887+ do := func (method string ) {
888+ // Make initial request to non-existing page that should return redirect that contains both query params from original request AND target URL in _redirects file.
889+ req := mustNewRequest (t , method , missingPageURL , nil )
890+ req .Header .Add ("Accept" , "text/html" )
891+ req .Host = "example.org"
892+
893+ res := mustDoWithoutRedirect (t , req )
894+ defer res .Body .Close ()
895+
896+ // Status code should match 301 from _redirects catch-all rule
897+ require .Equal (t , http .StatusMovedPermanently , res .StatusCode )
898+
899+ // Check Redirect target contains all query parameters
900+ redirectURL := res .Header .Get ("Location" )
901+ require .Equal (t , expectedTargetURL , redirectURL )
902+
903+ }
904+
905+ do (http .MethodGet )
906+ do (http .MethodHead )
907+ })
908+
909+ t .Run ("_redirects file redirect supports catch-all splat with dynamic query parameters" , func (t * testing.T ) {
910+ t .Parallel ()
911+
912+ backend , root := newMockBackend (t , "redirects-query-params.car" )
913+ backend .namesys ["/ipns/example.org" ] = newMockNamesysItem (path .FromCid (root ), 0 )
914+
915+ ts := newTestServerWithConfig (t , backend , Config {
916+ NoDNSLink : false ,
917+ PublicGateways : map [string ]* PublicGateway {
918+ "example.org" : {
919+ UseSubdomains : true ,
920+ DeserializedResponses : true ,
921+ },
922+ },
923+ DeserializedResponses : true ,
924+ })
925+
926+ // Request for missing page under path that is a catch-all splat (incl. some dynamic parameters)
927+ missingPageURL := ts .URL + "/source3/this/is/catch-all/splat-with?dynamic-query1=dynamic-value1&dynamic-query2=dynamic-value2"
928+
929+ // Expected response is redirect URL to a different server that preserves entire splat, including dynamic query parameters
930+ expectedTargetURL := "https://example.net/target3/this/is/catch-all/splat-with?dynamic-query1=dynamic-value1&dynamic-query2=dynamic-value2"
931+
932+ do := func (method string ) {
933+ // Make initial request to non-existing page that should return redirect that contains both query params from original request AND target URL in _redirects file.
934+ req := mustNewRequest (t , method , missingPageURL , nil )
935+ req .Header .Add ("Accept" , "text/html" )
936+ req .Host = "example.org"
937+
938+ res := mustDoWithoutRedirect (t , req )
939+ defer res .Body .Close ()
940+
941+ // Status code should match 301 from _redirects catch-all rule
942+ require .Equal (t , http .StatusMovedPermanently , res .StatusCode )
943+
944+ // Check Redirect target contains all query parameters
945+ redirectURL := res .Header .Get ("Location" )
946+ require .Equal (t , expectedTargetURL , redirectURL )
947+
948+ }
949+
950+ do (http .MethodGet )
951+ do (http .MethodHead )
952+ })
953+
819954 t .Run ("Superfluous namespace" , func (t * testing.T ) {
820955 t .Parallel ()
821956
0 commit comments