Skip to content

Commit

Permalink
Merge pull request #81 from will-lumley/feature/http-authentication
Browse files Browse the repository at this point in the history
HTTP Authentication
  • Loading branch information
will-lumley authored Aug 9, 2024
2 parents 87a632d + aaef59a commit 21e00c7
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 94 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/SwiftLint-Linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: SwiftLint - Linux

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/realm/swiftlint:latest

steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1

- name: Run SwiftLint and annotate
run: |
# Run SwiftLint and fail the job if any violations are found
swiftlint lint --reporter github-actions-logging || exit 1
42 changes: 42 additions & 0 deletions .github/workflows/SwiftLint-macOS.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: SwiftLint - macOS

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
lint:
name: Run SwiftLint
runs-on: macos-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0

- name: Install SwiftLint
run: brew install swiftlint

- name: Run SwiftLint and annotate
run: |
# Run SwiftLint and capture output
OUTPUT=$(swiftlint lint --reporter github-actions-logging)
# Print the output so GitHub Actions can pick up annotations
echo "$OUTPUT"
# Fail the job if any violations are found
if [ -n "$OUTPUT" ]; then
echo "SwiftLint violations found. Failing the action."
exit 1
else
echo "No SwiftLint violations found."
fi
32 changes: 0 additions & 32 deletions .github/workflows/swiftlint.yml

This file was deleted.

7 changes: 6 additions & 1 deletion Sources/FaviconFinder/FaviconFinder+Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public extension FaviconFinder {
/// Indicates if we should check for a meta-refresh-redirect tag in the HTML header
public let checkForMetaRefreshRedirect: Bool

/// The HTTP headers we'll pass along to our HTTP request
public let httpHeaders: [String: String?]?

public let prefetchedHTML: Document?

// MARK: - Lifecycle
Expand All @@ -31,12 +34,14 @@ public extension FaviconFinder {
preferredSource: FaviconSourceType = .html,
preferences: [FaviconSourceType: String] = [:],
checkForMetaRefreshRedirect: Bool = false,
prefetchedHTML: Document? = nil
prefetchedHTML: Document? = nil,
httpHeaders: [String: String?]? = nil
) {
self.preferredSource = preferredSource
self.preferences = preferences
self.checkForMetaRefreshRedirect = checkForMetaRefreshRedirect
self.prefetchedHTML = prefetchedHTML
self.httpHeaders = httpHeaders
}
}

Expand Down
155 changes: 94 additions & 61 deletions Sources/FaviconFinder/Toolbox/FaviconURLSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class FaviconURLSession {

static func dataTask(
with url: URL,
checkForMetaRefreshRedirect: Bool = false
checkForMetaRefreshRedirect: Bool = false,
httpHeaders: [String?: String]? = nil
) async throws -> Response {
#if os(Linux)
try await linuxDataTask(
Expand All @@ -56,87 +57,110 @@ private extension FaviconURLSession {

static func linuxDataTask(
with url: URL,
checkForMetaRefreshRedirect: Bool = false
checkForMetaRefreshRedirect: Bool = false,
httpHeaders: [String: String?]? = nil
) async throws -> Response {
let response = Response(try await URLSession.shared.data(from: url))

let data = response.data

if checkForMetaRefreshRedirect {
// Make sure we can parse the response into a string
guard let htmlStr = String(data: data, encoding: response.textEncoding) else {
return response
}

// Parse the string into a workable HTML object
let html = try SwiftSoup.parse(htmlStr)

// Get the head of the HTML
guard let head = html.head() else {
return response
}
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
try? httpClient.syncShutdown()
}

// Get all meta-refresh-redirect tag
let httpEquivs = try head.getElementsByAttribute("http-equiv")
guard let httpEquiv = try httpEquivs.whereAttr("http-equiv", equals: "refresh") else {
return response
// Convert headers to HTTPHeaders
var headers = HTTPHeaders()
if let httpHeaders = httpHeaders {
for (key, value) in httpHeaders {
headers.add(name: key, value: value ?? "")
}
}

// Get the URL
var redirectURLStr = try httpEquiv.attr("content")

// Remove the 0;URL=
redirectURLStr = redirectURLStr.replacingOccurrences(of: "0;URL=", with: "")
// Create the request
var request = HTTPClientRequest(url: url.absoluteString)
request.method = .GET
request.headers = headers

// Determine if this is a whole new URL, or something we should append to the current one
let brandNewURL = Regex.testForHttpsOrHttp(input: redirectURLStr)
// Send the request
let response = try await httpClient.execute(request, timeout: .seconds(30))

// If this is a brand new URL
if brandNewURL {
// If we can't form a valid redirect URL, we'll just return the data from the original page
guard let redirectURL = URL(string: redirectURLStr) else {
return response
}
// Collect the response body
let byteBuffer = try await response.body.collect(upTo: Int.max)
let data = Data(buffer: byteBuffer)

let redirectResponse = Response(try await URLSession.shared.data(from: redirectURL))
return redirectResponse
// Check for meta-refresh redirect if needed
if checkForMetaRefreshRedirect {
// swiftlint:disable:next non_optional_string_data_conversion
guard let htmlStr = String(data: data, encoding: .utf8) else {
throw URLError(.badServerResponse)
}
let html = try SwiftSoup.parse(htmlStr)

// If this something we should append to our current URL
else {
let needsPrependingSlash = url.absoluteString.last != "/" && redirectURLStr.first != "/"
if needsPrependingSlash {
redirectURLStr = "\(url.absoluteString)/\(redirectURLStr)"
} else {
redirectURLStr = "\(url.absoluteString)\(redirectURLStr)"
}

// If we can't form a valid redirect URL, we'll just return the data from the original page
guard let redirectURL = URL(string: redirectURLStr) else {
return response
if let head = html.head() {
let httpEquiv = try head
.getElementsByAttribute("http-equiv")
.whereAttr("http-equiv", equals: "refresh")

if let httpEquiv {
var redirectURLStr = try httpEquiv
.attr("content")
.replacingOccurrences(of: "0;URL=", with: "")
let brandNewURL = Regex.testForHttpsOrHttp(input: redirectURLStr)

if brandNewURL, let redirectURL = URL(string: redirectURLStr) {
return try await linuxDataTask(
with: redirectURL,
checkForMetaRefreshRedirect: false,
httpHeaders: httpHeaders
)
} else {
let needsPrependingSlash = url.absoluteString.last != "/" && redirectURLStr.first != "/"
if needsPrependingSlash {
redirectURLStr = "\(url.absoluteString)/\(redirectURLStr)"
} else {
redirectURLStr = "\(url.absoluteString)\(redirectURLStr)"
}
if let redirectURL = URL(string: redirectURLStr) {
return try await linuxDataTask(
with: redirectURL,
checkForMetaRefreshRedirect: false,
httpHeaders: httpHeaders
)
}
}
}

let redirectResponse = Response(try await URLSession.shared.data(from: redirectURL))
return redirectResponse
}
} else {
// We're not supposed to check for the meta-refresh-redirect,
// so just return the data.
return response
}

// Return the response with data and headers
return Response((data, response.headers))
}

#else

// swiftlint:disable:next cyclomatic_complexity
static func appleDataTask(
with url: URL,
checkForMetaRefreshRedirect: Bool = false
checkForMetaRefreshRedirect: Bool = false,
httpHeaders: [String: String?]? = nil
) async throws -> Response {
let response = Response(try await URLSession.shared.data(from: url))
// Create our request
var request = URLRequest(url: url)

// If there's HTTP headers, add them
if let httpHeaders {
for (key, value) in httpHeaders {
request.setValue(value, forHTTPHeaderField: key)
}
}

let data = response.data
// Fetch our response
let response = Response(
try await URLSession.shared.data(for: request)
)

// If the user wants to check for meta-refresh-redirect, do so and
// if we find a redirect, follow that up
if checkForMetaRefreshRedirect {
let data = response.data

// Make sure we can parse the response into a string
guard let htmlStr = String(data: data, encoding: response.textEncoding) else {
return response
Expand Down Expand Up @@ -191,7 +215,16 @@ private extension FaviconURLSession {
return response
}

let redirectResponse = Response(try await URLSession.shared.data(from: redirectURL))
var redirectRequest = URLRequest(url: redirectURL)
if let httpHeaders {
for (key, value) in httpHeaders {
redirectRequest.setValue(value, forHTTPHeaderField: key)
}
}

let redirectResponse = Response(
try await URLSession.shared.data(from: redirectURL)
)
return redirectResponse
}
}
Expand Down

0 comments on commit 21e00c7

Please sign in to comment.