HeadersAndCookies.vala 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. using Astralis;
  2. using Invercargill;
  3. using Invercargill.DataStructures;
  4. /**
  5. * HeadersAndCookies Example
  6. *
  7. * Demonstrates how to access and manipulate HTTP headers and cookies.
  8. * Uses Invercargill Dictionary and Enumerable for header/cookie processing.
  9. */
  10. void main() {
  11. var router = new Router();
  12. var server = new Server(8083, router);
  13. // Display all request headers
  14. router.map_func("/headers", (context) => {
  15. var parts = new Series<string>();
  16. parts.add("Request Headers:\n");
  17. parts.add("================\n\n");
  18. context.request.headers.to_immutable_buffer()
  19. .iterate((grouping) => {
  20. grouping.iterate((value) => {
  21. parts.add(@"$(grouping.key): $value\n");
  22. });
  23. });
  24. var result = parts.to_immutable_buffer()
  25. .aggregate<string>("", (acc, s) => acc + s);
  26. return new BufferedHttpResult.from_string(result);
  27. });
  28. // Check for specific header
  29. router.map_func("/user-agent", (context) => {
  30. var user_agent = context.request.user_agent ?? "Unknown";
  31. return new BufferedHttpResult.from_string(
  32. @"Your User-Agent is: $user_agent"
  33. );
  34. });
  35. // Check content type and content length
  36. router.map_func("/content-info", (context) => {
  37. var parts = new Series<string>();
  38. parts.add("Content Information:\n");
  39. parts.add("===================\n\n");
  40. parts.add(@"Content-Type: $(context.request.content_type)\n");
  41. parts.add(@"Content-Length: $(context.request.content_length)\n");
  42. parts.add(@"Content-Encoding: $(context.request.content_encoding ?? "none")\n");
  43. // parts.add(@"Has Body: $(context.request.has_body())\n");
  44. var result = parts.to_immutable_buffer()
  45. .aggregate<string>("", (acc, s) => acc + s);
  46. return new BufferedHttpResult.from_string(result);
  47. });
  48. // Check if header exists
  49. router.map_func("/check-header", (context) => {
  50. var header_name = context.request.get_query_or_default("name", "Accept");
  51. var exists = context.request.has_header(header_name);
  52. var value = context.request.get_header(header_name);
  53. return new BufferedHttpResult.from_string(
  54. @"Header '$header_name': $(exists ? "EXISTS" : "NOT FOUND")\nValue: $(value ?? "N/A")"
  55. );
  56. });
  57. // Set custom response headers
  58. router.map_func("/custom-headers", (context) => {
  59. var headers = new Catalogue<string, string>();
  60. headers.add("X-Custom-Header", "Custom-Value");
  61. headers.add("X-Request-Id", generate_request_id());
  62. headers.add("X-Powered-By", "Astralis");
  63. headers.add("X-Server-Time", new DateTime.now_local().format_iso8601());
  64. return new BufferedHttpResult.from_string(
  65. "Response with custom headers!",
  66. StatusCode.OK,
  67. headers
  68. );
  69. });
  70. // Display all cookies
  71. router.map_func("/cookies", (context) => {
  72. var parts = new Series<string>();
  73. parts.add("Request Cookies:\n");
  74. parts.add("================\n\n");
  75. if (context.request.cookies.to_immutable_buffer().count() == 0) {
  76. parts.add("(No cookies sent)\n");
  77. parts.add("\nTry setting a cookie first: /set-cookie?name=test&value=123\n");
  78. } else {
  79. context.request.cookies.to_immutable_buffer()
  80. .iterate((grouping) => {
  81. grouping.iterate((value) => {
  82. parts.add(@"$(grouping.key): $value\n");
  83. });
  84. });
  85. }
  86. var result = parts.to_immutable_buffer()
  87. .aggregate<string>("", (acc, s) => acc + s);
  88. return new BufferedHttpResult.from_string(result);
  89. });
  90. // Get specific cookie
  91. router.map_func("/get-cookie", (context) => {
  92. var name = context.request.get_query_or_default("name", "session");
  93. var value = context.request.get_cookie(name);
  94. if (value == null) {
  95. return new BufferedHttpResult.from_string(
  96. @"Cookie '$name' not found",
  97. StatusCode.NOT_FOUND
  98. );
  99. }
  100. return new BufferedHttpResult.from_string(
  101. @"Cookie '$name' = '$value'"
  102. );
  103. });
  104. // Set a cookie (via Set-Cookie header)
  105. router.map_func("/set-cookie", (context) => {
  106. var name = context.request.get_query_or_default("name", "test");
  107. var value = context.request.get_query_or_default("value", "123");
  108. var max_age = context.request.get_query_or_default("max_age", "3600");
  109. var headers = new Catalogue<string, string>();
  110. headers.add("Set-Cookie", @"$name=$value; Max-Age=$max_age; Path=/");
  111. return new BufferedHttpResult.from_string(
  112. @"Cookie '$name' set to '$value'",
  113. StatusCode.OK,
  114. headers
  115. );
  116. });
  117. // Set multiple cookies
  118. router.map_func("/set-cookies", (context) => {
  119. var headers = new Catalogue<string, string>();
  120. headers.add("Set-Cookie", "user=john; Max-Age=3600; Path=/");
  121. headers.add("Set-Cookie", "theme=dark; Max-Age=86400; Path=/");
  122. headers.add("Set-Cookie", "lang=en; Max-Age=31536000; Path=/");
  123. return new BufferedHttpResult.from_string(
  124. "Multiple cookies set!",
  125. StatusCode.OK,
  126. headers
  127. );
  128. });
  129. // Delete a cookie
  130. router.map_func("/delete-cookie", (context) => {
  131. var name = context.request.get_query_or_default("name", "test");
  132. var headers = new Catalogue<string, string>();
  133. headers.add("Set-Cookie", @"$name=; Max-Age=0; Path=/");
  134. return new BufferedHttpResult.from_string(
  135. @"Cookie '$name' deleted",
  136. StatusCode.OK,
  137. headers
  138. );
  139. });
  140. // Check if cookie exists
  141. router.map_func("/has-cookie", (context) => {
  142. var name = context.request.get_query_or_default("name", "session");
  143. var exists = context.request.has_cookie(name);
  144. return new BufferedHttpResult.from_string(
  145. @"Cookie '$name': $(exists ? "EXISTS" : "NOT FOUND")"
  146. );
  147. });
  148. // Cookie-based session simulation
  149. router.map_func("/session", (context) => {
  150. var session_id = context.request.get_cookie("session_id");
  151. if (session_id == null) {
  152. // Create new session
  153. var new_session_id = generate_session_id();
  154. var headers = new Catalogue<string, string>();
  155. headers.add("Set-Cookie", @"session_id=$new_session_id; Max-Age=3600; Path=/; HttpOnly");
  156. return new BufferedHttpResult.from_string(
  157. @"New session created!
  158. Session ID: $new_session_id
  159. Your session will expire in 1 hour.",
  160. StatusCode.OK,
  161. headers
  162. );
  163. }
  164. // Existing session
  165. return new BufferedHttpResult.from_string(
  166. @"Welcome back!
  167. Session ID: $session_id
  168. Your session is active."
  169. );
  170. });
  171. // CORS headers example
  172. router.map_func("/cors", (context) => {
  173. var origin = context.request.get_header("Origin") ?? "*";
  174. var headers = new Catalogue<string, string>();
  175. headers.add("Access-Control-Allow-Origin", origin);
  176. headers.add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  177. headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
  178. headers.add("Access-Control-Max-Age", "86400");
  179. return new BufferedHttpResult.from_string(
  180. @"CORS enabled for origin: $origin",
  181. StatusCode.OK,
  182. headers
  183. );
  184. });
  185. // OPTIONS method for CORS preflight
  186. router.map_func("/cors", (context) => {
  187. var origin = context.request.get_header("Origin") ?? "*";
  188. var headers = new Catalogue<string, string>();
  189. headers.add("Access-Control-Allow-Origin", origin);
  190. headers.add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  191. headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization");
  192. headers.add("Access-Control-Max-Age", "86400");
  193. headers.add("Content-Length", "0");
  194. return new BufferedHttpResult.from_string(
  195. "",
  196. StatusCode.NO_CONTENT,
  197. headers
  198. );
  199. });
  200. // Cache control headers
  201. router.map_func("/cache", (context) => {
  202. var cache_type = context.request.get_query_or_default("type", "public");
  203. var headers = new Catalogue<string, string>();
  204. headers.add("Cache-Control", @"$cache_type, max-age=3600");
  205. headers.add("Expires", get_expires_header(3600));
  206. headers.add("ETag", generate_etag());
  207. return new BufferedHttpResult.from_string(
  208. @"This response is cached ($cache_type cache, 1 hour)",
  209. StatusCode.OK,
  210. headers
  211. );
  212. });
  213. // Content negotiation
  214. router.map_func("/negotiate", (context) => {
  215. var accept = context.request.get_header("Accept") ?? "*/*";
  216. var headers = new Catalogue<string, string>();
  217. if (accept.contains("application/json")) {
  218. headers.add("Content-Type", "application/json");
  219. return new BufferedHttpResult.from_string(
  220. @"{ \"message\": \"JSON response\", \"format\": \"json\" }",
  221. StatusCode.OK,
  222. headers
  223. );
  224. } else if (accept.contains("text/xml")) {
  225. headers.add("Content-Type", "text/xml");
  226. return new BufferedHttpResult.from_string(
  227. @"<?xml version=\"1.0\"?><response><message>XML response</message><format>xml</format></response>",
  228. StatusCode.OK,
  229. headers
  230. );
  231. } else {
  232. headers.add("Content-Type", "text/plain");
  233. return new BufferedHttpResult.from_string(
  234. "Plain text response",
  235. StatusCode.OK,
  236. headers
  237. );
  238. }
  239. });
  240. // Security headers
  241. router.map_func("/secure", (context) => {
  242. var headers = new Catalogue<string, string>();
  243. headers.add("X-Content-Type-Options", "nosniff");
  244. headers.add("X-Frame-Options", "DENY");
  245. headers.add("X-XSS-Protection", "1; mode=block");
  246. headers.add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
  247. headers.add("Content-Security-Policy", "default-src 'self'");
  248. headers.add("Referrer-Policy", "strict-origin-when-cross-origin");
  249. return new BufferedHttpResult.from_string(
  250. "Response with security headers!",
  251. StatusCode.OK,
  252. headers
  253. );
  254. });
  255. print("Headers and Cookies Example Server running on port 8083\n");
  256. print("Try these endpoints:\n");
  257. print(" - http://localhost:8083/headers\n");
  258. print(" - http://localhost:8083/user-agent\n");
  259. print(" - http://localhost:8083/cookies\n");
  260. print(" - http://localhost:8083/set-cookie?name=test&value=hello\n");
  261. print(" - http://localhost:8083/set-cookies\n");
  262. print(" - http://localhost:8083/session\n");
  263. print(" - http://localhost:8083/cors\n");
  264. print(" - http://localhost:8083/cache\n");
  265. print(" - http://localhost:8083/negotiate\n");
  266. print(" - http://localhost:8083/secure\n");
  267. server.run();
  268. }
  269. // Helper functions
  270. string generate_request_id() {
  271. var timestamp = new DateTime.now_local().to_unix();
  272. var random = Random.next_int();
  273. return @"req-$timestamp-$random";
  274. }
  275. string generate_session_id() {
  276. var timestamp = new DateTime.now_local().to_unix();
  277. var random = Random.next_int();
  278. return @"sess-$timestamp-$random";
  279. }
  280. string generate_etag() {
  281. var timestamp = new DateTime.now_local().to_unix();
  282. return @"\"$timestamp\"";
  283. }
  284. string get_expires_header(int seconds) {
  285. var now = new DateTime.now_local();
  286. var expires = now.add_seconds(seconds);
  287. return expires.format("%a, %d %b %Y %H:%M:%S GMT");
  288. }