Compare commits

...

5 Commits

Author SHA1 Message Date
Luna 845ccb10ab don't overwrite content-length if its already there
we shouldn't overwrite something that the client likely did it
willlingly
2020-09-03 22:54:48 -03:00
Luna 61af3854cb route_handler: remove auto-HEAD 2020-09-03 22:54:48 -03:00
Luna aa378b7a90 add head method to dsl 2020-09-03 22:54:48 -03:00
Luna 2f9f859be9 websocket: default to error code 500 on any websocket error 2020-09-03 22:54:48 -03:00
Luna 19bc38b881 add support for error codes on websockets
copied from 541dfc9da5/src/onyx-http/middleware/router/websocket_handler.cr (L36)
2020-09-03 22:54:48 -03:00
5 changed files with 75 additions and 5 deletions

View File

@ -6,8 +6,8 @@
# - WebSocket(ws)
# - before_*
# - error
HTTP_METHODS = %w(get post put patch delete options)
FILTER_METHODS = %w(get post put patch delete options all)
HTTP_METHODS = %w(get post put patch delete options head)
FILTER_METHODS = %w(get post put patch delete options head all)
{% for method in HTTP_METHODS %}
def {{method.id}}(path : String, &block : HTTP::Server::Context -> _)

View File

@ -2,7 +2,7 @@ class HTTP::Server::Response
class Output
def close
# ameba:disable Style/NegatedConditionsInUnless
unless response.wrote_headers? && !response.headers.has_key?("Content-Range")
if !response.wrote_headers? && !response.headers.has_key?("Content-Range") && !response.headers.has_key?("Content-Length")
response.content_length = @out_count
end

View File

@ -21,7 +21,7 @@ module Kemal
# a corresponding `HEAD` route.
def add_route(method : String, path : String, &handler : HTTP::Server::Context -> _)
add_to_radix_tree method, path, Route.new(method, path, &handler)
add_to_radix_tree("HEAD", path, Route.new("HEAD", path) { }) if method == "GET"
# add_to_radix_tree("HEAD", path, Route.new("HEAD", path) { }) if method == "GET"
end
# Looks up the route from the Radix::Tree for the first time and caches to improve performance.

View File

@ -1,3 +1,5 @@
require "http"
module Kemal
# Takes 2 parameters: *path* and a *handler* to specify
# what action to be done if the route is matched.
@ -7,8 +9,56 @@ module Kemal
def initialize(@path : String, &@proc : HTTP::WebSocket, HTTP::Server::Context -> Void)
end
def error(code : Int16, message : String)
err = WebsocketError.new(code, message)
raise err
end
def call(context : HTTP::Server::Context)
super
if websocket_upgrade_request?(context.request)
response = context.response
version = context.request.headers["Sec-WebSocket-Version"]?
unless version == ::HTTP::WebSocket::Protocol::VERSION
response.headers["Sec-WebSocket-Version"] = ::HTTP::WebSocket::Protocol::VERSION
raise UpgradeRequired.new
end
key = context.request.headers["Sec-WebSocket-Key"]?
raise BadRequest.new("Sec-WebSocket-Key header is missing") unless key
accept_code = ::HTTP::WebSocket::Protocol.key_challenge(key)
response.status_code = 101
response.headers["Upgrade"] = "websocket"
response.headers["Connection"] = "Upgrade"
response.headers["Sec-WebSocket-Accept"] = accept_code
response.upgrade do |io|
socket = ::HTTP::WebSocket.new(io)
@proc.call(socket, context)
socket.run
rescue error : Exception
# TODO: check if 500 is what we're supposed to give
context.response.status_code = 500
if error.is_a?(WebsocketError)
code = error.code.to_i16
message = error.status_message
else
code = 1011_i16
message = "Exception"
end
raw = uninitialized UInt8[2]
IO::ByteFormat::BigEndian.encode(code, raw.to_slice)
socket.not_nil!.close(String.new(raw.to_slice) + message)
raise error unless error.is_a?(WebsocketError)
end
else
raise UpgradeRequired.new
end
end
end
end

View File

@ -1,4 +1,24 @@
module Kemal
class WebsocketError < Exception
property code : Int32 = 1011
property status_message : String = "websocket error"
def initialize(@code, @status_message : String)
end
end
class UpgradeRequired < WebsocketError
def initialize
super(426, "Upgrade required")
end
end
class BadRequest < WebsocketError
def initialize(status_message : String)
super(400, status_message)
end
end
class WebSocketHandler
include HTTP::Handler