-
Add option to config to parse or not command line parameters #483. Thanks @diegogub π
-
Allow to set filename for
send_file#512. Thanks @mamantoha π
send_file env, "./asset/image.jpeg", filename: "image.jpg"-
Set
status_codebefore response #513. Thanks @mamantohoa π -
Use Crystal MIME registry. #516 Thanks @Sija π
- Fix
params.filesmemoization kemalcr#503. Thanks @mamantoha π
- Crystal 0.27.0 support.
- [breaking change] Added back
env.params.files.
Here's a fully working sample for reading a image file upload image1 and saving it under public/uploads.
post "/upload" do |env|
file = env.params.files["image1"].tempfile
file_path = ::File.join [Kemal.config.public_folder, "uploads/", File.basename(file.path)]
File.open(file_path, "w") do |f|
IO.copy(file, f)
end
"Upload ok"
endTo test
curl -F "image1=@/Users/serdar/Downloads/kemal.png" http://localhost:3000/upload
- Cache HTTP routes to increase performance π kemalcr#493
- [breaking change] Removed
env.params.files. You can use Crystal's built-inHTTP::FormData.parseinstead
post "/upload" do |env|
HTTP::FormData.parse(env.request) do |upload|
filename = file.filename
if !filename.is_a?(String)
"No filename included in upload"
else
file_path = ::File.join [Kemal.config.public_folder, "uploads/", filename]
File.open(file_path, "w") do |f|
IO.copy(file.tmpfile, f)
end
"Upload OK"
end
end- [breaking change] From now on to access dynamic url params in a WebSocket route you have to use:
ws "/:id" do |socket, context|
id = context.ws_route_lookup.params["id"]
end-
[breaking change] Removed
_methodmagic param. -
Added new exception page #466. Thanks @mamantoha π
-
Support custom port binding. Thanks @straight-shoota π
Kemal.run do |config|
server = config.server.not_nil!
server.bind_tcp "127.0.0.1", 3000, reuse_port: true
server.bind_tcp "0.0.0.0", 3001, reuse_port: true
end- Crystal 0.25.0 support π
- Add
Kemal::Context.get?to safely access context storage π - [Security] Don't serve 404 image dynamically π
- Disable
X-Powered-Byheader #449. Thanks @Blacksmoke16 π
- Crystal 0.24.1 support π
- Only return string from route.#408 thanks @crisward π
- Don't crash on empty path when compiled in --release. #407 thanks @crisward π
- Rename
Kemal::CommonLogHandlertoKemal::LogHandlerandKemal::CommonExceptionHandlertoKemal::ExceptionHandler. - Allow videos to be opened with correct mime type. #406 thanks @crisward π
- Add webm mime type.#413 thanks @reindeer-cafe π
- Dynamically insert handlers πͺ Fixes #376.
- Add context to WebSocket. This allows one to use
HTTP::Server::Contextinwsdeclarations π Fixes #349.
ws "/:room_name" do |socket, env|
env.params.url["room_name"]
end- Add support for customizing the headers of built-in
Kemal::StaticFileHandlerπ¨ Useful for supportingCORSfor single page applications π
static_headers do |response, filepath, filestat|
if filepath =~ /\.html$/
response.headers.add("Access-Control-Allow-Origin", "*")
end
response.headers.add("Content-Size", filestat.size.to_s)
end
end-
Allow %w in Handler macros #385. Thanks @will π
-
Security: X-Content-Type-Options: nosniff for static files. Fixes #379. Thanks @crisward π
-
Performance: Remove tempfile management to OS. This brings %10 - 15 performance boost to Kemal π
- Crystal 0.23.0 support! As always, Kemal is compatible with the latest major release of Crystal π
- Great news everyone π All handlers are now completely customizable!. Use the default
Kemalhandlers or go wild, it's all up to you β
# Don't forget to add `Kemal::RouteHandler::INSTANCE` or your routes won't work!
Kemal.config.handlers = [Kemal::InitHandler.new, YourHandler.new, Kemal::RouteHandler::INSTANCE]You can also insert a handler into a specific position.
# This adds MyCustomHandler instance to 1 position. Be aware that the index starts from 0.
add_handler MyCustomHandler.new, 1- Updated Kilt to v0.4.0.
- Make
RouteaStruct. This improves the performance of route declarations.
- Return no body for head route fixes #323. (thanks @crisward)
- Update
radixto0.3.8. (thanks @waghanza) - User defined context store types. (thanks @neovitange)
class User
property name
end
add_context_storage_type(User)- Prevent `send_file returning filesize. (thanks @crisward)
- Dont call setup in
config#add_filter_handlerfixes #338.
- Remove
Gzip::Headermonkey patch since it's fixed inCrystal 0.21.1.
- Fix Gzip in Kemal Seems broken for static files. This was caused by
Gzip::WriterinCrystal 0.21.0and currently mitigated by monkey patchingGzip::Header.
- Crystal 0.21.0 support
- Drop
multipart.crdependency.multipartsupport is now built-into Crystal <3 - Since Crystal 0.21.0 comes built-in with
multipartthere are some improvements and deprecations.
meta has been removed from FileUpload and it has the following properties
tmpfile: This is temporary file for file upload. Useful for saving the upload file.filename: File name of the file upload. (logo.png, images.zip e.g)headers: Headers for the file upload.creation_time: Creation time of the file upload.modification_time: Last Modification time of the file upload.read_time: Read time of the file upload.size: Size of the file upload.
- Simpler file upload. File uploads can now be access from
HTTP::Server::Contextlikeenv.params.files["filename"].
env.params.files["filename"] has 5 methods
tmpfile: This is temporary file for file upload. Useful for saving the upload file.tmpfile_path: File path oftmpfile.filename: File name of the file upload. (logo.png, images.zip e.g)meta: Meta information for the file upload.headers: Headers for the file upload.
Here's a fully working sample for reading a image file upload image1 and saving it under public/uploads.
post "/upload" do |env|
file = env.params.files["image1"].tmpfile
file_path = ::File.join [Kemal.config.public_folder, "uploads/", file.filename]
File.open(file_path, "w") do |f|
IO.copy(file, f)
end
"Upload ok"
endTo test
curl -F "image1=@/Users/serdar/Downloads/kemal.png" http://localhost:3000/upload
-
RF7233 support a.k.a file streaming. (kemalcr#299) (thanks @denysvitali)
-
Update Radix to 0.3.7. Fixes kemalcr#293
-
Configurable startup / shutdown logging. kemalcr#291 and kemalcr#292 (thanks @twisterghost).
- Update multipart.cr to 0.1.2. Fixes #285 related to multipart.cr
- Support for Crystal 0.20.3
- Add
Kemal.stop. Fixes #269. HTTP::Handleris not a class anymore, it's a module. See https://github.com/crystal-lang/crystal/releases/tag/0.20.3
- Handle missing 404 image. Fixes #263
- Remove basic auth middleware from core and move to kemalcr/kemal-basic-auth.
- Use body.gets_to_end for parse_json. Fixes #260.
- Update Radix to 0.3.5 and lock pessimistically. (thanks @luislavena)
- Treat
HTTP::Requestbody as anIO. Fixes #257
- Reimplemented Request middleware / filter routing.
Now all requests will first go through the Middleware stack then Filters (before_*) and will finally reach the matching route.
Which is illustrated as,
Request -> Middleware -> Filter -> Route
- Rename
return_withashalt. - Route declaration must start with
/. Fixes #242 - Set default exception Content-Type to text/html. Fixes #202
- Add
onlyandexcludepaths forKemal::Handler. This change requires that all handlers must inherit fromKemal::Handler.
For example this handler will only work on / path. By default the HTTP method is GET.
class OnlyHandler < Kemal::Handler
only ["/"]
def call(env)
return call_next(env) unless only_match?(env)
puts "If the path is / i will be doing some processing here."
end
endThe handlers using exclude will work on the paths that isn't specified. For example this handler will work on any routes other than /.
class ExcludeHandler < Kemal::Handler
exclude ["/"]
def call(env)
return call_next(env) unless only_match?(env)
puts "If the path is NOT / i will be doing some processing here."
end
end- Close response on
halt. (thanks @samueleaton). - Update
Radixtov0.3.4. errorhandler now also yields error. For example you can get the error mesasage like
error 500 do |env, err|
err.message
end- Update
multipart.crtov0.1.1
- Improved Multipart support with more info on parsed files.
parse_multipart(env)now yields anUploadFileobject which has the following propertiesfield,data,meta,`headers.
post "/upload" do |env|
parse_multipart(env) do |f|
image1 = f.data if f.field == "image1"
image2 = f.data if f.field == "image2"
puts f.meta
puts f.headers
"Upload complete"
end
end- Multipart support <3 (thanks @RX14). Now you can handle file uploads.
post "/upload" do |env|
parse_multipart(env) do |field, data|
image1 = data if field == "image1"
image2 = data if field == "image2"
"Upload complete"
end
end- Make session configurable. Now you can specify session name and expire time wit
Kemal.config.session["name"] = "your_app"
Kemal.config.session["expire_time"] = 48.hours- Session now supports more types. (String, Int32, Float64, Bool)
- Add
gziphelper to enable / disable gzip compression on responses. - Static file caching with etag and gzip (thanks @crisward)
Kemal.runnow accepts port to listen.
- Don't forget to call_next on NullLogHandler
- Add context store
KEMAL_ENVrespects toKemal.config.envand needs to be explicitly set.Kemal::InitHandleris introduced. Adds initial configuration, headers likeX-Powered-By.- Add
send_fileto helpers. - Add mime types.
- Fix parsing JSON params when "charset" is present in "Content-Type" header.
- Use http-only cookie for session
- Inject STDOUT by default in CommonLogHandler