# 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 =~ %r!^\/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
