User Tools

Site Tools


lecture:nrepl:sources

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
lecture:nrepl:sources [2013/03/08 14:32]
psk810 [서버]
lecture:nrepl:sources [2019/02/04 14:26] (current)
Line 105: Line 105:
  
 Bencode.clj는 Bencode 인코딩/​디코딩 기능을 구현하고 있다. Bencode는 BitTorrent에서 사용하는 데이타 전송 및 저장을 위한 인코딩 방식으로 D. J. Bernstein(([[http://​cr.yp.to/​proto/​netstrings.txt|netstring]]))의 [[wp>​Netstring]]을 확장한 것이다. 바이너리 인코딩 방식에 비해 비효율적이지만,​ 단순하면서도 유연하고 컴퓨터 시스템마다 사용되는 엔디안이 달라도 그에 무관하게 동작한다는 특징이 있다. 스트링, 숫자, 리스트, 맵을 지원한다. Bencode.clj는 Bencode 인코딩/​디코딩 기능을 구현하고 있다. Bencode는 BitTorrent에서 사용하는 데이타 전송 및 저장을 위한 인코딩 방식으로 D. J. Bernstein(([[http://​cr.yp.to/​proto/​netstrings.txt|netstring]]))의 [[wp>​Netstring]]을 확장한 것이다. 바이너리 인코딩 방식에 비해 비효율적이지만,​ 단순하면서도 유연하고 컴퓨터 시스템마다 사용되는 엔디안이 달라도 그에 무관하게 동작한다는 특징이 있다. 스트링, 숫자, 리스트, 맵을 지원한다.
 +
 +===== netstring ​ 인코딩 =====
  
 우선 먼저 netstring은 다음과 같다. 우선 먼저 netstring은 다음과 같다.
Line 115: Line 117:
  
 처음 숫자는 데이타의 길이를 나타내고,​ 뒤이어 구분자로서 콜론(':'​)이 오며, 바로 뒤에 스트링 데이타가 온다. 마지막으로 ','​로 끝을 표시한다. 데이타의 길이가 먼저 나오기 때문에 어플리케이션은 미리 먼저 충분한 메모리를 확보할 수 있으며, 마지막 ','​는 에러 검증에 사용된다. 처음 숫자는 데이타의 길이를 나타내고,​ 뒤이어 구분자로서 콜론(':'​)이 오며, 바로 뒤에 스트링 데이타가 온다. 마지막으로 ','​로 끝을 표시한다. 데이타의 길이가 먼저 나오기 때문에 어플리케이션은 미리 먼저 충분한 메모리를 확보할 수 있으며, 마지막 ','​는 에러 검증에 사용된다.
 +
 +===== bencode 인코딩 =====
  
 bencode는 여기에 Integer, List, Dictionary 인코딩이 추가되었다. bencode는 여기에 Integer, List, Dictionary 인코딩이 추가되었다.
Line 128: Line 132:
   * bencoding은 JSON이나 YAML처럼 플랫폼에 독립적이지만 복잡하면서도 손쉽게 구조화되는 데이타 인코딩을 위한 목적이다.   * bencoding은 JSON이나 YAML처럼 플랫폼에 독립적이지만 복잡하면서도 손쉽게 구조화되는 데이타 인코딩을 위한 목적이다.
  
 +
 +===== bencode 소스 분석 =====
  
 이제 bencode.clj 클로져 소스 파일을 분석해 보자. 이제 bencode.clj 클로져 소스 파일을 분석해 보자.
 +
 +==== 상수 정의 =====
  
 소스에서는 우선 가장 먼저 becoding에 사용되는 구분자(delimiter)들을 상수로 정의하고 있다. 소스에서는 우선 가장 먼저 becoding에 사용되는 구분자(delimiter)들을 상수로 정의하고 있다.
Line 149: Line 157:
 (def colon 58) (def colon 58)
 </​code>​ </​code>​
 +
 +==== read-byte, read-bytes, read-long ====
  
 <code clojure> <code clojure>
Line 195: Line 205:
  
 string>​payload와 string>​payload 함수를 전방 선언한 것이다. :tag 메타데이타((클로져 1.0은 ^리더 매크로를 meta의 축약형으로 제공했지만,​ 클로져 1.1에서는 사용이 비승인되었다가,​ 클로져 1.2에서부터 #^가 사용이 비승인되면서 대신 사용하게 되었다. 하지만 ​ #^은 사용은 아직 가능하다.))를 "​[B"​와 java.lang.String으로 해서 리턴값의 타입을 지정한다. "​[B"​는 Java Byte Array []에 대한 Type Hint이다. ​ string>​payload와 string>​payload 함수를 전방 선언한 것이다. :tag 메타데이타((클로져 1.0은 ^리더 매크로를 meta의 축약형으로 제공했지만,​ 클로져 1.1에서는 사용이 비승인되었다가,​ 클로져 1.2에서부터 #^가 사용이 비승인되면서 대신 사용하게 되었다. 하지만 ​ #^은 사용은 아직 가능하다.))를 "​[B"​와 java.lang.String으로 해서 리턴값의 타입을 지정한다. "​[B"​는 Java Byte Array []에 대한 Type Hint이다. ​
 +
 +==== read-netstring ====
  
 <code clojure> <code clojure>
Line 210: Line 222:
     content))     content))
 </​code>​ </​code>​
 +
 +==== sting <-> bytes ====
  
 read-netstring*은 netstring의 처음 길이를 나타내는 숫자를 읽고, 다음 그 길이만큼의 데이타를 읽어 리턴한다. read-string 은 read-netstring*에서 리턴받은 것을 다시 리턴하는데,​ 만약 다음 데이타가 콤마가 아니면 예외를 던진다. read-netstring*은 netstring의 처음 길이를 나타내는 숫자를 읽고, 다음 그 길이만큼의 데이타를 읽어 리턴한다. read-string 은 read-netstring*에서 리턴받은 것을 다시 리턴하는데,​ 만약 다음 데이타가 콤마가 아니면 예외를 던진다.
Line 224: Line 238:
  
 string>​payload는 String을 입력받아 바이트 배열을 리턴하고,​ string<​payload는 바이트 배열을 입력받아 String을 리턴한다. string>​payload는 String을 입력받아 바이트 배열을 리턴하고,​ string<​payload는 바이트 배열을 입력받아 String을 리턴한다.
 +
 +==== write-netstring ====
  
 <code clojure> <code clojure>
Line 243: Line 259:
  
 write-netstring*과 write-netstring은 위의 read-netstring*과 read-netstring에 각각 대응된다. write-netstring*과 write-netstring은 위의 read-netstring*과 read-netstring에 각각 대응된다.
 +
 +==== read-token ====
  
 <code clojure> <code clojure>
Line 265: Line 283:
  
 함수 read-integer read-list read-map를 전방선언한다. 함수 read-integer read-list read-map를 전방선언한다.
 +
 +==== read-bencode ====
  
 <code clojure> <code clojure>
Line 316: Line 336:
  
 token-seq 함수는 '​e'​를 만날 때까지 bencode를 계속 읽어내는 Lazy-seq를 만들어 낸다. ->> 스레딩 문구의 시작 문구가 Input 스트림으로부터 bencode를 읽는 함수이다. 이것을 repeatedly로 무한 반복하는 Lazy-seq을 만든 indentify로 판단하는 take-while 문구를 통해 nil이 나올 때까지 읽어 들이게 된다. token-seq 함수는 '​e'​를 만날 때까지 bencode를 계속 읽어내는 Lazy-seq를 만들어 낸다. ->> 스레딩 문구의 시작 문구가 Input 스트림으로부터 bencode를 읽는 함수이다. 이것을 repeatedly로 무한 반복하는 Lazy-seq을 만든 indentify로 판단하는 take-while 문구를 통해 nil이 나올 때까지 읽어 들이게 된다.
 +
 +==== write-bencode ====
  
 <code clojure> <code clojure>
Line 363: Line 385:
  
 Transport는 nREPL의 전송단을 추상화한 인터페이스이다. 외부에는 전송에 관한 내부 구현에는 관계없이 Transport를 이용하여 데이타의 송수신을 할 수 있다. ​ Transport는 nREPL의 전송단을 추상화한 인터페이스이다. 외부에는 전송에 관한 내부 구현에는 관계없이 Transport를 이용하여 데이타의 송수신을 할 수 있다. ​
 +
 +===== Transport 프로토콜 =====
  
 Transpoort는 다음과 같이 프로토콜로 정의되어 있다. Transpoort는 다음과 같이 프로토콜로 정의되어 있다.
Line 374: Line 398:
 recv는 다음 수신된 메세지를 읽어 리턴한다. 메세지가 없으면 대기한다. 대기중 timeout이 지나거나 전송 채널이 종료되면 nil을 리턴한다. recv는 다음 수신된 메세지를 읽어 리턴한다. 메세지가 없으면 대기한다. 대기중 timeout이 지나거나 전송 채널이 종료되면 nil을 리턴한다.
 send는 메시지를 전송 채널에 보내 송신한다. 이것은 Transport 자신을 다시 리턴한다. send는 메시지를 전송 채널에 보내 송신한다. 이것은 Transport 자신을 다시 리턴한다.
 +
 +===== FnTransport 타입 =====
  
 Transport 프로토콜의 구현은 FnTransport와 QueueTransport이 있다. QueueTransport는 파이프 트랜스포트로 사용되지만,​ FnTransport가 실제 bencode를 전송단에서 사용하고 있다. Transport 프로토콜의 구현은 FnTransport와 QueueTransport이 있다. QueueTransport는 파이프 트랜스포트로 사용되지만,​ FnTransport가 실제 bencode를 전송단에서 사용하고 있다.
Line 398: Line 424:
 </​code>​ </​code>​
  
 +===== fn-transport 함수 =====
  
 fn-transport는 실질적으로 Transport 구현을 만들어내는 함수이다. fn-transport는 실질적으로 Transport 구현을 만들어내는 함수이다.
Line 434: Line 461:
  
 __read 함수는 읽어들인 메시지가 예외인 경우 failure 아톰을 nil -> 예외로 변경하고 예외를 던진다. 이후 다른 스레드에서 읽기 시도를 하면 다시 예외를 던지도록 한다.__ __read 함수는 읽어들인 메시지가 예외인 경우 failure 아톰을 nil -> 예외로 변경하고 예외를 던진다. 이후 다른 스레드에서 읽기 시도를 하면 다시 예외를 던지도록 한다.__
 +
 +===== bencode 함수 =====
  
 bencode 함수는 전송단에 실제의 스트림을 연결하여 전송단을 완성한다. bencode 함수는 전송단에 실제의 스트림을 연결하여 전송단을 완성한다.
Line 769: Line 798:
     * handler : 클라이언트 메시지를 처리하기 위한 핸들러. 기본은 ​ defualt-handler.     * handler : 클라이언트 메시지를 처리하기 위한 핸들러. 기본은 ​ defualt-handler.
     * ack-port : 어떤 포트값이 주어진다면,​ 새로 구동되는 서버의 포트를 알려주기 위한 다른 서버 포트. 클로져 도구 구현시에만 유용.     * ack-port : 어떤 포트값이 주어진다면,​ 새로 구동되는 서버의 포트를 알려주기 위한 다른 서버 포트. 클로져 도구 구현시에만 유용.
-  - InetSocketAddress 자바 클래스를 사용하여 bind-address를 만들고 있다. bind가 참일 경우는 해당 ip주소를 사용하고 아닐겨웅에는 localhost가 된다.+  - InetSocketAddress 자바 클래스를 사용하여 bind-address를 만들고 있다. bind가 참일 경우는 해당 ip주소를 사용하고 아닐경우에는 localhost가 된다.
   - ServerSocket 자바 클래스를 서버 소켓을 생성하여 ss로 받는다.   - ServerSocket 자바 클래스를 서버 소켓을 생성하여 ss로 받는다.
   - Server는 server.clj에서 정의된 defrecord이다. Server 레코드 생성후 :ss 슬롯 추가.   - Server는 server.clj에서 정의된 defrecord이다. Server 레코드 생성후 :ss 슬롯 추가.
Line 903: Line 932:
   * add-stdin : "​eval",​ "​stdin"​ op 처리.   * add-stdin : "​eval",​ "​stdin"​ op 처리.
   * session : "​ls-sessions",​ "​close",​ "​clone"​ op 처리.   * session : "​ls-sessions",​ "​close",​ "​clone"​ op 처리.
 +  * pr-values : 메시지의 value 값을 프린트.
  
 linearize-middleware-stack 함수는 middleware.clj에서 정의된 함수로 미들웨어의 의존성에 따라 미들웨어들을 재배열시킨다. linearize-middleware-stack 함수는 middleware.clj에서 정의된 함수로 미들웨어의 의존성에 따라 미들웨어들을 재배열시킨다.
Line 908: Line 938:
 ======= 미들웨어 ======= ======= 미들웨어 =======
  
 +nREPL 서버는 작은 단위의 기능들을 조합해서 서버의 기능을 구성해 낸다. 미들웨어는 이러한 작은 단위의 기능을 구현한다. 하지만 미들웨어가 서로 연결되는 순서가 중요해진다. 예를 들어 세션 미들웨어는 사용자의 세션을 메시지에 추가하는데,​ 이런 후에야 평가 미들웨어가 세션 데이타를 참조할 수 있다. 미들웨어의 순서를 결정하기 위해 각 미들웨어에 미들웨어 설명자를 메타데이타로 정의한다.
 +
 +===== 미들웨어 설명자 =====
 +
 +===== 미들웨어 재배치 =====
 +
 +==== linearize-middleware-stack ====
 +
 +linearize-middleware-stack 함수는 미들웨어 리스트를 입력받아서,​ 각 미들웨어 설명자에 기술된 미들웨어 의존성 정보에 맞추어서 미들웨어의 순서를 정한다.
 +
 +<code clojure>
 +(defn linearize-middleware-stack
 +  [middlewares]
 +  (->> middlewares
 +    extend-deps
 +    (sort-by (comp count (partial apply concat) (juxt :expects :​requires)))
 +    reverse
 +    (reduce #​(conj-sorted % comparator %2) [])
 +    (map :​implemented-by)))
 +</​code>​
 +
 +이 함수는 크게 extend-deps 함수에 의존하고 있다.
 +
 +====  extend-deps ====
 +
 +이 함수는 각 미들웨어 설명자에 기술된 의존성을 확장한다.
 +
 +<code clojure>
 +(defn- extend-deps
 +  [middlewares]
 +  (let [descriptor #(-> % meta ::​descriptor)
 +        middlewares (concat middlewares
 +                            (->> (map descriptor middlewares)
 +                              (mapcat (juxt :expects :requires))
 +                              (mapcat identity)
 +                              (filter var?)))]
 +    (doseq [m (remove descriptor middlewares)]
 +      (binding [*out* *err*]
 +        (printf "​[WARNING] No nREPL middleware descriptor in metadata of %s, see clojure.tools.middleware/​set-descriptor!"​ m)))
 +    (let [middlewares (set (for [m middlewares]
 +                             ​(->​ (descriptor m)
 +                               ; only conj'​ing m here to support direct reference to
 +                               ; middleware dependencies in :expects and :requires,
 +                               ; e.g. interruptable-eval'​s dep on
 +                               ; clojure.tools.nrepl.middleware.pr-values/​pr-values
 +                               ​(update-in [:handles] (comp set #(conj % m) keys))
 +                               ​(assoc :​implemented-by m))))]
 +      (set (for [m middlewares]
 +             ​(reduce
 +               #​(update-in % [%2] into (dependencies middlewares % %2))
 +               m #{:expects :​requires}))))))
 +</​code>​
 +
 +==== dependencies ====
 +
 +<code clojure>
 +(defn- dependencies
 +  [set start dir]
 +  (let [ops (start dir)
 +        deps (set/select
 +               (comp seq (partial set/​intersection ops) :handles)
 +               set)]
 +    (when (deps start)
 +      (throw (IllegalArgumentException.
 +               ​(format "​Middleware %s depends upon itself via %s"
 +                       ​(:​implemented-by start)
 +                       ​dir))))
 +    (concat ops
 +            (mapcat #​(dependencies set % dir) deps))))
 +</​code>​
 +
 +==== comparator ====
 +
 +<code clojure>
 +(defn- comparator
 +  [{a-requires :requires a-expects :expects a-handles :handles}
 +   ​{b-requires :requires b-expects :expects b-handles :handles}]
 +  (or (->> (into {} [[[a-requires b-handles] -1]
 +                     ​[[a-expects b-handles] 1]
 +                     ​[[b-requires a-handles] 1]
 +                     ​[[b-expects a-handles] -1]])
 +        (map (fn [[sets ret]]
 +               (and (seq (apply set/​intersection sets)) ret)))
 +        (some #{-1 1}))
 +      0))
 +</​code>​
 +
 +===== 미들웨어 함수들 =====
 +
 +==== session ====
 +==== add-stdin ====
 +==== wrap-describe ====
 +==== interruptible-eval ====
 +==== wrap-load-file ====
 +==== pr-values ====
  
lecture/nrepl/sources.1362753135.txt.gz · Last modified: 2019/02/04 14:26 (external edit)