@@ 1,7 1,7 @@
{-# LANGUAGE OverloadedStrings #-}
-- | Handles server-specified text decoding.
-module Network.URI.Charset(resolveCharset, convertCharset, charsets) where
+module Network.URI.Charset(resolveCharset, resolveCharset', convertCharset, charsets) where
import Data.Text (Text)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as B
@@ 18,6 18,10 @@ resolveCharset (mime:_:params) response = resolveCharset (mime:params) response
resolveCharset [mime] response = (mime, Right $ response)
resolveCharset [] response = ("text/plain", Left "Filetype unspecified")
+-- | As per `resolveCharset`, but also returns given URI (or other type).
+resolveCharset' :: a -> [String] -> ByteString -> (a, String, Either Text ByteString)
+resolveCharset' a mimes resp = let (mime, resp') = resolveCharset mimes resp in (a, mime, resp')
+
-- | Decodes bytes according to a charset identified by it's IANA-assigned name(s).
convertCharset "iso-8859-1" = decodeLatin1
convertCharset "latin1" = decodeLatin1
@@ 94,16 94,22 @@ fetchURL :: Session -- ^ The session of which this request is a part.
-> [String] -- ^ The expected MIMEtypes in priority order.
-> URI -- ^ The URL to retrieve
-> IO (String, Either Text ByteString) -- ^ The MIMEtype & possibly text-decoded response.
-fetchURL session mimes uri@(URI {uriScheme = "about:", uriPath = ""}) =
- fetchURL session mimes $ uri {uriPath = "version"}
-fetchURL Session {aboutPages = pages} _ URI {uriScheme = "about:", uriPath = path} =
- return (
+fetchURL sess mimes uri = do
+ (_, mime, resp) <- fetchURL' sess mimes uri
+ return (mime, resp)
+
+-- | As per `fetchURL`, but also returns the redirected URI.
+fetchURL' :: Session -> [String] -> URI -> IO (URI, String, Either Text ByteString)
+fetchURL' session mimes uri@(URI {uriScheme = "about:", uriPath = ""}) =
+ fetchURL' session mimes $ uri {uriPath = "version"}
+fetchURL' Session {aboutPages = pages} _ url@URI {uriScheme = "about:", uriPath = path} =
+ return (url,
Txt.unpack $ convertCharset "utf-8" $ B.toStrict $
llookup (path ++ ".mime") "text/html" pages,
Right $ llookup path "" pages)
#ifdef WITH_HTTP_URI
-fetchURL session accept@(defaultMIME:_) uri | uriScheme uri `elem` ["http:", "https:"] = do
+fetchURL' session accept@(defaultMIME:_) uri | uriScheme uri `elem` ["http:", "https:"] = do
request <- HTTP.requestFromURI uri
response <- HTTP.httpLbs request {
HTTP.cookieJar = Nothing, -- Will only be supported by Rhapsode when submitting a form.
@@ 116,18 122,18 @@ fetchURL session accept@(defaultMIME:_) uri | uriScheme uri `elem` ["http:", "ht
HTTP.responseBody response,
[val | ("content-type", val) <- HTTP.responseHeaders response]
) of
- ("", _) -> ("text/plain", Right $ B.fromStrict $ statusMessage $ HTTP.responseStatus response)
+ ("", _) -> (uri, "text/plain", Right $ B.fromStrict $ statusMessage $ HTTP.responseStatus response)
(response, (mimetype:_)) -> let mime = Txt.toLower $ convertCharset "utf-8" mimetype
- in resolveCharset (map (Txt.unpack . Txt.strip) $ Txt.splitOn ";" mime) response
- (response, []) -> (defaultMIME, Right response)
+ in resolveCharset' uri (map (Txt.unpack . Txt.strip) $ Txt.splitOn ";" mime) response
+ (response, []) -> (uri, defaultMIME, Right response)
`catches` [
- Handler $ \e -> do return ("text/plain", Left $ Txt.pack $ trans (locale session) $ Http e),
- Handler $ \(ErrorCall msg) -> do return ("text/plain", Left $ Txt.pack msg)
+ Handler $ \e -> do return (uri, "text/plain", Left $ Txt.pack $ trans (locale session) $ Http e),
+ Handler $ \(ErrorCall msg) -> do return (uri, "text/plain", Left $ Txt.pack msg)
]
#endif
#ifdef WITH_GEMINI_URI
-fetchURL sess@Session {connCtxt = ctxt} mimes uri@URI {
+fetchURL' sess@Session {connCtxt = ctxt} mimes uri@URI {
uriScheme = "gemini:", uriAuthority = Just (URIAuth _ host port)
} = do
conn <- connectTo ctxt $ ConnectionParams {
@@ 141,7 147,7 @@ fetchURL sess@Session {connCtxt = ctxt} mimes uri@URI {
header <- connectionGetLine 1024 conn
ret <- case parseHeader header of
-- NOTE: This case won't actually do anything until the caller (Rhapsode) implements forms.
- ('1', _, label) -> return ("application/xhtml+xml", Left $ Txt.concat [
+ ('1', _, label) -> return (uri, "application/xhtml+xml", Left $ Txt.concat [
"<form><label for='input'>",
Txt.replace "<" "<" $ Txt.replace "&" "&" label,
"</label><input id='input' /></form>"
@@ 149,12 155,12 @@ fetchURL sess@Session {connCtxt = ctxt} mimes uri@URI {
('2', _, mime) -> do
chunks <- mWhile (connectionWaitForInput conn 60000) $ connectionGetChunk conn
let mime' = map (Txt.unpack . Txt.strip) $ Txt.splitOn ";" mime
- return $ resolveCharset mime' $ B.fromChunks chunks
+ return $ resolveCharset' uri mime' $ B.fromChunks chunks
('3', _, redirect) | Just redirect' <- parseURIReference $ Txt.unpack redirect ->
- fetchURL sess mimes $ relativeTo redirect' uri
+ fetchURL' sess mimes $ relativeTo redirect' uri
-- TODO Implement client certificates, once I have a way for the user/caller to select one.
-- And once I figure out how to configure the TLS cryptography.
- (_, _, err) -> return ("text/plain", Left err)
+ (_, _, err) -> return (uri, "text/plain", Left err)
connectionClose conn
return ret
where
@@ 165,34 171,34 @@ fetchURL sess@Session {connCtxt = ctxt} mimes uri@URI {
#endif
#ifdef WITH_FILE_URI
-fetchURL Session {locale = l} (defaultMIME:_) uri@URI {uriScheme = "file:"} = do
+fetchURL' Session {locale = l} (defaultMIME:_) uri@URI {uriScheme = "file:"} = do
response <- B.readFile $ uriPath uri
- return (defaultMIME, Right response)
+ return (uri, defaultMIME, Right response)
`catch` \e -> do
- return (
+ return (uri,
"text/plain",
Left $ Txt.pack $ trans l $ ReadFailed $ displayException (e :: IOException))
#endif
#ifdef WITH_DATA_URI
-fetchURL _ (defaultMIME:_) uri@URI {uriScheme = "data:"} =
+fetchURL' _ (defaultMIME:_) uri@URI {uriScheme = "data:"} =
let request = uriPath uri ++ uriQuery uri ++ uriFragment uri
in case breakOn ',' $ unEscapeString request of
- ("", response) -> return (defaultMIME, Left $ Txt.pack response)
+ ("", response) -> return (uri, defaultMIME, Left $ Txt.pack response)
(mime', response) | '4':'6':'e':'s':'a':'b':';':mime <- reverse mime' ->
return $ case B64.decode $ C8.pack response of
- Left str -> ("text/plain", Left $ Txt.pack str)
- Right bytes -> (reverse mime, Right $ B.fromStrict bytes)
- (mime, response) -> return (mime, Left $ Txt.pack response)
+ Left str -> (uri, "text/plain", Left $ Txt.pack str)
+ Right bytes -> (uri, reverse mime, Right $ B.fromStrict bytes)
+ (mime, response) -> return (uri, mime, Left $ Txt.pack response)
#endif
#ifdef WITH_XDG
-fetchURL Session {locale = l, apps = a} _ uri@(URI {uriScheme = s}) = do
+fetchURL' Session {locale = l, apps = a} _ uri@(URI {uriScheme = s}) = do
app <- dispatchURIByMIME a uri ("x-scheme-handler/" ++ init s)
- return ("text/html", Left $ Txt.pack $ trans l $ app)
+ return (uri, "text/html", Left $ Txt.pack $ trans l $ app)
#else
-fetchURL Session {locale = l} _ URI {uriScheme = scheme} =
- return ("text/plain", Left $ Txt.pack $ trans l $ UnsupportedScheme scheme)
+fetchURL' Session {locale = l} _ URI {uriScheme = scheme} =
+ return (uri, "text/plain", Left $ Txt.pack $ trans l $ UnsupportedScheme scheme)
#endif
dispatchByMIME :: Session -> String -> URI -> IO (Maybe String)