1 module httpbin; 2 3 version(vibeD) { 4 } else 5 version (httpbin) 6 { 7 import requests.server.httpd; 8 import requests.utils; 9 import std.datetime; 10 import std.json; 11 import std.conv; 12 import std.range; 13 import std.string; 14 import core.thread; 15 import std.experimental.logger; 16 17 auto buildReply(ref HTTPD_Request rq) { 18 auto method = JSONValue(rq.method); 19 auto args = JSONValue(rq.query); 20 auto headers = JSONValue(rq.requestHeaders); 21 auto url = JSONValue(rq.uri.uri); 22 auto json = JSONValue(parseJSON(rq.json)); 23 auto data = JSONValue(rq.data); 24 auto form = JSONValue(rq.form); 25 auto files = JSONValue(rq.files); 26 auto reply = JSONValue([ 27 "method": method, 28 "headers": headers, 29 "args":args, 30 "json": json, 31 "url": url, 32 "data": data, 33 "form": form, 34 "files": files 35 ]); 36 return reply.toString(); 37 } 38 39 HTTPD httpbinApp() { 40 pragma(msg, "Compiling httpbin server"); 41 debug(requests) trace("start httpbin app"); 42 HTTPD server = new HTTPD(); 43 App httpbin = App("httpbin"); 44 45 httpbin.port = 8081; 46 httpbin.host = "127.0.0.1"; 47 httpbin.timeout = 10.seconds; 48 httpbin.rqLimit = 5; 49 server.app(httpbin); 50 51 auto root(in App app, ref HTTPD_Request rq, RequestArgs args) { 52 debug (httpd) trace("handler / called"); 53 auto rs = response(rq, buildReply(rq)); 54 rs.headers["Content-Type"] = "application/json"; 55 return rs; 56 } 57 auto get(in App app, ref HTTPD_Request rq, RequestArgs args) { 58 debug (httpd) trace("handler /get called"); 59 auto rs = response(rq, buildReply(rq)); 60 rs.headers["Content-Type"] = "application/json"; 61 return rs; 62 } 63 auto incomplete(in App app, ref HTTPD_Request rq, RequestArgs args) { 64 debug (httpd) trace("handler /incomplete called"); 65 auto rs = response(rq, buildReply(rq), 600); 66 rs._status_reason = ""; 67 return rs; 68 } 69 auto del(in App app, ref HTTPD_Request rq, RequestArgs args) { 70 if ( rq.method != "DELETE") { 71 auto rs = response(rq, "Illegal method %s".format(rq.method), 405); 72 return rs; 73 } 74 else { 75 auto rs = response(rq, buildReply(rq)); 76 return rs; 77 } 78 } 79 auto put(in App app, ref HTTPD_Request rq, RequestArgs args) { 80 if ( rq.method != "PUT") { 81 auto rs = response(rq, "Illegal method %s".format(rq.method), 405); 82 return rs; 83 } 84 else { 85 auto rs = response(rq, buildReply(rq)); 86 return rs; 87 } 88 } 89 auto patch(in App app, ref HTTPD_Request rq, RequestArgs args) { 90 if ( rq.method != "PATCH") { 91 auto rs = response(rq, "Illegal method %s".format(rq.method), 405); 92 return rs; 93 } 94 else { 95 auto rs = response(rq, buildReply(rq)); 96 return rs; 97 } 98 } 99 auto post(in App app, ref HTTPD_Request rq, RequestArgs args) { 100 auto rs = response(rq, buildReply(rq)); 101 return rs; 102 } 103 auto gzip(in App app, ref HTTPD_Request rq, RequestArgs args) { 104 auto content = ["gzipped":true]; 105 auto rs = response(rq, JSONValue(content).toPrettyString); 106 rs.compress(Compression.gzip); 107 rs.headers["Content-Type"] = "application/json"; 108 return rs; 109 } 110 auto deflate(in App app, ref HTTPD_Request rq, RequestArgs args) { 111 auto content = ["deflated":true]; 112 auto rs = response(rq, JSONValue(content).toPrettyString); 113 rs.compress(Compression.deflate); 114 return rs; 115 } 116 auto rel_redir(in App app, ref HTTPD_Request rq, RequestArgs args) { 117 auto rs = response(rq, buildReply(rq)); 118 auto redirects = to!long(args["redirects"]); 119 if ( redirects > 1 ) { 120 rs.headers["Location"] = "/relative-redirect/%d".format(redirects-1); 121 } else { 122 rs.headers["Location"] = "/get"; 123 } 124 rs.status = 302; 125 return rs; 126 } 127 auto abs_redir(in App app, ref HTTPD_Request rq, RequestArgs args) { 128 auto rs = response(rq, buildReply(rq)); 129 auto redirects = to!long(args["redirects"]); 130 if ( redirects > 1 ) { 131 rs.headers["Location"] = "http://127.0.0.1:8081/absolute-redirect/%d".format(redirects-1); 132 } else { 133 rs.headers["Location"] = "http://127.0.0.1:8081/get"; 134 } 135 rs.status = 302; 136 return rs; 137 } 138 auto cookiesSet(in App app, ref HTTPD_Request rq, RequestArgs args) { 139 Cookie[] cookies; 140 foreach(p; rq.query.byKeyValue) { 141 cookies ~= Cookie("/cookies", rq.requestHeaders["host"], p.key, p.value); 142 } 143 auto rs = response(rq, buildReply(rq), 302); 144 rs.headers["Location"] = "/cookies"; 145 rs.cookies = cookies; 146 return rs; 147 } 148 auto cookies(in App app, ref HTTPD_Request rq, RequestArgs args) { 149 auto cookies = ["cookies": JSONValue(rq.cookies)]; 150 auto rs = response(rq, JSONValue(cookies).toString); 151 return rs; 152 } 153 auto range(in App app, ref HTTPD_Request rq, RequestArgs args) { 154 auto size = to!size_t(args["size"]); 155 auto rs = response(rq, new ubyte[size].chunks(16)); 156 rs.compress(Compression.yes); 157 return rs; 158 } 159 auto basicAuth(in App app, ref HTTPD_Request rq, RequestArgs args) { 160 import std.base64; 161 auto user = args["user"]; 162 auto password= args["password"]; 163 auto auth = cast(string)Base64.decode(rq.requestHeaders["authorization"].split()[1]); 164 auto up = auth.split(":"); 165 short status; 166 if ( up[0]==user && up[1]==password) { 167 status = 200; 168 } else { 169 status = 401; 170 } 171 auto rs = response(rq, buildReply(rq), status); 172 rs.headers["Content-Type"] = "application/json"; 173 return rs; 174 } 175 auto delay(in App app, ref HTTPD_Request rq, RequestArgs args) { 176 auto delay = dur!"seconds"(to!long(args["delay"])); 177 Thread.sleep(delay); 178 auto rs = response(rq, buildReply(rq)); 179 rs.headers["Content-Type"] = "application/json"; 180 return rs; 181 } 182 auto stream(in App app, ref HTTPD_Request rq, RequestArgs args) { 183 auto lines = to!size_t(args["lines"]); 184 import std.stdio; 185 auto rs = response(rq, (buildReply(rq) ~ "\n").repeat(lines)); 186 rs.headers["Content-Type"] = "application/json"; 187 return rs; 188 } 189 server.addRoute(exactRoute(r"/", &root)). 190 addRoute(exactRoute(r"/get", &get)). 191 addRoute(exactRoute(r"/post", &post)). 192 addRoute(exactRoute(r"/delete", &del)). 193 addRoute(exactRoute(r"/put", &put)). 194 addRoute(exactRoute(r"/patch", &patch)). 195 addRoute(exactRoute(r"/cookies", &cookies)). 196 addRoute(exactRoute(r"/cookies/set", &cookiesSet)). 197 addRoute(exactRoute(r"/gzip", &gzip)). 198 addRoute(exactRoute(r"/deflate", &deflate)). 199 addRoute(exactRoute(r"/incomplete", &incomplete)). 200 addRoute(regexRoute(r"/delay/(?P<delay>\d+)", &delay)). 201 addRoute(regexRoute(r"/stream/(?P<lines>\d+)", &stream)). 202 addRoute(regexRoute(r"/range/(?P<size>\d+)", &range)). 203 addRoute(regexRoute(r"/relative-redirect/(?P<redirects>\d+)", &rel_redir)). 204 addRoute(regexRoute(r"/absolute-redirect/(?P<redirects>\d+)", &abs_redir)). 205 addRoute(regexRoute(r"/basic-auth/(?P<user>[^/]+)/(?P<password>[^/]+)", &basicAuth)); 206 207 return server; 208 } 209 }