mirror of
https://github.com/faye/websocket-driver-ruby.git
synced 2025-11-01 13:59:38 +00:00
173 lines
6.1 KiB
Java
173 lines
6.1 KiB
Java
import java.io.IOException;
|
|
|
|
import com.jcoglan.websocket.Extensions;
|
|
import com.jcoglan.websocket.Frame;
|
|
import com.jcoglan.websocket.Message;
|
|
import com.jcoglan.websocket.Observer;
|
|
import com.jcoglan.websocket.Parser;
|
|
import com.jcoglan.websocket.Unparser;
|
|
|
|
import org.jruby.Ruby;
|
|
import org.jruby.RubyArray;
|
|
import org.jruby.RubyBoolean;
|
|
import org.jruby.RubyClass;
|
|
import org.jruby.RubyFixnum;
|
|
import org.jruby.RubyModule;
|
|
import org.jruby.RubyObject;
|
|
import org.jruby.RubyString;
|
|
import org.jruby.RubySymbol;
|
|
import org.jruby.anno.JRubyMethod;
|
|
import org.jruby.runtime.ObjectAllocator;
|
|
import org.jruby.runtime.ThreadContext;
|
|
import org.jruby.runtime.builtin.IRubyObject;
|
|
import org.jruby.runtime.load.BasicLibraryService;
|
|
|
|
public class WebsocketDriverService implements BasicLibraryService {
|
|
private Ruby runtime;
|
|
|
|
public boolean basicLoad(Ruby runtime) throws IOException {
|
|
this.runtime = runtime;
|
|
RubyModule websocket = runtime.defineModule("WebSocketNative");
|
|
|
|
RubyClass parser = websocket.defineClassUnder("Parser", runtime.getObject(), new ObjectAllocator() {
|
|
public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
|
|
return new RParser(runtime, rubyClass);
|
|
}
|
|
});
|
|
parser.defineAnnotatedMethods(RParser.class);
|
|
|
|
RubyClass unparser = websocket.defineClassUnder("Unparser", runtime.getObject(), new ObjectAllocator() {
|
|
public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
|
|
return new RUnparser(runtime, rubyClass);
|
|
}
|
|
});
|
|
unparser.defineAnnotatedMethods(RUnparser.class);
|
|
|
|
return true;
|
|
}
|
|
|
|
public class RParser extends RubyObject {
|
|
private Parser parser;
|
|
private R r;
|
|
|
|
public RParser(final Ruby runtime, RubyClass rubyClass) {
|
|
super(runtime, rubyClass);
|
|
this.r = new R(runtime);
|
|
}
|
|
|
|
@JRubyMethod
|
|
public IRubyObject initialize(final ThreadContext context, final IRubyObject driver, IRubyObject requireMasking) {
|
|
Extensions extensions = new Extensions() {
|
|
public boolean validFrameRsv(boolean rsv1, boolean rsv2, boolean rsv3, int opcode) {
|
|
IRubyObject[] args = {r.symbol("valid_frame_rsv?"), r.bool(rsv1), r.bool(rsv2), r.bool(rsv3), r.fixnum(opcode)};
|
|
return ((RubyObject)driver).send(context, args, null).isTrue();
|
|
}
|
|
};
|
|
|
|
Observer observer = new Observer() {
|
|
public void onError(int code, String reason) {
|
|
IRubyObject[] args = {r.symbol("handle_error"), r.fixnum(code), r.string(reason.getBytes())};
|
|
((RubyObject)driver).send(context, args, null);
|
|
}
|
|
|
|
public void onMessage(Message message) {
|
|
IRubyObject[] args = {
|
|
r.symbol("handle_message"),
|
|
r.fixnum(message.opcode),
|
|
r.bool(message.rsv1),
|
|
r.bool(message.rsv2),
|
|
r.bool(message.rsv3),
|
|
r.string(message.copy())
|
|
};
|
|
((RubyObject)driver).send(context, args, null);
|
|
}
|
|
|
|
public void onClose(int code, byte[] reason) {
|
|
IRubyObject[] args = {r.symbol("handle_close"), r.fixnum(code), r.string(reason)};
|
|
((RubyObject)driver).send(context, args, null);
|
|
}
|
|
|
|
public void onPing(Frame frame) {
|
|
IRubyObject[] args = {r.symbol("handle_ping"), r.string(frame.payload)};
|
|
((RubyObject)driver).send(context, args, null);
|
|
}
|
|
|
|
public void onPong(Frame frame) {
|
|
IRubyObject[] args = {r.symbol("handle_pong"), r.string(frame.payload)};
|
|
((RubyObject)driver).send(context, args, null);
|
|
}
|
|
};
|
|
|
|
parser = new Parser(extensions, observer, requireMasking.isTrue());
|
|
return null;
|
|
}
|
|
|
|
@JRubyMethod
|
|
public IRubyObject parse(IRubyObject chunk) {
|
|
byte[] bytes = ((RubyString)chunk).getBytes();
|
|
parser.parse(bytes);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class RUnparser extends RubyObject {
|
|
private Unparser unparser;
|
|
private R r;
|
|
|
|
public RUnparser(Ruby runtime, RubyClass rubyClass) {
|
|
super(runtime, rubyClass);
|
|
this.r = new R(runtime);
|
|
}
|
|
|
|
@JRubyMethod
|
|
public IRubyObject initialize(IRubyObject driver, IRubyObject masking) {
|
|
unparser = new Unparser(masking.isTrue());
|
|
return null;
|
|
}
|
|
|
|
@JRubyMethod
|
|
public IRubyObject frame(IRubyObject head, IRubyObject maskingKey, IRubyObject payload) {
|
|
byte[] buffer = ((RubyString)payload).getBytes();
|
|
RubyArray args = (RubyArray)head;
|
|
|
|
Frame frame = new Frame();
|
|
frame.fin = (Boolean)args.get(0);
|
|
frame.rsv1 = (Boolean)args.get(1);
|
|
frame.rsv2 = (Boolean)args.get(2);
|
|
frame.rsv3 = (Boolean)args.get(3);
|
|
frame.opcode = ((Long)args.get(4)).intValue();
|
|
frame.length = buffer.length;
|
|
frame.maskingKey = ((RubyString)maskingKey).getBytes();
|
|
frame.payload = ((RubyString)payload).getBytes();
|
|
|
|
byte[] result = unparser.frame(frame);
|
|
|
|
return r.string(result);
|
|
}
|
|
}
|
|
|
|
class R {
|
|
private Ruby runtime;
|
|
|
|
R(Ruby runtime) {
|
|
this.runtime = runtime;
|
|
}
|
|
|
|
RubyBoolean bool(boolean value) {
|
|
return RubyBoolean.newBoolean(runtime, value);
|
|
}
|
|
|
|
RubyFixnum fixnum(int value) {
|
|
return RubyFixnum.newFixnum(runtime, value);
|
|
}
|
|
|
|
RubySymbol symbol(String name) {
|
|
return RubySymbol.newSymbol(runtime, name);
|
|
}
|
|
|
|
RubyString string(byte[] value) {
|
|
return new RubyString(runtime, RubyString.createStringClass(runtime), value);
|
|
}
|
|
}
|
|
}
|