Skip to content

Commit 450827e

Browse files
committed
Add proper handling for HEAD requests
1 parent d3143d3 commit 450827e

File tree

2 files changed

+160
-4
lines changed

2 files changed

+160
-4
lines changed

web.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ func (ctx *Context) Write(data []byte) (n int, err os.Error) {
4343
if !ctx.responseStarted {
4444
ctx.StartResponse(200)
4545
}
46+
47+
//if it's a HEAD request, we just write blank data
48+
if ctx.Request.Method == "HEAD" {
49+
data = []byte{}
50+
}
51+
4652
return ctx.conn.Write(data)
4753
}
4854
func (ctx *Context) WriteString(content string) {
@@ -247,7 +253,8 @@ func routeHandler(req *Request, c conn) {
247253
ctx.SetHeader("Server", "web.go", true)
248254

249255
for cr, route := range routes {
250-
if req.Method != route.method {
256+
//if the methods don't match, skip this handler (except HEAD can be used in place of GET)
257+
if req.Method != route.method && !(req.Method == "HEAD" && route.method == "GET") {
251258
continue
252259
}
253260

@@ -298,8 +305,10 @@ func routeHandler(req *Request, c conn) {
298305
sval, ok := ret[0].(*reflect.StringValue)
299306

300307
if ok && !ctx.responseStarted {
308+
outbytes := strings.Bytes(sval.Get())
309+
ctx.SetHeader("Content-Length", strconv.Itoa(len(outbytes)), true)
301310
ctx.StartResponse(200)
302-
ctx.WriteString(sval.Get())
311+
ctx.Write(outbytes)
303312
}
304313

305314
return

web_test.go

Lines changed: 149 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,14 @@ func buildTestResponse(buf *bytes.Buffer) *testResponse {
3939

4040
response := testResponse{headers: make(map[string][]string), cookies: make(map[string]string)}
4141
s := buf.String()
42+
4243
contents := strings.Split(s, "\r\n\r\n", 2)
4344

4445
header := contents[0]
45-
response.body = contents[1]
46+
47+
if len(contents) > 1 {
48+
response.body = contents[1]
49+
}
4650

4751
headers := strings.Split(header, "\r\n", 0)
4852

@@ -189,12 +193,58 @@ func buildTestRequest(method string, path string, body string, headers map[strin
189193
func TestRouting(t *testing.T) {
190194
for _, test := range (tests) {
191195
resp := getTestResponse(test.method, test.path, test.body, make(map[string]string))
196+
192197
if resp.statusCode != test.expectedStatus {
193198
t.Fatalf("expected status %d got %d", test.expectedStatus, resp.statusCode)
194199
}
195200
if resp.body != test.expectedBody {
196201
t.Fatalf("expected %q got %q", test.expectedBody, resp.body)
197202
}
203+
if cl, ok := resp.headers["Content-Length"]; ok {
204+
clExp, _ := strconv.Atoi(cl[0])
205+
clAct := len(strings.Bytes(resp.body))
206+
if clExp != clAct {
207+
t.Fatalf("Content-length doesn't match. expected %d got %d", clExp, clAct)
208+
}
209+
}
210+
}
211+
}
212+
213+
func TestHead(t *testing.T) {
214+
for _, test := range (tests) {
215+
216+
if test.method != "GET" {
217+
continue
218+
}
219+
getresp := getTestResponse("GET", test.path, test.body, make(map[string]string))
220+
headresp := getTestResponse("HEAD", test.path, test.body, make(map[string]string))
221+
222+
if getresp.statusCode != headresp.statusCode {
223+
t.Fatalf("head and get status differ. expected %d got %d", getresp.statusCode, headresp.statusCode)
224+
}
225+
if len(strings.Bytes(headresp.body)) != 0 {
226+
t.Fatalf("head request arrived with a body")
227+
}
228+
229+
var cl []string
230+
var getcl, headcl int
231+
var hascl1, hascl2 bool
232+
233+
if cl, hascl1 = getresp.headers["Content-Length"]; hascl1 {
234+
getcl, _ = strconv.Atoi(cl[0])
235+
}
236+
237+
if cl, hascl2 = headresp.headers["Content-Length"]; hascl2 {
238+
headcl, _ = strconv.Atoi(cl[0])
239+
}
240+
241+
if hascl1 != hascl2 {
242+
t.Fatalf("head and get: one has content-length, one doesn't")
243+
}
244+
245+
if hascl1 == true && getcl != headcl {
246+
t.Fatalf("head and get content-length differ")
247+
}
198248
}
199249
}
200250

@@ -269,6 +319,55 @@ func TestScgi(t *testing.T) {
269319
}
270320
}
271321

322+
func TestScgiHead(t *testing.T) {
323+
for _, test := range (tests) {
324+
325+
if test.method != "GET" {
326+
continue
327+
}
328+
329+
req := buildTestScgiRequest("GET", test.path, test.body, make(map[string]string))
330+
var output bytes.Buffer
331+
nb := tcpBuffer{input: req, output: &output}
332+
handleScgiRequest(&nb)
333+
getresp := buildTestResponse(&output)
334+
335+
req = buildTestScgiRequest("HEAD", test.path, test.body, make(map[string]string))
336+
var output2 bytes.Buffer
337+
nb = tcpBuffer{input: req, output: &output2}
338+
handleScgiRequest(&nb)
339+
headresp := buildTestResponse(&output2)
340+
341+
if getresp.statusCode != headresp.statusCode {
342+
t.Fatalf("head and get status differ. expected %d got %d", getresp.statusCode, headresp.statusCode)
343+
}
344+
if len(strings.Bytes(headresp.body)) != 0 {
345+
t.Fatalf("head request arrived with a body")
346+
}
347+
348+
var cl []string
349+
var getcl, headcl int
350+
var hascl1, hascl2 bool
351+
352+
if cl, hascl1 = getresp.headers["Content-Length"]; hascl1 {
353+
getcl, _ = strconv.Atoi(cl[0])
354+
}
355+
356+
if cl, hascl2 = headresp.headers["Content-Length"]; hascl2 {
357+
headcl, _ = strconv.Atoi(cl[0])
358+
}
359+
360+
if hascl1 != hascl2 {
361+
t.Fatalf("head and get: one has content-length, one doesn't")
362+
}
363+
364+
if hascl1 == true && getcl != headcl {
365+
t.Fatalf("head and get content-length differ")
366+
}
367+
}
368+
}
369+
370+
272371
func buildFcgiKeyValue(key string, val string) []byte {
273372

274373
var buf bytes.Buffer
@@ -389,8 +488,56 @@ func TestFcgi(t *testing.T) {
389488
}
390489
}
391490

491+
func TestFcgiHead(t *testing.T) {
492+
for _, test := range (tests) {
493+
494+
if test.method != "GET" {
495+
continue
496+
}
497+
req := buildTestFcgiRequest("GET", test.path, []string{test.body}, make(map[string]string))
498+
var output bytes.Buffer
499+
nb := tcpBuffer{input: req, output: &output}
500+
handleFcgiConnection(&nb)
501+
contents := getFcgiOutput(&output)
502+
getresp := buildTestResponse(contents)
503+
504+
req = buildTestFcgiRequest("HEAD", test.path, []string{test.body}, make(map[string]string))
505+
var output2 bytes.Buffer
506+
nb = tcpBuffer{input: req, output: &output2}
507+
handleFcgiConnection(&nb)
508+
contents = getFcgiOutput(&output2)
509+
headresp := buildTestResponse(contents)
510+
511+
if getresp.statusCode != headresp.statusCode {
512+
t.Fatalf("head and get status differ. expected %d got %d", getresp.statusCode, headresp.statusCode)
513+
}
514+
if len(strings.Bytes(headresp.body)) != 0 {
515+
t.Fatalf("head request arrived with a body")
516+
}
517+
518+
var cl []string
519+
var getcl, headcl int
520+
var hascl1, hascl2 bool
521+
522+
if cl, hascl1 = getresp.headers["Content-Length"]; hascl1 {
523+
getcl, _ = strconv.Atoi(cl[0])
524+
}
525+
526+
if cl, hascl2 = headresp.headers["Content-Length"]; hascl2 {
527+
headcl, _ = strconv.Atoi(cl[0])
528+
}
529+
530+
if hascl1 != hascl2 {
531+
t.Fatalf("head and get: one has content-length, one doesn't")
532+
}
533+
534+
if hascl1 == true && getcl != headcl {
535+
t.Fatalf("head and get content-length differ")
536+
}
537+
}
538+
}
392539

393-
func TestFcgiChucks(t *testing.T) {
540+
func TestFcgiChunks(t *testing.T) {
394541
//split up an fcgi request
395542
bodychunks := []string{`a=12&b=`, strings.Repeat("1234567890", 200)}
396543

0 commit comments

Comments
 (0)