1 /** 2 Stratum protocol is based on JSON-RPC 2.0. 3 (although it doesn't include "jsonrpc" information in every message). 4 Each message has to end with a line end character (n). 5 6 This module has 3 entry point: 7 $(UL 8 $(LI `RawStratumRpcAutoClient`) 9 $(LI `HttpStratumRpcAutoClient`) 10 $(LI `TcpStratumRpcAutoClient`) 11 ) 12 */ 13 module rpc.protocol.stratum; 14 15 public import rpc.core; 16 import rpc.protocol.json; 17 import vibe.data.json; 18 import std.typecons: Nullable, nullable; 19 20 21 class StratumRpcRequest : JsonRpcRequest!int 22 { 23 public: 24 static StratumRpcRequest make(T)(int id, string method, T params) 25 { 26 import vibe.data.json : serializeToJson; 27 28 auto request = new StratumRpcRequest(); 29 request.id = id; 30 request.method = method; 31 request.params = serializeToJson!T(params); 32 33 return request; 34 } 35 36 static StratumRpcRequest make(int id, string method) 37 { 38 auto request = new StratumRpcRequest(); 39 request.id = id; 40 request.method = method; 41 42 return request; 43 } 44 45 /** Strip the "jsonrpc" field. 46 */ 47 override Json toJson() const @safe 48 { 49 Json json = Json.emptyObject; 50 json["method"] = method; 51 json["id"] = id; 52 if (!params.isNull) 53 json["params"] = params.get; 54 return json; 55 } 56 57 /** Strip the "jsonrpc" field. 58 */ 59 static StratumRpcRequest fromJson(Json src) @safe 60 { 61 StratumRpcRequest request = new StratumRpcRequest(); 62 request.method = src["method"].to!string; 63 if (src["id"].type != Json.Type.undefined) 64 request.id = src["id"].to!int; 65 if (src["params"].type != Json.Type.undefined) 66 request.params = src["params"].nullable; 67 return request; 68 } 69 } 70 71 class StratumRpcResponse : JsonRpcResponse!int 72 { 73 public: 74 /** Strip the "jsonrpc" field. 75 */ 76 override Json toJson() const @safe 77 { 78 Json json = Json.emptyObject; 79 // the id must be 'null' in case of parse error 80 if (!id.isNull) 81 json["id"] = id.get; 82 else 83 json["id"] = null; 84 if (!result.isNull) 85 json["result"] = result.get; 86 if (!error.isNull) 87 json["error"] = serializeToJson!(const(JsonRpcError))(error.get); 88 return json; 89 } 90 91 /** Strip the "jsonrpc" field. 92 */ 93 static StratumRpcResponse fromJson(Json src) @safe 94 { 95 StratumRpcResponse request = new StratumRpcResponse(); 96 if (src["id"].type != Json.Type.undefined) 97 { 98 if (src["id"].type == Json.Type.null_) 99 request.id.nullify; 100 else 101 request.id = src["id"].to!int; 102 } 103 if (src["result"].type != Json.Type.undefined) 104 request.result = src["result"].nullable; 105 if (src["error"].type != Json.Type.undefined) 106 request.error = deserializeJson!JsonRpcError(src["error"]).nullable; 107 return request; 108 } 109 } 110 111 class RawStratumRPCServer : RawJsonRpcServer!(int, StratumRpcRequest, StratumRpcResponse) 112 { 113 import vibe.core.stream: InputStream, OutputStream; 114 115 public: 116 this(OutputStream ostream, InputStream istream) @safe 117 { 118 super(ostream, istream); 119 } 120 } 121 122 class HTTPStratumRPCServer : HttpJsonRpcServer!(int, StratumRpcRequest, StratumRpcResponse) 123 { 124 import vibe.http.router: URLRouter; 125 126 public: 127 this(URLRouter router, string path) 128 { 129 super(router, path); 130 } 131 } 132 133 class TCPStratumRPCServer : TcpJsonRpcServer!(int, StratumRpcRequest, StratumRpcResponse) 134 { 135 public: 136 this(ushort port, RpcInterfaceSettings settings = new RpcInterfaceSettings()) 137 { 138 super(port, settings); 139 } 140 } 141 142 class StratumRpcAutoClient(I) : I 143 { 144 import autointf; 145 import std.traits; 146 147 protected: 148 alias TId = int; // always an int 149 alias TReq = StratumRpcRequest; 150 alias TResp = StratumRpcResponse; 151 alias AutoClient(I) = JsonRpcAutoClient!(I, TId, TReq, TResp); 152 alias RPCClient = IRpcClient!(TId, TReq, TResp); 153 AutoClient!I _autoClient; 154 155 public: 156 this(RPCClient client, RpcInterfaceSettings settings) @safe 157 { 158 _autoClient = new AutoClient!I(client, settings); 159 } 160 161 pragma(inline, true) 162 ReturnType!Func executeMethod(alias Func, ARGS...)(ARGS args) @safe 163 { 164 return _autoClient.executeMethod!(Func, ARGS)(args); 165 } 166 167 pragma(inline, true) 168 @property auto client() @safe { return _autoClient.client; } 169 170 mixin(autoImplementMethods!(I,executeMethod)()); 171 } 172 173 /// 174 class RawStratumRpcAutoClient(I) : StratumRpcAutoClient!I 175 { 176 import vibe.core.stream: InputStream, OutputStream; 177 178 public: 179 this(OutputStream ostream, InputStream istream) @safe 180 { 181 super(new RawJsonRpcClient!(int, StratumRpcRequest, StratumRpcResponse)(ostream, istream), 182 new RpcInterfaceSettings()); 183 } 184 } 185 186 /// 187 class HttpStratumRpcAutoClient(I) : StratumRpcAutoClient!I 188 { 189 public: 190 this(string host) @safe 191 { 192 super(new HttpJsonRpcClient!(int, StratumRpcRequest, StratumRpcResponse)(host), 193 new RpcInterfaceSettings()); 194 } 195 } 196 197 /// 198 class TcpStratumRpcAutoClient(I) : StratumRpcAutoClient!I 199 { 200 public: 201 this(string host, ushort port, RpcInterfaceSettings settings = new RpcInterfaceSettings()) @safe 202 { 203 super(new TcpJsonRpcClient!(int, StratumRpcRequest, StratumRpcResponse)(host, port, settings), 204 settings); 205 } 206 }