Utility class to parse a request string and build a HttpRequest

The main method is parse_http_request.

Introduced properties

fun parse_http_request(full_request: String): nullable HttpRequest

nitcorn :: HttpRequestParser :: parse_http_request

Parse the first_line, header_fields and body of full_request.

Redefined properties

redef type SELF: HttpRequestParser

nitcorn $ HttpRequestParser :: SELF

Type of this instance, automatically specialized in every class
redef fun parse_http_request(text: String): nullable HttpRequest

nitcorn :: sessions $ HttpRequestParser :: parse_http_request

Parse the first_line, header_fields and body of full_request.

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun parse_http_request(full_request: String): nullable HttpRequest

nitcorn :: HttpRequestParser :: parse_http_request

Parse the first_line, header_fields and body of full_request.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
package_diagram nitcorn::HttpRequestParser HttpRequestParser core::Object Object nitcorn::HttpRequestParser->core::Object

Parents

interface Object

core :: Object

The root of the class hierarchy.

Class definitions

nitcorn $ HttpRequestParser
# Utility class to parse a request string and build a `HttpRequest`
#
# The main method is `parse_http_request`.
class HttpRequestParser
	# The current `HttpRequest` under construction
	private var http_request: HttpRequest is noinit

	# Untreated body
	private var body = ""

	# Lines of the header
	private var header_fields = new Array[String]

	# Words of the first line
	private var first_line = new Array[String]

	# Parse the `first_line`, `header_fields` and `body` of `full_request`.
	fun parse_http_request(full_request: String): nullable HttpRequest
	do
		clear_data

		var http_request = new HttpRequest
		self.http_request = http_request

		segment_http_request(full_request)

		# Parse first line, looks like "GET dir/index.html?user=xymus HTTP/1.0"
		if first_line.length < 3 then
			print "HTTP error: request first line apprears invalid: {first_line}"
			return null
		end
		http_request.method = first_line[0]
		http_request.url = first_line[1]
		http_request.http_version = first_line[2]

		# GET args
		if http_request.url.has('?') then
			http_request.uri = first_line[1].substring(0, first_line[1].index_of('?'))
			http_request.query_string = first_line[1].substring_from(first_line[1].index_of('?')+1)

			var parse_url = parse_url
			http_request.get_args = parse_url
			http_request.all_args.add_all parse_url
		else
			http_request.uri = first_line[1]
		end

		# POST args
		if http_request.method == "POST" or http_request.method == "PUT" then
			http_request.body = body
			var lines = body.split_with('&')
			for line in lines do if not line.trim.is_empty then
				var parts = line.split_once_on('=')
				if parts.length > 1 then
					var decoded = parts[1].replace('+', " ").from_percent_encoding
					http_request.post_args[parts[0]] = decoded
					http_request.all_args[parts[0]] = decoded
				end
			end
		end

		# Headers
		for i in header_fields do
			var temp_field = i.split_with(": ")

			if temp_field.length == 2 then
				http_request.header[temp_field[0]] = temp_field[1]
			end
		end

		# Cookies
		if http_request.header.keys.has("Cookie") then
			var cookie = http_request.header["Cookie"]
			for couple in cookie.split_with(';') do
				var words = couple.trim.split_with('=')
				if words.length != 2 then continue
				http_request.cookie[words[0]] = words[1]
			end
		end

		return http_request
	end

	private fun clear_data
	do
		first_line.clear
		header_fields.clear
	end

	private fun segment_http_request(http_request: String): Bool
	do
		var header_end = http_request.search("\r\n\r\n")

		if header_end == null then
			header_fields = http_request.split_with("\r\n")
		else
			header_fields = http_request.substring(0, header_end.from).split_with("\r\n")
			body = http_request.substring(header_end.after, http_request.length-1)
		end

		# If a line of the http_request is long it may change line, it has " " at the
		# end to indicate this. This section turns them into 1 line.
		if header_fields.length > 1 and header_fields[0].has_suffix(" ") then
			var temp_req = header_fields[0].substring(0, header_fields[0].length-1) + header_fields[1]

			first_line  = temp_req.split_with(' ')
			header_fields.shift
			header_fields.shift

			if first_line.length != 3 then return false
		else
			first_line = header_fields[0].split_with(' ')
			header_fields.shift

			if first_line.length != 3 then return false
		end

		# Cut off the header in lines
		var pos = 0
		while pos < header_fields.length do
			if pos < header_fields.length-1 and header_fields[pos].has_suffix(" ") then
				header_fields[pos] = header_fields[pos].substring(0, header_fields[pos].length-1) + header_fields[pos+1]
				header_fields.remove_at(pos+1)
				pos = pos-1
			end
			pos = pos+1
		end

		return true
	end

	# Extract args from the URL
	private fun parse_url: HashMap[String, String]
	do
		var query_strings = new HashMap[String, String]

		if http_request.url.has('?') then
			var get_args = http_request.query_string.split_with("&")
			for param in get_args do
				var key_value = param.split_with("=")
				if key_value.length < 2 then continue

				var key = key_value[0].from_percent_encoding
				var value = key_value[1].from_percent_encoding
				query_strings[key] = value
			end
		end

		return query_strings
	end
end
lib/nitcorn/http_request.nit:101,1--251,3

nitcorn :: sessions $ HttpRequestParser
redef class HttpRequestParser
	redef fun parse_http_request(text)
	do
		var request = super
		if request != null then
			if request.cookie.keys.has("nitcorn_session") then
				var id_hash = request.cookie["nitcorn_session"]

				if sys.sessions.keys.has(id_hash) then
					# Restore the session
					request.session = sys.sessions[id_hash]
				end
			end
		end
		return request
	end
end
lib/nitcorn/sessions.nit:78,1--94,3