summaryrefslogtreecommitdiffstats
path: root/tests/httpd
blob: aa2e51d60f3a579e002724dd62dc59df05ed3661 (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#
# The httpd_ procedures implement a stub http server.
#
# Copyright (c) 1997-1998 Sun Microsystems, Inc.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# SCCS: @(#) httpd 1.2 98/02/20 14:51:59

#set httpLog 1

proc httpd_init {{port 8015}} {
    socket -server httpdAccept $port
}
proc httpd_log {args} {
    global httpLog
    if {[info exists httpLog] && $httpLog} {
	puts stderr "httpd: [join $args { }]"
    }
}
array set httpdErrors {
    204 {No Content}
    400 {Bad Request}
    404 {Not Found}
    503 {Service Unavailable}
    504 {Service Temporarily Unavailable}
    }

proc httpdError {sock code args} {
    global httpdErrors
    puts $sock "$code $httpdErrors($code)"
    httpd_log "error: [join $args { }]"
}
proc httpdAccept {newsock ipaddr port} {
    global httpd
    upvar #0 httpd$newsock data

    fconfigure $newsock -blocking 0 -translation {auto crlf}
    httpd_log $newsock Connect $ipaddr $port
    set data(ipaddr) $ipaddr
    fileevent $newsock readable [list httpdRead $newsock]
}

# read data from a client request

proc httpdRead { sock } {
    upvar #0 httpd$sock data

    if {![info exists data(state)]} {
	set readCount [gets $sock line]
	if [regexp {(POST|GET|HEAD) ([^?]+)\??([^ ]*) HTTP/1.0} \
		$line x data(proto) data(url) data(query)] {
	    set data(state) mime
	    httpd_log $sock Query $line
	} else {
	    httpdError $sock 400
	    httpd_log $sock Error "bad first line:$line"
	    httpdSockDone $sock
	}
	return
    } elseif {$data(state) == "mime"} {
	set readCount [gets $sock line]
	if {[regexp {Content-Length: (\d+)} $line match length]} {
	    set data(length) $length
	}
    } elseif {$data(state) == "query"} {
	if {![info exist data(length_orig)]} {
	    set data(length_orig) $data(length)
	}
	set line [read $sock $data(length)]
	set readCount [string length $line]
	incr data(length) -$readCount
    }

    # string compare $readCount 0 maps -1 to -1, 0 to 0, and > 0 to 1

    set state [string compare $readCount 0],$data(state),$data(proto)
    httpd_log $sock $state
    switch -- $state {
	-1,mime,HEAD	-
	-1,mime,GET	-
	-1,mime,POST	{
	    # gets would block
	    return
	}
	0,mime,HEAD	-
	0,mime,GET	-
	0,query,POST	{ httpdRespond $sock }
	0,mime,POST	{ set data(state) query }
	1,mime,HEAD	-
	1,mime,POST	-
	1,mime,GET	{
	    if {[regexp {([^:]+):[ 	]*(.*)}  $line dummy key value]} {
		set data(mime,[string tolower $key]) $value
	    }
	}
	-1,query,POST	{
	    httpd_log $sock Error "unexpected eof on <$data(url)> request"
	    httpdError $sock 404
	    httpdSockDone $sock
	}
	1,query,POST	{
	    append data(query) $line
	    if {$data(length) <= 0} {
		set data(length) $data(length_orig)
		httpdRespond $sock
	    }
	}
	default {
	    if [eof $sock] {
		httpd_log $sock Error "unexpected eof on <$data(url)> request"
	    } else {
		httpd_log $sock Error "unhandled state <$state> fetching <$data(url)>"
	    }
	    httpdError $sock 404
	    httpdSockDone $sock
	}
    }
}
proc httpdSockDone { sock } {
upvar #0 httpd$sock data
    unset data
    close $sock
}

# Respond to the query.

proc httpdRespond { sock } {
    global httpd bindata port
    upvar #0 httpd$sock data

    switch -glob -- $data(url) {
	*binary* {
	    set html "$bindata[info hostname]:$port$data(url)"
	    set type application/octet-stream
	}
	*post* {
	    set html "Got [string length $data(query)] bytes"
	    set type text/plain
	}
	default {
	    set type text/html

	    set html "<html><head><title>HTTP/1.0 TEST</title></head><body>
<h1>Hello, World!</h1>
<h2>$data(proto) $data(url)</h2>
"
	    if {[info exists data(query)] && [string length $data(query)]} {
		append html "<h2>Query</h2>\n<dl>\n"
		foreach {key value} [split $data(query) &=] {
		    append html "<dt>$key<dd>$value\n"
		}
		append html </dl>\n
	    }
	    append html </body></html>
	}
    }

    if {$data(proto) == "HEAD"} {
	puts $sock "HTTP/1.0 200 OK"
    } else {
	puts $sock "HTTP/1.0 200 Data follows"
    }
    puts $sock "Date: [clock format [clock clicks]]"
    puts $sock "Content-Type: $type"
    puts $sock "Content-Length: [string length $html]"
    puts $sock ""
    if {$data(proto) != "HEAD"} {
	fconfigure $sock -translation binary
	puts -nonewline $sock $html
    }
    httpd_log $sock Done ""
    httpdSockDone $sock
}