Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Correctly handle 304 responses.

Also handle a 3-digit string message properly.
  • Loading branch information...
commit 61d015b2f1d40894bf70d8b69d2d84d55493b2cd 1 parent 7d82876
Jeremy Stashewsky authored October 06, 2010
48  Feersum.xs
@@ -117,9 +117,10 @@ struct feer_conn {
117 117
     U16 in_callback;
118 118
     U16 responding;
119 119
     U16 receiving;
120  
-    U16 _reservedflags:14;
  120
+    U16 _reservedflags:13;
121 121
     U16 is_http11:1;
122 122
     U16 poll_write_cb_is_io_handle:1;
  123
+    U16 auto_cl:1;
123 124
 };
124 125
 
125 126
 typedef struct feer_conn feer_conn_handle; // for typemap
@@ -156,6 +157,7 @@ static void respond_with_server_error(struct feer_conn *c, const char *msg, STRL
156 157
 
157 158
 static STRLEN add_sv_to_wbuf (struct feer_conn *c, SV *sv);
158 159
 static STRLEN add_const_to_wbuf (struct feer_conn *c, const char const *str, size_t str_len);
  160
+#define add_crlf_to_wbuf(c) add_const_to_wbuf(c,CRLF,2)
159 161
 static void finish_wbuf (struct feer_conn *c);
160 162
 static void add_chunk_sv_to_wbuf (struct feer_conn *c, SV *sv);
161 163
 static void add_placeholder_to_wbuf (struct feer_conn *c, SV **sv, struct iovec **iov_ref);
@@ -318,7 +320,7 @@ add_chunk_sv_to_wbuf(struct feer_conn *c, SV *sv)
318 320
     struct iovec *chunk_iov;
319 321
     add_placeholder_to_wbuf(c, &chunk, &chunk_iov);
320 322
     STRLEN cur = add_sv_to_wbuf(c, sv);
321  
-    add_const_to_wbuf(c, CRLF, 2);
  323
+    add_crlf_to_wbuf(c);
322 324
     sv_setpvf(chunk, "%x" CRLF, cur);
323 325
     update_wbuf_placeholder(c, chunk, chunk_iov);
324 326
 }
@@ -1455,21 +1457,38 @@ feersum_start_response (pTHX_ struct feer_conn *c, SV *message, AV *headers,
1455 1457
     }
1456 1458
 
1457 1459
     I32 avl = av_len(headers);
1458  
-    if (avl < 0 || (avl % 2 != 1)) {
1459  
-        croak("expected even-length array");
  1460
+    if (avl+1 % 2 == 1) {
  1461
+        croak("expected even-length array, got %d", avl+1);
1460 1462
     }
1461 1463
 
1462 1464
     // int or 3 chars? use a stock message
1463  
-    if (SvIOK(message) || (SvPOK(message) && SvCUR(message) == 3)) {
1464  
-        int code = SvIV(message);
  1465
+    UV code = 0;
  1466
+    if (SvIOK(message))
  1467
+        code = SvIV(message);
  1468
+    else if (SvUOK(message))
  1469
+        code = SvUV(message);
  1470
+    else {
  1471
+        const int numtype = grok_number(SvPVX_const(message),3,&code);
  1472
+        if (numtype != IS_NUMBER_IN_UV)
  1473
+            code = 0;
  1474
+    }
  1475
+    trace2("starting response fd=%d code=%u\n",c->fd,code);
  1476
+
  1477
+    if (!code)
  1478
+        croak("first parameter is not a number or doesn't start with digits");
  1479
+
  1480
+    if (!SvPOK(message) || SvCUR(message) == 3) {
1465 1481
         ptr = http_code_to_msg(code);
1466 1482
         len = strlen(ptr);
1467 1483
         message = sv_2mortal(newSVpvf("%d %.*s",code,len,ptr));
1468 1484
     }
  1485
+    
  1486
+    // don't generate or strip Content-Length headers for 304 responses.
  1487
+    c->auto_cl = (code == 304) ? 0 : 1;
1469 1488
 
1470 1489
     add_const_to_wbuf(c, c->is_http11 ? "HTTP/1.1 " : "HTTP/1.0 ", 9);
1471 1490
     add_sv_to_wbuf(c, message);
1472  
-    add_const_to_wbuf(c, CRLF, 2);
  1491
+    add_crlf_to_wbuf(c);
1473 1492
 
1474 1493
     for (i=0; i<avl; i+= 2) {
1475 1494
         SV **hdr = av_fetch(headers, i, 0);
@@ -1486,7 +1505,7 @@ feersum_start_response (pTHX_ struct feer_conn *c, SV *message, AV *headers,
1486 1505
 
1487 1506
         STRLEN hlen;
1488 1507
         const char *hp = SvPV(*hdr, hlen);
1489  
-        if (str_case_eq("content-length",14,hp,hlen)) {
  1508
+        if (c->auto_cl && str_case_eq("content-length",14,hp,hlen)) {
1490 1509
             trace("ignoring content-length header in the response\n");
1491 1510
             continue; 
1492 1511
         }
@@ -1494,7 +1513,7 @@ feersum_start_response (pTHX_ struct feer_conn *c, SV *message, AV *headers,
1494 1513
         add_sv_to_wbuf(c, *hdr);
1495 1514
         add_const_to_wbuf(c, ": ", 2);
1496 1515
         add_sv_to_wbuf(c, *val);
1497  
-        add_const_to_wbuf(c, CRLF, 2);
  1516
+        add_crlf_to_wbuf(c);
1498 1517
     }
1499 1518
 
1500 1519
     if (streaming) {
@@ -1538,7 +1557,10 @@ feersum_write_whole_body (pTHX_ struct feer_conn *c, SV *body)
1538 1557
 
1539 1558
     SV *cl_sv; // content-length future
1540 1559
     struct iovec *cl_iov;
1541  
-    add_placeholder_to_wbuf(c, &cl_sv, &cl_iov);
  1560
+    if (c->auto_cl)
  1561
+        add_placeholder_to_wbuf(c, &cl_sv, &cl_iov);
  1562
+    else
  1563
+        add_crlf_to_wbuf(c);
1542 1564
 
1543 1565
     if (body_is_string) {
1544 1566
         cur = add_sv_to_wbuf(c,body);
@@ -1562,8 +1584,10 @@ feersum_write_whole_body (pTHX_ struct feer_conn *c, SV *body)
1562 1584
         }
1563 1585
     }
1564 1586
 
1565  
-    sv_setpvf(cl_sv, "Content-Length: %d" CRLFx2, RETVAL);
1566  
-    update_wbuf_placeholder(c, cl_sv, cl_iov);
  1587
+    if (c->auto_cl) {
  1588
+        sv_setpvf(cl_sv, "Content-Length: %d" CRLFx2, RETVAL);
  1589
+        update_wbuf_placeholder(c, cl_sv, cl_iov);
  1590
+    }
1567 1591
 
1568 1592
     c->responding = RESPOND_SHUTDOWN;
1569 1593
     conn_write_ready(c);
69  t/10-respond-304.t
... ...
@@ -0,0 +1,69 @@
  1
+#!perl
  2
+use warnings;
  3
+use strict;
  4
+use Test::More tests => 21;
  5
+use Test::Exception;
  6
+use utf8;
  7
+use lib 't'; use Utils;
  8
+
  9
+BEGIN { use_ok('Feersum') };
  10
+
  11
+my ($socket,$port) = get_listen_socket();
  12
+ok $socket, "made listen socket";
  13
+ok $socket->fileno, "has a fileno";
  14
+
  15
+my $evh = Feersum->new();
  16
+
  17
+$evh->request_handler(sub {
  18
+    my $r = shift;
  19
+    isa_ok $r, 'Feersum::Connection', 'got an object!';
  20
+    my $env = $r->env;
  21
+    ok $env, 'got env';
  22
+    lives_ok {
  23
+        if ($env->{HTTP_X_CLIENT} == 1) {
  24
+            $r->send_response("304", [], []); # explicit string, not num
  25
+        }
  26
+        else {
  27
+            $r->send_response("304 Not Modified", ['Content-Length'=>123], []);
  28
+        }
  29
+    } 'sent response for '.$env->{HTTP_X_CLIENT};
  30
+});
  31
+
  32
+lives_ok {
  33
+    $evh->use_socket($socket);
  34
+} 'assigned socket';
  35
+
  36
+my $cv = AE::cv;
  37
+$cv->begin;
  38
+my $w = simple_client GET => '/?blef',
  39
+    headers => { 'X-Client' => 1 },
  40
+    timeout => 3,
  41
+    sub {
  42
+        my ($body, $headers) = @_;
  43
+        is $headers->{Status}, 304, "client got 304";
  44
+        ok !exists $headers->{'content-type'}, 'missing c-t';
  45
+        # 304 not-modifieds shouldn't auto-generate a content-length header or
  46
+        # any other "entity" headers.  These reflect the actual entity, and
  47
+        # can update cache's respresentation of the object.
  48
+        ok !exists $headers->{'content-length'},'no c-l generated';
  49
+        ok !$body, 'no body';
  50
+        $cv->end;
  51
+    };
  52
+
  53
+$cv->begin;
  54
+my $w2 = simple_client GET => '/?blef',
  55
+    headers => { 'X-Client' => 2 },
  56
+    timeout => 3,
  57
+    sub {
  58
+        my ($body, $headers) = @_;
  59
+        is $headers->{Status}, 304, "2nd client got 304";
  60
+        ok !exists $headers->{'content-type'}, 'missing c-t';
  61
+        # If the app specified a C-L, we should respect it for the same
  62
+        # reasons.
  63
+        is $headers->{'content-length'}, 123, 'c-l not replaced';
  64
+        ok !$body, 'no body';
  65
+        $cv->end;
  66
+    };
  67
+
  68
+$cv->recv;
  69
+pass "all done";
9  t/Utils.pm
@@ -128,7 +128,14 @@ sub simple_client ($$;@) {
128 128
 
129 129
         $hdrs{'content-length'} = 0 if ($hdrs{Status} == 204);
130 130
 
131  
-        if (exists $hdrs{'content-length'}) {
  131
+        if ($hdrs{Status} == 304) {
  132
+            # should have no body
  133
+            $h->on_read(sub {
  134
+                $buf .= substr($_[0]->{rbuf},0,length($_[0]->{rbuf}),'');
  135
+            });
  136
+            $h->on_eof($done);
  137
+        }
  138
+        elsif (exists $hdrs{'content-length'}) {
132 139
             return $done->() unless ($hdrs{'content-length'});
133 140
 #             Test::More::diag "$name waiting for C-L body";
134 141
             $h->push_read(chunk => $hdrs{'content-length'}, sub {

0 notes on commit 61d015b

Please sign in to comment.
Something went wrong with that request. Please try again.