1 /**********************************************************\ 2 | | 3 | hprose | 4 | | 5 | Official WebSite: http://www.hprose.com/ | 6 | http://www.hprose.org/ | 7 | | 8 \**********************************************************/ 9 10 /**********************************************************\ 11 * * 12 * hprose/rpc/httpservice.d * 13 * * 14 * hprose http service library for D. * 15 * * 16 * LastModified: Aug 3, 2016 * 17 * Author: Ma Bingyao <andot@hprose.com> * 18 * * 19 \**********************************************************/ 20 21 module hprose.rpc.httpservice; 22 23 import hprose.rpc.service; 24 import hprose.rpc.common; 25 import hprose.rpc.httpcontext; 26 import std.conv; 27 import std.file; 28 import std.stdio; 29 import std.traits; 30 import std.typecons; 31 import std.variant; 32 import vibe.http.router; 33 import vibe.http.server; 34 import vibe.stream.operations; 35 36 alias OnSendHeader = void delegate(HttpContext context); 37 38 class HttpService: Service { 39 private { 40 bool[string] origins; 41 42 void sendHeader(HttpContext context) { 43 if (onSendHeader !is null) { 44 onSendHeader(context); 45 } 46 HTTPServerRequest req = context.request; 47 HTTPServerResponse res = context.response; 48 res.headers["Content-Type"] = "text/plain"; 49 if (p3p) { 50 res.headers["P3P"] = "CP=\"CAO DSP COR CUR ADM DEV TAI PSA PSD " ~ 51 "IVAi IVDi CONi TELo OTPi OUR DELi SAMi " ~ 52 "OTRi UNRi PUBi IND PHY ONL UNI PUR FIN " ~ 53 "COM NAV INT DEM CNT STA POL HEA PRE GOV\""; 54 } 55 if (crossDomain) { 56 string origin = req.headers["Origin"]; 57 if (origin != "" && origin != "null") { 58 if (origins.length == 0 || origin in origins) { 59 res.headers["Access-Control-Allow-Origin"] = origin; 60 res.headers["Access-Control-Allow-Credentials"] = "true"; 61 } 62 } 63 else { 64 res.headers["Access-Control-Allow-Origin"] = "*"; 65 } 66 } 67 } 68 } 69 70 bool get; 71 bool p3p; 72 bool crossDomain; 73 OnSendHeader onSendHeader; 74 75 void handler(HTTPServerRequest req, HTTPServerResponse res) { 76 HttpContext context = new HttpContext(req, res); 77 sendHeader(context); 78 switch(req.method) { 79 case HTTPMethod.GET: { 80 if (get) { 81 res.writeBody(doFunctionList()); 82 } 83 else { 84 res.statusCode = HTTPStatus.forbidden; 85 } 86 break; 87 } 88 case HTTPMethod.POST: { 89 res.writeBody(handle(req.bodyReader.readAll(), context)); 90 break; 91 } 92 default: break; 93 } 94 } 95 } 96 97 class HttpServer: HttpService { 98 private { 99 string _crossDomainXmlFile; 100 string _clientAccessPolicyXmlFile; 101 string _lastModified = null; 102 string _etag = null; 103 } 104 105 @property string crossDomainXmlFile() { 106 return _crossDomainXmlFile; 107 } 108 109 @property string crossDomainXmlFile(string file) { 110 _crossDomainXmlFile = file; 111 crossDomainXmlContext = readText(file); 112 return file; 113 } 114 115 @property string clientAccessPolicyXmlFile() { 116 return _clientAccessPolicyXmlFile; 117 } 118 119 @property string clientAccessPolicyXmlFile(string file) { 120 _clientAccessPolicyXmlFile = file; 121 clientAccessPolicyXmlContent = readText(file); 122 return file; 123 } 124 125 string crossDomainXmlContext; 126 string clientAccessPolicyXmlContent; 127 HTTPServerSettings settings = new HTTPServerSettings(); 128 129 void crossDomainXmlHandler(HTTPServerRequest req, HTTPServerResponse res) { 130 if (req.headers["If-Modified-Since"] == _lastModified && 131 req.headers["If-None-Match"] == _etag) { 132 res.statusCode = HTTPStatus.notModified; 133 } 134 else { 135 res.headers["Last-Modified"] = _lastModified; 136 res.headers["Etag"] = _etag; 137 res.writeBody(crossDomainXmlContext, "text/xml"); 138 } 139 } 140 141 void clientAccessPolicyXmlHandler(HTTPServerRequest req, HTTPServerResponse res) { 142 if (req.headers["If-Modified-Since"] == _lastModified && 143 req.headers["If-None-Match"] == _etag) { 144 res.statusCode = HTTPStatus.notModified; 145 } 146 else { 147 res.headers["Last-Modified"] = _lastModified; 148 res.headers["Etag"] = _etag; 149 res.writeBody(clientAccessPolicyXmlContent, "text/xml"); 150 } 151 } 152 153 HTTPListener start(string path = "/") { 154 URLRouter router = new URLRouter(); 155 router.get("/crossdomain.xml", &crossDomainXmlHandler); 156 router.get("/clientaccesspolicy.xml", &clientAccessPolicyXmlHandler); 157 router.any(path, &handler); 158 159 return listenHTTP(settings, router); 160 } 161 } 162 163 unittest { 164 import hprose.rpc.httpclient; 165 import hprose.rpc.context; 166 import hprose.rpc.filter; 167 import std.datetime; 168 169 string hello(string name) { 170 return "hello " ~ name ~ "!"; 171 } 172 173 string goodbye(string name) { 174 return "goodbye " ~ name ~ "!"; 175 } 176 177 Variant missfunc(string name, Variant[] args) { 178 if (name == "mul") { 179 return args[0] * args[1]; 180 } 181 else if (name == "div") { 182 return args[0] / args[1]; 183 } 184 else { 185 return Variant(null); 186 } 187 } 188 189 int inc(ref int n, HttpContext context) { 190 auto req = context.request; 191 auto res = context.response; 192 n++; 193 return n; 194 } 195 196 class BaseTest { 197 int add(int a, int b) { 198 return a + b; 199 } 200 int sub(int a, int b) { 201 return a - b; 202 } 203 } 204 class Test: BaseTest { 205 int sum(int[] nums) { 206 int sum = 0; 207 foreach (x; nums) { 208 sum += x; 209 } 210 return sum; 211 } 212 static string[] test() { 213 return ["Tom", "Jerry"]; 214 } 215 static Variant[string] test2() { 216 return ["name": Variant("张三"), "age": Variant(18)]; 217 } 218 } 219 220 Test test = new Test(); 221 222 // Server 223 HttpServer server = new HttpServer(); 224 server.add!("hello")(&hello); 225 server.add!(["goodbye", "inc"])(&goodbye, &inc); 226 server.add!(["add", "sub", "sum"])(test); 227 server.add!("test", Test)(); // add Test.test method to the server 228 server.add!(Test, "test")(); // add all static methods on Test with prefix "test" to the server 229 server.addMissingFunction(&missfunc); 230 server.use(delegate Variant(string name, ref Variant[] args, Context context, NextInvokeHandler next) { 231 writeln(name); 232 writeln(args); 233 Variant result = next(name, args, context); 234 writeln(result); 235 return result; 236 }, delegate Variant(string name, ref Variant[] args, Context context, NextInvokeHandler next) { 237 writeln(Clock.currStdTime()); 238 Variant result = next(name, args, context); 239 writeln(Clock.currStdTime()); 240 return result; 241 }); 242 server.use!"beforeFilter"(delegate ubyte[](ubyte[] request, Context context, NextFilterHandler next) { 243 writeln("beforeFilter"); 244 writeln(cast(string)request); 245 ubyte[] response = next(request, context); 246 writeln("beforeFilter"); 247 writeln(cast(string)response); 248 writeln(); 249 return response; 250 }); 251 server.use!"afterFilter"(delegate ubyte[](ubyte[] request, Context context, NextFilterHandler next) { 252 writeln("afterFilter"); 253 writeln(cast(string)request); 254 ubyte[] response = next(request, context); 255 writeln("afterFilter"); 256 writeln(cast(string)response); 257 return response; 258 }); 259 server.settings.bindAddresses = ["127.0.0.1"]; 260 server.settings.port = 4444; 261 server.settings.sessionStore = new MemorySessionStore(); 262 server.start(); 263 264 // Client 265 interface Hello { 266 @Simple() string hello(string name); 267 string goodbye(string name); 268 int add(int a, int b); 269 int sub(int a, int b = 3); 270 int mul(int a, int b); 271 int div(int a, int b); 272 int sum(int[] nums...); 273 int inc(ref int n); 274 string[] test(); 275 Variant[string] test2(); 276 } 277 278 auto client = new HttpClient("http://127.0.0.1:4444/"); 279 Hello proxy = client.useService!Hello(); 280 281 Hello proxy2 = client.useService!(Hello, "test")(); 282 283 // client.filters ~= new class Filter { 284 // override ubyte[] inputFilter(ubyte[] data, Context context) { 285 // writeln(cast(string)data); 286 // return data; 287 // } 288 // 289 // override ubyte[] outputFilter(ubyte[] data, Context context) { 290 // writeln(cast(string)data); 291 // return data; 292 // }; 293 // }; 294 295 assert(proxy.hello("world") == "hello world!"); 296 assert(proxy.goodbye("world") == "goodbye world!"); 297 assert(proxy.add(1, 2) == 3); 298 assert(proxy.sub(1, 2) == -1); 299 assert(proxy.mul(1, 2) == 2); 300 assert(proxy.div(2, 2) == 1); 301 assert(proxy.sum(1, 2, 3) == 6); 302 assert(proxy.test() == ["Tom", "Jerry"]); 303 int n = 0; 304 assert(proxy.inc(n) == 1); 305 assert(proxy.inc(n) == 2); 306 assert(proxy.inc(n) == 3); 307 assert(n == 3); 308 assert(proxy2.test() == ["Tom", "Jerry"]); 309 assert(proxy2.test2() == ["name": Variant("张三"), "age": Variant(18)]); 310 311 }