ngx-export-tools-extra 1.0 → 1.1.0
raw patch · 4 files changed
+45/−22 lines, 4 filesdep +http-client-brread-timeout
Dependencies added: http-client-brread-timeout
Files
- Changelog.md +8/−0
- LICENSE +1/−1
- NgxExport/Tools/Subrequest.hs +34/−20
- ngx-export-tools-extra.cabal +2/−1
Changelog.md view
@@ -1,3 +1,11 @@+### 1.1.0++- Using *Network.HTTP.Client.BrReadWithTimeout* in module+ *NgxExport.Tools.Subrequest* to ensure that a request won't stall on a bad+ response from a buggy server.+- In module *NgxExport.Tools.Subrequest*, *srBody* was reimplemented using lazy+ bytestrings.+ ### 1.0 - Module *NgxExport.Tools.Aggregate* can now be built without support from the
LICENSE view
@@ -1,7 +1,7 @@ The following license covers this documentation, and the source code, except where otherwise indicated. -Copyright 2019-2021, Alexey Radkov. All rights reserved.+Copyright 2019-2022, Alexey Radkov. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
NgxExport/Tools/Subrequest.hs view
@@ -4,7 +4,7 @@ ----------------------------------------------------------------------------- -- | -- Module : NgxExport.Tools.Subrequest--- Copyright : (c) Alexey Radkov 2020-2021+-- Copyright : (c) Alexey Radkov 2020-2022 -- License : BSD-style -- -- Maintainer : alexey.radkov@gmail.com@@ -50,6 +50,7 @@ import Network.HTTP.Client hiding (ResponseTimeout) import qualified Network.HTTP.Client (HttpExceptionContent (ResponseTimeout))+import Network.HTTP.Client.BrReadWithTimeout import Network.HTTP.Types import qualified Network.Socket as S import qualified Network.Socket.ByteString as SB@@ -60,6 +61,7 @@ import qualified Data.ByteString.Lazy.Char8 as C8L import Data.IORef import qualified Data.Text.Encoding as T+import qualified Data.Text.Lazy.Encoding as TL import qualified Data.Binary as Binary import Data.HashSet (HashSet) import qualified Data.HashSet as HS@@ -232,7 +234,7 @@ data SubrequestConf = SubrequestConf { srMethod :: ByteString , srUri :: String- , srBody :: ByteString+ , srBody :: L.ByteString , srHeaders :: RequestHeaders , srResponseTimeout :: ResponseTimeout , srUseUDS :: Bool@@ -240,16 +242,15 @@ instance FromJSON SubrequestConf where parseJSON = withObject "SubrequestConf" $ \o -> do- srMethod <- maybeEmpty $ o .:? "method"+ srMethod <- maybe "" T.encodeUtf8 <$> o .:? "method" srUri <- o .: "uri"- srBody <- maybeEmpty $ o .:? "body"+ srBody <- maybe "" TL.encodeUtf8 <$> o .:? "body" srHeaders <- map (mk . T.encodeUtf8 *** T.encodeUtf8) <$> o .:? "headers" .!= [] srResponseTimeout <- maybe ResponseTimeoutDefault ResponseTimeout <$> o .:? "timeout" srUseUDS <- fromMaybe False <$> o .:? "useUDS" return SubrequestConf {..}- where maybeEmpty = fmap $ maybe "" T.encodeUtf8 data BridgeConf = BridgeConf { bridgeSource :: SubrequestConf@@ -267,9 +268,9 @@ req { method = if B.null srMethod then method req else srMethod- , requestBody = if B.null srBody+ , requestBody = if L.null srBody then requestBody req- else RequestBodyBS srBody+ else RequestBodyLBS srBody , requestHeaders = unionBy ((==) `on` fst) srHeaders $ requestHeaders req , responseTimeout = if srResponseTimeout == ResponseTimeoutDefault@@ -291,7 +292,7 @@ readIORef httpUDSManager else return httpManager req <- parseRequestF srUri- buildResponseF <$> httpLbs (makeRequest sub req) man+ buildResponseF <$> httpLbsBrReadWithTimeout (makeRequest sub req) man subrequestBody :: SubrequestConf -> IO L.ByteString subrequestBody = subrequest parseUrlThrow responseBody@@ -342,13 +343,14 @@ -- function can also be used to initiate an original HTTP request from a -- service handler. ----- Accepts a JSON object representing an opaque type /SubrequestConf/.--- The object may contain 5 fields: /method/ (optional, default is /GET/),--- /uri/ (mandatory), /body/ (optional, default is an empty value), /headers/--- (optional, default is an empty array), and /timeout/ (optional, default is--- the default response timeout of the HTTP manager which is normally 30+-- Accepts a JSON object representing an opaque type /SubrequestConf/. The+-- object may contain the following fields: /method/ (optional, default is+-- /GET/), /uri/ (mandatory), /body/ (optional, default is an empty string),+-- /headers/ (optional, default is an empty list), /timeout/ (optional, default+-- is the default response timeout of the HTTP manager which is normally 30 -- seconds, use value @{\"tag\": \"Unset\"}@ to disable response timeout--- completely).+-- completely), and /useUDS/ (an optional boolean flag to enable Unix domain+-- sockets as the transport protocol, off by default). -- -- Examples of subrequest configurations: --@@ -360,6 +362,9 @@ -- > ,"headers": [["Header1", "Value1"], ["Header2", "Value2"]] -- > } --+-- Note that the response timeout is in effect until receiving the response+-- headers as well as in between the successive body read events.+-- -- Returns the response body if HTTP status of the response is /2xx/, otherwise -- throws an error. To avoid leakage of error messages into variable handlers, -- put the corresponding variables into the list of directive@@ -1036,9 +1041,12 @@ notForwardableResponseHeaders True (responseHeaders respIn) }- givesPopper needsPopper = needsPopper $ brRead $ responseBody respIn+ tmo = fromResponseTimeout reqIn manIn+ givesPopper needsPopper = needsPopper $+ brReadWithTimeout tmo reqIn $ responseBody respIn buildResponseF <$>- httpLbs (makeStreamingRequest givesPopper bridgeSink reqOut') manOut+ httpLbsBrReadWithTimeout+ (makeStreamingRequest givesPopper bridgeSink reqOut') manOut bridgedSubrequestBody :: BridgeConf -> IO L.ByteString bridgedSubrequestBody = bridgedSubrequest parseUrlThrow responseBody@@ -1051,7 +1059,10 @@ -- -- This is the core function of the /makeBridgedSubrequest/ handler. From -- perspective of an Nginx request, it spawns two subrequests connecting the two--- ends of a /bridge/: the /source/ and the /sink/, hence the name.+-- ends of a /bridge/: the /source/ and the /sink/, hence the name. The+-- connection between the bridge ends is implemented via 'GivesPopper' and+-- 'RequestBodyStreamChunked' which means that the server bound at the sink end+-- must be capable of processing chunked requests. -- -- Accepts a JSON object representing an opaque type /BridgeConf/ with mandatory -- fields /source/ and /sink/.@@ -1069,7 +1080,9 @@ -- > } -- -- The sink method is always /POST/ while its body is always empty independently--- of whether or not and how exactly they were specified.+-- of whether or not and how exactly they were specified. The sink response+-- timeout should be big enough to fulfill streaming of the response from the+-- source to the sink. -- -- Returns the response body of the sink if HTTP status of the response is -- /2xx/, otherwise throws an error. To avoid leakage of error messages into@@ -1106,13 +1119,14 @@ -- > , srUri = "http://127.0.0.1/sink" -- > , srBody = "" -- > , srHeaders = []--- > , srResponseTimeout = ResponseTimeout (Sec 10)+-- > , srResponseTimeout = ResponseTimeout (Sec 30) -- > , srUseUDS = False -- > } -- > } -- -- The sink method is always /POST/ while its body is always empty independently--- of how exactly they were specified.+-- of how exactly they were specified. The sink response timeout should be big+-- enough to fulfill streaming of the response from the source to the sink. -- -- Notice that unlike JSON parsing, fields of /SubrequestConf/ comprising -- /bridgeSource/ and /bridgeSink/ are not omittable and must be listed in the
ngx-export-tools-extra.cabal view
@@ -1,5 +1,5 @@ name: ngx-export-tools-extra-version: 1.0+version: 1.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>.@@ -43,6 +43,7 @@ , ngx-export-tools >= 0.4.8.0 , http-types >= 0.7.0 , http-client >= 0.5.3+ , http-client-brread-timeout , network >= 2.4.0.0 , aeson >= 1.0.0.0 , time