packages feed

ngx-export-tools-extra 0.8.2.0 → 1.0

raw patch · 8 files changed

+319/−117 lines, 8 filesdep ~aeson

Dependency ranges changed: aeson

Files

Changelog.md view
@@ -1,3 +1,16 @@+### 1.0++- Module *NgxExport.Tools.Aggregate* can now be built without support from the+  *Snap framework* (flag *SnapAggregateServer* replaces flag *Aggregate*). The+  module now allows building native Nginx-based aggregate services (see updated+  docs).+- In module *NgxExport.Tools.Aggregate*, getting current time with *ngxNow* was+  replaced by *getCurrentTime* because the former is not safe in the async+  context.+- Removed upper bound restriction in the Cabal constraint on the version of+  package *aeson*.+- Package stability tag was promoted to stable.+ ### 0.8.2.0  - In module *NgxExport.Tools.Subrequest*, bridged HTTP subrequests return status
NgxExport/Tools/Aggregate.hs view
@@ -1,13 +1,14 @@-{-# LANGUAGE TemplateHaskell, OverloadedStrings, BangPatterns #-}+{-# LANGUAGE CPP, TemplateHaskell, ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings, BangPatterns #-}  ----------------------------------------------------------------------------- -- | -- Module      :  NgxExport.Tools.Aggregate--- Copyright   :  (c) Alexey Radkov 2019-2020+-- Copyright   :  (c) Alexey Radkov 2019-2021 -- License     :  BSD-style -- -- Maintainer  :  alexey.radkov@gmail.com--- Stability   :  experimental+-- Stability   :  stable -- Portability :  non-portable (requires Template Haskell) -- -- An aggregate service from the more extra tools collection for@@ -19,8 +20,13 @@ module NgxExport.Tools.Aggregate (     -- * The typed service exporter     -- $aggregateServiceExporter-                                  AggregateServerConf-                                 ,ngxExportAggregateService+#ifdef SNAP_AGGREGATE_SERVER+                                  AggregateServerConf,+#endif+                                  ngxExportAggregateService+    -- * Nginx-based aggregate service+    -- $nginxBasedAggregateService+     -- * The worker-side reporter                                  ,reportAggregate     -- * Re-exported data constructors from /Foreign.C/@@ -29,6 +35,7 @@                                  ,Foreign.C.Types.CUInt (..)                                  ) where +import           NgxExport import           NgxExport.Tools  import           Language.Haskell.TH@@ -42,22 +49,33 @@ import qualified Data.Map.Strict as M import           Data.IORef import           Data.Int-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import           Data.Time.Clock.POSIX+#if MIN_VERSION_time(1,9,1)+import           Data.Fixed+#endif+import           Data.Time.Clock+import           Data.Time.Calendar import           Data.Aeson import           Data.Maybe import           Control.Monad-import           Control.Monad.IO.Class-import           Control.Arrow import           Control.Exception-import           Control.Exception.Enclosed (handleAny) import           System.IO.Unsafe+import           Safe++#ifdef SNAP_AGGREGATE_SERVER+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import           Control.Monad.IO.Class+import           Control.Exception.Enclosed (handleAny) import           Snap.Http.Server import           Snap.Core+#endif -type Aggregate a = IORef (CTime, Map Int32 (CTime, Maybe a)) +type AggregateValue a = (UTCTime, Map Int32 (UTCTime, Maybe a))+type Aggregate a = IORef (AggregateValue a)++type ReportValue a = Maybe (Int32, Maybe a)+ -- $aggregateServiceExporter -- -- An aggregate service collects custom typed data reported by worker processes@@ -214,39 +232,39 @@ -- As far as /reportStats/ is a deferred service, we won't get useful data in 5 -- seconds after Nginx start. ----- > $ curl 'http://127.0.0.1:8020/' | jq+-- > $ curl -s 'http://127.0.0.1:8020/' | jq -- > [--- >   "1970-01-01T00:00:00Z",+-- >   "1858-11-17T00:00:00Z", -- >   {} -- > ] -- -- However, later we should get some useful data. ----- > $ curl 'http://127.0.0.1:8020/' | jq+-- > $ curl -s 'http://127.0.0.1:8020/' | jq -- > [--- >   "2019-04-22T14:19:04Z",+-- >   "2021-12-08T09:56:18.118132083Z", -- >   {--- >     "5910": [--- >       "2019-04-22T14:19:19Z",+-- >     "21651": [+-- >       "2021-12-08T09:56:18.12155413Z", -- >       {--- >         "bytesSent": 0,+-- >         "meanBytesSent": 0, -- >         "requests": 0,--- >         "meanBytesSent": 0+-- >         "bytesSent": 0 -- >       } -- >     ],--- >     "5911": [--- >       "2019-04-22T14:19:14Z",+-- >     "21652": [+-- >       "2021-12-08T09:56:18.118132083Z", -- >       {--- >         "bytesSent": 0,+-- >         "meanBytesSent": 0, -- >         "requests": 0,--- >         "meanBytesSent": 0+-- >         "bytesSent": 0 -- >       } -- >     ] -- >   } -- > ] -- -- Here we have collected stats from the two Nginx worker processes with /PIDs/--- /5910/ and /5911/. The timestamps show when the stats was updated the last+-- /21651/ and /21652/. The timestamps show when the stats was updated the last -- time. The topmost timestamp shows the time of the latest /purge/ event. The -- data itself have only zeros as soon we have made no request to the main -- server so far. Let's run 100 simultaneous requests and look at the stats (it@@ -256,29 +274,89 @@ -- -- Wait 5 seconds... ----- > $ curl 'http://127.0.0.1:8020/' | jq+-- > $ curl -s 'http://127.0.0.1:8020/' | jq -- > [--- >   "2019-04-22T14:29:04Z",+-- >   "2021-12-08T09:56:18.118132083Z", -- >   {--- >     "5910": [--- >       "2019-04-22T14:31:34Z",+-- >     "21651": [+-- >       "2021-12-08T09:56:48.159263993Z", -- >       {--- >         "bytesSent": 17751,--- >         "requests": 97,--- >         "meanBytesSent": 183+-- >         "meanBytesSent": 183,+-- >         "requests": 84,+-- >         "bytesSent": 15372 -- >       } -- >     ],--- >     "5911": [--- >       "2019-04-22T14:31:31Z",+-- >     "21652": [+-- >       "2021-12-08T09:56:48.136934713Z", -- >       {--- >         "bytesSent": 549,--- >         "requests": 3,--- >         "meanBytesSent": 183+-- >         "meanBytesSent": 183,+-- >         "requests": 16,+-- >         "bytesSent": 2928 -- >       } -- >     ] -- >   } -- > ] +throwUserError :: String -> IO a+throwUserError = ioError . userError++#if MIN_VERSION_time(1,9,1)+asIntegerPart :: forall a. HasResolution a => Integer -> Fixed a+asIntegerPart = MkFixed . (resolution (undefined :: Fixed a) *)+{-# SPECIALIZE INLINE asIntegerPart :: Integer -> Pico #-}+#endif++toNominalDiffTime :: TimeInterval -> NominalDiffTime+toNominalDiffTime =+#if MIN_VERSION_time(1,9,1)+    secondsToNominalDiffTime . asIntegerPart+#else+    fromRational . toRational . secondsToDiffTime+#endif+    . fromIntegral . toSec++updateAggregate :: Aggregate a -> ReportValue a -> NominalDiffTime -> IO ()+updateAggregate a s int = do+    let (pid, v) = fromJust s+    !t <- getCurrentTime+    atomicModifyIORef' a $+        \(t', v') ->+            (let (!tn, f) =+                     if diffUTCTime t t' >= int+                         then (t+                              ,M.filter $+                                  \(t'', _) -> diffUTCTime t t'' < int+                              )+                         else (t', id)+                 !vn = f $ M.alter+                           (\old ->+                               let !new' = if isNothing old || isJust v+                                               then v+                                               else snd $ fromJust old+                               in Just (t, new')+                           ) pid v'+             in (tn, vn)+            ,()+            )++receiveAggregate :: FromJSON a =>+    Aggregate a -> L.ByteString -> ByteString -> IO L.ByteString+receiveAggregate a v sint = do+    let !s = decode' v+        !int = toNominalDiffTime $ readDef (Min 5) $ C8.unpack sint+    when (isNothing s) $ throwUserError "Unreadable aggregate!"+    updateAggregate a (fromJust s) int+    return "done"++sendAggregate :: ToJSON a =>+    Aggregate a -> ByteString -> IO ContentHandlerResult+sendAggregate a = const $ do+    s <- readIORef a+    return (encode s, "text/plain", 200, [])+++#ifdef SNAP_AGGREGATE_SERVER+ -- | Configuration of an aggregate service. -- -- This type is exported because Template Haskell requires that. Though its@@ -292,8 +370,9 @@ -- @ -- -- The value of /asPort/ corresponds to the TCP port of the internal aggregate--- server. The /asPurgeInterval/ is the /purge/ interval. An aggregate service--- should sometimes purge data from worker processes which did not report for a+-- server (the IP address of the internal server is always /127.0.0.1/). The+-- /asPurgeInterval/ is the /purge/ interval. An aggregate service should+-- sometimes purge data from worker processes which have not reported for a -- long time. For example, it makes no sense to keep data from workers that -- have already been terminated. The inactive PIDs get checked every -- /asPurgeInterval/, and data which correspond to PIDs with timestamps older@@ -308,8 +387,10 @@  aggregateServer :: (FromJSON a, ToJSON a) =>     Aggregate a -> ByteString -> AggregateServerConf -> Bool -> IO L.ByteString-aggregateServer a u = ignitionService $ \conf ->-    simpleHttpServe (asConfig $ asPort conf) (asHandler a u conf) >> return ""+aggregateServer a u = ignitionService $ \conf -> do+    let !int = toNominalDiffTime $ asPurgeInterval conf+    simpleHttpServe (asConfig $ asPort conf) $ asHandler a u int+    return ""  asConfig :: Int -> Config Snap a asConfig p = setPort p@@ -319,48 +400,30 @@            $ setVerbose False mempty  asHandler :: (FromJSON a, ToJSON a) =>-    Aggregate a -> ByteString -> AggregateServerConf -> Snap ()-asHandler a u conf =-    route [(B.append "put/" u, Snap.Core.method POST $ receiveAggregate a conf)-          ,(B.append "get/" u, Snap.Core.method GET $ sendAggregate a)+    Aggregate a -> ByteString -> NominalDiffTime -> Snap ()+asHandler a u int =+    route [(B.append "put/" u+           ,Snap.Core.method POST $ receiveAggregateSnap a int+           )+          ,(B.append "get/" u+           ,Snap.Core.method GET $ sendAggregateSnap a+           )           ] -receiveAggregate :: FromJSON a =>-    Aggregate a -> AggregateServerConf -> Snap ()-receiveAggregate a conf =+receiveAggregateSnap :: FromJSON a => Aggregate a -> NominalDiffTime -> Snap ()+receiveAggregateSnap a int =     handleAggregateExceptions "Exception while receiving aggregate" $ do         !s <- decode' <$> readRequestBody 65536         when (isNothing s) $ liftIO $ throwUserError "Unreadable aggregate!"-        liftIO $ do-            let (pid, v) = fromJust s-                int = fromIntegral . toSec . asPurgeInterval $ conf-            !t <- ngxNow-            atomicModifyIORef' a $-                \(t', v') ->-                    (let (!tn, f) =-                             if t - t' >= int-                                 then (t, M.filter $ \(t'', _) -> t - t'' < int)-                                 else (t', id)-                         !vn = f $ M.alter-                                   (\old ->-                                       let !new' =-                                               if isNothing old || isJust v-                                                   then v-                                                   else snd $ fromJust old-                                       in Just (t, new')-                                   ) pid v'-                     in (tn, vn)-                    ,()-                    )+        liftIO $ updateAggregate a (fromJust s) int         finishWith emptyResponse -sendAggregate :: ToJSON a => Aggregate a -> Snap ()-sendAggregate a =+sendAggregateSnap :: ToJSON a => Aggregate a -> Snap ()+sendAggregateSnap a =     handleAggregateExceptions "Exception while sending aggregate" $ do         s <- liftIO $ readIORef a         modifyResponse $ setContentType "application/json"-        writeLBS $ encode $ (toUTCTime *** M.map (first toUTCTime)) s-    where toUTCTime (CTime t) = posixSecondsToUTCTime $ fromIntegral t+        writeLBS $ encode s  handleAggregateExceptions :: String -> Snap () -> Snap () handleAggregateExceptions cmsg = handleAny $ \e ->@@ -369,9 +432,9 @@               modifyResponse $ setResponseStatus c $ T.encodeUtf8 $ T.pack cmsg               writeBS $ T.encodeUtf8 $ T.pack msg -throwUserError :: String -> IO a-throwUserError = ioError . userError+#endif + -- | Exports a simple aggregate service with specified name and the aggregate --   type. --@@ -393,34 +456,159 @@                           -> Name         -- ^ Name of the aggregate type                           -> Q [Dec] ngxExportAggregateService f a = do-    let nameF = 'aggregateServer+    let sName = mkName $ "aggregate_storage_" ++ f+        storage = varE sName+#ifdef SNAP_AGGREGATE_SERVER+        nameF = 'aggregateServer         fName = mkName $ "aggregate_" ++ f-        sName = mkName $ "aggregate_storage_" ++ f         uName = mkName $ "aggregate_url_" ++ f+#endif+        nameRecv = 'receiveAggregate+        recvName = mkName $ "receiveAggregate_" ++ f+        nameSend = 'sendAggregate+        sendName = mkName $ "sendAggregate_" ++ f     concat <$> sequence         [sequence-            [sigD uName [t|ByteString|]-            ,funD uName [clause [] (normalB [|C8.pack f|]) []]-            ,sigD sName [t|Aggregate $(conT a)|]+            [sigD sName [t|Aggregate $(conT a)|]             ,funD sName                 [clause []-                    (normalB [|unsafePerformIO $ newIORef (0, M.empty)|])+                    (normalB [|unsafePerformIO $+                                 newIORef (UTCTime (ModifiedJulianDay 0) 0+                                          ,M.empty+                                          )+                             |]+                    )                     []                 ]             ,pragInlD sName NoInline FunLike AllPhases+#ifdef SNAP_AGGREGATE_SERVER+            ,sigD uName [t|ByteString|]+            ,funD uName [clause [] (normalB [|C8.pack f|]) []]             ,sigD fName [t|AggregateServerConf -> Bool -> IO L.ByteString|]             ,funD fName                 [clause []-                    (normalB [|$(varE nameF) $(varE sName) $(varE uName)|])+                    (normalB [|$(varE nameF) $(storage) $(varE uName)|])                     []                 ]+#endif+            ,sigD recvName [t|L.ByteString -> ByteString -> IO L.ByteString|]+            ,funD recvName+                [clause []+                    (normalB [|$(varE nameRecv) $(storage)|])+                    []+                ]+            ,sigD sendName [t|ByteString -> IO ContentHandlerResult|]+            ,funD sendName+                [clause []+                    (normalB [|$(varE nameSend) $(storage)|])+                    []+                ]             ]+#ifdef SNAP_AGGREGATE_SERVER         -- FIXME: name AggregateServerConf must be imported from the user's         -- module unqualified (see details in NgxExport/Tools.hs, function         -- ngxExportSimpleService')!         ,ngxExportSimpleServiceTyped             fName ''AggregateServerConf SingleShotService+#endif+        ,ngxExportAsyncOnReqBody recvName+        ,ngxExportAsyncHandler sendName         ]++-- $nginxBasedAggregateService+--+-- Service /simpleService_aggregate_stats/ was implemented using+-- /Snap framework/. Basically, a native Nginx implementation is not easy+-- because the service must listen on a single (not duplicated) file descriptor+-- which is not the case when Nginx spawns more than one worker processes.+-- Running /simpleService_aggregate_stats/ as a shared service is an elegant+-- solution as shared services guarantee that they occupy only one worker at a+-- time. However, /nginx-haskell-module/ provides directive /single_listener/+-- which can be used to apply the required restriction in a custom Nginx virtual+-- server. This directive requires that the virtual server listens with option+-- /reuseport/ and is only available on Linux with socket option+-- /SO_ATTACH_REUSEPORT_CBPF/.+--+-- Exporter 'ngxExportAggregateService' exports additional handlers to build a+-- native Nginx-based aggregate service. Let's replace service+-- /simpleService_aggregate_stats/ from the previous example with such a native+-- Nginx-based aggregate service using /single_listener/ and listening on port+-- /8100/.+--+-- ==== File /nginx.conf/+-- @+-- user                    nobody;+-- worker_processes        2;+--+-- events {+--     worker_connections  1024;+-- }+--+-- http {+--     default_type        application\/octet-stream;+--     sendfile            on;+--+--     haskell load \/var\/lib\/nginx\/test_tools_extra_aggregate.so;+--+--     haskell_run_service simpleService_reportStats $hs_reportStats 8100;+--+--     haskell_var_empty_on_error $hs_stats;+--+--     server {+--         listen       8010;+--         server_name  main;+--         error_log    \/tmp\/nginx-test-haskell-error.log;+--         access_log   \/tmp\/nginx-test-haskell-access.log;+--+--         haskell_run updateStats !$hs_updateStats $bytes_sent;+--+--         location \/ {+--             echo Ok;+--         }+--     }+--+--     server {+--         listen       8020;+--         server_name  stat;+--+--         location \/ {+--             allow 127.0.0.1;+--             deny all;+--             proxy_pass http:\/\/127.0.0.1:8100\/get\/stats;+--         }+--     }+--+--     server {+--         listen          8100 __/reuseport/__;+--         server_name     stats;+--+--         __/single_listener on/__;+--+--         location __/\/put\/stats/__ {+--             haskell_run_async_on_request_body __/receiveAggregate_stats/__+--                     $hs_stats \"Min 1\";+--+--             if ($hs_stats = \'\') {+--                 return 400;+--             }+--+--             return 200;+--         }+--+--         location __/\/get\/stats/__ {+--             haskell_async_content __/sendAggregate_stats/__ noarg;+--         }+--     }+-- }+-- @+--+-- Handler /receiveAggregate_stats/ accepts a time interval corresponding to the+-- value of /asPurgeInterval/ from service /simpleService_aggregate_stats/. If+-- the value is not readable (say, /noarg/) then it is defaulted to /Min 5/.+--+-- Notice that the stats server must listen on IP address /127.0.0.1/ because+-- 'reportAggregate' (being the base of service /simpleService_reportStats/)+-- reports stats to this address.  -- | Reports data to an aggregate service. --
NgxExport/Tools/EDE.hs view
@@ -3,11 +3,11 @@ ----------------------------------------------------------------------------- -- | -- Module      :  NgxExport.Tools.EDE--- Copyright   :  (c) Alexey Radkov 2020+-- Copyright   :  (c) Alexey Radkov 2020-2021 -- License     :  BSD-style -- -- Maintainer  :  alexey.radkov@gmail.com--- Stability   :  experimental+-- Stability   :  stable -- Portability :  non-portable (requires Template Haskell) -- -- EDE templates for parsing JSON objects from the more extra tools collection@@ -29,7 +29,7 @@  import           Text.EDE import           Text.EDE.Filters-#if EDE_USE_PRETTYPRINTER+#ifdef EDE_USE_PRETTYPRINTER #if MIN_VERSION_prettyprinter(1,7,0) import           Prettyprinter (unAnnotate) #else@@ -275,7 +275,7 @@                         Failure msg -> throwIO $ EDERenderError $ showPlain msg                         Success r -> return $ LT.encodeUtf8 r     where showPlain = show .-#if EDE_USE_PRETTYPRINTER+#ifdef EDE_USE_PRETTYPRINTER               unAnnotate #else               plain
NgxExport/Tools/PCRE.hs view
@@ -10,7 +10,7 @@ -- License     :  BSD-style -- -- Maintainer  :  alexey.radkov@gmail.com--- Stability   :  experimental+-- Stability   :  stable -- Portability :  non-portable (requires Template Haskell) -- -- PCRE matching and substitution from the more extra tools collection
NgxExport/Tools/Prometheus.hs view
@@ -4,11 +4,11 @@ ----------------------------------------------------------------------------- -- | -- Module      :  NgxExport.Tools.Prometheus--- Copyright   :  (c) Alexey Radkov 2020+-- Copyright   :  (c) Alexey Radkov 2020-2021 -- License     :  BSD-style -- -- Maintainer  :  alexey.radkov@gmail.com--- Stability   :  experimental+-- Stability   :  stable -- Portability :  non-portable (requires Template Haskell) -- -- Prometheus metrics from the more extra tools collection for@@ -440,8 +440,7 @@     writeIORef conf $ Just a     return "" -ngxExportSimpleServiceTyped-    'prometheusConf ''PrometheusConf SingleShotService+ngxExportSimpleServiceTyped 'prometheusConf ''PrometheusConf SingleShotService  toPrometheusMetrics' :: PrometheusConf -> AllMetrtics -> PrometheusMetrics toPrometheusMetrics' PrometheusConf {..} (srv, cnts, hs, ocnts) =@@ -934,50 +933,50 @@ --                     ,\"__/hst_request_time\@scope=(in_upstreams)_sum/__\" --                     ] --                 }\';--- +-- -- @ -- @ --         counter $__/cnt_status\@value=(4xx),from=(response)/__ inc $inc_cnt_4xx; --         counter $__/cnt_status\@value=(5xx),from=(response)/__ inc $inc_cnt_5xx;--- +-- --         haskell_run statusLayout $hs_upstream_status $upstream_status; --         counter $__/cnt_status\@value=(4xx),from=(upstream)/__ inc $inc_cnt_u_4xx; --         counter $__/cnt_status\@value=(5xx),from=(upstream)/__ inc $inc_cnt_u_5xx;--- +-- --         # cache $request_time and $bytes_sent --         haskell_run ! $hs_request_time $request_time; --         haskell_run ! $hs_bytes_sent $bytes_sent;--- +-- --         histogram $__/hst_request_time\@scope=(total)/__ 11 $request_time_bucket; --         haskell_run scale1000 $hs_request_time_scaled $hs_request_time; --         counter $hst_request_time\@scope=(total)_sum inc $hs_request_time_scaled;--- +-- --         histogram $hst_bytes_sent 6 $bytes_sent_bucket; --         counter $hst_bytes_sent_sum inc $hs_bytes_sent;--- +-- --         # cache $upstream_response_time --         haskell_run ! $hs_u_response_times $upstream_response_time;--- +-- --         histogram $__/hst_request_time\@scope=(in_upstreams)/__ 11 --                 $u_response_time_bucket; --         histogram $__/hst_request_time\@scope=(in_upstreams)/__ undo; --         haskell_run cumulativeFPValue $hs_u_response_time $hs_u_response_times; --         haskell_run scale1000 $hs_u_response_time_scaled $hs_u_response_time;--- +-- --         location \/ { --             echo_sleep 0.5; --             echo Ok; --         }--- +-- --         location \/1 { --             echo_sleep 1.0; --             echo Ok; --         }--- +-- --         location \/404 { --             return 404; --         }--- +-- --         location \/backends { --             histogram $__/hst_request_time\@scope=(in_upstreams)/__ reuse; --             counter $__/hst_request_time\@scope=(in_upstreams)_sum/__ inc@@ -986,7 +985,7 @@ --             proxy_intercept_errors on; --             proxy_pass http:\/\/backends; --         }--- +-- --         location \@status404 { --             histogram $__/hst_request_time\@scope=(in_upstreams)/__ reuse; --             counter $__/hst_request_time\@scope=(in_upstreams)_sum/__ inc@@ -1011,7 +1010,7 @@ -- > $ for i in {1..20} ; do curl -D- 'http://localhost:8010/backends' & done -- >   ... ----- > $ curl -s 'http://localhost:8020/' +-- > $ curl -s 'http://localhost:8020/' -- > # HELP cnt_status Number of responses with given status -- > # TYPE cnt_status counter -- > cnt_status{value="4xx",from="response"} 11.0@@ -1072,3 +1071,4 @@ -- > # TYPE hst_request_time_err counter -- > hst_request_time_err{scope="in_upstreams"} 0.0 -- > hst_request_time_err{scope="total"} 0.0+
NgxExport/Tools/ServiceHookAdaptor.hs view
@@ -7,7 +7,7 @@ -- License     :  BSD-style -- -- Maintainer  :  alexey.radkov@gmail.com--- Stability   :  experimental+-- Stability   :  stable -- Portability :  non-portable (requires Template Haskell) -- -- A service hook adaptor from the more extra tools collection for@@ -184,5 +184,6 @@ hookAdaptor :: ByteString -> Bool -> IO L.ByteString hookAdaptor = ignitionService $     const $ forever $ threadDelaySec $ toSec $ Hr 24+ ngxExportSimpleService 'hookAdaptor SingleShotService 
NgxExport/Tools/Subrequest.hs view
@@ -8,7 +8,7 @@ -- License     :  BSD-style -- -- Maintainer  :  alexey.radkov@gmail.com--- Stability   :  experimental+-- Stability   :  stable -- Portability :  non-portable (requires Template Haskell) -- -- Easy HTTP subrequests from the more extra tools collection for
ngx-export-tools-extra.cabal view
@@ -1,5 +1,5 @@ name:                       ngx-export-tools-extra-version:                    0.8.2.0+version:                    1.0 synopsis:                   More extra tools for Nginx haskell module description:                More extra tools for         <https://github.com/lyokha/nginx-haskell-module Nginx haskell module>.@@ -9,7 +9,7 @@ extra-source-files:         Changelog.md author:                     Alexey Radkov <alexey.radkov@gmail.com> maintainer:                 Alexey Radkov <alexey.radkov@gmail.com>-stability:                  experimental+stability:                  stable copyright:                  2019-2021 Alexey Radkov category:                   Network build-type:                 Simple@@ -19,9 +19,6 @@   type:                     git   location:                 https://github.com/lyokha/ngx-export-tools-extra -flag Aggregate-  description:              Build Aggregate module.- flag EDE   description:              Build EDE module. @@ -33,6 +30,9 @@ flag PCRE   description:              Build PCRE module. +flag SnapAggregateServer+  description:              Build Snap server for Aggregate module.+ library   default-language:         Haskell2010   build-depends:            base >= 4.8 && < 5@@ -41,10 +41,11 @@                           , base64 >= 0.3.0.0                           , ngx-export                           , ngx-export-tools >= 0.4.8.0-                          , aeson >= 1.0.0.0 && < 2.0.0.0                           , http-types >= 0.7.0                           , http-client >= 0.5.3                           , network >= 2.4.0.0+                          , aeson >= 1.0.0.0+                          , time                           , binary                           , case-insensitive                           , containers@@ -53,11 +54,11 @@                           , text                           , safe -  if flag(Aggregate)+  if flag(SnapAggregateServer)     build-depends:          snap-core                           , snap-server                           , enclosed-exceptions-                          , time+    cpp-options:           -DSNAP_AGGREGATE_SERVER    if flag(EDE)     if flag(EDEUsePrettyprinter)@@ -69,6 +70,7 @@       build-depends:        ede                           , ansi-wl-pprint >= 0.6.3                           , trifecta <= 2+                          , aeson >= 1.0.0.0 && < 2.0.0.0    if flag(PCRE)     build-depends:          pcre-light >= 0.4@@ -77,9 +79,7 @@   exposed-modules:          NgxExport.Tools.Prometheus                           , NgxExport.Tools.Subrequest                           , NgxExport.Tools.ServiceHookAdaptor--  if flag(Aggregate)-    exposed-modules:        NgxExport.Tools.Aggregate+                          , NgxExport.Tools.Aggregate    if flag(EDE)     exposed-modules:        NgxExport.Tools.EDE