Unable to create a custom header to use it in "withManager" - haskell

I can't create a custom header without using OverloadedStrings
import Data.Aeson
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit
import qualified Network.HTTP.Headers as HHeaders
import qualified Network.HTTP.Types as HTypes
import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Lazy.Char8 as LC8
-- myHeader1 = (HHeaders.HdrAccept $ C8.pack "some data")
myHeader1 = HHeaders.mkHeader HHeaders.HdrAccept "some data"
get :: String -> IO (Response LC8.ByteString)
get url1 = do
req1 <- parseUrl url1
res1 <- withManager $ httpLbs req1 { method = HTypes.methodGet, requestHeaders = [myHeader1] }
return res1
An error:
Couldn't match type ‘HHeaders.Header’
with ‘(HTypes.HeaderName, C8.ByteString)’
Expected type: HTypes.Header
Actual type: HHeaders.Header
In the expression: myHeader1
In the ‘requestHeaders’ field of a record
What am I doing wrong?

Network.HTTP.Conduit and Network.HTTP.Headers are from incompatible HTTP libraries. Request headers for http-conduit/http-client can be created using the http-types library:
import Data.Aeson
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit as HHeaders
import qualified Network.HTTP.Types as HTypes
import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Lazy.Char8 as LC8
myHeader1 = (HTypes.hAccept, C8.pack "some data")
get :: String -> IO (Response LC8.ByteString)
get url1 = do
req1 <- parseUrl "some url"
res1 <- withManager $ httpLbs req1 { method = HTypes.methodGet, requestHeaders = [myHeader1] }
return res1
To clarify:
You were importing Network.HTTP.Headers from the HTTP library. Although the imports look similar, this is a self-contained library which defines it's own header types and not intended to be used with http-types.
http-client is compatible with the http-types. If you follow the definition for RequestHeader it will lead you to http-types.
In the future, one way to check the compatibility of orthogonal libraries is to look at the build depends on the Hackage index or follow the types as far as possible.

Related

Create a Public/Private Key from a PEM Base64Encode string in Haskell

I'm trying to use RSA from cryptonite package to handle encryption and try to create an X509 cert (Data.X509) from a pem encoded cert base64Encoded string so i can access the Public and Private Key. However, i kept getting a "StreamConstructionWrongSize" error. I think i'm missing something here when reading the base64Encoded string in. I included the snippet of the codes. Perhaps, someone can spot the mistake. I'd really appreciate any help.
import Data.String.Conversions (cs)
import Crypto.Hash (hash, SHA256(..), Digest)
import Data.ByteString (ByteString)
import Data.Text.Encoding (encodeUtf8)
import qualified Data.Text.IO as TIO
import System.IO (hFlush, stdout)
import Data.ByteArray.Encoding (convertToBase, Base (Base64))
import Data.X509
main :: IO ()
main = do
let certBS = encodeUtf8 "MIIFXjCCBEagAwIBAgIIKPOmOJqfnTIwDQYJKoZIhvcNAQELBQAwgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwHhcNMTYwMjA5MjE1NzM5WhcNMTkwMzA3MjIzNTUxWjBrMQswCQYDVQQGEwJVUzENMAsGA1UECBMEVXRhaDEVMBMGA1UEBxMMU291dGggSm9yZGFuMRcwFQYDVQQKEw5BbGxlZ2lhbmNlIEluYzEdMBsGA1UEAwwUKi5hbGxlZ2lhbmNldGVjaC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHWaH6ziZnWy3Uy/wmM7zbHqrg/AmdsWk54x6wpe7+B2USbTbZg56rGjEL/I1Pe/PtcbM8uiPq1TshMxwePqWFCqKfJrj6/RS9/gKpWOwfh+Z2Qk78L3DQH97njDLJAYBf68MX0eKhM0PGBo+1BqKfAb9C3hEltROuABPEBO7GMFecZaDtZ6lDkYBidRs7nOqQnU91X8ly376U0dAEnTghVmDSh5v8yKvMm0AhOd8S7EulfXEg+cj6Y996pM56bDbv2H7NZNv7QD38FY2IQWTWT46z0riF/D93ilVF62cvNXtUwj2kx581Z/e4eB7+Q3cLFFG1KGLgnvDKIguuxK5LAgMBAAGjggG6MIIBtjAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCBaAwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZGlnMnMyLTIuY3JsMFMGA1UdIARMMEowSAYLYIZIAYb9bQEHFwIwOTA3BggrBgEFBQcCARYraHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzBABggrBgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRAwr0njsw0gzCiM9f7bLPwtCyAzjAzBgNVHREELDAqghQqLmFsbGVnaWFuY2V0ZWNoLmNvbYISYWxsZWdpYW5jZXRlY2guY29tMB0GA1UdDgQWBBSFAVaoKHe4qj41Axk07GZrLVx38zANBgkqhkiG9w0BAQsFAAOCAQEApsEOT1kL51QIQbpQjkamjQ1y+HoEMXd7npM1YMfaLxL+v4b6qShMamtNUWac+aJ5ul8849UBhPj3nNfLz6PJtdFmztKOHs58Th8lz6ksSDt1z/GRi3/EcWR7beBuDD2AKFGnckcSkCls+lOBSh/BxAIKPdlD3wzUEpqLuD1scucRfcjwt6X0K3otthMJ9PZ9tJQMSOLnUFKLvG+ZFlnkY+K3pmKWsk2ZMrua2m9wvqVXq4ZgEKQ6xr91HYkPCjfLt14ExnL3vNdXS0DR7LtjJDyXJeFkT0Ev81W9c5dk7gzbwg+EM4UYxW6qz+7WyHvV+uLNnHNmwm1b17tVfYvu3w=="
cert = decodeSignedCertificate certBS
case cert of
Left st -> putStrLn $ "Error reading cert: " ++ st
Right scrt -> do
let crt = getCertificate scrt
putStrLn $ show crt
Updated: I found the solution to my question. Hope this helps other who has the same problem
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.String.Conversions (cs)
import Crypto.Hash (hash, SHA256(..), Digest)
import qualified Data.ByteString as BS (ByteString, readFile)
import Data.Text.Encoding (encodeUtf8)
import qualified Data.Text.IO as TIO
import System.IO (hFlush, stdout)
import Data.ByteArray.Encoding (convertToBase, Base (Base64))
import Data.List (head)
import Data.Text (Text(..))
----- Read file import
import Data.Monoid ((<>))
import Control.Applicative
import Data.ASN1.Types
import Data.ASN1.BinaryEncoding
import Data.ASN1.Encoding
import Data.Maybe
import qualified Data.X509 as X509
import Data.X509.Memory (pemToKey)
import Data.PEM (pemParseLBS, pemParseBS, pemWriteBS, pemContent, pemName, PEM)
import qualified Data.ByteString.Lazy as L
-------------------------
main :: IO ()
main = do
--pems <- readPEMs "./certs/test.pem" -- read from file
let cert1 = "-----BEGIN CERTIFICATE-----\n" ++ "MIIFXjCCBEagAwIBAgIIKPOmOJqfnTIwDQYJKoZIhvcNAQELBQAwgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwHhcNMTYwMjA5MjE1NzM5WhcNMTkwMzA3MjIzNTUxWjBrMQswCQYDVQQGEwJVUzENMAsGA1UECBMEVXRhaDEVMBMGA1UEBxMMU291dGggSm9yZGFuMRcwFQYDVQQKEw5BbGxlZ2lhbmNlIEluYzEdMBsGA1UEAwwUKi5hbGxlZ2lhbmNldGVjaC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHWaH6ziZnWy3Uy/wmM7zbHqrg/AmdsWk54x6wpe7+B2USbTbZg56rGjEL/I1Pe/PtcbM8uiPq1TshMxwePqWFCqKfJrj6/RS9/gKpWOwfh+Z2Qk78L3DQH97njDLJAYBf68MX0eKhM0PGBo+1BqKfAb9C3hEltROuABPEBO7GMFecZaDtZ6lDkYBidRs7nOqQnU91X8ly376U0dAEnTghVmDSh5v8yKvMm0AhOd8S7EulfXEg+cj6Y996pM56bDbv2H7NZNv7QD38FY2IQWTWT46z0riF/D93ilVF62cvNXtUwj2kx581Z/e4eB7+Q3cLFFG1KGLgnvDKIguuxK5LAgMBAAGjggG6MIIBtjAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCBaAwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZGlnMnMyLTIuY3JsMFMGA1UdIARMMEowSAYLYIZIAYb9bQEHFwIwOTA3BggrBgEFBQcCARYraHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzBABggrBgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRAwr0njsw0gzCiM9f7bLPwtCyAzjAzBgNVHREELDAqghQqLmFsbGVnaWFuY2V0ZWNoLmNvbYISYWxsZWdpYW5jZXRlY2guY29tMB0GA1UdDgQWBBSFAVaoKHe4qj41Axk07GZrLVx38zANBgkqhkiG9w0BAQsFAAOCAQEApsEOT1kL51QIQbpQjkamjQ1y+HoEMXd7npM1YMfaLxL+v4b6qShMamtNUWac+aJ5ul8849UBhPj3nNfLz6PJtdFmztKOHs58Th8lz6ksSDt1z/GRi3/EcWR7beBuDD2AKFGnckcSkCls+lOBSh/BxAIKPdlD3wzUEpqLuD1scucRfcjwt6X0K3otthMJ9PZ9tJQMSOLnUFKLvG+ZFlnkY+K3pmKWsk2ZMrua2m9wvqVXq4ZgEKQ6xr91HYkPCjfLt14ExnL3vNdXS0DR7LtjJDyXJeFkT0Ev81W9c5dk7gzbwg+EM4UYxW6qz+7WyHvV+uLNnHNmwm1b17tVfYvu3w==" ++ "\n-----END CERTIFICATE-----"
pems <- readPEM cert1
let certx = X509.decodeSignedObject $ pemContent $ head pems
case certx of
Left st1 -> putStrLn $ "Error reading cert: " ++ st1
Right scrt1 -> do
let crt1 = X509.getCertificate scrt1
print crt1
readPEM :: String -> IO [PEM]
readPEM content = --do
return $ either error id $ pemParseBS $ cs content
readPEMs :: FilePath -> IO [PEM]
readPEMs filepath = do
content <- BS.readFile filepath
return $ either error id $ pemParseBS content

How to read response body in WAI middleware?

I'm trying to create some middleware that will send 500 errors to a remote server. The error information is in the response body.
How can I get the response body from a Response as any kind of string? I see responseToStream but I can't figure out how to use it.
import Network.Wai
import Data.ByteString.Lazy (ByteString)
responseBody :: Response -> IO ByteString
responseBody res = _
An implementation of the comment by #user2407038:
import Data.IORef (newIORef,modifyIORef',readIORef)
import Data.Monoid ((<>))
import Data.ByteString.Lazy (ByteString)
import Data.ByteString.Builder (toLazyByteString)
import Network.Wai
responseBody :: Response -> IO ByteString
responseBody res =
let (status,headers,body) = responseToStream res in
body $ \f -> do
content <- newIORef mempty
f (\chunk -> modifyIORef' content (<> chunk)) (return ())
toLazyByteString <$> readIORef content

Handling HTTP Query parameters in http-conduit

I want to download the content of the URL
http://example.com/foobar?key1=value1&key2=value2
using http-conduit (GET request).
How can I do that:
a) Assuming I already know the full (i.e. encoded URL)
b) If some parameters are dynamic and therefore not URL-encoded?
Note: This question was answered Q&A-style and therefore intentionally does not show any research effort.
Regarding a):
You can use simpleHttp with an URL containing query parameters just like the example in the docs:
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit
import qualified Data.ByteString.Lazy as LB
main :: IO ()
main =
simpleHttp "http://example.com/foobar?key1=value1&key2=value2" >>= LB.putStr
Regarding b):
You need a list of key/value tuples of type [(ByteString, Maybe ByteString)] that contains your query parameters.
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy.Char8 as LB
queryParams :: [(ByteString, Maybe ByteString)]
queryParams = [
("key1", Just "value1"),
("key2", Just "value2")]
main :: IO ()
main = do
request <- parseUrl "http://example.com/foobar"
let request' = setQueryString queryParams request
response <- withManager $ httpLbs request'
LB.putStrLn $ responseBody response
Note: This requires at least http-conduit 2.1.
Also note that it is recommended to reuse Manager instances where applicable.

Haskell Aeson destructuring generic parse

I have a JSON request in the style of
{"command":"get","params":{"something":"something else"}}
and this code snippet from the Yesod book
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai (Response, responseLBS, Application, requestBody)
import Network.HTTP.Types (status200, status400)
import Network.Wai.Handler.Warp (run)
import Data.Aeson.Parser (json)
import Data.Conduit.Attoparsec (sinkParser)
import Control.Monad.IO.Class (liftIO)
import Data.Aeson (Value(..), encode, object, (.=))
import Control.Exception (SomeException)
import Data.ByteString (ByteString)
import Data.Conduit (ResourceT, ($$))
import Control.Exception.Lifted (handle)
main :: IO ()
main = run 3000 app
app :: Application
app req = handle invalidJson $ do
value <- requestBody req $$ sinkParser json
newValue <- liftIO $ modValue value
return $ responseLBS
status200
[("Content-Type", "application/json")]
$ encode newValue
invalidJson :: SomeException -> ResourceT IO Response
invalidJson ex = return $ responseLBS
status400
[("Content-Type", "application/json")]
$ encode $ object
[ ("message" .= show ex)
]
-- Application-specific logic would go here.
modValue :: Value -> IO Value
modValue (Object o)
| -- key "command" corresponds to value "get"
| otherwise = fail "Invalid command"
But I can't wrap my head around how I would destructure the generated Value data structure. How do I go about getting the values of keys etc. I realise I could parse to an explicitly defined data structure, but that would bring other kinds of problems to my use case.
In modValue I've put a comment where I can't figure out what to put. I tried treating it as a Map, since that's how it is implemented inside Aeson, but that obviously doesn't type check.
EDIT:
Adding Data.HashMap to imports and using the line
| M.lookup "command" o == Just "get" = return $ object [("result" .= (String "YAY"))]
gives the following error message.
main.hs:39:26:
Couldn't match expected type `M.Map k0 a0'
with actual type `aeson-0.6.0.2:Data.Aeson.Types.Internal.Object'
In the second argument of `M.lookup', namely `o'
In the first argument of `(==)', namely `M.lookup "command" o'
In the expression: M.lookup "command" o == Just "get"
EDIT2:
On a sudden hunch, I tracked down an error message I got earlier involving "unordered-containers". This is the package that Aeson uses. But I realised that I also had the package hashmap installed, which is imported as Data.HashMap. The hashmaps from unordered-containers are imported as Data.HashMap.Strict or Lazy!
Changing the line import qualified Data.HashMap as M to import qualified Data.HashMap.Strict as M fixed it anyway. Now the given answer works!
Since an aeson JSON object is a Hashmap, you can use the Hasmap interface, in this case lookup.
import qualified Data.HashMap.Strict as M
M.lookup "command" o == Just "get"

Warning: Pattern match(es) are overlapped when matching on strings

I am trying to make a simple url route in Haskell and can't get around the warning:
Warning: Pattern match(es) are overlapped
In a case alternative: "/" -> ...
Ok, modules loaded: Main.
the snippet:
{-# LANGUAGE OverloadedStrings #-}
import Network.Wai
import Network.Wai.Handler.Warp (run)
import Network.Wai.Middleware.Debug (debug)
import Network.HTTP.Types (statusOK, status404)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.ByteString.Char8 (unpack)
import Data.ByteString.Lazy.Char8 (pack)
import qualified Data.Text.Lazy as T
import Control.Monad.IO.Class (liftIO, MonadIO)
application req = do
case unpack $ rawPathInfo req of
"/items" -> itemsJSON
"/" -> indexPage
_ -> return $ responseLBS status404 [("Content-Type", "text/plain")]
"Not found"
indexPage = do
page <- liftIO $ L.readFile "templates/index.html"
return $ responseLBS statusOK [("Content-Type", "text/html; charset=utf-8")] page
itemsJSON =
return $ responseLBS statusOK
[("Content-Type", "application/json; charset=utf-8")] "hi"
main = do
run 3000 $ debug $ application
UPD:
replaced snippet with complete program, and
$ ghc -V
The Glorious Glasgow Haskell Compilation System, version 6.12.1
This is a bug, and is fixed in newer versions of GHC.

Resources