main.cr 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #
  2. # Copyright (c) 2023 supercell
  3. #
  4. # SPDX-License-Identifier: BSD-3-Clause
  5. #
  6. # This is an example HTTP server which
  7. # accepts a Markdown request and returns
  8. # the parsed HTML.
  9. require "ecr"
  10. require "http"
  11. require "log"
  12. require "./default_markdown"
  13. require "../src/luce.cr"
  14. PORT = ENV.fetch("LUCE_PORT", "8080").to_i32
  15. def serve_index(response : HTTP::Server::Response, params : URI::Params) : Nil
  16. response.status_code = 200
  17. response.content_type = "text/html"
  18. markdown = params.fetch("markdown", DEFAULT_MARKDOWN)
  19. extension_set = params.fetch("extension", "CommonMark")
  20. es = case extension_set
  21. when "CommonMark"
  22. Luce::ExtensionSet::COMMON_MARK
  23. when "GitHub"
  24. Luce::ExtensionSet::GITHUB_WEB
  25. else
  26. Luce::ExtensionSet::NONE
  27. end
  28. # `ren_markdown` is used in "index.ecr"
  29. # ameba:disable Lint/UselessAssign
  30. ren_markdown = Luce.to_html(markdown, extension_set: es)
  31. renderer = ECR.render "index.ecr"
  32. bytes = renderer.unsafe_byte_slice(0)
  33. response.content_length = bytes.size
  34. response.write(bytes)
  35. end
  36. def method_not_allowed(response : HTTP::Server::Response) : Nil
  37. response.headers["Allow"] = "GET"
  38. response.respond_with_status(405)
  39. end
  40. DEFAULT_PARAMS = URI::Params.new({"markdown" => [DEFAULT_MARKDOWN], "extension" => ["CommonMark"]})
  41. def process_request(request : HTTP::Request) : URI::Params
  42. return DEFAULT_PARAMS if request.body.nil?
  43. body = request.body.not_nil!
  44. form = body.gets_to_end
  45. URI::Params.parse(form)
  46. end
  47. def plain(response : HTTP::Server::Response, resource : String) : Nil
  48. css = File.read(".#{resource}")
  49. response.status_code = 200
  50. css_bytes = css.unsafe_byte_slice(0)
  51. response.content_length = css_bytes.size
  52. response.write(css_bytes)
  53. end
  54. server = HTTP::Server.new do |context|
  55. # Probably not the best way to do this.
  56. case context.request.resource
  57. when "/preview"
  58. if context.request.method == "POST"
  59. params = process_request(context.request)
  60. serve_index(context.response, params)
  61. else
  62. method_not_allowed(context.response)
  63. end
  64. when "/", "/index.html"
  65. serve_index(context.response, DEFAULT_PARAMS)
  66. when /.*\.css/, /.*\.js/
  67. plain(context.response, context.request.resource)
  68. when /favicon/
  69. # There are probably other requests that should
  70. # just be ignored, but this is just an example.
  71. context.response.respond_with_status(404)
  72. else
  73. context.response.status_code = 301
  74. context.response.headers["Location"] = "/index.html"
  75. end
  76. # makes this example look official ;p
  77. Log.info { "#{context.request.method}: #{context.response.status_code} #{context.request.resource}" }
  78. end
  79. address = server.bind_tcp(PORT)
  80. puts "Running Crystal #{Crystal::VERSION}"
  81. puts "Listening on http://#{address}"
  82. server.listen