@@ -252,6 +252,9 @@ extension SwiftLinkPreview {
252252 request. httpMethod = " HEAD "
253253
254254 task = session. dataTask ( with: request, completionHandler: { data, response, error in
255+ guard !cancellable. isCancelled
256+ else { return }
257+
255258 if error != nil {
256259 self . workQueue. async {
257260 if !cancellable. isCancelled {
@@ -262,12 +265,57 @@ extension SwiftLinkPreview {
262265 } else {
263266 if let finalResult = response? . url {
264267 if ( finalResult. absoluteString == url. absoluteString) {
265- self . workQueue. async {
266- if !cancellable. isCancelled {
267- completion ( url)
268+ if response? . mimeType? . contains ( " /html " ) ?? false {
269+ var request = URLRequest ( url: url )
270+ request. addValue ( " text/html,application/xhtml+xml,application/xml " , forHTTPHeaderField: " Accept " )
271+ self . session. dataTask ( with: request, completionHandler: { data, response, error in
272+ guard !cancellable. isCancelled
273+ else { return }
274+
275+ if error != nil {
276+ self . workQueue. async {
277+ if !cancellable. isCancelled {
278+ onError ( . cannotBeOpened( " \( url. absoluteString) : \( error. debugDescription) " ) )
279+ }
280+ }
281+ return
282+ }
283+
284+ if let response = response, let data = data {
285+ let encoding = response. textEncodingName. flatMap {
286+ String . Encoding ( rawValue: CFStringConvertEncodingToNSStringEncoding (
287+ CFStringConvertIANACharSetNameToEncoding ( $0 as CFString ) ) )
288+ } ?? . utf8
289+ if let html = String ( data: data, encoding: encoding ) {
290+ for meta in Regex . pregMatchAll ( html, regex: Regex . metatagPattern, index: 1 ) {
291+ if ( meta. contains ( " http-equiv= \" refresh \" " ) || meta. contains ( " http-equiv='refresh' " ) ) ,
292+ let value = Regex . pregMatchFirst ( meta, regex: Regex . metatagContentPattern, index: 2 ) ? . decoded. extendedTrim,
293+ let redirectString = value. split ( separator: " ; " )
294+ . first ( where: { $0. lowercased ( ) . starts ( with: " url= " ) } ) ?
295+ . split ( separator: " = " , maxSplits: 1 ) . last,
296+ let redirectURL = URL ( string: self . addImagePrefixIfNeeded ( String ( redirectString ) , url: url ) ) {
297+ self . unshortenURL ( redirectURL, cancellable: cancellable, completion: completion, onError: onError )
298+ return
299+ }
300+ }
301+ }
302+ }
303+
304+ self . workQueue. async {
305+ if !cancellable. isCancelled {
306+ completion ( url )
307+ }
308+ }
309+ } ) . resume ( )
310+ }
311+ else {
312+ self . workQueue. async {
313+ if !cancellable. isCancelled {
314+ completion ( url )
315+ }
268316 }
317+ task = nil
269318 }
270- task = nil
271319 } else {
272320 task? . cancel ( )
273321 task = nil
@@ -603,11 +651,19 @@ extension SwiftLinkPreview {
603651 }
604652
605653 // Add prefix image if needed
654+ fileprivate func addImagePrefixIfNeeded( _ image: String , url: URL ) -> String {
655+ addImagePrefixIfNeeded ( image, canonicalUrl: self . extractCanonicalURL ( url ) , finalUrl: self . extractInURLRedirectionIfNeeded ( url ) . absoluteString )
656+ }
657+
606658 fileprivate func addImagePrefixIfNeeded( _ image: String , result: Response ) -> String {
659+ addImagePrefixIfNeeded ( image, canonicalUrl: result. canonicalUrl, finalUrl: result. finalUrl? . absoluteString )
660+ }
607661
662+ fileprivate func addImagePrefixIfNeeded( _ image: String , canonicalUrl: String ? , finalUrl: String ? ) -> String {
608663 var image = image
609664
610- if let canonicalUrl = result. canonicalUrl, let finalUrl = result. finalUrl? . absoluteString {
665+ // TODO: account for HTML <base>
666+ if let canonicalUrl = canonicalUrl, let finalUrl = finalUrl {
611667 if finalUrl. hasPrefix ( " https: " ) {
612668 if image. hasPrefix ( " // " ) {
613669 image = " https: " + image
0 commit comments