summaryrefslogtreecommitdiff
path: root/http-request_target.tcl
blob: ca567f9f37df37cb61abafb53109de2fd9ff8428 (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
namespace eval request_target {

    # get     channel first_line
    # Get request_target from the first line
    #
    # file    request_target
    # File portion of request_target
    #
    # query   request_target
    # Decoded query portion of request_target
    #
    # encode  query
    # Encode a query
    #
    # decode  query
    # Decode a query

    ## Gets the request_target from the first client line.
    proc get {channel first_line} {
		
		## Reject if first line is not a properly formatted GET request.
		##             ^GET RWS  -  HTTP/(Versions)      $ 
		if { ![regexp {^(GET ){1}.* HTTP/(0.9|1.0|1.1){1}$} $first_line] } {
			::http::respond $channel 400
		}
		## Return between first and last space.
		set request_target [string trim \
								[string range $first_line \
									 [string first " " $first_line] \
									 [string last  " " $first_line]]]
		puts "|$request_target|"

		## If there are two periods in a request target (trying to access parent files), reject.
		if [regexp {\.\./} $request_target] {
			respond 400
		}

		if [regexp {^/.*$} $request_target] {
			puts "origin-form"
			return $request_target
		}
		
		## Turn absolute-form to origin-form
		if [regexp {^http(s)?://[[:alpha:].]+/[[:graph:]*]} $request_target] {
			set request_target \
				[string range $request_target \
					 [expr [string first / [string range $request_target 8 end]] + 8] \
					 end]
			return $request_target
		}

		puts "Neither origin-form or absolute-form"
		respond 400;
    }


    #TODO: Will need to improve these functions later.

    ## Take the file part of the request target.
    proc file {request_target} {
		set t [string first ? $request_target]
		if {$t == -1} {
			return [string range $request_target 1 end]
		}
		else {
			return [string range $request_target 1 [expr $t - 1]]
		}
    }
    
    ## Process request_target into a query string.
    proc query {request_target} {
		set t [string first ? $request_target]
		if {$t == -1} {
			return {}
		}
		else {
			return [decode [string map {& { } = { }} \
								[string range $request_target \
									 [expr $t + 1] end]]]
		}
    }
    
    ## Create dictionaries for coding and decoding query strings.
    variable character2code {};
    variable code2character {};
    for {set i 0} {$i <= 255} {incr i 1} {
		set ch [format %c $i];
		if {[expr !( [string is alpha $ch] || [string is digit $ch] )]} {
			set co "\%[format %02X $i]";
			dict append character2code $ch $co
			dict append code2character $co $ch
		}
    }
    
    proc encode {query} {
		variable character2code
		for {set i 0} {$i < [llength $query]} {incr i 1} {
			lset query $i \
				[string map -nocase $character2code [lindex $query $i]]
		}
		set query [string map -nocase {{ } {%20}} $query]
		return $query
    }

    proc decode {query} {
		variable code2character
		for {set i 0} {$i < [llength $query]} {incr i 1} {
			lset query $i \
				[string map -nocase $code2character [lindex $query $i]]
		}
		set query [string map -nocase {{%20} { }} $query]
		return $query
    }

}