teehee :3

This commit is contained in:
Hannah-Dagemark 2024-06-01 18:51:48 +02:00
commit 0f893c1d83
27 changed files with 3905 additions and 68 deletions

View file

@ -1,13 +1,18 @@
# Request class used as instance object of an incoming TCP request to manage that request. Splits the request into it's method, resource, version, headers and params to be read by the router.
class Request
attr_reader :method, :resource, :version, :headers, :params
# @param input [String] the string of the request
def initialize(input)
variable_definer(input)
end
private
def variable_definer(input) ##Defines required class variables by splitting up the input text
# Defines required class variables by splitting up the input text
#
# @param input [String] the string of the request
def variable_definer(input)
rows = input.split(/\r\n/)
@method, @resource, @version = rows[0].split(' ')
@headers, @params = {}, {}
@ -25,6 +30,10 @@ class Request
end
end
# Reorganizes the information in the header based on the format
#
# @param rows [Array] A list of the rows in the header
# @param limit [Int] The limit where the header ends.
def header_constructor(rows, limit)
for row in rows[1..limit-1]
context, information = row.split(' ')
@ -33,6 +42,9 @@ class Request
end
end
# Organizes the parameter information into a dict of parameters
#
# @param rows [Array] A list of the rows of parameters
def param_constructor(rows)
for row in rows[rows.find_index("")+1..rows.length]
information = row.split(/&/)
@ -43,6 +55,7 @@ class Request
end
end
# Organizes the resource information into a dict of resource parameters
def resource_deconstructor
information = @resource.split('?')[1].split(/&/)
for info in information

View file

@ -0,0 +1,73 @@
# Reponse class used to format and respond to a request. Takes in the touple returned by the route class and returns either the requested method or the file in the requested position (if the match returned a 404 and a file is found in the requested position)
class Response
# Initialize the response by checking if a response is applicable and what information should be returned
#
# @param routeReturn [Array] A list consisting of the matched request and the status
def initialize(routeReturn)
outputContent = {}
status = routeReturn[1]
if status == 200
if routeReturn[0][:block] != nil
outputContent[:info] = routeReturn[0][:block].call
print("Recieved HTML, enabling as called:\n #{outputContent[:info]}")
outputContent[:type] = "text/html"
status = 200
else
outputContent[:info] = nil
status = 200
end
elsif status == 404
print("HTML route not found, searching for target file...\n")
if File.file?("./public/#{routeReturn[0].resource}")
print("Found file: ./public/#{routeReturn[0].resource}\n")
outputContent[:info] = File.open("./public#{routeReturn[0].resource}", "rb")
if outputContent[:type].to_s.split("/")[0] == "text"
print("Successfully opened file \"./public#{routeReturn[0].resource}\" with contents:\n#{outputContent[:info].read}\n ")
end
outputContent[:type] = getMime(File.extname("./public/#{routeReturn[0].resource}").to_str)
status = 200
else
print("Could not find file: \"./public/#{routeReturn[0].resource}\"\n")
status = 404
end
end
@status = status
@outputContent = outputContent
end
# Responds to the request with the information requested
#
# @param session [TCPsocket] A client request through the TCP port awaiting a response
def print(session)
print("\nStatus: #{@status.to_s}\n")
session.print "HTTP/1.1 #{@status}\r\n"
if @status != 404
session.print "Content-Type: #{@outputContent[:type]}\r\n"
print("\nReading content type: #{@outputContent[:type]}\n")
print("\nLoaded is of type: #{@outputContent[:info].class}\n")
print("\nSize reading: #{File.size(@outputContent[:info])}\n")
session.print "Content-Length: #{File.size(@outputContent[:info])}\r\n"
session.print "\r\n"
printContent = @outputContent[:info].read
if printContent; session.print printContent end
end
session.close
end
# Gets the mime extension for respond file
#
# @param extension [Str] a string containing the extension of the file to find the MIME format for
# @return [String] the MIME format of the requested extension.
def getMime(extension)
mimes = { ".css" => "text/css", ".html" => "text/html", ".ico" => "image/vnd.microsoft.icon", ".jpg" => "image/jpeg", ".png" => "image/png"}
if mimes[extension] != nil
print("Found #{mimes[extension]} result for extension #{extension}\n")
return mimes[extension]
else
print("Found no result for extension #{extension}\n")
return nil
end
end
end

View file

@ -1,19 +1,27 @@
require 'debug'
# Router class for pairing incoming requests with routes
class Router
attr_reader :routes
def initialize()
@routes = []
end
# Method for adding a route to the router. Incoming requests will be checked against these routes to see if the incoming requests has a valid target.
#
# @param method [String] A string with the method for the request
# @param input [String] A string for the input to match the request with
# @param &block [Block] The block to execute and respond with if the route is matched.
def add_route(method, input, &block)
routeHash = {method: method, resource: input, block: block}
@routes << routeHash
p "added successfully, @routes are: #{@routes}"
end
# Method for matching the incoming requests with the expected routes. Returns a touple with either the matched object and an int 200 representing status or the request and an int 404 representing status
#
# @param request [Request] the Request class to find a match for
# @return [Array] the array consisting of the matched method or the request and the status of the match.
def match_route(request)
#spec ["3" "bostongurka"]
match = @routes.find {|route| (route[:method] == request.method) && (request.resource.match?(route[:resource]))}
@ -22,9 +30,9 @@ class Router
returner = [match, 200]
return returner
else
if request.resource.match?(@routes[0][:resource]) != true
if request.resource.match?(@routes[0][:resource]) != true #If the requested resource did not match the expected resource for that method
p "failed to find: #{request.resource} resource.\nExpected: #{String(@routes[0][:resource])} resource"
elsif @routes[0][:method] != request.method
elsif @routes[0][:method] != request.method #If the current method did not match the expected method for that resource
p "failed to find #{request.method} method.\nExpected: #{@routes[0][:method]} method"
else
p "Failed to match, got no reason, just kinda didn't feel like it"

View file

@ -1,14 +1,21 @@
require 'socket'
require_relative 'request'
require_relative 'route'
require_relative 'response'
# Main class for managing and talking between sub-classes during request-response cycles. Utalizes the request, route and response classes.
class HTTPServer
# Initializes the HTTPServer object
#
# @param port [Int] An integer representing the port number for the http server
# @param router [Router] The Router class holding the expected routes for the server
def initialize(port, router)
@port = port
@router = router
end
# Starts the TCP server and begins reading for requests on the designated port. When a request comes in it turns it into a request object using the request class, and sends that to the router. It then sends the matched information to the response class.
def start
server = TCPServer.new(@port)
puts "Listening on #{@port}"
@ -27,63 +34,9 @@ class HTTPServer
pp request
routeReturn = @router.match_route(request)
# pil nedåt ska in i response-klassen
response = Response.new(routeReturn, session)
response.print()
outputContent = {}
if routeReturn[1] == 200
if routeReturn[0][:block] != nil
outputContent[:info] = routeReturn[0][:block].call
print("Recieved HTML, enabling as called:\n #{outputContent[:info]}")
outputContent[:type] = "text/html"
status = 200
else
outputContent[:info] = nil
status = 200
end
elsif routeReturn[1] == 404
print("HTML route not found, searching for target file...\n")
if File.file?("./public/#{routeReturn[0].resource}")
print("Found file: ./public/#{routeReturn[0].resource}\n")
outputContent[:info] = File.open("./public#{routeReturn[0].resource}", "rb")
if outputContent[:type].to_s.split("/")[0] == "text"
print("Successfully opened file \"./public#{routeReturn[0].resource}\" with contents:\n#{outputContent[:info].read}\n ")
end
outputContent[:type] = getMime(File.extname("./public/#{routeReturn[0].resource}").to_str)
status = 200
else
print("Could not find file: \"./public/#{routeReturn[0].resource}\"\n")
status = 404
end
end
# Nedanstående bör göras i er Response-klass
print("\nStatus: #{status.to_s}\n")
session.print "HTTP/1.1 #{status}\r\n"
if status != 404
session.print "Content-Type: #{outputContent[:type]}\r\n"
print("\nReading content type: #{outputContent[:type]}\n")
print("\nLoaded is of type: #{outputContent[:info].class}\n")
print("\nSize reading: #{File.size(outputContent[:info])}\n")
session.print "Content-Length: #{File.size(outputContent[:info])}\r\n"
session.print "\r\n"
printContent = outputContent[:info].read
if printContent; session.print printContent end
end
session.close
end
end
def getMime(extension)
mimes = { ".css" => "text/css", ".html" => "text/html", ".ico" => "image/vnd.microsoft.icon", ".jpg" => "image/jpeg", ".png" => "image/png"}
if mimes[extension] != nil
print("Found #{mimes[extension]} result for extension #{extension}\n")
return mimes[extension]
else
print("Found no result for extension #{extension}\n")
return nil
response = Response.new(routeReturn)
response.print(session)
end
end
end