@@ -44,6 +44,12 @@ function responseLocationURL (response, requestFragment) {
44
44
// 3. If location is a header value, then set location to the result of
45
45
// parsing location with response’s URL.
46
46
if ( location !== null && isValidHeaderValue ( location ) ) {
47
+ if ( ! isValidEncodedURL ( location ) ) {
48
+ // Some websites respond location header in UTF-8 form without encoding them as ASCII
49
+ // and major browsers redirect them to correctly UTF-8 encoded addresses.
50
+ // Here, we handle that behavior in the same way.
51
+ location = normalizeBinaryStringToUtf8 ( location )
52
+ }
47
53
location = new URL ( location , responseURL ( response ) )
48
54
}
49
55
@@ -57,6 +63,36 @@ function responseLocationURL (response, requestFragment) {
57
63
return location
58
64
}
59
65
66
+ /**
67
+ * @see https://www.rfc-editor.org/rfc/rfc1738#section-2.2
68
+ * @param {string } url
69
+ * @returns {boolean }
70
+ */
71
+ function isValidEncodedURL ( url ) {
72
+ for ( const c of url ) {
73
+ const code = c . charCodeAt ( 0 )
74
+ // Not used in US-ASCII
75
+ if ( code >= 0x80 ) {
76
+ return false
77
+ }
78
+ // Control characters
79
+ if ( ( code >= 0x00 && code <= 0x1F ) || code === 0x7F ) {
80
+ return false
81
+ }
82
+ }
83
+ return true
84
+ }
85
+
86
+ /**
87
+ * If string contains non-ASCII characters, assumes it's UTF-8 encoded and decodes it.
88
+ * Since UTF-8 is a superset of ASCII, this will work for ASCII strings as well.
89
+ * @param {string } value
90
+ * @returns {string }
91
+ */
92
+ function normalizeBinaryStringToUtf8 ( value ) {
93
+ return Buffer . from ( value , 'binary' ) . toString ( 'utf8' )
94
+ }
95
+
60
96
/** @returns {URL } */
61
97
function requestCurrentURL ( request ) {
62
98
return request . urlList [ request . urlList . length - 1 ]
0 commit comments