A WEBrick like server with Crystal

对着背影说爱祢 提交于 2020-04-16 04:27:10

问题


Is it possible to create a simple web server with Crystal that can serve HTML, CSS and JS pages?

My current code is:

require "http/server"
Port = 8080
Mime = "text/html"

server = HTTP::Server.new([HTTP::ErrorHandler.new, HTTP::LogHandler.new]) do |context|
    req = context.request
    if req.method == "GET"
        filename = File.join(Dir.current, "index.html")
        context.response.content_type = Mime
        context.response.content_length = File.size(filename)
        File.open(filename) { |file| IO.copy(file, context.response) }
        next
    end
    context.response.content_type = Mime

end

puts "\e[1;33mStarted Listening on Port #{Port}\e[0m"
server.listen(Port)

When I run compile and run the program, it initializes the server, but there are a couple of problems:

  1. In the Firefox browser's "Inspect Element" console, I see:
The stylesheet http://127.0.0.1:8080/styling.css was not loaded because its MIME type, "text/html", is not "text/css". 127.0.0.1:8080

The script from “http://127.0.0.1:8080/javascript.js” was loaded even though its MIME type (“text/html”) is not a valid JavaScript MIME type. 127.0.0.1:8080

SyntaxError: expected expression, got '<' javascript.js:1

The server just shows only the content of index.html.

The codes HTML, CSS and JS is perfectly valid when I run using WEBrick or load up the index.html directly to the browser.

  1. My server isn't accessible from any other devices on the local network.

回答1:


You might want to use HTTP::StaticFileHandler for this. It serves files from a local directory.

  1. In the handler you always read file index.html whatever the request is. This can't work.
  2. HTTP::Server#listen listens on 127.0.0.1, so it's only available from localhost. In order to be accessible from the network, you need to listen to an address that is available on the network. For example server.listen("0.0.0.0", Port) will listen on all interfaces.



回答2:


Many thanks to @Johannes Müller which solved my problem. Here, I am sharing the code for what I wanted exactly.

Code:

#!/usr/bin/env crystal
require "http/server"

# Get the Address
ADDR = (ARGV.find { |x| x.split(".").size == 4 } || "0.0.0.0").tap { |x| ARGV.delete(x) }
        .split(".").map { |x| x.to_i { 0 } }.join(".")

# Get the Port
PORT = ARGV.find { |x| x.to_i { 0 } > 0 }.tap { |x| ARGV.delete(x) }.to_s.to_i { 8080 }

# Get the path
d = Dir.current
dir = ARGV[0] rescue d
path = Dir.exists?(dir) ? dir : Dir.exists?(File.join(d, dir)) ? File.join(d, dir) : d
listing = !!Dir.children(path).find { |x| x == "index.html" }
actual_path = listing ? File.join(path, "index.html") : path

server = HTTP::Server.new([
        HTTP::ErrorHandler.new,
        HTTP::LogHandler.new,
        HTTP::StaticFileHandler.new(path, directory_listing: !listing)
    ]) do |context|
        context.response.content_type = "text/html"
        File.open(actual_path) { |file| IO.copy(file, context.response) }
end

puts "\e[1;33m:: Starting Sharing \e[38;5;75m#{actual_path}\e[1;31m on \e[38;5;226mhttp://#{ADDR}:#{PORT}\e[0m"
server.listen(::ADDR, ::PORT)

This code looks for a "index.html" file to the provided path (default Dir.current), if found, it shares the index.html file to the IP address (default 0.0.0.0) and port (default 8080) provided, else it just shares the current directory contents.

Running:

crystal code.cr /tmp/ 5020 127.0.0.1

The options can be shuffled. For example:

crystal code.cr 5020 /tmp/ 127.0.0.1

Or

crystal code.cr 5020 127.0.0.1 /tmp

This will start the server and share the /tmp direcotory. If the index.html file is found inside the /tmp/ directory, the requested browser will display the index.html content, or it will work similar to an FTP (although it's not).

Compiling and Running:

crystal build code.cr
./code [options]



回答3:


If you don't want to deep dive into native code & wants a simple solution - You should use any framework which provides file server.

You can use Shivneri framework for this. Shivneri has inbuilt file server which is easy to configure -

Shivneri.folders = [{
    path: "/",
    folder:  File.join(Dir.current, "assets"),
}]

You can add as many folder as you want to use. Every folder will be mapped with provided path.

For more info read doc - https://shivneriforcrystal.com/tutorial/file-server/



来源:https://stackoverflow.com/questions/58602639/a-webrick-like-server-with-crystal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!