summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/jekyll-4.4.1/lib/jekyll/commands/serve/websockets.rb
blob: 6aa522cc96b3bcb7722221278534caacfe1c7a31 (plain)
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
# frozen_string_literal: true

require "http/parser"

module Jekyll
  module Commands
    class Serve
      # The LiveReload protocol requires the server to serve livereload.js over HTTP
      # despite the fact that the protocol itself uses WebSockets.  This custom connection
      # class addresses the dual protocols that the server needs to understand.
      class HttpAwareConnection < EventMachine::WebSocket::Connection
        attr_reader :reload_body, :reload_size

        def initialize(_opts)
          # If EventMachine SSL support on Windows ever gets better, the code below will
          # set up the reactor to handle SSL
          #
          # @ssl_enabled = opts["ssl_cert"] && opts["ssl_key"]
          # if @ssl_enabled
          #   em_opts[:tls_options] = {
          #   :private_key_file => Jekyll.sanitized_path(opts["source"], opts["ssl_key"]),
          #   :cert_chain_file  => Jekyll.sanitized_path(opts["source"], opts["ssl_cert"])
          #   }
          #   em_opts[:secure] = true
          # end

          # This is too noisy even for --verbose, but uncomment if you need it for
          # a specific WebSockets issue.  Adding ?LR-verbose=true onto the URL will
          # enable logging on the client side.
          # em_opts[:debug] = true

          em_opts = {}
          super(em_opts)

          reload_file = File.join(Serve.singleton_class::LIVERELOAD_DIR, "livereload.js")

          @reload_body = File.read(reload_file)
          @reload_size = @reload_body.bytesize
        end

        # rubocop:disable Metrics/MethodLength
        def dispatch(data)
          parser = Http::Parser.new
          parser << data

          # WebSockets requests will have a Connection: Upgrade header
          if parser.http_method != "GET" || parser.upgrade?
            super
          elsif parser.request_url.start_with?("/livereload.js")
            headers = [
              "HTTP/1.1 200 OK",
              "Content-Type: application/javascript",
              "Content-Length: #{reload_size}",
              "",
              "",
            ].join("\r\n")
            send_data(headers)

            # stream_file_data would free us from keeping livereload.js in memory
            # but JRuby blocks on that call and never returns
            send_data(reload_body)
            close_connection_after_writing
          else
            body = "This port only serves livereload.js over HTTP.\n"
            headers = [
              "HTTP/1.1 400 Bad Request",
              "Content-Type: text/plain",
              "Content-Length: #{body.bytesize}",
              "",
              "",
            ].join("\r\n")
            send_data(headers)
            send_data(body)
            close_connection_after_writing
          end
        end
        # rubocop:enable Metrics/MethodLength
      end
    end
  end
end