mirror of
				https://gitea.invidious.io/iv-org/shard-kemal.git
				synced 2024-08-15 00:53:36 +00:00 
			
		
		
		
	Add RFC7233 support (#299)
Add RFC7233 support a.k.a "Range" headers support
This commit is contained in:
		
							parent
							
								
									b07dd04372
								
							
						
					
					
						commit
						0543142a10
					
				
					 3 changed files with 101 additions and 1 deletions
				
			
		|  | @ -12,7 +12,9 @@ private def handle(request, fallthrough = true) | |||
| end | ||||
| 
 | ||||
| describe Kemal::StaticFileHandler do | ||||
|   file_text = File.read "#{__DIR__}/static/dir/test.txt" | ||||
|   file = File.open "#{__DIR__}/static/dir/test.txt" | ||||
|   file_size = file.size | ||||
|   file_text = file.to_s | ||||
| 
 | ||||
|   it "should serve a file with content type and etag" do | ||||
|     response = handle HTTP::Request.new("GET", "/dir/test.txt") | ||||
|  | @ -99,4 +101,33 @@ describe Kemal::StaticFileHandler do | |||
|       response.headers["Allow"].should eq("GET, HEAD") | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   it "should send part of files when requested (RFC7233)" do | ||||
|     %w(POST PUT DELETE HEAD).each do |method| | ||||
|       headers = HTTP::Headers{"Range" => "0-100"} | ||||
|       response = handle HTTP::Request.new(method, "/dir/test.txt", headers) | ||||
|       response.status_code.should_not eq(206) | ||||
|       response.headers.has_key?("Content-Range").should eq(false) | ||||
|     end | ||||
| 
 | ||||
|     %w(GET).each do |method| | ||||
|       headers = HTTP::Headers{"Range" => "0-100"} | ||||
|       response = handle HTTP::Request.new(method, "/dir/test.txt", headers) | ||||
|       response.status_code.should eq(206 || 200) | ||||
|       if response.status_code == 206 | ||||
|         response.headers.has_key?("Content-Range").should eq true | ||||
|         match = response.headers["Content-Range"].match(/bytes (\d+)-(\d+)\/(\d+)/) | ||||
|         match.should_not be nil | ||||
|         if match | ||||
|           start_range = match[1].to_i {0} | ||||
|           end_range = match[2].to_i {0} | ||||
|           range_size = match[3].to_i {0} | ||||
| 
 | ||||
|           range_size.should eq file_size | ||||
|           (end_range < file_size).should eq true | ||||
|           (start_range < end_range).should eq true | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
							
								
								
									
										14
									
								
								src/kemal/response.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/kemal/response.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| class HTTP::Server::Response | ||||
|   class Output | ||||
|     def close | ||||
| 
 | ||||
|       unless response.wrote_headers? && !response.headers.has_key?("Content-Range") | ||||
|         response.content_length = @out_count | ||||
|       end | ||||
| 
 | ||||
|       ensure_headers_written | ||||
| 
 | ||||
|       super | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -56,6 +56,9 @@ module Kemal | |||
|         request_headers = context.request.headers | ||||
|         filesize = File.size(file_path) | ||||
|         File.open(file_path) do |file| | ||||
|           if context.request.headers.has_key?("Range") && context.request.method == "GET" | ||||
|             next multipart(file, context) | ||||
|           end | ||||
|           if request_headers.includes_word?("Accept-Encoding", "gzip") && config.is_a?(Hash) && config["gzip"] == true && filesize > minsize && Utils.zip_types(file_path) | ||||
|             context.response.headers["Content-Encoding"] = "gzip" | ||||
|             Zlib::Deflate.gzip(context.response) do |deflate| | ||||
|  | @ -76,6 +79,58 @@ module Kemal | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def multipart(file, env) | ||||
|       #See http://httpwg.org/specs/rfc7233.html | ||||
|       fileb = file.size | ||||
| 
 | ||||
|       range = env.request.headers["Range"] | ||||
|       match = range.match(/bytes=(\d{1,})-(\d{0,})/) | ||||
| 
 | ||||
|       startb = 0 | ||||
|       endb = 0 | ||||
| 
 | ||||
|       if match | ||||
|         if match.size >= 2 | ||||
|           startb = match[1].to_i { 0 } | ||||
|         end | ||||
| 
 | ||||
|         if match.size >= 3 | ||||
|           endb = match[2].to_i { 0 } | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       if endb == 0 | ||||
|           endb = fileb | ||||
|       end | ||||
| 
 | ||||
|       if startb < endb && endb <= fileb | ||||
|         env.response.status_code = 206 | ||||
|         env.response.content_length = endb - startb | ||||
|         env.response.headers["Accept-Ranges"] = "bytes" | ||||
|         env.response.headers["Content-Range"] = "bytes #{startb}-#{endb - 1}/#{fileb}" # MUST | ||||
| 
 | ||||
|         if startb > 1024 | ||||
|           skipped = 0 | ||||
|           # file.skip only accepts values less or equal to 1024 (buffer size, undocumented) | ||||
|           until skipped + 1024 > startb | ||||
|             file.skip(1024) | ||||
|             skipped += 1024 | ||||
|           end | ||||
|           if skipped - startb > 0 | ||||
|             file.skip(skipped - startb) | ||||
|           end | ||||
|         else | ||||
|           file.skip(startb) | ||||
|         end | ||||
| 
 | ||||
|         IO.copy(file, env.response, endb - startb) | ||||
|       else | ||||
|         env.response.content_length = fileb | ||||
|         env.response.status_code = 200 # Range not satisfable, see 4.4 Note | ||||
|         IO.copy(file, env.response) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def etag(context, file_path) | ||||
|       etag = %{W/"#{File.lstat(file_path).mtime.epoch.to_s}"} | ||||
|       context.response.headers["ETag"] = etag | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue