Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/htmx.js
Original file line number Diff line number Diff line change
Expand Up @@ -4039,11 +4039,11 @@ var htmx = (function() {

/**
* @param {XMLHttpRequest} xhr
* @param {RegExp} regexp
* @param {string} name
* @return {boolean}
*/
function hasHeader(xhr, regexp) {
return regexp.test(xhr.getAllResponseHeaders())
function hasHeader(xhr, name) {
return xhr.getResponseHeader(name) !== null
}

/**
Expand Down Expand Up @@ -4668,13 +4668,13 @@ var htmx = (function() {
//= ==========================================
let pathFromHeaders = null
let typeFromHeaders = null
if (hasHeader(xhr, /HX-Push:/i)) {
if (hasHeader(xhr, 'HX-Push')) {
pathFromHeaders = xhr.getResponseHeader('HX-Push')
typeFromHeaders = 'push'
} else if (hasHeader(xhr, /HX-Push-Url:/i)) {
} else if (hasHeader(xhr, 'HX-Push-Url')) {
pathFromHeaders = xhr.getResponseHeader('HX-Push-Url')
typeFromHeaders = 'push'
} else if (hasHeader(xhr, /HX-Replace-Url:/i)) {
} else if (hasHeader(xhr, 'HX-Replace-Url')) {
pathFromHeaders = xhr.getResponseHeader('HX-Replace-Url')
typeFromHeaders = 'replace'
}
Expand Down Expand Up @@ -4809,11 +4809,11 @@ var htmx = (function() {

if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return

if (hasHeader(xhr, /HX-Trigger:/i)) {
if (hasHeader(xhr, 'HX-Trigger')) {
handleTriggerHeader(xhr, 'HX-Trigger', elt)
}

if (hasHeader(xhr, /HX-Location:/i)) {
if (hasHeader(xhr, 'HX-Location')) {
let redirectPath = xhr.getResponseHeader('HX-Location')
/** @type {HtmxAjaxHelperContext&{path?:string}} */
var redirectSwapSpec = {}
Expand All @@ -4828,9 +4828,9 @@ var htmx = (function() {
return
}

const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
const shouldRefresh = hasHeader(xhr, 'HX-Refresh') && xhr.getResponseHeader('HX-Refresh') === 'true'

if (hasHeader(xhr, /HX-Redirect:/i)) {
if (hasHeader(xhr, 'HX-Redirect')) {
responseInfo.keepIndicators = true
htmx.location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && htmx.location.reload()
Expand Down Expand Up @@ -4859,11 +4859,11 @@ var htmx = (function() {
}

// response headers override response handling config
if (hasHeader(xhr, /HX-Retarget:/i)) {
if (hasHeader(xhr, 'HX-Retarget')) {
responseInfo.target = resolveRetarget(elt, xhr.getResponseHeader('HX-Retarget'))
}

if (hasHeader(xhr, /HX-Reswap:/i)) {
if (hasHeader(xhr, 'HX-Reswap')) {
swapOverride = xhr.getResponseHeader('HX-Reswap')
}

Expand Down Expand Up @@ -4919,7 +4919,7 @@ var htmx = (function() {
selectOverride = responseInfoSelect
}

if (hasHeader(xhr, /HX-Reselect:/i)) {
if (hasHeader(xhr, 'HX-Reselect')) {
selectOverride = xhr.getResponseHeader('HX-Reselect')
}

Expand All @@ -4933,7 +4933,7 @@ var htmx = (function() {
anchor: responseInfo.pathInfo.anchor,
contextElement: elt,
afterSwapCallback: function() {
if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) {
if (hasHeader(xhr, 'HX-Trigger-After-Swap')) {
let finalElt = elt
if (!bodyContains(elt)) {
finalElt = getDocument().body
Expand All @@ -4942,7 +4942,7 @@ var htmx = (function() {
}
},
afterSettleCallback: function() {
if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) {
if (hasHeader(xhr, 'HX-Trigger-After-Settle')) {
let finalElt = elt
if (!bodyContains(elt)) {
finalElt = getDocument().body
Expand Down
16 changes: 16 additions & 0 deletions test/core/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,4 +533,20 @@ describe('Core htmx AJAX headers', function() {
htmx._('loadHistoryFromServer')('/test')
this.server.respond()
})

// Regression for #527: response headers whose names *contain* an htmx
// header name as a substring (e.g. X-HX-Trigger) must not be treated as
// the htmx header. The previous implementation tested a regex against
// getAllResponseHeaders() and false-positively matched the substring,
// then crashed when getResponseHeader returned null.
it('does not crash or fire trigger when X-HX-Trigger header is present without HX-Trigger', function() {
this.server.respondWith('GET', '/test', [200, { 'X-HX-Trigger': 'foo' }, ''])

var div = make('<div hx-get="/test"></div>')
var invokedEvent = false
div.addEventListener('foo', function() { invokedEvent = true })
div.click()
this.server.respond()
invokedEvent.should.equal(false)
})
})
Loading