HeadersAndCookies.vala 14 KB

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