@@ -13,10 +13,12 @@ import Control.Exception as E
1313import Control.Monad (when, unless, void)
1414import Data.ByteString (ByteString)
1515import qualified Data.ByteString as S
16+import Data.Char (chr)
17+import Data.IP (toHostAddress, toHostAddress6)
1618import Data.IORef (IORef, newIORef, readIORef, writeIORef)
1719import Data.Streaming.Network (bindPortTCP)
1820import Network (sClose, Socket)
19−import Network.Socket (accept, withSocketsDo, SockAddr)
21+import Network.Socket (accept, withSocketsDo, SockAddr(SockAddrInet, SockAddrInet6))
2022import qualified Network.Socket.ByteString as Sock
2123import Network.Wai
2224import Network.Wai.Handler.Warp.Buffer
@@ -24,6 +26,7 @@ import Network.Wai.Handler.Warp.Counter
2426import qualified Network.Wai.Handler.Warp.Date as D
2527import qualified Network.Wai.Handler.Warp.FdCache as F
2628import Network.Wai.Handler.Warp.Header
29+import Network.Wai.Handler.Warp.ReadInt
2730import Network.Wai.Handler.Warp.Recv
2831import Network.Wai.Handler.Warp.Request
2932import Network.Wai.Handler.Warp.Response
@@ -271,28 +274,69 @@ serveConnection :: Connection
271274 -> Settings
272275 -> Application
273276 -> IO ()
274−serveConnection conn ii addr isSecure' settings app = do
277+serveConnection conn ii origAddr isSecure' settings app = do
275278 istatus <- newIORef False
276279 src <- mkSource (connSource conn th istatus)
277− recvSendLoop istatus src `E.catch` \e -> do
278− sendErrorResponse istatus e
280+ addr <- getProxyProtocolAddr src
281+ recvSendLoop addr istatus src `E.catch` \e -> do
282+ sendErrorResponse addr istatus e
279283 throwIO (e :: SomeException)
280284
281285 where
286+ getProxyProtocolAddr src =
287+ case settingsProxyProtocol settings of
288+ ProxyProtocolNone ->
289+ return origAddr
290+ ProxyProtocolRequired -> do
291+ seg <- readSource src
292+ parseProxyProtocolHeader src seg
293+ ProxyProtocolOptional -> do
294+ seg <- readSource src
295+ if S.isPrefixOf "PROXY " seg
296+ then parseProxyProtocolHeader src seg
297+ else do leftoverSource src seg
298+ return origAddr
299+
300+ parseProxyProtocolHeader src seg = do
301+ let (header,seg') = S.break (== 0x0d) seg -- 0x0d == CR
302+ maybeAddr = case S.split 0x20 header of -- 0x20 == space
303+ ["PROXY","TCP4",clientAddr,_,clientPort,_] ->
304+ case [x | (x, t) <- reads (decodeAscii clientAddr), null t] of
305+ [a] -> Just (SockAddrInet (readInt clientPort)
306+ (toHostAddress a))
307+ _ -> Nothing
308+ ["PROXY","TCP6",clientAddr,_,clientPort,_] ->
309+ case [x | (x, t) <- reads (decodeAscii clientAddr), null t] of
310+ [a] -> Just (SockAddrInet6 (readInt clientPort)
311+ 0
312+ (toHostAddress6 a)
313+ 0)
314+ _ -> Nothing
315+ ("PROXY":"UNKNOWN":_) ->
316+ Just origAddr
317+ _ ->
318+ Nothing
319+ case maybeAddr of
320+ Nothing -> throwIO (BadProxyHeader (decodeAscii header))
321+ Just a -> do leftoverSource src (S.drop 2 seg') -- drop CRLF
322+ return a
323+
324+ decodeAscii = map (chr . fromEnum) . S.unpack
325+
282326 th = threadHandle ii
283327
284− sendErrorResponse istatus e = do
328+ sendErrorResponse addr istatus e = do
285329 status <- readIORef istatus
286330 when status $ void $
287331 sendResponse
288332 (settingsServerName settings)
289− conn ii dummyreq defaultIndexRequestHeader (return S.empty) (errorResponse e)
333+ conn ii (dummyreq addr) defaultIndexRequestHeader (return S.empty) (errorResponse e)
290334
291− dummyreq = defaultRequest { remoteHost = addr }
335+ dummyreq addr = defaultRequest { remoteHost = addr }
292336
293337 errorResponse e = settingsOnExceptionResponse settings e
294338
295− recvSendLoop istatus fromClient = do
339+ recvSendLoop addr istatus fromClient = do
296340 (req', mremainingRef, idxhdr) <- recvRequest settings conn ii addr fromClient
297341 let req = req' { isSecure = isSecure' }
298342 -- Let the application run for as long as it wants
@@ -333,14 +377,14 @@ serveConnection conn ii addr isSecure' settings app = do
333377 Nothing -> do
334378 flushEntireBody (requestBody req)
335379 T.resume th
336− recvSendLoop istatus fromClient
380+ recvSendLoop addr istatus fromClient
337381 Just maxToRead -> do
338382 let tryKeepAlive = do
339383 -- flush the rest of the request body
340384 isComplete <- flushBody (requestBody req) maxToRead
341385 when isComplete $ do
342386 T.resume th
343− recvSendLoop istatus fromClient
387+ recvSendLoop addr istatus fromClient
344388 case mremainingRef of
345389 Just ref -> do
346390 remaining <- readIORef ref