如何处理TlsNotSupported并使用Network.HTTP.Client调用HTTPS URL?

时间:2014-08-25 16:28:02

标签: http haskell ssl

我正在尝试使用Network.HTTP.Client调用API,并试图找出如何正确处理TlsNotSupported异常并通过SSL调用API。 documentation中没有示例,也没有(令人惊讶的)我可以在网络上找到的任何示例。

这是我现有的代码:

module Main where

import Network.URL
import qualified Network.URI as URI
import qualified Network.HTTP as HTTP
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Base64 as B64
import qualified Network.HTTP.Client as HTTPClient
import qualified Network.HTTP.Types.Header as HTTPHeaders
import qualified Data.ByteString.Char8 as C
import qualified Network.HTTP.Types.Status as HTTPStatus

import qualified Data.Text as T
import qualified Control.Exception as E
import qualified Data.Text.Encoding as TE

import Data.Aeson
import Control.Applicative ((<*>), (<$>), pure)
import Control.Monad (mzero)

data Bookmark = Bookmark {
    url :: T.Text,
    title :: Maybe T.Text
} deriving Show

data Note = Note {
    author :: T.Text,
    text :: T.Text
} deriving Show

instance FromJSON Bookmark where
    parseJSON (Object v) = Bookmark <$>
        v .: T.pack "href" <*>
        v .: T.pack "description"

    parseJSON _ = mzero

b64Encode :: String -> String
b64Encode = T.unpack . TE.decodeUtf8 . B64.encode . TE.encodeUtf8 . T.pack

basicAuthHeader :: String -> String -> String
basicAuthHeader username password = "Authorization: " ++
    b64Encode (username ++ ":" ++ username)

postsURL token = "https://api.pinboard.in/posts/all?format=json&auth_token=" ++ token

parse :: BS.ByteString -> Maybe [Bookmark]
parse response = decode (LBS.fromStrict response)

transform = LBS.fromStrict . C.pack

errorHandler :: HTTPClient.HttpException -> IO (Maybe a)
errorHandler (HTTPClient.StatusCodeException status _ _) = return Nothing
errorHandler (HTTPClient.InvalidUrlException _ _) = return Nothing
errorHandler (HTTPClient.HttpParserException _) = return Nothing
errorHandler e = do
    case e of
         HTTPClient.TlsNotSupported -> (putStrLn $ "Bummer. " ++ show e) >> return Nothing

main = do
    putStrLn "Enter auth token: "
    token <- getLine
    manager <- HTTPClient.newManager HTTPClient.defaultManagerSettings
    request <- HTTPClient.parseUrl $ postsURL token
    putStrLn $ "Calling " ++ postsURL token
    response <- (Just <$> HTTPClient.httpLbs request manager) `E.catch` errorHandler
    return ()

这是一个示例会话:

$ runhaskell Pinboard.hs
Enter auth token:
blah
Calling https://api.pinboard.in/posts/all?format=json&auth_token=asd
Bummer. TlsNotSupported

提前致谢!

2 个答案:

答案 0 :(得分:7)

您需要使用http-client-tls。请特别将defaultManagerSettings的使用情况替换为tlsManagerSettings

答案 1 :(得分:0)

Calling https://api.pinboard.in/posts/all?format=json&auth_token=asd
Bummer. TlsNotSupported

我得到了不同的结果。似乎支持TLS。

您使用 Gandi标准SSL CA UTN-USERFirst-Hardware 作为信任锚吗?


$ echo -e "GET /posts/all?format=json&auth_token=asd HTTP/1.1\r\nHost:api.pinboard.in\r\n\r\n" | \
    openssl s_client -tls1 -connect api.pinboard.in:443 -servername api.pinboard.in -ign_eof
CONNECTED(00000003)
depth=1 C = FR, O = GANDI SAS, CN = Gandi Standard SSL CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.pinboard.in
   i:/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
 1 s:/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIE4zCCA8ugAwIBAgIRAJhTQpn18jrbs6EACUBuCEEwDQYJKoZIhvcNAQEFBQAw
QTELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUdBTkRJIFNBUzEeMBwGA1UEAxMVR2Fu
ZGkgU3RhbmRhcmQgU1NMIENBMB4XDTEzMDgwNTAwMDAwMFoXDTE1MDkwMzIzNTk1
OVowYTEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSQwIgYDVQQL
ExtHYW5kaSBTdGFuZGFyZCBXaWxkY2FyZCBTU0wxFjAUBgNVBAMUDSoucGluYm9h
cmQuaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmwELG8RLaC+PD
YRSORwf5dZ+OGDFNnot9It+nEJh3Y9e95y2xxfQQnMfGcrj4UdMNx6Vie2Baz3yD
hU6hweT5ZlpWA43u7xoF2DP5NsgktcTZzQ+IZ124uVvDs+Q5LAH6/aHUplzmdZDz
xDM9JSz8pxXmS9HSJmR1tamYi9B+d30/yxHPibe62Ku6FQ4yoa+f2GVGdvqqvvpZ
7gbwBgu6PKLVNRQPrhUdjdgEj0h44/4DZ/sUw3Jw2cti0yELh4eDLgXonvqCUrOQ
79NJTEzqBBHBUER0ltdUbCXczAm5IQk4pTzSUaI5rML/fcphBaWh0t0XSg9cMFAl
biNqpr9tAgMBAAGjggG0MIIBsDAfBgNVHSMEGDAWgBS2qP+iqC/Qps1LsWjz51AQ
Mad5ITAdBgNVHQ4EFgQUzuFnVq27SbN342v9mBiEc7tygJIwDgYDVR0PAQH/BAQD
AgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MGAGA1UdIARZMFcwSwYLKwYBBAGyMQECAhowPDA6BggrBgEFBQcCARYuaHR0cDov
L3d3dy5nYW5kaS5uZXQvY29udHJhY3RzL2ZyL3NzbC9jcHMvcGRmLzAIBgZngQwB
AgEwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5nYW5kaS5uZXQvR2FuZGlT
dGFuZGFyZFNTTENBLmNybDBqBggrBgEFBQcBAQReMFwwNwYIKwYBBQUHMAKGK2h0
dHA6Ly9jcnQuZ2FuZGkubmV0L0dhbmRpU3RhbmRhcmRTU0xDQS5jcnQwIQYIKwYB
BQUHMAGGFWh0dHA6Ly9vY3NwLmdhbmRpLm5ldDAlBgNVHREEHjAcgg0qLnBpbmJv
YXJkLmluggtwaW5ib2FyZC5pbjANBgkqhkiG9w0BAQUFAAOCAQEAn9i7ilujiOhL
QKMAAuS7xWTvERddqjnnOPBwUw7FCd+VaEnpNCCjqxwrTdWjm4MkjtN2HfDesw1c
IqpLVAMNn35m3aqu7fvyCbBKCkjXLnj1TuKsd/IIFJuqgHNjqyvfe6IIW/Mss+Qq
2TUmVF0HLF2+fyihsdYTlqcv5bR/X7dwbFi1xecoMaDf6K8TTiEKjmr2wNWuKRGy
TsWTqMPBPkyHBJPL589/ETBvqvx1cu/CU81hiadlVino/Buha0cDjNYZra2gOfRR
U2+vK9tsN9Ct0lfOYAamHSiIwGD1HfzdV8xItmZSNsLm3jBZQ1bsqN82+9n2CKWG
IS0WjmfyaA==
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.pinboard.in
issuer=/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
---
No client certificate CA names sent
---
SSL handshake has read 3332 bytes and written 438 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: C4D0B1D4C4DA50734AFA09A3675A9A6828053B022A516F53E6C2BEA303C49AFC
    Session-ID-ctx: 
    Master-Key: 34A2E6C6B1D17AE7214380462438E9C670CA1E8F9E719D0DEFB7EDE1EC87D847D1DF317523BAAE05278A10E1EDAE51C5
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 15 99 b9 ce d4 d9 bc 6f-d5 4b 12 83 cd 6f eb b0   .......o.K...o..
    0010 - f6 37 a3 66 21 ea ff d1-cf 73 56 fa 25 99 61 1c   .7.f!....sV.%.a.
    0020 - 38 15 a6 e9 e8 47 cc f8-2b df 8d 64 68 13 1c be   8....G..+..dh...
    0030 - 8d 8a 32 a5 ca dd 79 d7-f6 d0 0c 1e e4 50 01 64   ..2...y......P.d
    0040 - 73 3e 9f 34 42 3d 4d 56-a3 cc 09 d8 aa 7b 2a 82   s>.4B=MV.....{*.
    0050 - 5d 96 c3 1f 3e 19 48 c7-90 c6 4c 07 75 15 e5 42   ]...>.H...L.u..B
    0060 - 13 31 c1 fc b4 cc 5f 8e-0b a1 cd 5f bc 7a 16 9c   .1...._...._.z..
    0070 - 24 3c 5b e7 52 97 ce 15-4f b1 01 44 dc 72 35 82   $<[.R...O..D.r5.
    0080 - 4e c9 f9 19 69 26 1c 82-44 f5 c0 6a 57 99 54 da   N...i&..D..jW.T.
    0090 - cf a8 f4 6f 6b ab c6 ec-98 c6 91 31 d1 20 5c 5c   ...ok......1. \\
    00a0 - 0f 94 42 5a 8f f5 f7 0d-cd 31 71 04 66 89 5f c1   ..BZ.....1q.f._.
    00b0 - 00 84 cd 9e c1 99 52 4f-c0 1e 43 25 f2 36 b9 28   ......RO..C%.6.(

    Start Time: 1408986495
    Timeout   : 7200 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---
HTTP/1.1 403 Forbidden
Date: Mon, 25 Aug 2014 17:07:55 GMT
Server: Apache/2.2.22 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 292
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /posts/all
on this server.</p>
<hr>
<address>Apache/2.2.22 (Ubuntu) Server at api.pinboard.in Port 80</address>
</body></html>
read:errno=0
riemann::~$