Class: FileServer

Inherits:
EventParsers::Http11Parser::Request show all
Defined in:
bin/httphere

Class Attribute Summary collapse

Attributes inherited from EventParsers::Http11Parser::Request

#connection, #entity, #headers, #http_version, #method, #query_params, #resource_uri, #response

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from EventParsers::Http11Parser::Request

#basic_auth, #has_entity?, #initialize, #inspect, #params, #ready!, #ready?

Constructor Details

This class inherits a constructor from EventParsers::Http11Parser::Request

Class Attribute Details

.cacheObject

Returns the value of attribute cache.



580
581
582
# File 'bin/httphere', line 580

def cache
  @cache
end

Class Method Details

.cache!(filename, content_type, response_body) ⇒ Object



598
599
600
601
602
603
604
605
606
607
608
609
610
611
# File 'bin/httphere', line 598

def cache!(filename,content_type,response_body)
  if cache?(filename,response_body)
    puts "Caching #{filename} / #{response_body.size}"
    cache[filename] = [
      {
        :last_accessed_at => Time.now,
        :mtime => File.mtime(filename),
        :size => response_body.size,
        :content_type => content_type
      },
      response_body
    ]
  end
end

.cache?(filename, response_body) ⇒ Boolean

Returns:

  • (Boolean)


594
595
596
597
# File 'bin/httphere', line 594

def cache?(filename,response_body)
  # Obviously, don't cache if it's bigger than the cache max size
  cache && File.exists?(filename) && response_body.size < cache.max_size
end

.cached?(filename) ⇒ Boolean

Returns:

  • (Boolean)


581
582
583
584
585
586
587
588
# File 'bin/httphere', line 581

def cached?(filename)
  # Serve from cache if it's in the cache and if the file hasn't changed.
  if cache
    # Delete from cache if the file's been modified since last cache.
    cache.delete(filename) if cache.has_key?(filename) && File.mtime(filename) > cache[filename][0][:mtime]
    cache.has_key?(filename)
  end
end

.from_cache(filename) ⇒ Object



589
590
591
592
593
# File 'bin/httphere', line 589

def from_cache(filename)
  cache[filename][0][:last_accessed_at] = Time.now
  puts "From cache: #{filename} / #{cache[filename][0][:size]}"
  [cache[filename][1], cache[filename][0][:content_type]]
end

Instance Method Details

#processObject

This is where the routing is processed.



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
# File 'bin/httphere', line 615

def process
  # Get the filename desired
  filename, query = resource_uri.split('?',2)
  @filename = filename.sub(/^\//,'')
  # Default to any file named index.*
  @filename = Dir["index.*"].first if @filename.to_s == '' && Dir["index.*"].length > 0
  file_extension = (filename.match(/\.([^\.]+)$/) || [])[1]

  if File.exists?(@filename) && !File.directory?(@filename)

    # Read from cache if possible
    file_handle = FileServer.cached?(@filename) ? :from_cache : :from_disk
    if file_handle == :from_disk
      if $DEBUG
        Benchmark.bm do |x|
          x.report("Getting MIME type") do
            @content_type = MIME.check(@filename).type
          end
        end
      else
        @content_type = MIME.check(@filename).type
      end

      file_handle = File.open(@filename)

      # If .markdown, render as Markdown
      if file_extension == 'markdown'
        file_handle = Renderers::Markdown.render_content(file_handle.read)
        @content_type = 'text/html'
      end

      # If .textile, render as Textile
      if file_extension == 'textile'
        file_handle = Renderers::Textile.render_content(file_handle.read)
        @content_type = 'text/html'
      end
    end

    # Send Response
    respond!('200 Ok', @content_type, file_handle)
  else
    respond!('404 Not Found', 'text/plain', "Could not find file: '#{resource_uri}'")
  end
end

#respond(status, content_type, body) ⇒ Object



664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
# File 'bin/httphere', line 664

def respond(status, content_type, body)
  if body == :from_cache
    body, content_type = FileServer.from_cache(@filename)
  else
    body = StringIO.new(body.to_s) if !body.is_a?(IO) && body.respond_to?(:to_s)
    
    # Convert to UTF-8 if possible
    chardet = UniversalDetector::chardet(body.read(512)) rescue {'confidence' => 0.8, 'encoding' => 'utf-8'}; body.rewind # Detect charset only from the first 512 bytes
    if chardet['confidence'] > 0.7 && !['utf-8', 'ascii'].include?(chardet['encoding'])
      if $DEBUG
        Benchmark.bm do |x|
          x.report("Converting from #{chardet['encoding']} to UTF-8") do
            charset = chardet['encoding']
            body = StringIO.new(Iconv.conv('utf-8', charset, body.read))
          end
        end
      else
        charset = chardet['encoding']
        body = StringIO.new(Iconv.conv('utf-8', charset, body.read))
      end
    else # no conversion
      puts "No charset conversion necessary." if $DEBUG
    end

    # Write to cache if we should
    FileServer.cache!(@filename, content_type, body)
  end

  body_length = body.size
  body.rewind

  # Send the response!
  connection.send_response! "HTTP/1.1 #{status}\r\nServer: HTTP-Here, version #{$VERSION}\r\nContent-Type: #{content_type}\r\nContent-Length: #{body_length+2}\r\n\r\n#{body.read}\r\n"
  span = (Time.now - connection.time).to_f
  puts (status =~ /200/ ?
    "Served #{resource_uri} (#{content_type})" :
    "404 #{resource_uri}"
  ) + " at #{1 / span} requests/second" unless status =~ /404/ && resource_uri == '/favicon.ico'
end

#respond!(status, content_type, body) ⇒ Object



660
661
662
663
# File 'bin/httphere', line 660

def respond!(status, content_type, body)
  respond(status, content_type, body)
  connection.halt!
end