http_parser.rb 0.5.1-x86-mswin32-60 → 0.5.2-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/Gemfile.lock +16 -16
  2. data/LICENSE-MIT +20 -0
  3. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +68 -11
  4. data/ext/ruby_http_parser/ruby_http_parser.c +74 -6
  5. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +26 -1
  6. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +23 -143
  7. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +3 -0
  8. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  9. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +115 -61
  10. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +19 -3
  11. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  12. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
  15. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +64 -52
  16. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +27 -28
  19. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +259 -85
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +324 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +51 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +15 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +47 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +183 -447
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +61 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +2 -1
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +26 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +165 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +58 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +232 -29
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -1
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -1
  35. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +1 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +154 -7
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  38. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
  39. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +52 -10
  40. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +3 -1
  41. data/ext/ruby_http_parser/vendor/http-parser/test.c +89 -3
  42. data/http_parser.rb.gemspec +8 -2
  43. data/lib/http_parser.rb +17 -0
  44. data/spec/parser_spec.rb +97 -6
  45. data/tasks/compile.rake +3 -1
  46. metadata +83 -20
  47. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
@@ -0,0 +1,61 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.*;
4
+
5
+ import static http_parser.lolevel.Util.*;
6
+
7
+ public class TestNoOverflowLongBody {
8
+
9
+ public static void test (http_parser.ParserType type, int len) {
10
+ HTTPParser parser = new HTTPParser(type);
11
+ ByteBuffer buf = getBytes(type, len);
12
+
13
+ int buflen = buf.limit();
14
+
15
+ parser.execute(Util.SETTINGS_NULL, buf);
16
+
17
+ check(buflen == buf.position());
18
+
19
+ buf = buffer("a");
20
+ buflen = buf.limit();
21
+
22
+ for (int i = 0; i!= len; ++i) {
23
+ parser.execute(Util.SETTINGS_NULL, buf);
24
+ check(buflen == buf.position());
25
+ buf.rewind();
26
+ }
27
+
28
+ buf = getBytes(type, len);
29
+ buflen = buf.limit();
30
+
31
+ parser.execute(Util.SETTINGS_NULL, buf);
32
+
33
+ check(buflen == buf.position());
34
+
35
+ }
36
+
37
+ static ByteBuffer getBytes (http_parser.ParserType type, int length) {
38
+ if (http_parser.ParserType.HTTP_BOTH == type) {
39
+ throw new RuntimeException("only HTTP_REQUEST and HTTP_RESPONSE");
40
+ }
41
+
42
+ String template = "%s\r\nConnection: Keep-Alive\r\nContent-Length: %d\r\n\r\n";
43
+ String str = null;
44
+ if (http_parser.ParserType.HTTP_REQUEST == type) {
45
+ str = String.format(template, "GET / HTTP/1.1", length);
46
+ } else {
47
+ str = String.format(template, "HTTP/1.0 200 OK", length);
48
+ }
49
+ return buffer(str);
50
+ }
51
+
52
+ public static void test () {
53
+ test(http_parser.ParserType.HTTP_REQUEST, 1000);
54
+ test(http_parser.ParserType.HTTP_REQUEST, 100000);
55
+ test(http_parser.ParserType.HTTP_RESPONSE, 1000);
56
+ test(http_parser.ParserType.HTTP_RESPONSE, 100000);
57
+ }
58
+
59
+
60
+
61
+ }
@@ -2,6 +2,7 @@ package http_parser.lolevel;
2
2
 
3
3
  import java.nio.ByteBuffer;
4
4
  import http_parser.HTTPException;
5
+ import http_parser.Util;
5
6
 
6
7
  public class UnitTest {
7
8
 
@@ -108,7 +109,7 @@ public class UnitTest {
108
109
  }
109
110
 
110
111
 
111
- public static void main (String [] args) {
112
+ public static void test () {
112
113
  testErrorFormat();
113
114
  testErrorCallback();
114
115
  }
@@ -0,0 +1,26 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.*;
4
+ import java.util.*;
5
+
6
+ import http_parser.ParserType;
7
+
8
+ import static http_parser.lolevel.Util.*;
9
+
10
+ public class Upgrade {
11
+ static final String upgrade = "GET /demo HTTP/1.1\r\n" +
12
+ "Connection: Upgrade\r\n" +
13
+ "Upgrade: WebSocket\r\n\r\n" +
14
+ "third key data";
15
+ static void test () {
16
+ HTTPParser parser = new HTTPParser(ParserType.HTTP_REQUEST);
17
+ ByteBuffer buf = buffer(upgrade);
18
+
19
+ int read = parser.execute(Util.SETTINGS_NULL, buf);
20
+ check (63 == read);
21
+ String s = str(buf);
22
+ check ("third key data".equals(str(buf)));
23
+
24
+ }
25
+
26
+ }
@@ -0,0 +1,165 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.*;
4
+ import java.util.*;
5
+
6
+ import primitive.collection.ByteList;
7
+
8
+ import http_parser.*;
9
+
10
+ public class Util {
11
+
12
+ static final ParserSettings SETTINGS_NULL = new ParserSettings();
13
+
14
+ static String str (ByteBuffer b, int pos, int len) {
15
+ byte [] by = new byte[len];
16
+ int saved = b.position();
17
+ b.position(pos);
18
+ b.get(by);
19
+ b.position(saved);
20
+ return new String(by);
21
+ }
22
+ static String str (ByteBuffer b) {
23
+ int len = b.limit() - b.position();
24
+ byte [] by = new byte[len];
25
+ int saved = b.position();
26
+ b.get(by);
27
+ b.position(saved);
28
+ return new String(by);
29
+ }
30
+
31
+ static ByteBuffer buffer(String str) {
32
+ return ByteBuffer.wrap(str.getBytes());
33
+ }
34
+
35
+ static ByteBuffer empty() {
36
+ return ByteBuffer.wrap(new byte[0]);
37
+ }
38
+
39
+ static void check(boolean betterBtrue) {
40
+ if (!betterBtrue) {
41
+ throw new RuntimeException("!");
42
+ }
43
+ }
44
+
45
+ static void test_message(Message mes) {
46
+ int raw_len = mes.raw.length;
47
+ for (int msg1len = 0; msg1len != raw_len; ++msg1len) {
48
+ mes.reset();
49
+ ByteBuffer msg1 = ByteBuffer.wrap(mes.raw, 0, msg1len);
50
+ ByteBuffer msg2 = ByteBuffer.wrap(mes.raw, msg1len, mes.raw.length - msg1len);
51
+
52
+ HTTPParser parser = new HTTPParser(mes.type);
53
+ ParserSettings settings = mes.settings();
54
+
55
+ int read = 0;
56
+ if (msg1len !=0) {
57
+ read = parser.execute(settings, msg1);
58
+ if (mes.upgrade && parser.upgrade) {
59
+ // Messages have a settings() that checks itself...
60
+ check(1 == mes.num_called);
61
+ continue;
62
+ }
63
+ check(read == msg1len);
64
+ }
65
+
66
+ read = parser.execute(settings, msg2);
67
+ if (mes.upgrade && parser.upgrade) {
68
+ check(1 == mes.num_called);
69
+ continue;
70
+ }
71
+
72
+ check(read == mes.raw.length - msg1len);
73
+
74
+ ByteBuffer empty = Util.empty();
75
+ read = parser.execute(settings, empty);
76
+
77
+ if (mes.upgrade && parser.upgrade) {
78
+ check(1 == mes.num_called);
79
+ continue;
80
+ }
81
+ check(empty.position() == empty.limit());
82
+ check(0 == read);
83
+ check(1 == mes.num_called);
84
+
85
+ }
86
+ }
87
+
88
+ static void test_multiple3(Message r1, Message r2, Message r3) {
89
+ int message_count = 1;
90
+ if (!r1.upgrade) {
91
+ message_count++;
92
+ if (!r2.upgrade) {
93
+ message_count++;
94
+ }
95
+ }
96
+ boolean has_upgrade = (message_count < 3 || r3.upgrade);
97
+
98
+ ByteList blist = new ByteList();
99
+ blist.addAll(r1.raw);
100
+ blist.addAll(r2.raw);
101
+ blist.addAll(r3.raw);
102
+
103
+ byte [] raw = blist.toArray();
104
+ ByteBuffer buf = ByteBuffer.wrap(raw);
105
+
106
+ Util.Settings settings = Util.settings();
107
+ HTTPParser parser = new HTTPParser(r1.type);
108
+
109
+ int read = parser.execute(settings, buf);
110
+ if (has_upgrade && parser.upgrade) {
111
+ check(settings.numCalled == message_count);
112
+ return;
113
+ }
114
+
115
+ check(read == raw.length);
116
+
117
+ buf = Util.empty();
118
+ read = parser.execute(settings, buf);
119
+ if (has_upgrade && parser.upgrade) {
120
+ check(settings.numCalled == message_count);
121
+ return;
122
+ }
123
+
124
+ check(0 == read);
125
+ check(settings.numCalled == message_count);
126
+ }
127
+ static void p (Object o) {
128
+ System.out.println(o);
129
+ }
130
+
131
+ static Settings settings() {
132
+ return new Settings();
133
+ }
134
+ static Message find(List<Message> list, String name) {
135
+ for (Message m : list) {
136
+ if (name.equals(m.name)) {
137
+ return m;
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+
143
+ static class Settings extends ParserSettings {
144
+ public int numCalled;
145
+ public int bodyCount;
146
+ Settings() {
147
+ this.on_message_complete = new HTTPCallback() {
148
+ public int cb (HTTPParser parser) {
149
+ numCalled++;
150
+ return 0;
151
+ }
152
+ };
153
+ this.on_body = new HTTPDataCallback() {
154
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len) {
155
+ bodyCount += len;
156
+ return 0;
157
+ }
158
+ };
159
+ }
160
+
161
+ int numCalled () {
162
+ return this.numCalled;
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,58 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.*;
4
+ import java.util.*;
5
+
6
+ import http_parser.ParserType;
7
+
8
+ import static http_parser.lolevel.Util.*;
9
+
10
+ public class WrongContentLength {
11
+ static final String contentLength = "GET / HTTP/1.0\r\n" +
12
+ "Content-Length: 5\r\n" +
13
+ "\r\n" +
14
+ "hello" +
15
+ "hello_again";
16
+ static void test () {
17
+ HTTPParser parser = new HTTPParser(ParserType.HTTP_REQUEST);
18
+ ByteBuffer buf = buffer(contentLength);
19
+
20
+ Settings settings = new Settings();
21
+
22
+ int read = parser.execute(settings, buf);
23
+ check (settings.msg_cmplt_called);
24
+ check ("invalid method".equals(settings.err));
25
+
26
+ }
27
+ public static void main(String [] args) {
28
+ test();
29
+ }
30
+
31
+ static class Settings extends ParserSettings {
32
+ public int bodyCount;
33
+ public boolean msg_cmplt_called;
34
+ public String err;
35
+ Settings () {
36
+ this.on_message_complete = new HTTPCallback () {
37
+ public int cb (HTTPParser p) {
38
+ check (5 == bodyCount);
39
+ msg_cmplt_called = true;
40
+ return 0;
41
+ }
42
+ };
43
+ this.on_body = new HTTPDataCallback() {
44
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len) {
45
+ bodyCount += len;
46
+ p(str(b, pos, len));
47
+ return 0;
48
+ }
49
+ };
50
+ this.on_error = new HTTPErrorCallback() {
51
+ public void cb (HTTPParser p, String mes, ByteBuffer b, int i) {
52
+ err = mes;
53
+ }
54
+ };
55
+ }
56
+ }
57
+
58
+ }
@@ -1,4 +1,4 @@
1
- /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
2
  *
3
3
  * Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  * of this software and associated documentation files (the "Software"), to
@@ -31,7 +31,7 @@
31
31
  #undef FALSE
32
32
  #define FALSE 0
33
33
 
34
- #define MAX_HEADERS 10
34
+ #define MAX_HEADERS 13
35
35
  #define MAX_ELEMENT_SIZE 500
36
36
 
37
37
  #define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -498,7 +498,7 @@ const struct message requests[] =
498
498
  #define CONNECT_REQUEST 17
499
499
  , {.name = "connect request"
500
500
  ,.type= HTTP_REQUEST
501
- ,.raw= "CONNECT home.netscape.com:443 HTTP/1.0\r\n"
501
+ ,.raw= "CONNECT home0.netscape.com:443 HTTP/1.0\r\n"
502
502
  "User-agent: Mozilla/1.1N\r\n"
503
503
  "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
504
504
  "\r\n"
@@ -510,7 +510,7 @@ const struct message requests[] =
510
510
  ,.query_string= ""
511
511
  ,.fragment= ""
512
512
  ,.request_path= ""
513
- ,.request_url= "home.netscape.com:443"
513
+ ,.request_url= "home0.netscape.com:443"
514
514
  ,.num_headers= 2
515
515
  ,.upgrade=1
516
516
  ,.headers= { { "User-agent", "Mozilla/1.1N" }
@@ -538,6 +538,128 @@ const struct message requests[] =
538
538
  ,.body= ""
539
539
  }
540
540
 
541
+ #define NO_HTTP_VERSION 19
542
+ , {.name= "request with no http version"
543
+ ,.type= HTTP_REQUEST
544
+ ,.raw= "GET /\r\n"
545
+ "\r\n"
546
+ ,.should_keep_alive= FALSE
547
+ ,.message_complete_on_eof= FALSE
548
+ ,.http_major= 0
549
+ ,.http_minor= 9
550
+ ,.method= HTTP_GET
551
+ ,.query_string= ""
552
+ ,.fragment= ""
553
+ ,.request_path= "/"
554
+ ,.request_url= "/"
555
+ ,.num_headers= 0
556
+ ,.headers= {}
557
+ ,.body= ""
558
+ }
559
+
560
+ #define MSEARCH_REQ 20
561
+ , {.name= "m-search request"
562
+ ,.type= HTTP_REQUEST
563
+ ,.raw= "M-SEARCH * HTTP/1.1\r\n"
564
+ "HOST: 239.255.255.250:1900\r\n"
565
+ "MAN: \"ssdp:discover\"\r\n"
566
+ "ST: \"ssdp:all\"\r\n"
567
+ "\r\n"
568
+ ,.should_keep_alive= TRUE
569
+ ,.message_complete_on_eof= FALSE
570
+ ,.http_major= 1
571
+ ,.http_minor= 1
572
+ ,.method= HTTP_MSEARCH
573
+ ,.query_string= ""
574
+ ,.fragment= ""
575
+ ,.request_path= "*"
576
+ ,.request_url= "*"
577
+ ,.num_headers= 3
578
+ ,.headers= { { "HOST", "239.255.255.250:1900" }
579
+ , { "MAN", "\"ssdp:discover\"" }
580
+ , { "ST", "\"ssdp:all\"" }
581
+ }
582
+ ,.body= ""
583
+ }
584
+
585
+ #define UTF8_PATH_REQ 21
586
+ , {.name= "utf-8 path request"
587
+ ,.type= HTTP_REQUEST
588
+ ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
589
+ "Host: github.com\r\n"
590
+ "\r\n"
591
+ ,.should_keep_alive= TRUE
592
+ ,.message_complete_on_eof= FALSE
593
+ ,.http_major= 1
594
+ ,.http_minor= 1
595
+ ,.method= HTTP_GET
596
+ ,.query_string= "q=1"
597
+ ,.fragment= "narf"
598
+ ,.request_path= "/δ¶/δt/pope"
599
+ ,.request_url= "/δ¶/δt/pope?q=1#narf"
600
+ ,.num_headers= 1
601
+ ,.headers= { {"Host", "github.com" }
602
+ }
603
+ ,.body= ""
604
+ }
605
+
606
+ #define QUERY_TERMINATED_HOST 22
607
+ , {.name= "host terminated by a query string"
608
+ ,.type= HTTP_REQUEST
609
+ ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
610
+ "\r\n"
611
+ ,.should_keep_alive= TRUE
612
+ ,.message_complete_on_eof= FALSE
613
+ ,.http_major= 1
614
+ ,.http_minor= 1
615
+ ,.method= HTTP_GET
616
+ ,.query_string= "hail=all"
617
+ ,.fragment= ""
618
+ ,.request_path= ""
619
+ ,.request_url= "http://hypnotoad.org?hail=all"
620
+ ,.num_headers= 0
621
+ ,.headers= { }
622
+ ,.body= ""
623
+ }
624
+
625
+ #define QUERY_TERMINATED_HOSTPORT 23
626
+ , {.name= "host:port terminated by a query string"
627
+ ,.type= HTTP_REQUEST
628
+ ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
629
+ "\r\n"
630
+ ,.should_keep_alive= TRUE
631
+ ,.message_complete_on_eof= FALSE
632
+ ,.http_major= 1
633
+ ,.http_minor= 1
634
+ ,.method= HTTP_GET
635
+ ,.query_string= "hail=all"
636
+ ,.fragment= ""
637
+ ,.request_path= ""
638
+ ,.request_url= "http://hypnotoad.org:1234?hail=all"
639
+ ,.num_headers= 0
640
+ ,.headers= { }
641
+ ,.body= ""
642
+ }
643
+
644
+ #define SPACE_TERMINATED_HOSTPORT 24
645
+ , {.name= "host:port terminated by a space"
646
+ ,.type= HTTP_REQUEST
647
+ ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
648
+ "\r\n"
649
+ ,.should_keep_alive= TRUE
650
+ ,.message_complete_on_eof= FALSE
651
+ ,.http_major= 1
652
+ ,.http_minor= 1
653
+ ,.method= HTTP_GET
654
+ ,.query_string= ""
655
+ ,.fragment= ""
656
+ ,.request_path= ""
657
+ ,.request_url= "http://hypnotoad.org:1234"
658
+ ,.num_headers= 0
659
+ ,.headers= { }
660
+ ,.body= ""
661
+ }
662
+
541
663
  , {.name= NULL } /* sentinel */
542
664
  };
543
665
 
@@ -551,9 +673,10 @@ const struct message responses[] =
551
673
  "Content-Type: text/html; charset=UTF-8\r\n"
552
674
  "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
553
675
  "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
676
+ "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
554
677
  "Cache-Control: public, max-age=2592000\r\n"
555
678
  "Server: gws\r\n"
556
- "Content-Length: 219\r\n"
679
+ "Content-Length: 219 \r\n"
557
680
  "\r\n"
558
681
  "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
559
682
  "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
@@ -566,15 +689,16 @@ const struct message responses[] =
566
689
  ,.http_major= 1
567
690
  ,.http_minor= 1
568
691
  ,.status_code= 301
569
- ,.num_headers= 7
692
+ ,.num_headers= 8
570
693
  ,.headers=
571
694
  { { "Location", "http://www.google.com/" }
572
695
  , { "Content-Type", "text/html; charset=UTF-8" }
573
696
  , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
574
697
  , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
698
+ , { "X-$PrototypeBI-Version", "1.6.0.3" }
575
699
  , { "Cache-Control", "public, max-age=2592000" }
576
700
  , { "Server", "gws" }
577
- , { "Content-Length", "219" }
701
+ , { "Content-Length", "219 " }
578
702
  }
579
703
  ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
580
704
  "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
@@ -833,6 +957,71 @@ const struct message responses[] =
833
957
  ,.body= "<xml>hello</xml>"
834
958
  }
835
959
 
960
+
961
+ #define RES_FIELD_UNDERSCORE 10
962
+ /* Should handle spaces in header fields */
963
+ , {.name= "field underscore"
964
+ ,.type= HTTP_RESPONSE
965
+ ,.raw= "HTTP/1.1 200 OK\r\n"
966
+ "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
967
+ "Server: Apache\r\n"
968
+ "Cache-Control: no-cache, must-revalidate\r\n"
969
+ "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
970
+ ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
971
+ "Vary: Accept-Encoding\r\n"
972
+ "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
973
+ "_onnection: Keep-Alive\r\n" /* semantic value ignored */
974
+ "Transfer-Encoding: chunked\r\n"
975
+ "Content-Type: text/html\r\n"
976
+ "Connection: close\r\n"
977
+ "\r\n"
978
+ "0\r\n\r\n"
979
+ ,.should_keep_alive= FALSE
980
+ ,.message_complete_on_eof= FALSE
981
+ ,.http_major= 1
982
+ ,.http_minor= 1
983
+ ,.status_code= 200
984
+ ,.num_headers= 11
985
+ ,.headers=
986
+ { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
987
+ , { "Server", "Apache" }
988
+ , { "Cache-Control", "no-cache, must-revalidate" }
989
+ , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
990
+ , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
991
+ , { "Vary", "Accept-Encoding" }
992
+ , { "_eep-Alive", "timeout=45" }
993
+ , { "_onnection", "Keep-Alive" }
994
+ , { "Transfer-Encoding", "chunked" }
995
+ , { "Content-Type", "text/html" }
996
+ , { "Connection", "close" }
997
+ }
998
+ ,.body= ""
999
+ }
1000
+
1001
+ #define NON_ASCII_IN_STATUS_LINE 11
1002
+ /* Should handle non-ASCII in status line */
1003
+ , {.name= "non-ASCII in status line"
1004
+ ,.type= HTTP_RESPONSE
1005
+ ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1006
+ "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1007
+ "Content-Length: 0\r\n"
1008
+ "Connection: close\r\n"
1009
+ "\r\n"
1010
+ ,.should_keep_alive= FALSE
1011
+ ,.message_complete_on_eof= FALSE
1012
+ ,.http_major= 1
1013
+ ,.http_minor= 1
1014
+ ,.status_code= 500
1015
+ ,.num_headers= 3
1016
+ ,.headers=
1017
+ { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1018
+ , { "Content-Length", "0" }
1019
+ , { "Connection", "close" }
1020
+ }
1021
+ ,.body= ""
1022
+ }
1023
+
1024
+
836
1025
  , {.name= NULL } /* sentinel */
837
1026
  };
838
1027
 
@@ -1538,8 +1727,8 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
1538
1727
  char *
1539
1728
  quote(const char * orig) {
1540
1729
  size_t j, i, len = strlen(orig);
1541
- char * quoted = malloc(len*2); // hm..
1542
- bzero(quoted, len*2);
1730
+ char * quoted = malloc(len == 0 ? 1 : len*2); // hm..
1731
+ bzero(quoted, len == 0 ? 1 : len*2);
1543
1732
  for (i=0, j=0; i!=len; ++i) {
1544
1733
  switch (orig[i]){
1545
1734
  case '\n':
@@ -1578,27 +1767,33 @@ dump_message(const struct message * m)
1578
1767
  printf("type :HTTP_BOTH\n");
1579
1768
  }
1580
1769
  switch (m->method) {
1581
- case HTTP_DELETE: printf("method: HTTP_DELETE\n");break;
1582
- case HTTP_GET: printf("method: HTTP_GET\n");break;
1583
- case HTTP_HEAD: printf("method: HTTP_HEAD\n");break;
1584
- case HTTP_POST: printf("method: HTTP_POST\n");break;
1585
- case HTTP_PUT: printf("method: HTTP_PUT\n");break;
1586
- case HTTP_CONNECT: printf("method: HTTP_CONNECT\n");break;
1587
- case HTTP_OPTIONS: printf("method: HTTP_OPTIONS\n");break;
1588
- case HTTP_TRACE: printf("method: HTTP_TRACE\n");break;
1589
- case HTTP_COPY: printf("method: HTTP_COPY\n");break;
1590
- case HTTP_LOCK: printf("method: HTTP_LOCK\n");break;
1591
- case HTTP_MKCOL: printf("method: HTTP_MKCOL\n");break;
1592
- case HTTP_MOVE: printf("method: HTTP_MOVE\n");break;
1593
- case HTTP_PROPFIND: printf("method: HTTP_PROPFIND\n");break;
1594
- case HTTP_PROPPATCH: printf("method: HTTP_PROPPATCH\n");break;
1595
- case HTTP_UNLOCK: printf("method: HTTP_UNLOCK\n");break;
1770
+ case HTTP_DELETE: printf("method: HTTP_DELETE\n");break;
1771
+ case HTTP_GET: printf("method: HTTP_GET\n");break;
1772
+ case HTTP_HEAD: printf("method: HTTP_HEAD\n");break;
1773
+ case HTTP_POST: printf("method: HTTP_POST\n");break;
1774
+ case HTTP_PUT: printf("method: HTTP_PUT\n");break;
1775
+ case HTTP_CONNECT: printf("method: HTTP_CONNECT\n");break;
1776
+ case HTTP_OPTIONS: printf("method: HTTP_OPTIONS\n");break;
1777
+ case HTTP_TRACE: printf("method: HTTP_TRACE\n");break;
1778
+ case HTTP_COPY: printf("method: HTTP_COPY\n");break;
1779
+ case HTTP_LOCK: printf("method: HTTP_LOCK\n");break;
1780
+ case HTTP_MKCOL: printf("method: HTTP_MKCOL\n");break;
1781
+ case HTTP_MOVE: printf("method: HTTP_MOVE\n");break;
1782
+ case HTTP_PROPFIND: printf("method: HTTP_PROPFIND\n");break;
1783
+ case HTTP_PROPPATCH: printf("method: HTTP_PROPPATCH\n");break;
1784
+ case HTTP_UNLOCK: printf("method: HTTP_UNLOCK\n");break;
1596
1785
  /* subversion */
1597
- case HTTP_REPORT: printf("method: REPORT\n"); break;
1598
- case HTTP_MKACTIVITY: printf("method: MKACTIVITY\n"); break;
1599
- case HTTP_CHECKOUT: printf("method: CHECKOUT\n"); break;
1600
- case HTTP_MERGE: printf("method: MERGE\n"); break;
1786
+ case HTTP_REPORT: printf("method: HTTP_REPORT\n"); break;
1787
+ case HTTP_MKACTIVITY: printf("method: HTTP_MKACTIVITY\n"); break;
1788
+ case HTTP_CHECKOUT: printf("method: HTTP_CHECKOUT\n"); break;
1789
+ case HTTP_MERGE: printf("method: HTTP_MERGE\n"); break;
1790
+
1791
+ case HTTP_MSEARCH: printf("method: HTTP_MSEARCH\n"); break;
1792
+ case HTTP_NOTIFY: printf("method: HTTP_NOTIFY\n"); break;
1793
+ case HTTP_SUBSCRIBE: printf("method: HTTP_SUBSCRIBE\n"); break;
1794
+ case HTTP_UNSUBSCRIBE: printf("method: HTTP_UNSUBSCRIBE\n"); break;
1601
1795
  default:
1796
+ printf("method: UNKNOWN\n"); break;
1602
1797
  break;
1603
1798
  }
1604
1799
  printf("status_code :%d\n", m->status_code);
@@ -1734,14 +1929,22 @@ main (int argc, char * argv[])
1734
1929
 
1735
1930
  /// REQUESTS
1736
1931
 
1737
-
1738
1932
  test_simple("hello world", 0);
1739
1933
  test_simple("GET / HTP/1.1\r\n\r\n", 0);
1740
1934
 
1935
+
1741
1936
  test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1742
1937
  test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1743
1938
  test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
1744
1939
 
1940
+ // Well-formed but incomplete
1941
+ test_simple("GET / HTTP/1.1\r\n"
1942
+ "Content-Type: text/plain\r\n"
1943
+ "Content-Length: 6\r\n"
1944
+ "\r\n"
1945
+ "fooba",
1946
+ 0);
1947
+
1745
1948
  static const char *all_methods[] = {
1746
1949
  "DELETE",
1747
1950
  "GET",