Skip to content

Commit

Permalink
Initial work to detect cross-origin redirect visit requests
Browse files Browse the repository at this point in the history
  • Loading branch information
jayohms committed Mar 28, 2024
1 parent 90227d6 commit 3398de4
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Source/Session/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ extension Session: WebViewDelegate {
delegate?.session(self, didProposeVisit: proposal)
}

func webView(_ bridge: WebViewBridge, didProposeVisitToCrossOriginRedirect location: URL) {
// TODO
}

func webView(_ webView: WebViewBridge, didStartFormSubmissionToLocation location: URL) {
delegate?.sessionDidStartFormSubmission(self)
}
Expand Down
1 change: 1 addition & 0 deletions Source/WebView/ScriptMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ extension ScriptMessage {
case pageLoadFailed
case errorRaised
case visitProposed
case visitProposedToCrossOriginRedirect
case visitProposalScrollingToAnchor
case visitProposalRefreshingPage
case visitStarted
Expand Down
3 changes: 3 additions & 0 deletions Source/WebView/WebViewBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import WebKit

protocol WebViewDelegate: AnyObject {
func webView(_ webView: WebViewBridge, didProposeVisitToLocation location: URL, options: VisitOptions)
func webView(_ webView: WebViewBridge, didProposeVisitToCrossOriginRedirect location: URL)
func webViewDidInvalidatePage(_ webView: WebViewBridge)
func webView(_ webView: WebViewBridge, didStartFormSubmissionToLocation location: URL)
func webView(_ webView: WebViewBridge, didFinishFormSubmissionToLocation location: URL)
Expand Down Expand Up @@ -129,6 +130,8 @@ extension WebViewBridge: ScriptMessageHandlerDelegate {
delegate?.webViewDidInvalidatePage(self)
case .visitProposed:
delegate?.webView(self, didProposeVisitToLocation: message.location!, options: message.options!)
case .visitProposedToCrossOriginRedirect:
delegate?.webView(self, didProposeVisitToCrossOriginRedirect: message.location!)
case .visitProposalScrollingToAnchor:
break
case .visitProposalRefreshingPage:
Expand Down
29 changes: 27 additions & 2 deletions Source/WebView/turbo.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,18 @@
this.loadResponseForVisitWithIdentifier(visit.identifier)
}

visitRequestFailedWithStatusCode(visit, statusCode) {
this.postMessage("visitRequestFailed", { identifier: visit.identifier, statusCode: statusCode })
async visitRequestFailedWithStatusCode(visit, statusCode) {
// Turbo does not permit cross-origin fetch redirect attempts and
// they'll lead to a visit request failure. Attempt to see if the
// visit request failure was due to a cross-origin redirect.
const redirect = await this.fetchFailedRequestCrossOriginRedirect(visit, statusCode)
const location = visit.location.toString()

if (redirect != null) {
this.postMessage("visitProposedToCrossOriginRedirect", { location: redirect.toString(), identifier: visit.identifier })
} else {
this.postMessage("visitRequestFailed", { location: location, identifier: visit.identifier, statusCode: statusCode })
}
}

visitRequestFinished(visit) {
Expand Down Expand Up @@ -174,6 +184,21 @@
}

// Private

async fetchFailedRequestCrossOriginRedirect(visit, statusCode) {
// Non-HTTP status codes are sent by Turbo for network
// failures, including cross-origin fetch redirect attempts.
if (statusCode <= 0) {
try {
const response = await fetch(visit.location, { redirect: "follow" })
if (response.url != null && response.url.origin != visit.location.origin) {
return response.url
}
} catch {}
}

return null
}

postMessage(name, data = {}) {
data["timestamp"] = Date.now()
Expand Down

0 comments on commit 3398de4

Please sign in to comment.