-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzmqc.coffee
210 lines (183 loc) · 7.09 KB
/
zmqc.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env node
# zmqc: a small but powerful command-line interface to ZMQ
## Usage:
## Examples:
# zmqc -r -t=SUB -c='tcp://127.0.0.1:5000'
#
# Subscribe to 'tcp://127.0.0.1:5000', reading messages from it and printing
# them to the console. This will subscribe to all messages by default.
#
# ls | zmqc -w -t=PUSH -b='tcp://*:4000'
#
# Send the name of every file in the current directory as a message from a
# PUSH socket bound to port 4000 on all interfaces. Don't forget to quote the
# address to avoid glob expansion.
#
# zmqc -r -t=PULL -c'tcp://127.0.0.1:5202' | tee $TTY | zmqc -w -t=PUSH -c='tcp://127.0.0.1:5404'
#
# Read messages coming from a PUSH socket bound to port 5202 (note that we're
# connecting with a PULL socket), echo them to the active console, and
# forward them to a PULL socket bound to port 5404 (so we're connecting with
# a PUSH).
#
# zmqc -n 10 -0=0 -r -t=PULL -b='tcp://*:4123' | xargs -0 grep 'pattern'
#
# Bind to a PULL socket on port 4123, receive 10 messages from the socket
# (with each message representing a filename), and grep the files for
# `'pattern'`. The `-0` option means messages will be NULL-delimited rather
# than separated by newlines, so that filenames with spaces in them are not
# considered two separate arguments by xargs.
#
# echo "hello" | zmqc -t=REQ -c='tcp://127.0.0.1:4000'
#
# Send the string "hello" through a REQ socket connected to localhost port
# 4000, print whatever you get back and finish. In this way, REQ sockets can
# be used for a rudimentary form of RPC in shell scripts.
#
# coproc zmqc -t=REP -b='tcp://*:4000'
# tr -u '[a-z]' '[A-Z]' <&p >&p &
# echo "hello" | zmqc -c REQ 'tcp://127.0.0.1:4000'
#
# First, start a ZeroMQ REP socket listening on port 4000. The 'coproc' shell
# command runs this as a shell coprocess, which allows us to run the next
# line, tr. This will read its input from the REP socket's output, translate
# all lowercase characters to uppercase, and send them back to the REP
# socket's input. This, again, is run in the background. Finally, connect a
# REQ socket to that REP socket and send the string "hello" through it: you
# should just see the string "HELLO" printed on stdout.
## History:
# Based on https://github.com/zacharyvoase/zmqc.git written in Python
## License:
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# For more information, please refer to <http://unlicense.org/>
Args = require 'arg-parser'
zmq = require 'zmq'
readline = require 'readline'
args = new Args 'zmqc', '0.1', 'A small but powerful command-line interface to ZMQ', 'https://github.com/edwardchoh/zmqc'
args.add
name: 'delimiter'
desc: "Separate messages on input/output should be delimited by the ASCII code (instead of newlines). Use this if your messages may contain other delimiters, and you want to avoid ambiguous message borders."
switches: ['-0', '--delimiter']
default: 0x0a
value: 'delimiter'
args.add
name: 'number'
desc: "Receive/send only NUM messages. By default, zmqc lives forever in 'read' mode, or until the end of input in 'write' mode."
switches: ['-n', '--NUM']
default: 0
args.add
name: 'type'
desc: "Which type of socket to create. Must be one of 'PUSH', 'PULL', 'PUB', 'SUB', 'REQ', 'REP' or 'PAIR'."
switches: ['-t', '--type']
required: true
value: 'type'
args.add
name: 'read'
desc: "Read messages from the socket onto stdout."
switches: ['-r', '--read']
args.add
name: 'write'
desc: "Write messages from stdin to the socket."
switches: ['-w', '--write']
args.add
name: 'bind'
desc: "Bind to the specified address(es)."
switches: ['-b', '--bind']
value: 'bind'
args.add
name: 'connect'
desc: "Connect to the specified address(es)."
switches: ['-c', '--connect']
value: 'connect'
args.add
name: 'options'
desc: "Socket option names and values to set on the created socket. Consult `man zmq_setsockopt` for a comprehensive list of options. Note that you can safely omit the 'ZMQ_' prefix from the option name. If the created socket is of type 'SUB', and no 'SUBSCRIBE' options are given, the socket will automatically be subscribed to everything."
switches: ['-o', '--options']
value: 'options'
args.add
name: 'topic'
desc: "Specify topic for PUB or SUB socket. Defaults to all topics"
switches: ['-t', '--topic']
value: 'topic'
default: ''
splitBuffer = (buf, delim) ->
arr = []
p = 0
for i in [0..buf.length-1]
continue if buf[i] != delim
if i == 0
p = 1
continue # skip if delim is at the start of buffer
else
arr.push buf.slice(p, i)
p = i + 1
if arr.length == 0
# just return the buf if delim not found
return buf
if p < buf.length
# add final part
arr.push buf.slice(p, buf.length)
arr
main = ->
sock = zmq.socket args.params.type
if args.params.bind
sock.bindSync args.params.bind
else
sock.connect args.params.connect
if args.params.write
#process.stdin.setEncoding 'utf8'
multipart = []
process.stdin.on 'data', (chunk) ->
# chop off any trailing \n
if chunk[chunk.length - 1] == args.params.delimiter
chunk = chunk.slice 0, chunk.length - 1
if chunk.length == 0
# multipart is done
sock.send multipart
multipart = []
else
multipart.push chunk
else
delim = new Buffer([args.params.delimiter])
sock.on 'message', (data) ->
for arg, idx in Array.prototype.slice.call(arguments, 0)
process.stdout.write arg
process.stdout.write delim
process.stdout.write delim
if not args.parse()
return args.help()
args.params.type = args.params.type.toLowerCase()
# Check for conformance
if (args.params.read and args.params.write) or (not args.params.read and not args.params.write)
console.warn "Must specify either --read or --write"
else if (args.params.bind and args.params.connect) or (not args.params.bind and not args.params.connect)
console.warn "Must specify either --bind or --connect"
else if args.params.write and args.params.type == 'sub'
console.warn "Cannot write to a SUB socket"
else if args.params.read and args.params.type == 'pub'
console.warn "Cannot read from a PUB socket"
else
return main()
args.help()
return