2012-02-27 11 views
1

更新:質問には最終的に編集された回答が含まれています。私は今、次の(最終的な答えを)使用Haskellのlocalhost:3389に最小オーバーヘッドプロキシを書き込む方法は?

module Main where 

import Control.Concurrent  (forkIO) 
import Control.Monad    (when,forever,void) 
import Network     (PortID(PortNumber),listenOn) 
import Network.Socket hiding  (listen,recv,send) 
import Network.Socket.ByteString (recv,sendAll) 
import qualified Data.ByteString as B 
import System 

type Host = String 
type Port = PortNumber 

main :: IO() 
main = do 
    [lp,h,p] <- getArgs 
    start (port lp) h (port p) 
    where 
    port = fromInteger . read 

start :: Port -> Host -> Port -> IO() 
start lp rh rp = withSocketsDo $ do 
    proxy <- listenOn $ PortNumber lp 
    forever $ do 
    (client,_) <- accept proxy 
    void . forkIO $ (client >-<) =<< rh [email protected] rp 

([email protected]) :: Host -> Port -> IO Socket 
host [email protected] port = do 
    addr:_ <- getAddrInfo Nothing (Just host) (Just $ show port) 
    server <- socket (addrFamily addr) Stream defaultProtocol 
    connect server (addrAddress addr) 
    return server 

(>-<) :: Socket -> Socket -> IO() 
x >-< y = do x >- y; y >- x 

(>-) :: Socket -> Socket -> IO() 
s >- r = void . forkIO . handle $ forever stream 
    where 
    stream = recv s (64 * 1024) >>= ifNot0 >>= sendAll r 
    ifNot0 = \c -> do when (B.null c) $ handle (error "0"); return c 
    handle = flip catch $ \e -> print e >> sClose s >> sClose r 

このように実行することができます:2000年、私は:私はローカルホストに接続する場合、mRemoteを使用して

proxy 2000 localhost 3389 

ローカルマシンのログイン画面が表示されます。 :)

* (>-)を改善する方法が見つかった場合は、この回答を更新します。

+1

彼のコードで解決しようとしている問題は何ですか? –

+0

@MikePennington、私はちょうど私が質問の最後に解決しようとしている問題を説明しました。 –

+0

@Cetin Sert:あなたの質問はこれをもっと速くする方法です。 –

答えて

2

情報を探すときにthis tcp proxy gistに来たようです。この時点で、isは壊れていて、少し乱雑です。そのような場合は、著者(この場合は私)にpingをして、将来の参考のために要点を修正できるようにしてください:)

私はすぐに修正し、このSOの質問にリンクします。固定版にはsendAllだけでなく、このSOの質問から来るすべての素晴らしい提案が含まれますので、あなたの最高の考えを共有してください。参考のために、this branch of throttleにはすでにsendAllの修正がありました。

編集:要点は今や修正されました

+0

私はギターの速いペースの環境に追いついているので、要点やピングのようなコンセプトは私の現在の地平線を少し越えています:)。要点が今修正される予定だとうれしいです。 –

+1

最も簡単な方法はおそらくGist自体にコメントをドロップすることですが、残念ながらログインする必要があります。多くの人と同様に、私はGistを使って、仲間と素早く汚れた概念を共有するコードを貼り付ける傾向があります。結果として、私の主義者(そして他の多くの人たち)は、ほとんどの場合、常に設計が不適切で壊れています。だからこそ、検索エンジンがそのうちの1つを索引につけて、興味深い人がいると、作者に醜いハックが少し注意を払う必要があることを知らせることは非常に重要です。 –

+0

48行のコードと、信頼性の高い、低mem、高性能な素敵なプロキシ。また、このコードは非常に鮮明で、本当に実行可能な意識の流れであり、ノイズはありませんが、私が今までに見たことのない最も美しい機能プログラミング言語の純粋な意図です。私は本当にうれしく思います。私はあなたの要点を見つけました。 :) –

2

見つけた数ヶ月前、私がハスケルを使い始めたときに見つけたthis gist

本当にシンプルで理解しやすいです。

EDIT:上記の要点に基づいて、ここにテスト済みのRDPプロキシがあります。差異はsendsendAllに置き換えて、すべてのデータが配信されていることを確認します。 linuxのrdpサーバー(大きなペイロードの切断)でテストするときにこの問題が見つかりました。

module Main where 

import Control.Concurrent  (forkIO) 
import Control.Monad   (forever,unless) 
import Network     (PortID(PortNumber),listenOn) 
import qualified Data.ByteString as S 
import Network.Socket hiding (listen,recv,send) 
import Network.Socket.ByteString (recv,sendAll) 
import System.Posix   (Handler(Ignore),installHandler,sigPIPE) 


localPort :: PortNumber 
localPort = 3390 

remoteHost :: String 
remoteHost = "localhost" 

remotePort :: Integer 
remotePort = 3389 

main :: IO() 
main = do 
    ignore $ installHandler sigPIPE Ignore Nothing 
    start 

start :: IO() 
start = withSocketsDo $ do 
    listener <- listenOn $ PortNumber localPort 
    forever $ do 
    (client,_) <- accept listener 
    ignore $ forkIO $ do 
     server <- connectToServer 
     client `proxyTo` server 
     server `proxyTo` client 
    return() 
    where 
    connectToServer = do 
     addrinfos <- getAddrInfo Nothing (Just remoteHost) (Just $ show remotePort) 
     let serveraddr = head addrinfos 
     server <- socket (addrFamily serveraddr) Stream defaultProtocol 
     connect server (addrAddress serveraddr) 
     return server 
    proxyTo from to = do 
     ignore $ forkIO $ flip catch (close from to) $ forever $ do 
     content <- recv from 1024 
     unless (S.null content) $ sendAll to content 
     return() 
    close a b _ = do 
     sClose a 
     sClose b 

-- | Run an action and ignore the result. 
ignore :: Monad m => m a -> m() 
ignore m = m >> return() 
+0

リンクをありがとう。私は実際に質問で同じものを使用しました...私の質問はproxyToの効率性ですが、私はいつでもすぐに回答が得られないかもしれないと思っています。 –

+1

githubからコードを修正しました。それはあなたのコードと同じ問題を抱え、データストリームを混乱させました。 – h0tw1r3

関連する問題