diff options
-rw-r--r-- | krpc.cabal | 7 | ||||
-rw-r--r-- | src/Data/BEncode/Pretty.hs | 75 | ||||
-rw-r--r-- | src/Network/KRPC/Manager.hs | 15 |
3 files changed, 93 insertions, 4 deletions
@@ -38,6 +38,9 @@ flag builder | |||
38 | description: Use older bytestring package and bytestring-builder. | 38 | description: Use older bytestring package and bytestring-builder. |
39 | default: False | 39 | default: False |
40 | 40 | ||
41 | flag aeson | ||
42 | description: Use aeson for pretty-printing bencoded data. | ||
43 | default: True | ||
41 | 44 | ||
42 | library | 45 | library |
43 | default-language: Haskell2010 | 46 | default-language: Haskell2010 |
@@ -48,6 +51,7 @@ library | |||
48 | Network.KRPC.Message | 51 | Network.KRPC.Message |
49 | Network.KRPC.Method | 52 | Network.KRPC.Method |
50 | Network.KRPC.Manager | 53 | Network.KRPC.Manager |
54 | Data.BEncode.Pretty | ||
51 | build-depends: base == 4.* | 55 | build-depends: base == 4.* |
52 | , text >= 0.11 | 56 | , text >= 0.11 |
53 | , data-default-class | 57 | , data-default-class |
@@ -60,6 +64,9 @@ library | |||
60 | , network >= 2.3 | 64 | , network >= 2.3 |
61 | , cereal | 65 | , cereal |
62 | , containers | 66 | , containers |
67 | if flag(aeson) | ||
68 | build-depends: aeson, aeson-pretty, unordered-containers, vector | ||
69 | ghc-options: -DBENCODE_AESON | ||
63 | if flag(builder) | 70 | if flag(builder) |
64 | build-depends: bytestring >= 0.9, bytestring-builder | 71 | build-depends: bytestring >= 0.9, bytestring-builder |
65 | else | 72 | else |
diff --git a/src/Data/BEncode/Pretty.hs b/src/Data/BEncode/Pretty.hs new file mode 100644 index 00000000..7b0d46a0 --- /dev/null +++ b/src/Data/BEncode/Pretty.hs | |||
@@ -0,0 +1,75 @@ | |||
1 | {-# LANGUAGE CPP #-} | ||
2 | module Data.BEncode.Pretty where -- (showBEncode) where | ||
3 | |||
4 | import Data.BEncode.Types | ||
5 | import qualified Data.ByteString as BS | ||
6 | import qualified Data.ByteString.Lazy as BL | ||
7 | import qualified Data.ByteString.Lazy.Char8 as BL8 | ||
8 | import Data.Text (Text) | ||
9 | import qualified Data.Text as T | ||
10 | #ifdef BENCODE_AESON | ||
11 | import Data.BEncode.BDict hiding (map) | ||
12 | import Data.Aeson.Types hiding (parse) | ||
13 | import Data.Aeson.Encode.Pretty | ||
14 | import qualified Data.HashMap.Strict as HashMap | ||
15 | import qualified Data.Vector as Vector | ||
16 | import Data.Foldable as Foldable | ||
17 | import Data.Text.Encoding | ||
18 | import Text.Printf | ||
19 | #endif | ||
20 | |||
21 | #ifdef BENCODE_AESON | ||
22 | |||
23 | unhex :: Text -> BS.ByteString | ||
24 | unhex t = BS.pack $ map unhex1 [0 .. BS.length nibs `div` 2] | ||
25 | where | ||
26 | nibs = encodeUtf8 t | ||
27 | unhex1 i = unnib (BS.index nibs (i * 2)) * 0x10 | ||
28 | + unnib (BS.index nibs (i * 2 + 1)) | ||
29 | unnib a | a <= 0x39 = a - 0x30 | ||
30 | | otherwise = a - (0x41 - 10) | ||
31 | |||
32 | hex :: BS.ByteString -> Text | ||
33 | hex bs = T.concat $ map (T.pack . printf "%02X") $ BS.unpack bs | ||
34 | |||
35 | quote_chr :: Char | ||
36 | quote_chr = ' ' | ||
37 | |||
38 | quote :: Text -> Text | ||
39 | quote t = quote_chr `T.cons` t `T.snoc` quote_chr | ||
40 | |||
41 | |||
42 | instance ToJSON BValue where | ||
43 | toJSON (BInteger x) = Number $ fromIntegral x | ||
44 | toJSON (BString s) = String $ either (const $ hex s) quote $ decodeUtf8' s | ||
45 | toJSON (BList xs) = Array $ Vector.fromList $ map toJSON xs | ||
46 | toJSON (BDict d) = toJSON d | ||
47 | |||
48 | instance ToJSON a => ToJSON (BDictMap a) where | ||
49 | toJSON d = Object $ HashMap.fromList $ map convert $ toAscList d | ||
50 | where | ||
51 | convert (k,v) = (decodeUtf8 k,toJSON v) | ||
52 | |||
53 | instance FromJSON BValue where | ||
54 | parseJSON (Number x) = pure $ BInteger (truncate x) | ||
55 | parseJSON (Bool x) = pure $ BInteger $ if x then 1 else 0 | ||
56 | parseJSON (String s) | ||
57 | | T.head s==quote_chr = pure $ BString $ encodeUtf8 (T.takeWhile (/=quote_chr) $ T.drop 1 s) | ||
58 | | otherwise = pure $ BString $ unhex s | ||
59 | parseJSON (Array v) = BList <$> traverse parseJSON (Foldable.toList v) | ||
60 | parseJSON (Object d) = BDict <$> parseJSON (Object d) | ||
61 | parseJSON (Null) = pure $ BDict Nil | ||
62 | |||
63 | instance FromJSON v => FromJSON (BDictMap v) where | ||
64 | parseJSON (Object d) = fromAscList <$> traverse convert (HashMap.toList d) | ||
65 | where | ||
66 | convert (k,v) = (,) (encodeUtf8 k) <$> parseJSON v | ||
67 | parseJSON _ = fail "Not a BDict" | ||
68 | #endif | ||
69 | |||
70 | showBEncode :: BValue -> BL.ByteString | ||
71 | #ifdef BENCODE_AESON | ||
72 | showBEncode b = encodePretty $ toJSON b | ||
73 | #else | ||
74 | showBEncode b = BL8.pack (show b) | ||
75 | #endif | ||
diff --git a/src/Network/KRPC/Manager.hs b/src/Network/KRPC/Manager.hs index 9477d23c..c90c92f9 100644 --- a/src/Network/KRPC/Manager.hs +++ b/src/Network/KRPC/Manager.hs | |||
@@ -52,6 +52,7 @@ import Control.Monad.Reader | |||
52 | import Control.Monad.Trans.Control | 52 | import Control.Monad.Trans.Control |
53 | import Data.BEncode as BE | 53 | import Data.BEncode as BE |
54 | import Data.BEncode.Internal as BE | 54 | import Data.BEncode.Internal as BE |
55 | import Data.BEncode.Pretty (showBEncode) | ||
55 | import Data.ByteString as BS | 56 | import Data.ByteString as BS |
56 | import Data.ByteString.Char8 as BC | 57 | import Data.ByteString.Char8 as BC |
57 | import Data.ByteString.Lazy as BL | 58 | import Data.ByteString.Lazy as BL |
@@ -428,11 +429,17 @@ dispatchHandler q @ KQuery {..} addr = do | |||
428 | -- peer B fork too many threads | 429 | -- peer B fork too many threads |
429 | -- ... space leak | 430 | -- ... space leak |
430 | -- | 431 | -- |
431 | handleQuery :: MonadKRPC h m => KQuery -> SockAddr -> m () | 432 | handleQuery :: MonadKRPC h m => BValue -> KQuery -> SockAddr -> m () |
432 | handleQuery q addr = void $ fork $ do | 433 | handleQuery raw q addr = void $ fork $ do |
433 | Manager {..} <- getManager | 434 | Manager {..} <- getManager |
434 | res <- dispatchHandler q addr | 435 | res <- dispatchHandler q addr |
435 | sendMessage sock addr $ either toBEncode toBEncode res | 436 | let resbe = either toBEncode toBEncode res |
437 | $(logOther "q") $ T.unlines | ||
438 | [ either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode raw) | ||
439 | , "==>" | ||
440 | , either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode resbe) | ||
441 | ] | ||
442 | sendMessage sock addr resbe | ||
436 | 443 | ||
437 | handleResponse :: MonadKRPC h m => BValue -> KResult -> SockAddr -> m () | 444 | handleResponse :: MonadKRPC h m => BValue -> KResult -> SockAddr -> m () |
438 | handleResponse raw result addr = do | 445 | handleResponse raw result addr = do |
@@ -445,7 +452,7 @@ handleResponse raw result addr = do | |||
445 | Just ares -> putMVar ares (raw,result) | 452 | Just ares -> putMVar ares (raw,result) |
446 | 453 | ||
447 | handleMessage :: MonadKRPC h m => BValue -> KMessage -> SockAddr -> m () | 454 | handleMessage :: MonadKRPC h m => BValue -> KMessage -> SockAddr -> m () |
448 | handleMessage _ (Q q) = handleQuery q | 455 | handleMessage raw (Q q) = handleQuery raw q |
449 | handleMessage raw (R r) = handleResponse raw (Right r) | 456 | handleMessage raw (R r) = handleResponse raw (Right r) |
450 | handleMessage raw (E e) = handleResponse raw (Left e) | 457 | handleMessage raw (E e) = handleResponse raw (Left e) |
451 | 458 | ||