-
Notifications
You must be signed in to change notification settings - Fork 11
/
router.cr
125 lines (114 loc) 路 3.4 KB
/
router.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
require "radix"
require "http/web_socket"
require "./ext/http/request/action"
require "./ext/http/request/path_params"
module Prism
# Routes a request's path, injecting matching `ContextProc` into `context.request.action` and path params into `context.request.path_params`.
#
# Always calls next handler.
#
# ```
# require "prism/router"
#
# router = Prism::Router.new do
# get "/" do |context|
# context.response.print("Hello world!")
# end
# end
#
# server = HTTP::Server.new(5000, [router]) do |context|
# action.call(context) if action = context.request.action
# end
# ```
class Router
include HTTP::Handler
alias ContextProc = ::Proc(HTTP::Server::Context, Nil)
alias WebSocketProc = ::Proc(HTTP::WebSocket, HTTP::Server::Context, Nil)
alias Node = ContextProc | HTTP::WebSocketHandler
# :nodoc:
HTTP_METHODS = %w(get post put patch delete options)
@tree = Radix::Tree(Node).new
# Initialize a new router and yield it. You can define routes in *&block*.
#
# ```
# # The simplest router
# router = Prism::Router.new do |r|
# r.get "/" do |env|
# env.response.print "Hello world!"
# end
# end
# ```
def initialize(&block)
yield self
end
def call(context : HTTP::Server::Context)
if context.request.headers["Upgrade"]? == "websocket"
result = @tree.find("/ws" + context.request.path)
else
result = @tree.find("/" + context.request.method.downcase + context.request.path)
end
if result.found?
context.request.action = result.payload
context.request.path_params = result.params
end
call_next(context)
end
# Draw a route for *path* and *methods*.
#
# ```
# router = Prism::Router.new do |r|
# r.on "/foo", methods: %w(get post) do |context|
# context.response.print("Hello from #{context.request.method} /foo!")
# end
# end
# ```
def on(path, methods : Array(String), &proc : ContextProc)
methods.map(&.downcase).each do |method|
begin
@tree.add("/" + method + path, proc)
rescue Radix::Tree::DuplicateError
raise DuplicateRouteError.new(method.upcase + " " + path)
end
end
end
{% for method in HTTP_METHODS %}
# Draw a route for *path* with `{{method.upcase.id}}` method.
#
# ```
# router = Prism::Router.new do
# {{method.id}} "/bar" do |context|
# context.response.print("Hello from {{method.upcase.id}} /bar!")
# end
# end
# ```
def {{method.id}}(path, &proc : ContextProc)
on(path, [{{method}}], &proc)
end
{% end %}
# Draw a WebSocket route for *path*.
#
# NOTE: Such route will be accessible **only** via `ws://` or `wss://` schemes!
#
# ```
# router = Prism::Router.new do |r|
# r.ws "/foo/:bar" do |socket, context|
# socket.send("Hello WS!")
# end
# end
# ```
def ws(path, &proc : WebSocketProc)
begin
@tree.add("/ws" + path, HTTP::WebSocketHandler.new(&proc))
rescue Radix::Tree::DuplicateError
raise DuplicateRouteError.new("WS " + path)
end
end
# Raised if duplicate route found
class DuplicateRouteError < Exception
getter route : String
def initialize(@route)
super("Duplicate route found: #{route}")
end
end
end
end