summaryrefslogtreecommitdiffstats
path: root/tests/httpd
blob: ddfa6bf516fb3952d430d820b101299604588ade (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
#
# 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

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

    set readCount [gets $sock line]
    if {![info exists data(state)]} {
	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
    }
    # Extra check to handle -1,query,POST case, where we may see eof,
    # although the data is there, just without a final newline.  A proper
    # server would handle this better.
    if {[regexp {Content-Length: (\d+)} $line match length]} {
	set data(length) $length
    }

    # 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	{
	    if {[info exists data(length)]} {
		append data(query) [read $sock $data(length)]
		httpdRespond $sock
		return
	    }
	    httpd_log $sock Error "unexpected eof on <$data(url)> request"
	    httpdError $sock 404
	    httpdSockDone $sock
	}
	1,query,POST	{
	    append data(query) $line
	    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

    if {[string match *binary* $data(url)]} {
	set html "$bindata[info hostname]:$port$data(url)"
	set type application/octet-stream
    } else {
	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
}