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