Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/http/server.rb
Views: 11704
# -*- coding: binary -*-1require 'rex/socket'234module Rex5module Proto6module Http78###9#10# Acts as an HTTP server, processing requests and dispatching them to11# registered procs. Some of this server was modeled after webrick.12#13###14class Server1516include Proto1718#19# A hash that associated a file extension with a mime type for use as the20# content type of responses.21#22ExtensionMimeTypes =23{24"rhtml" => "text/html",25"html" => "text/html",26"htm" => "text/htm",27"jpg" => "image/jpeg",28"jpeg" => "image/jpeg",29"gif" => "image/gif",30"png" => "image/png",31"bmp" => "image/bmp",32"txt" => "text/plain",33"css" => "text/css",34"ico" => "image/x-icon",35}3637#38# The default server name that will be returned in the Server attribute of39# a response.40#41DefaultServer = "Rex"4243#44# Initializes an HTTP server as listening on the provided port and45# hostname.46#47def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {},48comm = nil, ssl_cert = nil, ssl_compression = false,49ssl_cipher = nil, ssl_version = nil)50self.listen_host = listen_host51self.listen_port = port52self.ssl = ssl53self.context = context54self.comm = comm55self.ssl_cert = ssl_cert56self.ssl_compression = ssl_compression57self.ssl_cipher = ssl_cipher58self.ssl_version = ssl_version59self.listener = nil60self.resources = {}61self.server_name = DefaultServer62end6364# More readable inspect that only shows the url and resources65# @return [String]66def inspect67resources_str = resources.keys.map{|r| r.inspect }.join ", "6869"#<#{self.class} http#{ssl ? "s" : ""}://#{listen_host}:#{listen_port} [ #{resources_str} ]>"70end7172#73# Returns the hardcore alias for the HTTP service74#75def self.hardcore_alias(*args)76"#{(args[0] || '')}-#{(args[1] || '')}-#{args[4] || ''}"77end7879#80# HTTP server.81#82def alias83super || "HTTP Server"84end8586#87# Listens on the defined port and host and starts monitoring for clients.88#89def start9091self.listener = Rex::Socket::TcpServer.create(92'LocalHost' => self.listen_host,93'LocalPort' => self.listen_port,94'Context' => self.context,95'SSL' => self.ssl,96'SSLCert' => self.ssl_cert,97'SSLCompression' => self.ssl_compression,98'SSLCipher' => self.ssl_cipher,99'SSLVersion' => self.ssl_version,100'Comm' => self.comm101)102103# Register callbacks104self.listener.on_client_connect_proc = Proc.new { |cli|105on_client_connect(cli)106}107self.listener.on_client_data_proc = Proc.new { |cli|108on_client_data(cli)109}110111self.listener.start112end113114#115# Terminates the monitor thread and turns off the listener.116#117def stop118self.listener.stop119self.listener.close120end121122123#124# Waits for the HTTP service to terminate125#126def wait127self.listener.wait if self.listener128end129130#131# Closes the supplied client, if valid.132#133def close_client(cli)134listener.close_client(cli)135end136137#138# Mounts a directory or resource as being serviced by the supplied handler.139#140def mount(root, handler, long_call = false, *args)141resources[root] = [ handler, long_call, args ]142end143144#145# Remove the mount point.146#147def unmount(root)148resources.delete(root)149end150151#152# Adds a resource handler, such as one for /, which will be called whenever153# the resource is requested. The ``opts'' parameter can have any of the154# following:155#156# Proc (proc) - The procedure to call when a request comes in for this resource.157# LongCall (bool) - Hints to the server that this resource may have long158# request processing times.159#160def add_resource(name, opts)161if (resources[name])162raise RuntimeError,163"The supplied resource '#{name}' is already added.", caller164end165166# If a procedure was passed, mount the resource with it.167if (opts['Proc'])168mount(name, Handler::Proc, false, opts['Proc'], opts['VirtualDirectory'])169else170raise ArgumentError, "You must specify a procedure."171end172end173174#175# Removes the supplied resource handler.176#177def remove_resource(name)178self.resources.delete(name)179end180181#182# Adds Server headers and stuff.183#184def add_response_headers(resp)185resp['Server'] = self.server_name if not resp['Server']186end187188#189# Returns the mime type associated with the supplied file. Right now the190# set of mime types is fairly limited.191#192def mime_type(file)193type = nil194195if (file =~ /\.(.+?)$/)196type = ExtensionMimeTypes[$1.downcase]197end198199type || "text/plain"200end201202#203# Sends a 404 error to the client for a given request.204#205def send_e404(cli, request)206resp = Response::E404.new207208resp['Content-Type'] = 'text/html'209210resp.body =211"<html><head>" +212"<title>404 Not Found</title>" +213"</head><body>" +214"<h1>Not found</h1>" +215"The requested URL #{html_escape(request.resource)} was not found on this server.<p><hr>" +216"</body></html>"217218# Send the response to the client like what219cli.send_response(resp)220end221222attr_accessor :listen_port, :listen_host, :server_name, :context, :comm223attr_accessor :ssl, :ssl_cert, :ssl_compression, :ssl_cipher, :ssl_version224attr_accessor :listener, :resources225226protected227228#229# Extends new clients with the ServerClient module and initializes them.230#231def on_client_connect(cli)232cli.extend(ServerClient)233234cli.init_cli(self)235end236237#238# Processes data coming in from a client.239#240def on_client_data(cli)241begin242data = cli.read(65535)243244raise ::EOFError if not data245raise ::EOFError if data.empty?246247case cli.request.parse(data)248when Packet::ParseCode::Completed249dispatch_request(cli, cli.request)250cli.reset_cli251252when Packet::ParseCode::Partial253# Return and wait for the on_client_data handler to be called again254# The Request object tracks the state of the request for us255return256257when Packet::ParseCode::Error258close_client(cli)259end260rescue EOFError261if (cli.request.completed?)262dispatch_request(cli, cli.request)263264cli.reset_cli265end266267close_client(cli)268end269end270271#272# Dispatches the supplied request for a given connection.273#274def dispatch_request(cli, request)275# Is the client requesting keep-alive?276if ((request['Connection']) and277(request['Connection'].downcase == 'Keep-Alive'.downcase))278cli.keepalive = true279end280281# Search for the resource handler for the requested URL. This is pretty282# inefficient right now, but we can spruce it up later.283p = nil284len = 0285root = nil286287resources.each_pair { |k, val|288if (request.resource =~ /^#{k}/ and k.length > len)289p = val290len = k.length291root = k292end293}294295if (p)296# Create an instance of the handler for this resource297handler = p[0].new(self, *p[2])298299# If the handler class requires a relative resource...300if (handler.relative_resource_required?)301# Substituted the mount point root in the request to make things302# relative to the mount point.303request.relative_resource = request.resource.gsub(/^#{root}/, '')304request.relative_resource = '/' + request.relative_resource if (request.relative_resource !~ /^\//)305end306307308# If we found the resource handler for this resource, call its309# procedure.310if (p[1] == true)311Rex::ThreadFactory.spawn("HTTPServerRequestHandler", false) {312handler.on_request(cli, request)313}314else315handler.on_request(cli, request)316end317else318elog("Failed to find handler for resource: #{request.resource}", LogSource)319320send_e404(cli, request)321end322323# If keep-alive isn't enabled for this client, close the connection324if (cli.keepalive == false)325close_client(cli)326end327end328329end330331end332end333end334335336337