fcp.rkt 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #lang racket/base
  2. ;; Copyright (c) 2015 dinky's evil twin sone://EWtk1limedjBM2LnGE3~z98tC8bLTu9ryLIMcFgg8PI
  3. ;; License: LGPL
  4. (require
  5. racket/pretty
  6. racket/tcp
  7. racket/string)
  8. ;; note: data/skip-list takes hella long to load, so use alists
  9. ;; for better performance ordered dicts!
  10. (displayln 'OK)
  11. (define (startup-time)
  12. (displayln (exact->inexact (current-process-milliseconds)))
  13. (flush-output)
  14. (exit))
  15. ;; (startup-time)
  16. ;; racket/port takes 400ms to load!
  17. (define (copy-port in out)
  18. (let ((buf (make-bytes #x1000)))
  19. (let loop ()
  20. (let ((amt (read-bytes! buf in)))
  21. (when (not (eof-object? amt))
  22. (write-bytes buf out)
  23. (loop))))))
  24. (define (find-identifier name opts)
  25. (let ((identifier (assq 'Identifier opts)))
  26. (if identifier
  27. (string->symbol (car identifier))
  28. name)))
  29. (define (fcp-loop app)
  30. (define waiters (make-immutable-hash))
  31. (define aliases (make-immutable-hash))
  32. (define in (current-input-port))
  33. (define out (current-output-port))
  34. (define data-buf (make-bytes #x1000))
  35. (define (write-line s)
  36. (write-string s out)
  37. (newline out))
  38. (define (send name opts (data #f) (data-length 0))
  39. (write-line (symbol->string name))
  40. (for-each
  41. (λ (pair)
  42. (let ((name (car pair))
  43. (value (cdr pair)))
  44. (write-line (string-append (symbol->string name)
  45. "=" (cond
  46. ((symbol? value) (symbol->string value))
  47. ((string? value) value)
  48. ((bytes? value) (bytes->string/utf-8 value))
  49. ((number? value) (number->string value))
  50. ((eq? value #f) "false")
  51. ((eq? value #t) "true")
  52. (else
  53. (error "wat is ~s" value)))))))
  54. opts)
  55. (if data
  56. (begin
  57. (write-line (string-append "Data-Length=" (number->string data-length)))
  58. (write-line "Data")
  59. (cond
  60. ((procedure? data)
  61. (data (λ (chunk) (write-bytes chunk out))))
  62. ((input-port? data)
  63. (copy-port data out))
  64. ((bytes? data)
  65. (write-bytes data out))
  66. ((string? data)
  67. (write-bytes (string->bytes/utf-8 data) out))
  68. (else
  69. (error "How to write this data?" data))))
  70. (begin
  71. (write-line "EndMessage")
  72. (newline out))))
  73. (define expect
  74. (case-lambda
  75. ((identifier newaliases waiter)
  76. (set! aliases
  77. (apply hash-set* aliases
  78. (let loop ((result '()) (newaliases newaliases))
  79. (if (null? newaliases)
  80. (reverse result)
  81. (let ((alias (car newaliases)))
  82. (when (hash-ref aliases alias #f)
  83. (error "Already waiting on alias" alias identifier))
  84. (loop (cons identifier (cons (car newaliases) result)) (cdr newaliases)))))))
  85. (expect identifier waiter))
  86. ((identifier waiter)
  87. (if (list? identifier)
  88. (expect (car identifier) (cdr identifier) waiter)
  89. (begin
  90. (set! waiters (hash-set waiters identifier waiter)))))))
  91. (define (doit shutdown)
  92. (app send expect shutdown)
  93. (let read-a-message ()
  94. (define name (string->symbol
  95. (let ((line (read-line in 'linefeed)))
  96. (when (eof-object? line)
  97. (error "Fffail"))
  98. line)))
  99. (let properties ((opts '()))
  100. (define line (read-line in 'linefeed))
  101. (case line
  102. (("Data" "EndMessage")
  103. (define identifier (find-identifier
  104. (hash-ref aliases name name)
  105. opts))
  106. (define waiter (hash-ref waiters identifier))
  107. (if (equal? line "Data")
  108. (let-values (((feed finished) (waiter name identifier opts))
  109. ((total) (string->number (cdr (assoc "DataLength" opts)))))
  110. (let reading-data ((left total))
  111. (if (<= left 0)
  112. (finished total)
  113. (let* ((max-to-read (min left (bytes-length data-buf)))
  114. (amount (read-bytes! data-buf in 0 max-to-read)))
  115. (when (eof-object? amount)
  116. (error "FCP server closed connection"))
  117. (cond
  118. ((procedure? feed)
  119. (feed data-buf amount left total))
  120. ((output-port? feed)
  121. (write-bytes data-buf amount feed))
  122. (else
  123. (error "How the heay ~s" feed)))
  124. (reading-data (- left amount))))))
  125. (waiter name identifier opts))
  126. (read-a-message))
  127. (else
  128. (define-values (name value) (apply values
  129. (string-split
  130. line
  131. "="
  132. #:repeat? #f)))
  133. (properties (cons (cons name value) opts)))))
  134. (read-a-message)))
  135. (dynamic-wind
  136. (λ ()
  137. (set!-values (in out) (tcp-connect/enable-break "127.0.0.1" 9481))
  138. (file-stream-buffer-mode out 'none))
  139. (λ ()
  140. (call/cc doit))
  141. (λ ()
  142. (close-input-port in)
  143. (close-output-port out)
  144. (set! in #f))))
  145. (define make-identifier (let ((counter 0))
  146. (λ (sym)
  147. (begin0
  148. (string-append (symbol->string sym) "-" (number->string counter))
  149. (set! counter (+ counter 1))))))
  150. (define uri (let ((uri (getenv "URI")))
  151. (if (or (not uri) (= 0 (string-length uri)))
  152. "KSK@gpl.txt"
  153. uri)))
  154. (fcp-loop
  155. (λ (send expect shutdown)
  156. (expect 'NodeHello
  157. (λ (name identifier opts)
  158. (pretty-print (list 'got name opts))
  159. (expect '(SimpleProgress ProtocolError)
  160. (λ (name identifier opts)
  161. (pretty-print (list 'progress name opts))))
  162. (expect '(DataFound)
  163. (λ (name identifier opts)
  164. (displayln "Found it!")));
  165. (expect 'AllData
  166. (λ (name identifier opts)
  167. (pretty-print (list 'receiving-data name opts))
  168. (values
  169. (λ (buf amount left total)
  170. (println (list 'got-data amount left total)))
  171. (λ (total)
  172. (println 'all-done)
  173. (shutdown)))))
  174. (expect 'GetFailed
  175. (λ (name identifier opts)
  176. (pretty-print (list "Aww! It didn't come" uri opts))
  177. (shutdown)))
  178. (send 'ClientGet `((Identifier . ,(make-identifier 'get))
  179. (URI . ,uri)
  180. (Verbosity . 1)
  181. (ReturnType . direct)))))
  182. (send 'ClientHello '((Name . "Racket FCP")
  183. (ExpectedVersion . 2.0)))))