rugged 0.17.0.b7 → 0.18.0.b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +88 -32
  3. data/ext/rugged/extconf.rb +4 -2
  4. data/ext/rugged/rugged.c +72 -10
  5. data/ext/rugged/rugged.h +14 -10
  6. data/ext/rugged/rugged_blob.c +8 -10
  7. data/ext/rugged/rugged_branch.c +11 -14
  8. data/ext/rugged/rugged_commit.c +31 -24
  9. data/ext/rugged/rugged_config.c +2 -2
  10. data/ext/rugged/rugged_index.c +133 -198
  11. data/ext/rugged/rugged_note.c +372 -0
  12. data/ext/rugged/rugged_object.c +50 -22
  13. data/ext/rugged/rugged_reference.c +122 -130
  14. data/ext/rugged/rugged_remote.c +72 -29
  15. data/ext/rugged/rugged_repo.c +402 -20
  16. data/ext/rugged/rugged_revwalk.c +7 -3
  17. data/ext/rugged/rugged_settings.c +110 -0
  18. data/ext/rugged/rugged_signature.c +23 -7
  19. data/ext/rugged/rugged_tag.c +32 -16
  20. data/ext/rugged/rugged_tree.c +44 -15
  21. data/lib/rugged.rb +1 -0
  22. data/lib/rugged/index.rb +8 -0
  23. data/lib/rugged/remote.rb +13 -0
  24. data/lib/rugged/repository.rb +3 -3
  25. data/lib/rugged/version.rb +1 -1
  26. data/test/blob_test.rb +13 -15
  27. data/test/branch_test.rb +32 -67
  28. data/test/commit_test.rb +50 -12
  29. data/test/config_test.rb +12 -11
  30. data/test/coverage/HEAD.json +1 -1
  31. data/test/coverage/cover.rb +40 -21
  32. data/test/errors_test.rb +34 -0
  33. data/test/fixtures/alternate/objects/14/6ae76773c91e3b1d00cf7a338ec55ae58297e2 +0 -0
  34. data/test/fixtures/alternate/objects/14/9c32d47e99d0a3572ff1e70a2e0051bbf347a9 +0 -0
  35. data/test/fixtures/alternate/objects/14/fb3108588f9421bf764041e5e3ac305eb6277f +0 -0
  36. data/test/fixtures/testrepo.git/logs/refs/notes/commits +1 -0
  37. data/test/fixtures/testrepo.git/objects/44/1034f860c1d5d90e4188d11ae0d325176869a8 +1 -0
  38. data/test/fixtures/testrepo.git/objects/60/d415052a33de2150bf68757f6461df4f563ae4 +0 -0
  39. data/test/fixtures/testrepo.git/objects/68/8a8f4ef7496901d15322972f96e212a9e466cc +1 -0
  40. data/test/fixtures/testrepo.git/objects/94/eca2de348d5f672faf56b0decafa5937e3235e +0 -0
  41. data/test/fixtures/testrepo.git/objects/9b/7384fe1676186192842f5d3e129457b62db9e3 +0 -0
  42. data/test/fixtures/testrepo.git/objects/b7/4713326bc972cc15751ed504dca6f6f3b91f7a +3 -0
  43. data/test/fixtures/testrepo.git/refs/notes/commits +1 -0
  44. data/test/index_test.rb +65 -69
  45. data/test/lib_test.rb +76 -11
  46. data/test/note_test.rb +158 -0
  47. data/test/object_test.rb +8 -11
  48. data/test/reference_test.rb +77 -85
  49. data/test/remote_test.rb +86 -8
  50. data/test/repo_pack_test.rb +9 -7
  51. data/test/repo_reset_test.rb +80 -0
  52. data/test/repo_test.rb +176 -53
  53. data/test/tag_test.rb +44 -7
  54. data/test/test_helper.rb +63 -35
  55. data/test/tree_test.rb +34 -13
  56. data/test/walker_test.rb +14 -14
  57. data/vendor/libgit2/Makefile.embed +1 -1
  58. data/vendor/libgit2/deps/http-parser/http_parser.c +974 -578
  59. data/vendor/libgit2/deps/http-parser/http_parser.h +106 -70
  60. data/vendor/libgit2/deps/regex/regcomp.c +7 -6
  61. data/vendor/libgit2/deps/regex/regex_internal.c +1 -1
  62. data/vendor/libgit2/deps/regex/regex_internal.h +12 -3
  63. data/vendor/libgit2/deps/regex/regexec.c +5 -5
  64. data/vendor/libgit2/include/git2.h +5 -1
  65. data/vendor/libgit2/include/git2/attr.h +4 -2
  66. data/vendor/libgit2/include/git2/blob.h +39 -12
  67. data/vendor/libgit2/include/git2/branch.h +123 -35
  68. data/vendor/libgit2/include/git2/checkout.h +206 -48
  69. data/vendor/libgit2/include/git2/clone.h +72 -27
  70. data/vendor/libgit2/include/git2/commit.h +20 -17
  71. data/vendor/libgit2/include/git2/common.h +67 -1
  72. data/vendor/libgit2/include/git2/config.h +81 -60
  73. data/vendor/libgit2/include/git2/cred_helpers.h +53 -0
  74. data/vendor/libgit2/include/git2/diff.h +459 -150
  75. data/vendor/libgit2/include/git2/errors.h +9 -1
  76. data/vendor/libgit2/include/git2/graph.h +41 -0
  77. data/vendor/libgit2/include/git2/ignore.h +7 -6
  78. data/vendor/libgit2/include/git2/index.h +323 -97
  79. data/vendor/libgit2/include/git2/indexer.h +27 -59
  80. data/vendor/libgit2/include/git2/inttypes.h +4 -0
  81. data/vendor/libgit2/include/git2/merge.h +13 -3
  82. data/vendor/libgit2/include/git2/message.h +14 -8
  83. data/vendor/libgit2/include/git2/net.h +9 -7
  84. data/vendor/libgit2/include/git2/notes.h +88 -29
  85. data/vendor/libgit2/include/git2/object.h +16 -6
  86. data/vendor/libgit2/include/git2/odb.h +80 -17
  87. data/vendor/libgit2/include/git2/odb_backend.h +47 -11
  88. data/vendor/libgit2/include/git2/oid.h +26 -17
  89. data/vendor/libgit2/include/git2/pack.h +62 -8
  90. data/vendor/libgit2/include/git2/push.h +131 -0
  91. data/vendor/libgit2/include/git2/refdb.h +103 -0
  92. data/vendor/libgit2/include/git2/refdb_backend.h +109 -0
  93. data/vendor/libgit2/include/git2/reflog.h +30 -21
  94. data/vendor/libgit2/include/git2/refs.h +215 -193
  95. data/vendor/libgit2/include/git2/refspec.h +22 -2
  96. data/vendor/libgit2/include/git2/remote.h +158 -37
  97. data/vendor/libgit2/include/git2/repository.h +150 -31
  98. data/vendor/libgit2/include/git2/reset.h +43 -9
  99. data/vendor/libgit2/include/git2/revparse.h +48 -4
  100. data/vendor/libgit2/include/git2/revwalk.h +25 -10
  101. data/vendor/libgit2/include/git2/signature.h +20 -12
  102. data/vendor/libgit2/include/git2/stash.h +121 -0
  103. data/vendor/libgit2/include/git2/status.h +122 -53
  104. data/vendor/libgit2/include/git2/strarray.h +17 -11
  105. data/vendor/libgit2/include/git2/submodule.h +42 -7
  106. data/vendor/libgit2/include/git2/tag.h +72 -59
  107. data/vendor/libgit2/include/git2/threads.h +4 -2
  108. data/vendor/libgit2/include/git2/trace.h +68 -0
  109. data/vendor/libgit2/include/git2/transport.h +328 -0
  110. data/vendor/libgit2/include/git2/tree.h +149 -120
  111. data/vendor/libgit2/include/git2/types.h +13 -12
  112. data/vendor/libgit2/include/git2/version.h +3 -3
  113. data/vendor/libgit2/src/amiga/map.c +2 -2
  114. data/vendor/libgit2/src/attr.c +58 -48
  115. data/vendor/libgit2/src/attr.h +4 -18
  116. data/vendor/libgit2/src/attr_file.c +30 -6
  117. data/vendor/libgit2/src/attr_file.h +6 -8
  118. data/vendor/libgit2/src/attrcache.h +24 -0
  119. data/vendor/libgit2/src/blob.c +30 -7
  120. data/vendor/libgit2/src/blob.h +1 -1
  121. data/vendor/libgit2/src/branch.c +361 -68
  122. data/vendor/libgit2/src/branch.h +17 -0
  123. data/vendor/libgit2/src/bswap.h +1 -1
  124. data/vendor/libgit2/src/buf_text.c +291 -0
  125. data/vendor/libgit2/src/buf_text.h +122 -0
  126. data/vendor/libgit2/src/buffer.c +27 -101
  127. data/vendor/libgit2/src/buffer.h +54 -39
  128. data/vendor/libgit2/src/cache.c +15 -6
  129. data/vendor/libgit2/src/cache.h +1 -1
  130. data/vendor/libgit2/src/cc-compat.h +3 -1
  131. data/vendor/libgit2/src/checkout.c +1165 -222
  132. data/vendor/libgit2/src/checkout.h +24 -0
  133. data/vendor/libgit2/src/clone.c +171 -86
  134. data/vendor/libgit2/src/commit.c +44 -45
  135. data/vendor/libgit2/src/commit.h +3 -3
  136. data/vendor/libgit2/src/commit_list.c +194 -0
  137. data/vendor/libgit2/src/commit_list.h +49 -0
  138. data/vendor/libgit2/src/common.h +44 -10
  139. data/vendor/libgit2/src/compress.c +1 -1
  140. data/vendor/libgit2/src/compress.h +1 -1
  141. data/vendor/libgit2/src/config.c +211 -124
  142. data/vendor/libgit2/src/config.h +23 -4
  143. data/vendor/libgit2/src/config_cache.c +2 -2
  144. data/vendor/libgit2/src/config_file.c +129 -53
  145. data/vendor/libgit2/src/config_file.h +10 -8
  146. data/vendor/libgit2/src/crlf.c +66 -67
  147. data/vendor/libgit2/src/date.c +12 -12
  148. data/vendor/libgit2/src/delta-apply.c +14 -1
  149. data/vendor/libgit2/src/delta-apply.h +18 -1
  150. data/vendor/libgit2/src/delta.c +40 -107
  151. data/vendor/libgit2/src/delta.h +19 -17
  152. data/vendor/libgit2/src/diff.c +347 -496
  153. data/vendor/libgit2/src/diff.h +27 -1
  154. data/vendor/libgit2/src/diff_output.c +564 -249
  155. data/vendor/libgit2/src/diff_output.h +15 -8
  156. data/vendor/libgit2/src/diff_tform.c +687 -0
  157. data/vendor/libgit2/src/errors.c +27 -36
  158. data/vendor/libgit2/src/fetch.c +13 -351
  159. data/vendor/libgit2/src/fetch.h +13 -3
  160. data/vendor/libgit2/src/fetchhead.c +295 -0
  161. data/vendor/libgit2/src/fetchhead.h +34 -0
  162. data/vendor/libgit2/src/filebuf.c +42 -15
  163. data/vendor/libgit2/src/filebuf.h +4 -2
  164. data/vendor/libgit2/src/fileops.c +466 -113
  165. data/vendor/libgit2/src/fileops.h +154 -28
  166. data/vendor/libgit2/src/filter.c +3 -75
  167. data/vendor/libgit2/src/filter.h +1 -29
  168. data/vendor/libgit2/src/fnmatch.c +1 -1
  169. data/vendor/libgit2/src/fnmatch.h +1 -1
  170. data/vendor/libgit2/src/global.c +54 -10
  171. data/vendor/libgit2/src/global.h +10 -1
  172. data/vendor/libgit2/src/graph.c +178 -0
  173. data/vendor/libgit2/src/hash.c +25 -52
  174. data/vendor/libgit2/src/hash.h +21 -9
  175. data/vendor/libgit2/src/{sha1/sha1.c → hash/hash_generic.c} +20 -12
  176. data/vendor/libgit2/src/hash/hash_generic.h +24 -0
  177. data/vendor/libgit2/src/hash/hash_openssl.h +45 -0
  178. data/vendor/libgit2/src/hash/hash_win32.c +291 -0
  179. data/vendor/libgit2/src/hash/hash_win32.h +140 -0
  180. data/vendor/libgit2/src/hashsig.c +368 -0
  181. data/vendor/libgit2/src/hashsig.h +72 -0
  182. data/vendor/libgit2/src/ignore.c +22 -15
  183. data/vendor/libgit2/src/ignore.h +6 -1
  184. data/vendor/libgit2/src/index.c +770 -171
  185. data/vendor/libgit2/src/index.h +13 -5
  186. data/vendor/libgit2/src/indexer.c +286 -431
  187. data/vendor/libgit2/src/iterator.c +854 -466
  188. data/vendor/libgit2/src/iterator.h +134 -109
  189. data/vendor/libgit2/src/map.h +1 -1
  190. data/vendor/libgit2/src/merge.c +296 -0
  191. data/vendor/libgit2/src/merge.h +22 -0
  192. data/vendor/libgit2/src/message.c +1 -1
  193. data/vendor/libgit2/src/message.h +1 -1
  194. data/vendor/libgit2/src/mwindow.c +35 -30
  195. data/vendor/libgit2/src/mwindow.h +2 -2
  196. data/vendor/libgit2/src/netops.c +162 -98
  197. data/vendor/libgit2/src/netops.h +50 -15
  198. data/vendor/libgit2/src/notes.c +109 -58
  199. data/vendor/libgit2/src/notes.h +2 -1
  200. data/vendor/libgit2/src/object.c +46 -57
  201. data/vendor/libgit2/src/object.h +1 -8
  202. data/vendor/libgit2/src/odb.c +151 -40
  203. data/vendor/libgit2/src/odb.h +5 -1
  204. data/vendor/libgit2/src/odb_loose.c +4 -5
  205. data/vendor/libgit2/src/odb_pack.c +122 -80
  206. data/vendor/libgit2/src/offmap.h +65 -0
  207. data/vendor/libgit2/src/oid.c +12 -4
  208. data/vendor/libgit2/src/oidmap.h +1 -1
  209. data/vendor/libgit2/src/pack-objects.c +88 -61
  210. data/vendor/libgit2/src/pack-objects.h +8 -8
  211. data/vendor/libgit2/src/pack.c +293 -28
  212. data/vendor/libgit2/src/pack.h +49 -4
  213. data/vendor/libgit2/src/path.c +103 -14
  214. data/vendor/libgit2/src/path.h +23 -7
  215. data/vendor/libgit2/src/pathspec.c +168 -0
  216. data/vendor/libgit2/src/pathspec.h +40 -0
  217. data/vendor/libgit2/src/pool.c +29 -4
  218. data/vendor/libgit2/src/pool.h +8 -1
  219. data/vendor/libgit2/src/posix.c +26 -27
  220. data/vendor/libgit2/src/posix.h +2 -3
  221. data/vendor/libgit2/src/pqueue.c +23 -1
  222. data/vendor/libgit2/src/pqueue.h +23 -1
  223. data/vendor/libgit2/src/push.c +653 -0
  224. data/vendor/libgit2/src/push.h +51 -0
  225. data/vendor/libgit2/src/refdb.c +185 -0
  226. data/vendor/libgit2/src/refdb.h +46 -0
  227. data/vendor/libgit2/src/refdb_fs.c +1024 -0
  228. data/vendor/libgit2/src/refdb_fs.h +15 -0
  229. data/vendor/libgit2/src/reflog.c +77 -45
  230. data/vendor/libgit2/src/reflog.h +1 -3
  231. data/vendor/libgit2/src/refs.c +366 -1326
  232. data/vendor/libgit2/src/refs.h +22 -13
  233. data/vendor/libgit2/src/refspec.c +46 -7
  234. data/vendor/libgit2/src/refspec.h +11 -1
  235. data/vendor/libgit2/src/remote.c +758 -120
  236. data/vendor/libgit2/src/remote.h +10 -5
  237. data/vendor/libgit2/src/repo_template.h +6 -6
  238. data/vendor/libgit2/src/repository.c +315 -96
  239. data/vendor/libgit2/src/repository.h +5 -3
  240. data/vendor/libgit2/src/reset.c +99 -81
  241. data/vendor/libgit2/src/revparse.c +157 -84
  242. data/vendor/libgit2/src/revwalk.c +68 -470
  243. data/vendor/libgit2/src/revwalk.h +44 -0
  244. data/vendor/libgit2/src/sha1_lookup.c +1 -1
  245. data/vendor/libgit2/src/sha1_lookup.h +1 -1
  246. data/vendor/libgit2/src/signature.c +68 -200
  247. data/vendor/libgit2/src/signature.h +1 -1
  248. data/vendor/libgit2/src/stash.c +663 -0
  249. data/vendor/libgit2/src/status.c +101 -79
  250. data/vendor/libgit2/src/strmap.h +1 -1
  251. data/vendor/libgit2/src/submodule.c +67 -51
  252. data/vendor/libgit2/src/submodule.h +1 -1
  253. data/vendor/libgit2/src/tag.c +35 -29
  254. data/vendor/libgit2/src/tag.h +1 -1
  255. data/vendor/libgit2/src/thread-utils.c +1 -1
  256. data/vendor/libgit2/src/thread-utils.h +2 -2
  257. data/vendor/libgit2/src/trace.c +39 -0
  258. data/vendor/libgit2/src/trace.h +56 -0
  259. data/vendor/libgit2/src/transport.c +81 -34
  260. data/vendor/libgit2/src/transports/cred.c +60 -0
  261. data/vendor/libgit2/src/transports/cred_helpers.c +49 -0
  262. data/vendor/libgit2/src/transports/git.c +234 -127
  263. data/vendor/libgit2/src/transports/http.c +761 -433
  264. data/vendor/libgit2/src/transports/local.c +460 -64
  265. data/vendor/libgit2/src/transports/smart.c +345 -0
  266. data/vendor/libgit2/src/transports/smart.h +179 -0
  267. data/vendor/libgit2/src/{pkt.c → transports/smart_pkt.c} +131 -12
  268. data/vendor/libgit2/src/transports/smart_protocol.c +856 -0
  269. data/vendor/libgit2/src/transports/winhttp.c +1136 -0
  270. data/vendor/libgit2/src/tree-cache.c +2 -2
  271. data/vendor/libgit2/src/tree-cache.h +1 -1
  272. data/vendor/libgit2/src/tree.c +239 -166
  273. data/vendor/libgit2/src/tree.h +11 -2
  274. data/vendor/libgit2/src/tsort.c +39 -23
  275. data/vendor/libgit2/src/unix/map.c +1 -1
  276. data/vendor/libgit2/src/unix/posix.h +12 -2
  277. data/vendor/libgit2/src/unix/realpath.c +30 -0
  278. data/vendor/libgit2/src/util.c +250 -13
  279. data/vendor/libgit2/src/util.h +71 -14
  280. data/vendor/libgit2/src/vector.c +123 -60
  281. data/vendor/libgit2/src/vector.h +24 -22
  282. data/vendor/libgit2/src/win32/dir.c +1 -1
  283. data/vendor/libgit2/src/win32/dir.h +1 -1
  284. data/vendor/libgit2/src/win32/error.c +77 -0
  285. data/vendor/libgit2/src/win32/error.h +13 -0
  286. data/vendor/libgit2/src/win32/findfile.c +143 -54
  287. data/vendor/libgit2/src/win32/findfile.h +10 -6
  288. data/vendor/libgit2/src/win32/map.c +1 -1
  289. data/vendor/libgit2/src/win32/mingw-compat.h +1 -1
  290. data/vendor/libgit2/src/win32/msvc-compat.h +10 -1
  291. data/vendor/libgit2/src/win32/posix.h +10 -1
  292. data/vendor/libgit2/src/win32/posix_w32.c +132 -63
  293. data/vendor/libgit2/src/win32/precompiled.c +1 -1
  294. data/vendor/libgit2/src/win32/pthread.c +1 -1
  295. data/vendor/libgit2/src/win32/pthread.h +1 -1
  296. data/vendor/libgit2/src/win32/utf-conv.c +5 -5
  297. data/vendor/libgit2/src/win32/utf-conv.h +3 -3
  298. data/vendor/libgit2/src/win32/version.h +20 -0
  299. metadata +308 -252
  300. data/test/fixtures/testrepo.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  301. data/test/fixtures/testrepo.git/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 +0 -0
  302. data/test/fixtures/testrepo.git/objects/a3/e05719b428a2d0ed7a55c4ce53dcc5768c6d5e +0 -0
  303. data/test/index_test.rb~ +0 -218
  304. data/vendor/libgit2/src/pkt.h +0 -91
  305. data/vendor/libgit2/src/ppc/sha1.c +0 -70
  306. data/vendor/libgit2/src/ppc/sha1.h +0 -26
  307. data/vendor/libgit2/src/protocol.c +0 -110
  308. data/vendor/libgit2/src/protocol.h +0 -21
  309. data/vendor/libgit2/src/sha1.h +0 -33
  310. data/vendor/libgit2/src/transport.h +0 -148
data/test/tag_test.rb CHANGED
@@ -1,12 +1,9 @@
1
1
  require "test_helper"
2
2
 
3
- context "Rugged::Tag tests" do
4
- setup do
5
- @path = File.dirname(__FILE__) + '/fixtures/testrepo.git/'
6
- @repo = Rugged::Repository.new(@path)
7
- end
3
+ class TagTest < Rugged::TestCase
4
+ include Rugged::RepositoryAccess
8
5
 
9
- test "can read the tag data" do
6
+ def test_reading_a_tag
10
7
  oid = "0c37a5391bbff43c37f0d0371823a5509eed5b1d"
11
8
  obj = @repo.lookup(oid)
12
9
 
@@ -22,10 +19,50 @@ context "Rugged::Tag tests" do
22
19
  assert_equal "schacon@gmail.com", c[:email]
23
20
  end
24
21
 
25
- test "can read the tag oid only" do
22
+ def test_reading_the_oid_of_a_tag
26
23
  oid = "0c37a5391bbff43c37f0d0371823a5509eed5b1d"
27
24
  obj = @repo.lookup(oid)
28
25
 
29
26
  assert_equal "5b5b025afb0b4c913b4c338a42934a3863bf3644", obj.target_oid
30
27
  end
31
28
  end
29
+
30
+ class TagWriteTest < Rugged::TestCase
31
+ include Rugged::TempRepositoryAccess
32
+
33
+ def test_writing_a_tag
34
+ person = {:name => 'Scott', :email => 'schacon@gmail.com', :time => Time.now }
35
+ tag_oid = Rugged::Tag.create(@repo,
36
+ :name => 'tag',
37
+ :message => "test tag message\n",
38
+ :target => "5b5b025afb0b4c913b4c338a42934a3863bf3644",
39
+ :tagger => person)
40
+
41
+ tag = @repo.lookup(tag_oid)
42
+ assert_equal :tag, tag.type
43
+ assert_equal "5b5b025afb0b4c913b4c338a42934a3863bf3644", tag.target.oid
44
+ assert_equal "test tag message\n", tag.message
45
+ assert_equal "Scott", tag.tagger[:name]
46
+ assert_equal "schacon@gmail.com", tag.tagger[:email]
47
+ end
48
+
49
+ def test_tag_invalid_message_type
50
+ person = {:name => 'Scott', :email => 'schacon@gmail.com', :time => Time.now }
51
+ assert_raises TypeError do
52
+ Rugged::Tag.create(@repo,
53
+ :name => 'tag',
54
+ :message => :invalid_message,
55
+ :target => "5b5b025afb0b4c913b4c338a42934a3863bf3644",
56
+ :tagger => person)
57
+ end
58
+ end
59
+
60
+ def test_writing_light_tags
61
+ Rugged::Tag.create(@repo,
62
+ :name => 'tag',
63
+ :target => "5b5b025afb0b4c913b4c338a42934a3863bf3644")
64
+
65
+ tag = Rugged::Reference.lookup(@repo, "refs/tags/tag")
66
+ assert_equal "5b5b025afb0b4c913b4c338a42934a3863bf3644", tag.target
67
+ end
68
+ end
data/test/test_helper.rb CHANGED
@@ -1,61 +1,89 @@
1
+ require 'rubygems'
2
+
1
3
  require 'tempfile'
2
4
  require 'tmpdir'
3
- require 'rubygems'
4
5
  require 'minitest/autorun'
5
6
  require 'rugged'
6
7
  require 'pp'
7
8
 
8
- # backwards compat with test/spec/mini 3
9
- alias :context :describe
10
-
11
9
  module Rugged
12
- class TestCase < MiniTest::Spec
10
+ class TestCase < MiniTest::Unit::TestCase
13
11
  TEST_DIR = File.dirname(File.expand_path(__FILE__))
14
12
 
15
- class << self
16
- # backwards compat with test/spec/mini 3
17
- alias :setup :before
18
- alias :teardown :after
19
- alias :test :it
20
- end
13
+ protected
14
+ def with_default_encoding(encoding, &block)
15
+ old_encoding = Encoding.default_internal
21
16
 
22
- # backwards compat with test/unit
23
- alias :assert_not_nil :refute_nil
24
- alias :assert_raise :assert_raises
17
+ new_encoding = Encoding.find(encoding)
18
+ Encoding.default_internal = new_encoding
25
19
 
26
- private
20
+ yield new_encoding
27
21
 
28
- def temp_repo(repo)
29
- dir = Dir.mktmpdir 'dir'
30
- repo_dir = File.join(TEST_DIR, (File.join('fixtures', repo, '.')))
31
- `git clone #{repo_dir} #{dir}`
32
- dir
22
+ Encoding.default_internal = old_encoding
33
23
  end
24
+ end
34
25
 
35
- def rm_loose(oid)
36
- base_path = File.join(@path, "objects", oid[0, 2])
26
+ class SandboxedTestCase < TestCase
27
+ def setup
28
+ super
29
+ @_sandbox_path = Dir.mktmpdir("rugged_sandbox")
30
+ end
37
31
 
38
- file = File.join(base_path, oid[2, 38])
39
- dir_contents = File.join(base_path, "*")
32
+ def teardown
33
+ FileUtils.remove_entry_secure @_sandbox_path
34
+ super
35
+ end
40
36
 
41
- File.delete(file)
37
+ # Fills the current sandbox folder with the files
38
+ # found in the given repository
39
+ def sandbox_init(repository)
40
+ FileUtils.cp_r(File.join(TestCase::TEST_DIR, 'fixtures', repository), @_sandbox_path)
42
41
 
43
- if Dir[dir_contents].empty?
44
- Dir.delete(base_path)
42
+ Dir.chdir(File.join(@_sandbox_path, repository)) do
43
+ File.rename(".gitted", ".git") if File.exists?(".gitted")
44
+ File.rename("gitattributes", ".gitattributes") if File.exists?("gitattributes")
45
+ File.rename("gitignore", ".gitignore") if File.exists?("gitignore")
45
46
  end
47
+
48
+ Rugged::Repository.new(File.join(@_sandbox_path, repository))
46
49
  end
47
50
 
48
- def with_default_encoding(encoding, &block)
49
- old_encoding = Encoding.default_internal
51
+ def sandbox_clone(repository, name)
52
+ Dir.chdir(@_sandbox_path) do
53
+ `git clone #{repository} #{name}`
54
+ end
50
55
 
51
- new_encoding = Encoding.find(encoding)
52
- Encoding.default_internal = new_encoding
56
+ Rugged::Repository.new(File.join(@_sandbox_path, name))
57
+ end
58
+ end
53
59
 
54
- yield new_encoding
60
+ module RepositoryAccess
61
+ def setup
62
+ @path = File.dirname(__FILE__) + '/fixtures/testrepo.git/'
63
+ @repo = Rugged::Repository.new(@path)
64
+ end
65
+ end
55
66
 
56
- Encoding.default_internal = old_encoding
67
+ module TempRepositoryAccess
68
+ def setup
69
+ @path = temp_repo("testrepo.git")
70
+ @repo = Rugged::Repository.new(@path)
71
+ end
72
+
73
+ def teardown
74
+ destroy_temp_repo(@path)
75
+ end
76
+
77
+ protected
78
+ def temp_repo(repo)
79
+ dir = Dir.mktmpdir 'dir'
80
+ repo_dir = File.join(TestCase::TEST_DIR, (File.join('fixtures', repo, '.')))
81
+ `git clone #{repo_dir} #{dir}`
82
+ dir
83
+ end
84
+
85
+ def destroy_temp_repo(path)
86
+ FileUtils.remove_entry_secure(path)
57
87
  end
58
88
  end
59
89
  end
60
-
61
- MiniTest::Spec.register_spec_type(/./, Rugged::TestCase)
data/test/tree_test.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  require "test_helper"
2
2
 
3
- context "Rugged::Tree tests" do
4
- setup do
5
- path = File.dirname(__FILE__) + '/fixtures/testrepo.git/'
6
- @repo = Rugged::Repository.new(path)
3
+ class TreeTest < Rugged::TestCase
4
+ include Rugged::RepositoryAccess
5
+
6
+ def setup
7
+ super
7
8
  @oid = "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"
8
9
  @tree = @repo.lookup(@oid)
9
10
  end
10
11
 
11
- test "can read the tree data" do
12
+ def test_read_tree_data
12
13
  assert_equal @oid, @tree.oid
13
14
  assert_equal :tree, @tree.type
14
15
  assert_equal 3, @tree.count
@@ -16,7 +17,7 @@ context "Rugged::Tree tests" do
16
17
  assert_equal "fa49b077972391ad58037050f2a75f74e3671e92", @tree[1][:oid]
17
18
  end
18
19
 
19
- test "can read the tree entry data" do
20
+ def test_read_tree_entry_data
20
21
  bent = @tree[0]
21
22
  tent = @tree[2]
22
23
 
@@ -30,7 +31,24 @@ context "Rugged::Tree tests" do
30
31
  assert_equal :tree, @repo.lookup(tent[:oid]).type
31
32
  end
32
33
 
33
- test "can iterate over the tree" do
34
+ def test_get_entry_by_oid
35
+ bent = @tree.get_entry_by_oid("1385f264afb75a56a5bec74243be9b367ba4ca08")
36
+ assert_equal "README", bent[:name]
37
+ assert_equal :blob, bent[:type]
38
+ end
39
+
40
+ def test_get_entry_by_oid_returns_nil_if_no_oid
41
+ nada = @tree.get_entry_by_oid("1385f264afb75a56a5bec74243be9b367ba4ca07")
42
+ assert_equal nil, nada
43
+ end
44
+
45
+ def test_get_entry_by_oid_throws_error_if_wrong_type
46
+ assert_raises TypeError do
47
+ @tree.get_entry_by_oid(:not_a_string)
48
+ end
49
+ end
50
+
51
+ def test_tree_iteration
34
52
  enum_test = @tree.sort { |a, b| a[:oid] <=> b[:oid] }.map { |e| e[:name] }.join(':')
35
53
  assert_equal "README:subdir:new.txt", enum_test
36
54
 
@@ -38,23 +56,27 @@ context "Rugged::Tree tests" do
38
56
  assert enum.kind_of? Enumerable
39
57
  end
40
58
 
41
- test "can walk the tree, yielding only trees" do
59
+ def test_tree_walk_only_trees
42
60
  @tree.walk_trees {|root, entry| assert_equal :tree, entry[:type]}
43
61
  end
44
62
 
45
- test "can walk the tree, yielding only blobs" do
63
+ def test_tree_walk_only_blobs
46
64
  @tree.walk_blobs {|root, entry| assert_equal :blob, entry[:type]}
47
65
  end
48
66
 
49
- test "can iterate over the subtrees a tree" do
67
+ def test_iterate_subtrees
50
68
  @tree.each_tree {|tree| assert_equal :tree, tree[:type]}
51
69
  end
52
70
 
53
- test "can iterate over the subtrees a tree" do
71
+ def test_iterate_subtree_blobs
54
72
  @tree.each_blob {|tree| assert_equal :blob, tree[:type]}
55
73
  end
74
+ end
75
+
76
+ class TreeWriteTest < Rugged::TestCase
77
+ include Rugged::TempRepositoryAccess
56
78
 
57
- test "can write the tree data" do
79
+ def test_write_tree_data
58
80
  entry = {:type => :blob,
59
81
  :name => "README.txt",
60
82
  :oid => "1385f264afb75a56a5bec74243be9b367ba4ca08",
@@ -66,5 +88,4 @@ context "Rugged::Tree tests" do
66
88
  obj = @repo.lookup(sha)
67
89
  assert_equal 38, obj.read_raw.len
68
90
  end
69
-
70
91
  end
data/test/walker_test.rb CHANGED
@@ -1,21 +1,22 @@
1
1
  require "test_helper"
2
2
  require 'base64'
3
3
 
4
- context "Rugged::Walker stuff" do
5
- setup do
6
- @path = File.dirname(__FILE__) + '/fixtures/testrepo.git/'
7
- @repo = Rugged::Repository.new(@path)
4
+ class WalkerTest < Rugged::TestCase
5
+ include Rugged::RepositoryAccess
6
+
7
+ def setup
8
+ super
8
9
  @walker = Rugged::Walker.new(@repo)
9
10
  end
10
11
 
11
- test "can walk a simple revlist" do
12
+ def test_walk_revlist
12
13
  @walker.push("9fd738e8f7967c078dceed8190330fc8648ee56a")
13
14
  data = @walker.each.to_a
14
15
  oids = data.sort { |a, b| a.oid <=> b.oid }.map {|a| a.oid[0,5]}.join('.')
15
16
  assert_equal "4a202.5b5b0.84960.9fd73", oids
16
17
  end
17
18
 
18
- test "can walk a part of a revlist" do
19
+ def test_walk_partial_revlist
19
20
  oid = "8496071c1b46c854b31185ea97743be6a8774479"
20
21
  @walker.push(oid)
21
22
  walk = @walker.each.to_a
@@ -23,14 +24,14 @@ context "Rugged::Walker stuff" do
23
24
  assert_equal 1, walk.count
24
25
  end
25
26
 
26
- test "can hide part of a list" do
27
+ def test_hide_part_of_list
27
28
  @walker.push("9fd738e8f7967c078dceed8190330fc8648ee56a")
28
29
  @walker.hide("5b5b025afb0b4c913b4c338a42934a3863bf3644")
29
30
  assert_equal 2, @walker.each.count
30
31
  end
31
32
 
32
33
  # resetting a walker emtpies the walking queue
33
- test "can reset a walker" do
34
+ def test_resetting_walker
34
35
  oid = "8496071c1b46c854b31185ea97743be6a8774479"
35
36
  @walker.push(oid)
36
37
  walk = @walker.each.to_a
@@ -41,7 +42,7 @@ context "Rugged::Walker stuff" do
41
42
  assert_equal 0, walk.count
42
43
  end
43
44
 
44
- test "can enumerable" do
45
+ def test_walk_is_enumberable
45
46
  @walker.push("9fd738e8f7967c078dceed8190330fc8648ee56a")
46
47
  enum = @walker.sort { |a, b| a.oid <=> b.oid }.map { |a| a.oid[0, 4] }.join('.')
47
48
  assert_equal "4a20.5b5b.8496.9fd7", enum
@@ -65,24 +66,23 @@ context "Rugged::Walker stuff" do
65
66
  end
66
67
  end
67
68
 
68
- test "can sort order by date" do
69
+ def test_sort_by_date
69
70
  time = revlist_with_sorting(Rugged::SORT_DATE)
70
71
  assert_equal "a4a7d.c4780.9fd73.4a202.5b5b0.84960", time
71
72
  end
72
73
 
73
- test "can sort order by topo" do
74
+ def test_sort_by_topo
74
75
  sort_list = do_sort(Rugged::SORT_TOPO)
75
76
  assert_equal is_toposorted(sort_list), true
76
77
  end
77
78
 
78
- test "can sort order by date reversed" do
79
+ def test_sort_by_date_reversed
79
80
  time = revlist_with_sorting(Rugged::SORT_DATE | Rugged::SORT_REVERSE)
80
81
  assert_equal "84960.5b5b0.4a202.9fd73.c4780.a4a7d", time
81
82
  end
82
83
 
83
- test "can sort order by topo reversed" do
84
+ def test_sort_by_topo_reverse
84
85
  sort_list = do_sort(Rugged::SORT_TOPO | Rugged::SORT_REVERSE).reverse
85
86
  assert_equal is_toposorted(sort_list), true
86
87
  end
87
-
88
88
  end
@@ -15,7 +15,7 @@ INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
15
15
  DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES)
16
16
  CFLAGS= -g $(DEFINES) -Wall -Wextra -O2 $(EXTRA_CFLAGS)
17
17
 
18
- SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) $(wildcard src/sha1/*.c)
18
+ SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c
19
19
 
20
20
  ifeq ($(PLATFORM),Msys)
21
21
  SRCS += $(wildcard src/win32/*.c) $(wildcard src/compat/*.c) deps/regex/regex.c
@@ -21,61 +21,100 @@
21
21
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
22
  * IN THE SOFTWARE.
23
23
  */
24
- #include <http_parser.h>
24
+ #include "http_parser.h"
25
25
  #include <assert.h>
26
26
  #include <stddef.h>
27
+ #include <ctype.h>
28
+ #include <stdlib.h>
29
+ #include <string.h>
30
+ #include <limits.h>
27
31
 
32
+ #ifndef ULLONG_MAX
33
+ # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
34
+ #endif
28
35
 
29
36
  #ifndef MIN
30
37
  # define MIN(a,b) ((a) < (b) ? (a) : (b))
31
38
  #endif
32
39
 
40
+ #ifndef ARRAY_SIZE
41
+ # define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
42
+ #endif
43
+
44
+ #ifndef BIT_AT
45
+ # define BIT_AT(a, i) \
46
+ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
47
+ (1 << ((unsigned int) (i) & 7))))
48
+ #endif
49
+
50
+ #ifndef ELEM_AT
51
+ # define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
52
+ #endif
33
53
 
34
- #if HTTP_PARSER_DEBUG
35
- #define SET_ERRNO(e) \
36
- do { \
37
- parser->http_errno = (e); \
38
- parser->error_lineno = __LINE__; \
39
- } while (0)
40
- #else
41
54
  #define SET_ERRNO(e) \
42
55
  do { \
43
56
  parser->http_errno = (e); \
44
57
  } while(0)
45
- #endif
46
58
 
47
59
 
48
- #define CALLBACK2(FOR) \
60
+ /* Run the notify callback FOR, returning ER if it fails */
61
+ #define CALLBACK_NOTIFY_(FOR, ER) \
49
62
  do { \
63
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
64
+ \
50
65
  if (settings->on_##FOR) { \
51
66
  if (0 != settings->on_##FOR(parser)) { \
52
67
  SET_ERRNO(HPE_CB_##FOR); \
53
- return (p - data); \
68
+ } \
69
+ \
70
+ /* We either errored above or got paused; get out */ \
71
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
72
+ return (ER); \
54
73
  } \
55
74
  } \
56
75
  } while (0)
57
76
 
77
+ /* Run the notify callback FOR and consume the current byte */
78
+ #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
58
79
 
59
- #define MARK(FOR) \
60
- do { \
61
- FOR##_mark = p; \
62
- } while (0)
80
+ /* Run the notify callback FOR and don't consume the current byte */
81
+ #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
63
82
 
64
- #define CALLBACK(FOR) \
83
+ /* Run data callback FOR with LEN bytes, returning ER if it fails */
84
+ #define CALLBACK_DATA_(FOR, LEN, ER) \
65
85
  do { \
86
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
87
+ \
66
88
  if (FOR##_mark) { \
67
89
  if (settings->on_##FOR) { \
68
- if (0 != settings->on_##FOR(parser, \
69
- FOR##_mark, \
70
- p - FOR##_mark)) \
71
- { \
90
+ if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \
72
91
  SET_ERRNO(HPE_CB_##FOR); \
73
- return (p - data); \
92
+ } \
93
+ \
94
+ /* We either errored above or got paused; get out */ \
95
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
96
+ return (ER); \
74
97
  } \
75
98
  } \
76
99
  FOR##_mark = NULL; \
77
100
  } \
78
101
  } while (0)
102
+
103
+ /* Run the data callback FOR and consume the current byte */
104
+ #define CALLBACK_DATA(FOR) \
105
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
106
+
107
+ /* Run the data callback FOR and don't consume the current byte */
108
+ #define CALLBACK_DATA_NOADVANCE(FOR) \
109
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
110
+
111
+ /* Set the mark FOR; non-destructive if mark is already set */
112
+ #define MARK(FOR) \
113
+ do { \
114
+ if (!FOR##_mark) { \
115
+ FOR##_mark = p; \
116
+ } \
117
+ } while (0)
79
118
 
80
119
 
81
120
  #define PROXY_CONNECTION "proxy-connection"
@@ -89,30 +128,10 @@ do { \
89
128
 
90
129
 
91
130
  static const char *method_strings[] =
92
- { "DELETE"
93
- , "GET"
94
- , "HEAD"
95
- , "POST"
96
- , "PUT"
97
- , "CONNECT"
98
- , "OPTIONS"
99
- , "TRACE"
100
- , "COPY"
101
- , "LOCK"
102
- , "MKCOL"
103
- , "MOVE"
104
- , "PROPFIND"
105
- , "PROPPATCH"
106
- , "UNLOCK"
107
- , "REPORT"
108
- , "MKACTIVITY"
109
- , "CHECKOUT"
110
- , "MERGE"
111
- , "M-SEARCH"
112
- , "NOTIFY"
113
- , "SUBSCRIBE"
114
- , "UNSUBSCRIBE"
115
- , "PATCH"
131
+ {
132
+ #define XX(num, name, string) #string,
133
+ HTTP_METHOD_MAP(XX)
134
+ #undef XX
116
135
  };
117
136
 
118
137
 
@@ -133,9 +152,9 @@ static const char tokens[256] = {
133
152
  /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
134
153
  0, 0, 0, 0, 0, 0, 0, 0,
135
154
  /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
136
- ' ', '!', '"', '#', '$', '%', '&', '\'',
155
+ 0, '!', 0, '#', '$', '%', '&', '\'',
137
156
  /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
138
- 0, 0, '*', '+', 0, '-', '.', '/',
157
+ 0, 0, '*', '+', 0, '-', '.', 0,
139
158
  /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
140
159
  '0', '1', '2', '3', '4', '5', '6', '7',
141
160
  /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
@@ -155,7 +174,7 @@ static const char tokens[256] = {
155
174
  /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
156
175
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
157
176
  /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
158
- 'x', 'y', 'z', 0, '|', '}', '~', 0 };
177
+ 'x', 'y', 'z', 0, '|', 0, '~', 0 };
159
178
 
160
179
 
161
180
  static const int8_t unhex[256] =
@@ -170,40 +189,48 @@ static const int8_t unhex[256] =
170
189
  };
171
190
 
172
191
 
173
- static const uint8_t normal_url_char[256] = {
192
+ #if HTTP_PARSER_STRICT
193
+ # define T(v) 0
194
+ #else
195
+ # define T(v) v
196
+ #endif
197
+
198
+
199
+ static const uint8_t normal_url_char[32] = {
174
200
  /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
175
- 0, 0, 0, 0, 0, 0, 0, 0,
201
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
176
202
  /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
177
- 0, 0, 0, 0, 0, 0, 0, 0,
203
+ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
178
204
  /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
179
- 0, 0, 0, 0, 0, 0, 0, 0,
205
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
180
206
  /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
181
- 0, 0, 0, 0, 0, 0, 0, 0,
207
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
182
208
  /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
183
- 0, 1, 1, 0, 1, 1, 1, 1,
209
+ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
184
210
  /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
185
- 1, 1, 1, 1, 1, 1, 1, 1,
211
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
186
212
  /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
187
- 1, 1, 1, 1, 1, 1, 1, 1,
213
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
188
214
  /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
189
- 1, 1, 1, 1, 1, 1, 1, 0,
215
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
190
216
  /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
191
- 1, 1, 1, 1, 1, 1, 1, 1,
217
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
192
218
  /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
193
- 1, 1, 1, 1, 1, 1, 1, 1,
219
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
194
220
  /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
195
- 1, 1, 1, 1, 1, 1, 1, 1,
221
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
196
222
  /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
197
- 1, 1, 1, 1, 1, 1, 1, 1,
223
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
198
224
  /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
199
- 1, 1, 1, 1, 1, 1, 1, 1,
225
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
200
226
  /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
201
- 1, 1, 1, 1, 1, 1, 1, 1,
227
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
202
228
  /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
203
- 1, 1, 1, 1, 1, 1, 1, 1,
229
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
204
230
  /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
205
- 1, 1, 1, 1, 1, 1, 1, 0, };
231
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
206
232
 
233
+ #undef T
207
234
 
208
235
  enum state
209
236
  { s_dead = 1 /* important that this is > 0 */
@@ -231,8 +258,9 @@ enum state
231
258
  , s_req_schema
232
259
  , s_req_schema_slash
233
260
  , s_req_schema_slash_slash
234
- , s_req_host
235
- , s_req_port
261
+ , s_req_server_start
262
+ , s_req_server
263
+ , s_req_server_with_at
236
264
  , s_req_path
237
265
  , s_req_query_string_start
238
266
  , s_req_query_string
@@ -261,9 +289,11 @@ enum state
261
289
  , s_chunk_size
262
290
  , s_chunk_parameters
263
291
  , s_chunk_size_almost_done
264
-
292
+
265
293
  , s_headers_almost_done
266
- /* Important: 's_headers_almost_done' must be the last 'header' state. All
294
+ , s_headers_done
295
+
296
+ /* Important: 's_headers_done' must be the last 'header' state. All
267
297
  * states beyond this must be 'body' states. It is used for overflow
268
298
  * checking. See the PARSING_HEADER() macro.
269
299
  */
@@ -274,10 +304,12 @@ enum state
274
304
 
275
305
  , s_body_identity
276
306
  , s_body_identity_eof
307
+
308
+ , s_message_done
277
309
  };
278
310
 
279
311
 
280
- #define PARSING_HEADER(state) (state <= s_headers_almost_done)
312
+ #define PARSING_HEADER(state) (state <= s_headers_done)
281
313
 
282
314
 
283
315
  enum header_states
@@ -306,22 +338,43 @@ enum header_states
306
338
  , h_connection_close
307
339
  };
308
340
 
341
+ enum http_host_state
342
+ {
343
+ s_http_host_dead = 1
344
+ , s_http_userinfo_start
345
+ , s_http_userinfo
346
+ , s_http_host_start
347
+ , s_http_host_v6_start
348
+ , s_http_host
349
+ , s_http_host_v6
350
+ , s_http_host_v6_end
351
+ , s_http_host_port_start
352
+ , s_http_host_port
353
+ };
309
354
 
310
355
  /* Macros for character classes; depends on strict-mode */
311
356
  #define CR '\r'
312
357
  #define LF '\n'
313
358
  #define LOWER(c) (unsigned char)(c | 0x20)
314
- #define TOKEN(c) (tokens[(unsigned char)c])
315
359
  #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
316
360
  #define IS_NUM(c) ((c) >= '0' && (c) <= '9')
317
361
  #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
362
+ #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
363
+ #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
364
+ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
365
+ (c) == ')')
366
+ #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
367
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
368
+ (c) == '$' || (c) == ',')
318
369
 
319
370
  #if HTTP_PARSER_STRICT
320
- #define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)])
371
+ #define TOKEN(c) (tokens[(unsigned char)c])
372
+ #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
321
373
  #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
322
374
  #else
375
+ #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
323
376
  #define IS_URL_CHAR(c) \
324
- (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
377
+ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
325
378
  #define IS_HOST_CHAR(c) \
326
379
  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
327
380
  #endif
@@ -355,6 +408,166 @@ static struct {
355
408
  };
356
409
  #undef HTTP_STRERROR_GEN
357
410
 
411
+ int http_message_needs_eof(const http_parser *parser);
412
+
413
+ /* Our URL parser.
414
+ *
415
+ * This is designed to be shared by http_parser_execute() for URL validation,
416
+ * hence it has a state transition + byte-for-byte interface. In addition, it
417
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
418
+ * work of turning state transitions URL components for its API.
419
+ *
420
+ * This function should only be invoked with non-space characters. It is
421
+ * assumed that the caller cares about (and can detect) the transition between
422
+ * URL and non-URL states by looking for these.
423
+ */
424
+ static enum state
425
+ parse_url_char(enum state s, const char ch)
426
+ {
427
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
428
+ return s_dead;
429
+ }
430
+
431
+ #if HTTP_PARSER_STRICT
432
+ if (ch == '\t' || ch == '\f') {
433
+ return s_dead;
434
+ }
435
+ #endif
436
+
437
+ switch (s) {
438
+ case s_req_spaces_before_url:
439
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
440
+ * All methods except CONNECT are followed by '/' or '*'.
441
+ */
442
+
443
+ if (ch == '/' || ch == '*') {
444
+ return s_req_path;
445
+ }
446
+
447
+ if (IS_ALPHA(ch)) {
448
+ return s_req_schema;
449
+ }
450
+
451
+ break;
452
+
453
+ case s_req_schema:
454
+ if (IS_ALPHA(ch)) {
455
+ return s;
456
+ }
457
+
458
+ if (ch == ':') {
459
+ return s_req_schema_slash;
460
+ }
461
+
462
+ break;
463
+
464
+ case s_req_schema_slash:
465
+ if (ch == '/') {
466
+ return s_req_schema_slash_slash;
467
+ }
468
+
469
+ break;
470
+
471
+ case s_req_schema_slash_slash:
472
+ if (ch == '/') {
473
+ return s_req_server_start;
474
+ }
475
+
476
+ break;
477
+
478
+ case s_req_server_with_at:
479
+ if (ch == '@') {
480
+ return s_dead;
481
+ }
482
+
483
+ /* FALLTHROUGH */
484
+ case s_req_server_start:
485
+ case s_req_server:
486
+ if (ch == '/') {
487
+ return s_req_path;
488
+ }
489
+
490
+ if (ch == '?') {
491
+ return s_req_query_string_start;
492
+ }
493
+
494
+ if (ch == '@') {
495
+ return s_req_server_with_at;
496
+ }
497
+
498
+ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
499
+ return s_req_server;
500
+ }
501
+
502
+ break;
503
+
504
+ case s_req_path:
505
+ if (IS_URL_CHAR(ch)) {
506
+ return s;
507
+ }
508
+
509
+ switch (ch) {
510
+ case '?':
511
+ return s_req_query_string_start;
512
+
513
+ case '#':
514
+ return s_req_fragment_start;
515
+ }
516
+
517
+ break;
518
+
519
+ case s_req_query_string_start:
520
+ case s_req_query_string:
521
+ if (IS_URL_CHAR(ch)) {
522
+ return s_req_query_string;
523
+ }
524
+
525
+ switch (ch) {
526
+ case '?':
527
+ /* allow extra '?' in query string */
528
+ return s_req_query_string;
529
+
530
+ case '#':
531
+ return s_req_fragment_start;
532
+ }
533
+
534
+ break;
535
+
536
+ case s_req_fragment_start:
537
+ if (IS_URL_CHAR(ch)) {
538
+ return s_req_fragment;
539
+ }
540
+
541
+ switch (ch) {
542
+ case '?':
543
+ return s_req_fragment;
544
+
545
+ case '#':
546
+ return s;
547
+ }
548
+
549
+ break;
550
+
551
+ case s_req_fragment:
552
+ if (IS_URL_CHAR(ch)) {
553
+ return s;
554
+ }
555
+
556
+ switch (ch) {
557
+ case '?':
558
+ case '#':
559
+ return s;
560
+ }
561
+
562
+ break;
563
+
564
+ default:
565
+ break;
566
+ }
567
+
568
+ /* We should never fall out of the switch above unless there's an error */
569
+ return s_dead;
570
+ }
358
571
 
359
572
  size_t http_parser_execute (http_parser *parser,
360
573
  const http_parser_settings *settings,
@@ -363,27 +576,24 @@ size_t http_parser_execute (http_parser *parser,
363
576
  {
364
577
  char c, ch;
365
578
  int8_t unhex_val;
366
- const char *p = data, *pe;
367
- size_t to_read;
368
- enum state state;
369
- enum header_states header_state;
370
- size_t index = parser->index;
371
- size_t nread = parser->nread;
372
- const char *header_field_mark, *header_value_mark, *url_mark;
373
- const char *matcher;
579
+ const char *p = data;
580
+ const char *header_field_mark = 0;
581
+ const char *header_value_mark = 0;
582
+ const char *url_mark = 0;
583
+ const char *body_mark = 0;
374
584
 
375
585
  /* We're in an error state. Don't bother doing anything. */
376
586
  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
377
587
  return 0;
378
588
  }
379
589
 
380
- state = (enum state) parser->state;
381
- header_state = (enum header_states) parser->header_state;
382
-
383
590
  if (len == 0) {
384
- switch (state) {
591
+ switch (parser->state) {
385
592
  case s_body_identity_eof:
386
- CALLBACK2(message_complete);
593
+ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
594
+ * we got paused.
595
+ */
596
+ CALLBACK_NOTIFY_NOADVANCE(message_complete);
387
597
  return 0;
388
598
 
389
599
  case s_dead:
@@ -398,42 +608,49 @@ size_t http_parser_execute (http_parser *parser,
398
608
  }
399
609
  }
400
610
 
401
- /* technically we could combine all of these (except for url_mark) into one
402
- variable, saving stack space, but it seems more clear to have them
403
- separated. */
404
- header_field_mark = 0;
405
- header_value_mark = 0;
406
- url_mark = 0;
407
611
 
408
- if (state == s_header_field)
612
+ if (parser->state == s_header_field)
409
613
  header_field_mark = data;
410
- if (state == s_header_value)
614
+ if (parser->state == s_header_value)
411
615
  header_value_mark = data;
412
- if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
413
- || state == s_req_schema_slash_slash || state == s_req_port
414
- || state == s_req_query_string_start || state == s_req_query_string
415
- || state == s_req_host
416
- || state == s_req_fragment_start || state == s_req_fragment)
616
+ switch (parser->state) {
617
+ case s_req_path:
618
+ case s_req_schema:
619
+ case s_req_schema_slash:
620
+ case s_req_schema_slash_slash:
621
+ case s_req_server_start:
622
+ case s_req_server:
623
+ case s_req_server_with_at:
624
+ case s_req_query_string_start:
625
+ case s_req_query_string:
626
+ case s_req_fragment_start:
627
+ case s_req_fragment:
417
628
  url_mark = data;
629
+ break;
630
+ }
418
631
 
419
- for (p=data, pe=data+len; p != pe; p++) {
632
+ for (p=data; p != data + len; p++) {
420
633
  ch = *p;
421
634
 
422
- if (PARSING_HEADER(state)) {
423
- ++nread;
635
+ if (PARSING_HEADER(parser->state)) {
636
+ ++parser->nread;
424
637
  /* Buffer overflow attack */
425
- if (nread > HTTP_MAX_HEADER_SIZE) {
638
+ if (parser->nread > HTTP_MAX_HEADER_SIZE) {
426
639
  SET_ERRNO(HPE_HEADER_OVERFLOW);
427
640
  goto error;
428
641
  }
429
642
  }
430
643
 
431
- switch (state) {
644
+ reexecute_byte:
645
+ switch (parser->state) {
432
646
 
433
647
  case s_dead:
434
648
  /* this state is used after a 'Connection: close' message
435
649
  * the parser will error out if it reads another message
436
650
  */
651
+ if (ch == CR || ch == LF)
652
+ break;
653
+
437
654
  SET_ERRNO(HPE_CLOSED_CONNECTION);
438
655
  goto error;
439
656
 
@@ -442,23 +659,25 @@ size_t http_parser_execute (http_parser *parser,
442
659
  if (ch == CR || ch == LF)
443
660
  break;
444
661
  parser->flags = 0;
445
- parser->content_length = -1;
662
+ parser->content_length = ULLONG_MAX;
446
663
 
447
- CALLBACK2(message_begin);
664
+ if (ch == 'H') {
665
+ parser->state = s_res_or_resp_H;
448
666
 
449
- if (ch == 'H')
450
- state = s_res_or_resp_H;
451
- else {
667
+ CALLBACK_NOTIFY(message_begin);
668
+ } else {
452
669
  parser->type = HTTP_REQUEST;
453
- goto start_req_method_assign;
670
+ parser->state = s_start_req;
671
+ goto reexecute_byte;
454
672
  }
673
+
455
674
  break;
456
675
  }
457
676
 
458
677
  case s_res_or_resp_H:
459
678
  if (ch == 'T') {
460
679
  parser->type = HTTP_RESPONSE;
461
- state = s_res_HT;
680
+ parser->state = s_res_HT;
462
681
  } else {
463
682
  if (ch != 'E') {
464
683
  SET_ERRNO(HPE_INVALID_CONSTANT);
@@ -467,21 +686,19 @@ size_t http_parser_execute (http_parser *parser,
467
686
 
468
687
  parser->type = HTTP_REQUEST;
469
688
  parser->method = HTTP_HEAD;
470
- index = 2;
471
- state = s_req_method;
689
+ parser->index = 2;
690
+ parser->state = s_req_method;
472
691
  }
473
692
  break;
474
693
 
475
694
  case s_start_res:
476
695
  {
477
696
  parser->flags = 0;
478
- parser->content_length = -1;
479
-
480
- CALLBACK2(message_begin);
697
+ parser->content_length = ULLONG_MAX;
481
698
 
482
699
  switch (ch) {
483
700
  case 'H':
484
- state = s_res_H;
701
+ parser->state = s_res_H;
485
702
  break;
486
703
 
487
704
  case CR:
@@ -492,44 +709,46 @@ size_t http_parser_execute (http_parser *parser,
492
709
  SET_ERRNO(HPE_INVALID_CONSTANT);
493
710
  goto error;
494
711
  }
712
+
713
+ CALLBACK_NOTIFY(message_begin);
495
714
  break;
496
715
  }
497
716
 
498
717
  case s_res_H:
499
718
  STRICT_CHECK(ch != 'T');
500
- state = s_res_HT;
719
+ parser->state = s_res_HT;
501
720
  break;
502
721
 
503
722
  case s_res_HT:
504
723
  STRICT_CHECK(ch != 'T');
505
- state = s_res_HTT;
724
+ parser->state = s_res_HTT;
506
725
  break;
507
726
 
508
727
  case s_res_HTT:
509
728
  STRICT_CHECK(ch != 'P');
510
- state = s_res_HTTP;
729
+ parser->state = s_res_HTTP;
511
730
  break;
512
731
 
513
732
  case s_res_HTTP:
514
733
  STRICT_CHECK(ch != '/');
515
- state = s_res_first_http_major;
734
+ parser->state = s_res_first_http_major;
516
735
  break;
517
736
 
518
737
  case s_res_first_http_major:
519
- if (ch < '1' || ch > '9') {
738
+ if (ch < '0' || ch > '9') {
520
739
  SET_ERRNO(HPE_INVALID_VERSION);
521
740
  goto error;
522
741
  }
523
742
 
524
743
  parser->http_major = ch - '0';
525
- state = s_res_http_major;
744
+ parser->state = s_res_http_major;
526
745
  break;
527
746
 
528
747
  /* major HTTP version or dot */
529
748
  case s_res_http_major:
530
749
  {
531
750
  if (ch == '.') {
532
- state = s_res_first_http_minor;
751
+ parser->state = s_res_first_http_minor;
533
752
  break;
534
753
  }
535
754
 
@@ -557,14 +776,14 @@ size_t http_parser_execute (http_parser *parser,
557
776
  }
558
777
 
559
778
  parser->http_minor = ch - '0';
560
- state = s_res_http_minor;
779
+ parser->state = s_res_http_minor;
561
780
  break;
562
781
 
563
782
  /* minor HTTP version or end of request line */
564
783
  case s_res_http_minor:
565
784
  {
566
785
  if (ch == ' ') {
567
- state = s_res_first_status_code;
786
+ parser->state = s_res_first_status_code;
568
787
  break;
569
788
  }
570
789
 
@@ -595,7 +814,7 @@ size_t http_parser_execute (http_parser *parser,
595
814
  goto error;
596
815
  }
597
816
  parser->status_code = ch - '0';
598
- state = s_res_status_code;
817
+ parser->state = s_res_status_code;
599
818
  break;
600
819
  }
601
820
 
@@ -604,13 +823,13 @@ size_t http_parser_execute (http_parser *parser,
604
823
  if (!IS_NUM(ch)) {
605
824
  switch (ch) {
606
825
  case ' ':
607
- state = s_res_status;
826
+ parser->state = s_res_status;
608
827
  break;
609
828
  case CR:
610
- state = s_res_line_almost_done;
829
+ parser->state = s_res_line_almost_done;
611
830
  break;
612
831
  case LF:
613
- state = s_header_field_start;
832
+ parser->state = s_header_field_start;
614
833
  break;
615
834
  default:
616
835
  SET_ERRNO(HPE_INVALID_STATUS);
@@ -634,19 +853,19 @@ size_t http_parser_execute (http_parser *parser,
634
853
  /* the human readable status. e.g. "NOT FOUND"
635
854
  * we are not humans so just ignore this */
636
855
  if (ch == CR) {
637
- state = s_res_line_almost_done;
856
+ parser->state = s_res_line_almost_done;
638
857
  break;
639
858
  }
640
859
 
641
860
  if (ch == LF) {
642
- state = s_header_field_start;
861
+ parser->state = s_header_field_start;
643
862
  break;
644
863
  }
645
864
  break;
646
865
 
647
866
  case s_res_line_almost_done:
648
867
  STRICT_CHECK(ch != LF);
649
- state = s_header_field_start;
868
+ parser->state = s_header_field_start;
650
869
  break;
651
870
 
652
871
  case s_start_req:
@@ -654,18 +873,15 @@ size_t http_parser_execute (http_parser *parser,
654
873
  if (ch == CR || ch == LF)
655
874
  break;
656
875
  parser->flags = 0;
657
- parser->content_length = -1;
658
-
659
- CALLBACK2(message_begin);
876
+ parser->content_length = ULLONG_MAX;
660
877
 
661
878
  if (!IS_ALPHA(ch)) {
662
879
  SET_ERRNO(HPE_INVALID_METHOD);
663
880
  goto error;
664
881
  }
665
882
 
666
- start_req_method_assign:
667
883
  parser->method = (enum http_method) 0;
668
- index = 1;
884
+ parser->index = 1;
669
885
  switch (ch) {
670
886
  case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
671
887
  case 'D': parser->method = HTTP_DELETE; break;
@@ -676,341 +892,158 @@ size_t http_parser_execute (http_parser *parser,
676
892
  case 'N': parser->method = HTTP_NOTIFY; break;
677
893
  case 'O': parser->method = HTTP_OPTIONS; break;
678
894
  case 'P': parser->method = HTTP_POST;
679
- /* or PROPFIND or PROPPATCH or PUT or PATCH */
895
+ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
680
896
  break;
681
897
  case 'R': parser->method = HTTP_REPORT; break;
682
- case 'S': parser->method = HTTP_SUBSCRIBE; break;
898
+ case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
683
899
  case 'T': parser->method = HTTP_TRACE; break;
684
900
  case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
685
901
  default:
686
902
  SET_ERRNO(HPE_INVALID_METHOD);
687
903
  goto error;
688
904
  }
689
- state = s_req_method;
905
+ parser->state = s_req_method;
906
+
907
+ CALLBACK_NOTIFY(message_begin);
908
+
690
909
  break;
691
910
  }
692
911
 
693
912
  case s_req_method:
694
913
  {
914
+ const char *matcher;
695
915
  if (ch == '\0') {
696
916
  SET_ERRNO(HPE_INVALID_METHOD);
697
917
  goto error;
698
918
  }
699
919
 
700
920
  matcher = method_strings[parser->method];
701
- if (ch == ' ' && matcher[index] == '\0') {
702
- state = s_req_spaces_before_url;
703
- } else if (ch == matcher[index]) {
921
+ if (ch == ' ' && matcher[parser->index] == '\0') {
922
+ parser->state = s_req_spaces_before_url;
923
+ } else if (ch == matcher[parser->index]) {
704
924
  ; /* nada */
705
925
  } else if (parser->method == HTTP_CONNECT) {
706
- if (index == 1 && ch == 'H') {
926
+ if (parser->index == 1 && ch == 'H') {
707
927
  parser->method = HTTP_CHECKOUT;
708
- } else if (index == 2 && ch == 'P') {
928
+ } else if (parser->index == 2 && ch == 'P') {
709
929
  parser->method = HTTP_COPY;
710
930
  } else {
711
931
  goto error;
712
932
  }
713
933
  } else if (parser->method == HTTP_MKCOL) {
714
- if (index == 1 && ch == 'O') {
934
+ if (parser->index == 1 && ch == 'O') {
715
935
  parser->method = HTTP_MOVE;
716
- } else if (index == 1 && ch == 'E') {
936
+ } else if (parser->index == 1 && ch == 'E') {
717
937
  parser->method = HTTP_MERGE;
718
- } else if (index == 1 && ch == '-') {
938
+ } else if (parser->index == 1 && ch == '-') {
719
939
  parser->method = HTTP_MSEARCH;
720
- } else if (index == 2 && ch == 'A') {
940
+ } else if (parser->index == 2 && ch == 'A') {
721
941
  parser->method = HTTP_MKACTIVITY;
722
942
  } else {
723
943
  goto error;
724
944
  }
725
- } else if (index == 1 && parser->method == HTTP_POST) {
945
+ } else if (parser->method == HTTP_SUBSCRIBE) {
946
+ if (parser->index == 1 && ch == 'E') {
947
+ parser->method = HTTP_SEARCH;
948
+ } else {
949
+ goto error;
950
+ }
951
+ } else if (parser->index == 1 && parser->method == HTTP_POST) {
726
952
  if (ch == 'R') {
727
953
  parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
728
954
  } else if (ch == 'U') {
729
- parser->method = HTTP_PUT;
955
+ parser->method = HTTP_PUT; /* or HTTP_PURGE */
730
956
  } else if (ch == 'A') {
731
957
  parser->method = HTTP_PATCH;
732
958
  } else {
733
959
  goto error;
734
960
  }
735
- } else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
736
- parser->method = HTTP_UNSUBSCRIBE;
737
- } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
961
+ } else if (parser->index == 2) {
962
+ if (parser->method == HTTP_PUT) {
963
+ if (ch == 'R') parser->method = HTTP_PURGE;
964
+ } else if (parser->method == HTTP_UNLOCK) {
965
+ if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE;
966
+ }
967
+ } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
738
968
  parser->method = HTTP_PROPPATCH;
739
969
  } else {
740
970
  SET_ERRNO(HPE_INVALID_METHOD);
741
971
  goto error;
742
972
  }
743
973
 
744
- ++index;
974
+ ++parser->index;
745
975
  break;
746
976
  }
977
+
747
978
  case s_req_spaces_before_url:
748
979
  {
749
980
  if (ch == ' ') break;
750
981
 
751
- if (ch == '/' || ch == '*') {
752
- MARK(url);
753
- state = s_req_path;
754
- break;
982
+ MARK(url);
983
+ if (parser->method == HTTP_CONNECT) {
984
+ parser->state = s_req_server_start;
755
985
  }
756
986
 
757
- /* Proxied requests are followed by scheme of an absolute URI (alpha).
758
- * CONNECT is followed by a hostname, which begins with alphanum.
759
- * All other methods are followed by '/' or '*' (handled above).
760
- */
761
- if (IS_ALPHA(ch) || (parser->method == HTTP_CONNECT && IS_NUM(ch))) {
762
- MARK(url);
763
- state = (parser->method == HTTP_CONNECT) ? s_req_host : s_req_schema;
764
- break;
987
+ parser->state = parse_url_char((enum state)parser->state, ch);
988
+ if (parser->state == s_dead) {
989
+ SET_ERRNO(HPE_INVALID_URL);
990
+ goto error;
765
991
  }
766
992
 
767
- SET_ERRNO(HPE_INVALID_URL);
768
- goto error;
993
+ break;
769
994
  }
770
995
 
771
996
  case s_req_schema:
772
- {
773
- if (IS_ALPHA(ch)) break;
774
-
775
- if (ch == ':') {
776
- state = s_req_schema_slash;
777
- break;
778
- }
779
-
780
- SET_ERRNO(HPE_INVALID_URL);
781
- goto error;
782
- }
783
-
784
997
  case s_req_schema_slash:
785
- STRICT_CHECK(ch != '/');
786
- state = s_req_schema_slash_slash;
787
- break;
788
-
789
998
  case s_req_schema_slash_slash:
790
- STRICT_CHECK(ch != '/');
791
- state = s_req_host;
792
- break;
793
-
794
- case s_req_host:
795
- {
796
- if (IS_HOST_CHAR(ch)) break;
797
- switch (ch) {
798
- case ':':
799
- state = s_req_port;
800
- break;
801
- case '/':
802
- state = s_req_path;
803
- break;
804
- case ' ':
805
- /* The request line looks like:
806
- * "GET http://foo.bar.com HTTP/1.1"
807
- * That is, there is no path.
808
- */
809
- CALLBACK(url);
810
- state = s_req_http_start;
811
- break;
812
- case '?':
813
- state = s_req_query_string_start;
814
- break;
815
- default:
816
- SET_ERRNO(HPE_INVALID_HOST);
817
- goto error;
818
- }
819
- break;
820
- }
821
-
822
- case s_req_port:
823
- {
824
- if (IS_NUM(ch)) break;
825
- switch (ch) {
826
- case '/':
827
- state = s_req_path;
828
- break;
829
- case ' ':
830
- /* The request line looks like:
831
- * "GET http://foo.bar.com:1234 HTTP/1.1"
832
- * That is, there is no path.
833
- */
834
- CALLBACK(url);
835
- state = s_req_http_start;
836
- break;
837
- case '?':
838
- state = s_req_query_string_start;
839
- break;
840
- default:
841
- SET_ERRNO(HPE_INVALID_PORT);
842
- goto error;
843
- }
844
- break;
845
- }
846
-
847
- case s_req_path:
999
+ case s_req_server_start:
848
1000
  {
849
- if (IS_URL_CHAR(ch)) break;
850
-
851
1001
  switch (ch) {
1002
+ /* No whitespace allowed here */
852
1003
  case ' ':
853
- CALLBACK(url);
854
- state = s_req_http_start;
855
- break;
856
1004
  case CR:
857
- CALLBACK(url);
858
- parser->http_major = 0;
859
- parser->http_minor = 9;
860
- state = s_req_line_almost_done;
861
- break;
862
1005
  case LF:
863
- CALLBACK(url);
864
- parser->http_major = 0;
865
- parser->http_minor = 9;
866
- state = s_header_field_start;
867
- break;
868
- case '?':
869
- state = s_req_query_string_start;
870
- break;
871
- case '#':
872
- state = s_req_fragment_start;
873
- break;
874
- default:
875
- SET_ERRNO(HPE_INVALID_PATH);
1006
+ SET_ERRNO(HPE_INVALID_URL);
876
1007
  goto error;
877
- }
878
- break;
879
- }
880
-
881
- case s_req_query_string_start:
882
- {
883
- if (IS_URL_CHAR(ch)) {
884
- state = s_req_query_string;
885
- break;
886
- }
887
-
888
- switch (ch) {
889
- case '?':
890
- break; /* XXX ignore extra '?' ... is this right? */
891
- case ' ':
892
- CALLBACK(url);
893
- state = s_req_http_start;
894
- break;
895
- case CR:
896
- CALLBACK(url);
897
- parser->http_major = 0;
898
- parser->http_minor = 9;
899
- state = s_req_line_almost_done;
900
- break;
901
- case LF:
902
- CALLBACK(url);
903
- parser->http_major = 0;
904
- parser->http_minor = 9;
905
- state = s_header_field_start;
906
- break;
907
- case '#':
908
- state = s_req_fragment_start;
909
- break;
910
1008
  default:
911
- SET_ERRNO(HPE_INVALID_QUERY_STRING);
912
- goto error;
1009
+ parser->state = parse_url_char((enum state)parser->state, ch);
1010
+ if (parser->state == s_dead) {
1011
+ SET_ERRNO(HPE_INVALID_URL);
1012
+ goto error;
1013
+ }
913
1014
  }
914
- break;
915
- }
916
1015
 
917
- case s_req_query_string:
918
- {
919
- if (IS_URL_CHAR(ch)) break;
920
-
921
- switch (ch) {
922
- case '?':
923
- /* allow extra '?' in query string */
924
- break;
925
- case ' ':
926
- CALLBACK(url);
927
- state = s_req_http_start;
928
- break;
929
- case CR:
930
- CALLBACK(url);
931
- parser->http_major = 0;
932
- parser->http_minor = 9;
933
- state = s_req_line_almost_done;
934
- break;
935
- case LF:
936
- CALLBACK(url);
937
- parser->http_major = 0;
938
- parser->http_minor = 9;
939
- state = s_header_field_start;
940
- break;
941
- case '#':
942
- state = s_req_fragment_start;
943
- break;
944
- default:
945
- SET_ERRNO(HPE_INVALID_QUERY_STRING);
946
- goto error;
947
- }
948
1016
  break;
949
1017
  }
950
1018
 
1019
+ case s_req_server:
1020
+ case s_req_server_with_at:
1021
+ case s_req_path:
1022
+ case s_req_query_string_start:
1023
+ case s_req_query_string:
951
1024
  case s_req_fragment_start:
952
- {
953
- if (IS_URL_CHAR(ch)) {
954
- state = s_req_fragment;
955
- break;
956
- }
957
-
958
- switch (ch) {
959
- case ' ':
960
- CALLBACK(url);
961
- state = s_req_http_start;
962
- break;
963
- case CR:
964
- CALLBACK(url);
965
- parser->http_major = 0;
966
- parser->http_minor = 9;
967
- state = s_req_line_almost_done;
968
- break;
969
- case LF:
970
- CALLBACK(url);
971
- parser->http_major = 0;
972
- parser->http_minor = 9;
973
- state = s_header_field_start;
974
- break;
975
- case '?':
976
- state = s_req_fragment;
977
- break;
978
- case '#':
979
- break;
980
- default:
981
- SET_ERRNO(HPE_INVALID_FRAGMENT);
982
- goto error;
983
- }
984
- break;
985
- }
986
-
987
1025
  case s_req_fragment:
988
1026
  {
989
- if (IS_URL_CHAR(ch)) break;
990
-
991
1027
  switch (ch) {
992
1028
  case ' ':
993
- CALLBACK(url);
994
- state = s_req_http_start;
1029
+ parser->state = s_req_http_start;
1030
+ CALLBACK_DATA(url);
995
1031
  break;
996
1032
  case CR:
997
- CALLBACK(url);
998
- parser->http_major = 0;
999
- parser->http_minor = 9;
1000
- state = s_req_line_almost_done;
1001
- break;
1002
1033
  case LF:
1003
- CALLBACK(url);
1004
1034
  parser->http_major = 0;
1005
1035
  parser->http_minor = 9;
1006
- state = s_header_field_start;
1007
- break;
1008
- case '?':
1009
- case '#':
1036
+ parser->state = (ch == CR) ?
1037
+ s_req_line_almost_done :
1038
+ s_header_field_start;
1039
+ CALLBACK_DATA(url);
1010
1040
  break;
1011
1041
  default:
1012
- SET_ERRNO(HPE_INVALID_FRAGMENT);
1013
- goto error;
1042
+ parser->state = parse_url_char((enum state)parser->state, ch);
1043
+ if (parser->state == s_dead) {
1044
+ SET_ERRNO(HPE_INVALID_URL);
1045
+ goto error;
1046
+ }
1014
1047
  }
1015
1048
  break;
1016
1049
  }
@@ -1018,7 +1051,7 @@ size_t http_parser_execute (http_parser *parser,
1018
1051
  case s_req_http_start:
1019
1052
  switch (ch) {
1020
1053
  case 'H':
1021
- state = s_req_http_H;
1054
+ parser->state = s_req_http_H;
1022
1055
  break;
1023
1056
  case ' ':
1024
1057
  break;
@@ -1030,22 +1063,22 @@ size_t http_parser_execute (http_parser *parser,
1030
1063
 
1031
1064
  case s_req_http_H:
1032
1065
  STRICT_CHECK(ch != 'T');
1033
- state = s_req_http_HT;
1066
+ parser->state = s_req_http_HT;
1034
1067
  break;
1035
1068
 
1036
1069
  case s_req_http_HT:
1037
1070
  STRICT_CHECK(ch != 'T');
1038
- state = s_req_http_HTT;
1071
+ parser->state = s_req_http_HTT;
1039
1072
  break;
1040
1073
 
1041
1074
  case s_req_http_HTT:
1042
1075
  STRICT_CHECK(ch != 'P');
1043
- state = s_req_http_HTTP;
1076
+ parser->state = s_req_http_HTTP;
1044
1077
  break;
1045
1078
 
1046
1079
  case s_req_http_HTTP:
1047
1080
  STRICT_CHECK(ch != '/');
1048
- state = s_req_first_http_major;
1081
+ parser->state = s_req_first_http_major;
1049
1082
  break;
1050
1083
 
1051
1084
  /* first digit of major HTTP version */
@@ -1056,14 +1089,14 @@ size_t http_parser_execute (http_parser *parser,
1056
1089
  }
1057
1090
 
1058
1091
  parser->http_major = ch - '0';
1059
- state = s_req_http_major;
1092
+ parser->state = s_req_http_major;
1060
1093
  break;
1061
1094
 
1062
1095
  /* major HTTP version or dot */
1063
1096
  case s_req_http_major:
1064
1097
  {
1065
1098
  if (ch == '.') {
1066
- state = s_req_first_http_minor;
1099
+ parser->state = s_req_first_http_minor;
1067
1100
  break;
1068
1101
  }
1069
1102
 
@@ -1091,19 +1124,19 @@ size_t http_parser_execute (http_parser *parser,
1091
1124
  }
1092
1125
 
1093
1126
  parser->http_minor = ch - '0';
1094
- state = s_req_http_minor;
1127
+ parser->state = s_req_http_minor;
1095
1128
  break;
1096
1129
 
1097
1130
  /* minor HTTP version or end of request line */
1098
1131
  case s_req_http_minor:
1099
1132
  {
1100
1133
  if (ch == CR) {
1101
- state = s_req_line_almost_done;
1134
+ parser->state = s_req_line_almost_done;
1102
1135
  break;
1103
1136
  }
1104
1137
 
1105
1138
  if (ch == LF) {
1106
- state = s_header_field_start;
1139
+ parser->state = s_header_field_start;
1107
1140
  break;
1108
1141
  }
1109
1142
 
@@ -1133,23 +1166,22 @@ size_t http_parser_execute (http_parser *parser,
1133
1166
  goto error;
1134
1167
  }
1135
1168
 
1136
- state = s_header_field_start;
1169
+ parser->state = s_header_field_start;
1137
1170
  break;
1138
1171
  }
1139
1172
 
1140
1173
  case s_header_field_start:
1141
- header_field_start:
1142
1174
  {
1143
1175
  if (ch == CR) {
1144
- state = s_headers_almost_done;
1176
+ parser->state = s_headers_almost_done;
1145
1177
  break;
1146
1178
  }
1147
1179
 
1148
1180
  if (ch == LF) {
1149
1181
  /* they might be just sending \n instead of \r\n so this would be
1150
1182
  * the second \n to denote the end of headers*/
1151
- state = s_headers_almost_done;
1152
- goto headers_almost_done;
1183
+ parser->state = s_headers_almost_done;
1184
+ goto reexecute_byte;
1153
1185
  }
1154
1186
 
1155
1187
  c = TOKEN(ch);
@@ -1161,28 +1193,28 @@ size_t http_parser_execute (http_parser *parser,
1161
1193
 
1162
1194
  MARK(header_field);
1163
1195
 
1164
- index = 0;
1165
- state = s_header_field;
1196
+ parser->index = 0;
1197
+ parser->state = s_header_field;
1166
1198
 
1167
1199
  switch (c) {
1168
1200
  case 'c':
1169
- header_state = h_C;
1201
+ parser->header_state = h_C;
1170
1202
  break;
1171
1203
 
1172
1204
  case 'p':
1173
- header_state = h_matching_proxy_connection;
1205
+ parser->header_state = h_matching_proxy_connection;
1174
1206
  break;
1175
1207
 
1176
1208
  case 't':
1177
- header_state = h_matching_transfer_encoding;
1209
+ parser->header_state = h_matching_transfer_encoding;
1178
1210
  break;
1179
1211
 
1180
1212
  case 'u':
1181
- header_state = h_matching_upgrade;
1213
+ parser->header_state = h_matching_upgrade;
1182
1214
  break;
1183
1215
 
1184
1216
  default:
1185
- header_state = h_general;
1217
+ parser->header_state = h_general;
1186
1218
  break;
1187
1219
  }
1188
1220
  break;
@@ -1193,31 +1225,31 @@ size_t http_parser_execute (http_parser *parser,
1193
1225
  c = TOKEN(ch);
1194
1226
 
1195
1227
  if (c) {
1196
- switch (header_state) {
1228
+ switch (parser->header_state) {
1197
1229
  case h_general:
1198
1230
  break;
1199
1231
 
1200
1232
  case h_C:
1201
- index++;
1202
- header_state = (c == 'o' ? h_CO : h_general);
1233
+ parser->index++;
1234
+ parser->header_state = (c == 'o' ? h_CO : h_general);
1203
1235
  break;
1204
1236
 
1205
1237
  case h_CO:
1206
- index++;
1207
- header_state = (c == 'n' ? h_CON : h_general);
1238
+ parser->index++;
1239
+ parser->header_state = (c == 'n' ? h_CON : h_general);
1208
1240
  break;
1209
1241
 
1210
1242
  case h_CON:
1211
- index++;
1243
+ parser->index++;
1212
1244
  switch (c) {
1213
1245
  case 'n':
1214
- header_state = h_matching_connection;
1246
+ parser->header_state = h_matching_connection;
1215
1247
  break;
1216
1248
  case 't':
1217
- header_state = h_matching_content_length;
1249
+ parser->header_state = h_matching_content_length;
1218
1250
  break;
1219
1251
  default:
1220
- header_state = h_general;
1252
+ parser->header_state = h_general;
1221
1253
  break;
1222
1254
  }
1223
1255
  break;
@@ -1225,60 +1257,60 @@ size_t http_parser_execute (http_parser *parser,
1225
1257
  /* connection */
1226
1258
 
1227
1259
  case h_matching_connection:
1228
- index++;
1229
- if (index > sizeof(CONNECTION)-1
1230
- || c != CONNECTION[index]) {
1231
- header_state = h_general;
1232
- } else if (index == sizeof(CONNECTION)-2) {
1233
- header_state = h_connection;
1260
+ parser->index++;
1261
+ if (parser->index > sizeof(CONNECTION)-1
1262
+ || c != CONNECTION[parser->index]) {
1263
+ parser->header_state = h_general;
1264
+ } else if (parser->index == sizeof(CONNECTION)-2) {
1265
+ parser->header_state = h_connection;
1234
1266
  }
1235
1267
  break;
1236
1268
 
1237
1269
  /* proxy-connection */
1238
1270
 
1239
1271
  case h_matching_proxy_connection:
1240
- index++;
1241
- if (index > sizeof(PROXY_CONNECTION)-1
1242
- || c != PROXY_CONNECTION[index]) {
1243
- header_state = h_general;
1244
- } else if (index == sizeof(PROXY_CONNECTION)-2) {
1245
- header_state = h_connection;
1272
+ parser->index++;
1273
+ if (parser->index > sizeof(PROXY_CONNECTION)-1
1274
+ || c != PROXY_CONNECTION[parser->index]) {
1275
+ parser->header_state = h_general;
1276
+ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
1277
+ parser->header_state = h_connection;
1246
1278
  }
1247
1279
  break;
1248
1280
 
1249
1281
  /* content-length */
1250
1282
 
1251
1283
  case h_matching_content_length:
1252
- index++;
1253
- if (index > sizeof(CONTENT_LENGTH)-1
1254
- || c != CONTENT_LENGTH[index]) {
1255
- header_state = h_general;
1256
- } else if (index == sizeof(CONTENT_LENGTH)-2) {
1257
- header_state = h_content_length;
1284
+ parser->index++;
1285
+ if (parser->index > sizeof(CONTENT_LENGTH)-1
1286
+ || c != CONTENT_LENGTH[parser->index]) {
1287
+ parser->header_state = h_general;
1288
+ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
1289
+ parser->header_state = h_content_length;
1258
1290
  }
1259
1291
  break;
1260
1292
 
1261
1293
  /* transfer-encoding */
1262
1294
 
1263
1295
  case h_matching_transfer_encoding:
1264
- index++;
1265
- if (index > sizeof(TRANSFER_ENCODING)-1
1266
- || c != TRANSFER_ENCODING[index]) {
1267
- header_state = h_general;
1268
- } else if (index == sizeof(TRANSFER_ENCODING)-2) {
1269
- header_state = h_transfer_encoding;
1296
+ parser->index++;
1297
+ if (parser->index > sizeof(TRANSFER_ENCODING)-1
1298
+ || c != TRANSFER_ENCODING[parser->index]) {
1299
+ parser->header_state = h_general;
1300
+ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
1301
+ parser->header_state = h_transfer_encoding;
1270
1302
  }
1271
1303
  break;
1272
1304
 
1273
1305
  /* upgrade */
1274
1306
 
1275
1307
  case h_matching_upgrade:
1276
- index++;
1277
- if (index > sizeof(UPGRADE)-1
1278
- || c != UPGRADE[index]) {
1279
- header_state = h_general;
1280
- } else if (index == sizeof(UPGRADE)-2) {
1281
- header_state = h_upgrade;
1308
+ parser->index++;
1309
+ if (parser->index > sizeof(UPGRADE)-1
1310
+ || c != UPGRADE[parser->index]) {
1311
+ parser->header_state = h_general;
1312
+ } else if (parser->index == sizeof(UPGRADE)-2) {
1313
+ parser->header_state = h_upgrade;
1282
1314
  }
1283
1315
  break;
1284
1316
 
@@ -1286,7 +1318,7 @@ size_t http_parser_execute (http_parser *parser,
1286
1318
  case h_content_length:
1287
1319
  case h_transfer_encoding:
1288
1320
  case h_upgrade:
1289
- if (ch != ' ') header_state = h_general;
1321
+ if (ch != ' ') parser->header_state = h_general;
1290
1322
  break;
1291
1323
 
1292
1324
  default:
@@ -1297,20 +1329,20 @@ size_t http_parser_execute (http_parser *parser,
1297
1329
  }
1298
1330
 
1299
1331
  if (ch == ':') {
1300
- CALLBACK(header_field);
1301
- state = s_header_value_start;
1332
+ parser->state = s_header_value_start;
1333
+ CALLBACK_DATA(header_field);
1302
1334
  break;
1303
1335
  }
1304
1336
 
1305
1337
  if (ch == CR) {
1306
- state = s_header_almost_done;
1307
- CALLBACK(header_field);
1338
+ parser->state = s_header_almost_done;
1339
+ CALLBACK_DATA(header_field);
1308
1340
  break;
1309
1341
  }
1310
1342
 
1311
1343
  if (ch == LF) {
1312
- CALLBACK(header_field);
1313
- state = s_header_field_start;
1344
+ parser->state = s_header_field_start;
1345
+ CALLBACK_DATA(header_field);
1314
1346
  break;
1315
1347
  }
1316
1348
 
@@ -1324,36 +1356,36 @@ size_t http_parser_execute (http_parser *parser,
1324
1356
 
1325
1357
  MARK(header_value);
1326
1358
 
1327
- state = s_header_value;
1328
- index = 0;
1359
+ parser->state = s_header_value;
1360
+ parser->index = 0;
1329
1361
 
1330
1362
  if (ch == CR) {
1331
- CALLBACK(header_value);
1332
- header_state = h_general;
1333
- state = s_header_almost_done;
1363
+ parser->header_state = h_general;
1364
+ parser->state = s_header_almost_done;
1365
+ CALLBACK_DATA(header_value);
1334
1366
  break;
1335
1367
  }
1336
1368
 
1337
1369
  if (ch == LF) {
1338
- CALLBACK(header_value);
1339
- state = s_header_field_start;
1370
+ parser->state = s_header_field_start;
1371
+ CALLBACK_DATA(header_value);
1340
1372
  break;
1341
1373
  }
1342
1374
 
1343
1375
  c = LOWER(ch);
1344
1376
 
1345
- switch (header_state) {
1377
+ switch (parser->header_state) {
1346
1378
  case h_upgrade:
1347
1379
  parser->flags |= F_UPGRADE;
1348
- header_state = h_general;
1380
+ parser->header_state = h_general;
1349
1381
  break;
1350
1382
 
1351
1383
  case h_transfer_encoding:
1352
1384
  /* looking for 'Transfer-Encoding: chunked' */
1353
1385
  if ('c' == c) {
1354
- header_state = h_matching_transfer_encoding_chunked;
1386
+ parser->header_state = h_matching_transfer_encoding_chunked;
1355
1387
  } else {
1356
- header_state = h_general;
1388
+ parser->header_state = h_general;
1357
1389
  }
1358
1390
  break;
1359
1391
 
@@ -1369,17 +1401,17 @@ size_t http_parser_execute (http_parser *parser,
1369
1401
  case h_connection:
1370
1402
  /* looking for 'Connection: keep-alive' */
1371
1403
  if (c == 'k') {
1372
- header_state = h_matching_connection_keep_alive;
1404
+ parser->header_state = h_matching_connection_keep_alive;
1373
1405
  /* looking for 'Connection: close' */
1374
1406
  } else if (c == 'c') {
1375
- header_state = h_matching_connection_close;
1407
+ parser->header_state = h_matching_connection_close;
1376
1408
  } else {
1377
- header_state = h_general;
1409
+ parser->header_state = h_general;
1378
1410
  }
1379
1411
  break;
1380
1412
 
1381
1413
  default:
1382
- header_state = h_general;
1414
+ parser->header_state = h_general;
1383
1415
  break;
1384
1416
  }
1385
1417
  break;
@@ -1389,19 +1421,20 @@ size_t http_parser_execute (http_parser *parser,
1389
1421
  {
1390
1422
 
1391
1423
  if (ch == CR) {
1392
- CALLBACK(header_value);
1393
- state = s_header_almost_done;
1424
+ parser->state = s_header_almost_done;
1425
+ CALLBACK_DATA(header_value);
1394
1426
  break;
1395
1427
  }
1396
1428
 
1397
1429
  if (ch == LF) {
1398
- CALLBACK(header_value);
1399
- goto header_almost_done;
1430
+ parser->state = s_header_almost_done;
1431
+ CALLBACK_DATA_NOADVANCE(header_value);
1432
+ goto reexecute_byte;
1400
1433
  }
1401
1434
 
1402
1435
  c = LOWER(ch);
1403
1436
 
1404
- switch (header_state) {
1437
+ switch (parser->header_state) {
1405
1438
  case h_general:
1406
1439
  break;
1407
1440
 
@@ -1411,70 +1444,83 @@ size_t http_parser_execute (http_parser *parser,
1411
1444
  break;
1412
1445
 
1413
1446
  case h_content_length:
1447
+ {
1448
+ uint64_t t;
1449
+
1414
1450
  if (ch == ' ') break;
1451
+
1415
1452
  if (!IS_NUM(ch)) {
1416
1453
  SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1417
1454
  goto error;
1418
1455
  }
1419
1456
 
1420
- parser->content_length *= 10;
1421
- parser->content_length += ch - '0';
1457
+ t = parser->content_length;
1458
+ t *= 10;
1459
+ t += ch - '0';
1460
+
1461
+ /* Overflow? */
1462
+ if (t < parser->content_length || t == ULLONG_MAX) {
1463
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1464
+ goto error;
1465
+ }
1466
+
1467
+ parser->content_length = t;
1422
1468
  break;
1469
+ }
1423
1470
 
1424
1471
  /* Transfer-Encoding: chunked */
1425
1472
  case h_matching_transfer_encoding_chunked:
1426
- index++;
1427
- if (index > sizeof(CHUNKED)-1
1428
- || c != CHUNKED[index]) {
1429
- header_state = h_general;
1430
- } else if (index == sizeof(CHUNKED)-2) {
1431
- header_state = h_transfer_encoding_chunked;
1473
+ parser->index++;
1474
+ if (parser->index > sizeof(CHUNKED)-1
1475
+ || c != CHUNKED[parser->index]) {
1476
+ parser->header_state = h_general;
1477
+ } else if (parser->index == sizeof(CHUNKED)-2) {
1478
+ parser->header_state = h_transfer_encoding_chunked;
1432
1479
  }
1433
1480
  break;
1434
1481
 
1435
1482
  /* looking for 'Connection: keep-alive' */
1436
1483
  case h_matching_connection_keep_alive:
1437
- index++;
1438
- if (index > sizeof(KEEP_ALIVE)-1
1439
- || c != KEEP_ALIVE[index]) {
1440
- header_state = h_general;
1441
- } else if (index == sizeof(KEEP_ALIVE)-2) {
1442
- header_state = h_connection_keep_alive;
1484
+ parser->index++;
1485
+ if (parser->index > sizeof(KEEP_ALIVE)-1
1486
+ || c != KEEP_ALIVE[parser->index]) {
1487
+ parser->header_state = h_general;
1488
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
1489
+ parser->header_state = h_connection_keep_alive;
1443
1490
  }
1444
1491
  break;
1445
1492
 
1446
1493
  /* looking for 'Connection: close' */
1447
1494
  case h_matching_connection_close:
1448
- index++;
1449
- if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
1450
- header_state = h_general;
1451
- } else if (index == sizeof(CLOSE)-2) {
1452
- header_state = h_connection_close;
1495
+ parser->index++;
1496
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
1497
+ parser->header_state = h_general;
1498
+ } else if (parser->index == sizeof(CLOSE)-2) {
1499
+ parser->header_state = h_connection_close;
1453
1500
  }
1454
1501
  break;
1455
1502
 
1456
1503
  case h_transfer_encoding_chunked:
1457
1504
  case h_connection_keep_alive:
1458
1505
  case h_connection_close:
1459
- if (ch != ' ') header_state = h_general;
1506
+ if (ch != ' ') parser->header_state = h_general;
1460
1507
  break;
1461
1508
 
1462
1509
  default:
1463
- state = s_header_value;
1464
- header_state = h_general;
1510
+ parser->state = s_header_value;
1511
+ parser->header_state = h_general;
1465
1512
  break;
1466
1513
  }
1467
1514
  break;
1468
1515
  }
1469
1516
 
1470
1517
  case s_header_almost_done:
1471
- header_almost_done:
1472
1518
  {
1473
1519
  STRICT_CHECK(ch != LF);
1474
1520
 
1475
- state = s_header_value_lws;
1521
+ parser->state = s_header_value_lws;
1476
1522
 
1477
- switch (header_state) {
1523
+ switch (parser->header_state) {
1478
1524
  case h_connection_keep_alive:
1479
1525
  parser->flags |= F_CONNECTION_KEEP_ALIVE;
1480
1526
  break;
@@ -1487,44 +1533,47 @@ size_t http_parser_execute (http_parser *parser,
1487
1533
  default:
1488
1534
  break;
1489
1535
  }
1536
+
1490
1537
  break;
1491
1538
  }
1492
1539
 
1493
1540
  case s_header_value_lws:
1494
1541
  {
1495
1542
  if (ch == ' ' || ch == '\t')
1496
- state = s_header_value_start;
1543
+ parser->state = s_header_value_start;
1497
1544
  else
1498
1545
  {
1499
- state = s_header_field_start;
1500
- goto header_field_start;
1546
+ parser->state = s_header_field_start;
1547
+ goto reexecute_byte;
1501
1548
  }
1502
1549
  break;
1503
1550
  }
1504
1551
 
1505
1552
  case s_headers_almost_done:
1506
- headers_almost_done:
1507
1553
  {
1508
1554
  STRICT_CHECK(ch != LF);
1509
1555
 
1510
1556
  if (parser->flags & F_TRAILING) {
1511
1557
  /* End of a chunked request */
1512
- CALLBACK2(message_complete);
1513
- state = NEW_MESSAGE();
1558
+ parser->state = NEW_MESSAGE();
1559
+ CALLBACK_NOTIFY(message_complete);
1514
1560
  break;
1515
1561
  }
1516
1562
 
1517
- nread = 0;
1563
+ parser->state = s_headers_done;
1518
1564
 
1519
- if (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT) {
1520
- parser->upgrade = 1;
1521
- }
1565
+ /* Set this here so that on_headers_complete() callbacks can see it */
1566
+ parser->upgrade =
1567
+ (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
1522
1568
 
1523
1569
  /* Here we call the headers_complete callback. This is somewhat
1524
1570
  * different than other callbacks because if the user returns 1, we
1525
1571
  * will interpret that as saying that this message has no body. This
1526
1572
  * is needed for the annoying case of recieving a response to a HEAD
1527
1573
  * request.
1574
+ *
1575
+ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
1576
+ * we have to simulate it by handling a change in errno below.
1528
1577
  */
1529
1578
  if (settings->on_headers_complete) {
1530
1579
  switch (settings->on_headers_complete(parser)) {
@@ -1536,40 +1585,54 @@ size_t http_parser_execute (http_parser *parser,
1536
1585
  break;
1537
1586
 
1538
1587
  default:
1539
- parser->state = state;
1540
1588
  SET_ERRNO(HPE_CB_headers_complete);
1541
1589
  return p - data; /* Error */
1542
1590
  }
1543
1591
  }
1544
1592
 
1593
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
1594
+ return p - data;
1595
+ }
1596
+
1597
+ goto reexecute_byte;
1598
+ }
1599
+
1600
+ case s_headers_done:
1601
+ {
1602
+ STRICT_CHECK(ch != LF);
1603
+
1604
+ parser->nread = 0;
1605
+
1545
1606
  /* Exit, the rest of the connect is in a different protocol. */
1546
1607
  if (parser->upgrade) {
1547
- CALLBACK2(message_complete);
1608
+ parser->state = NEW_MESSAGE();
1609
+ CALLBACK_NOTIFY(message_complete);
1548
1610
  return (p - data) + 1;
1549
1611
  }
1550
1612
 
1551
1613
  if (parser->flags & F_SKIPBODY) {
1552
- CALLBACK2(message_complete);
1553
- state = NEW_MESSAGE();
1614
+ parser->state = NEW_MESSAGE();
1615
+ CALLBACK_NOTIFY(message_complete);
1554
1616
  } else if (parser->flags & F_CHUNKED) {
1555
1617
  /* chunked encoding - ignore Content-Length header */
1556
- state = s_chunk_size_start;
1618
+ parser->state = s_chunk_size_start;
1557
1619
  } else {
1558
1620
  if (parser->content_length == 0) {
1559
1621
  /* Content-Length header given but zero: Content-Length: 0\r\n */
1560
- CALLBACK2(message_complete);
1561
- state = NEW_MESSAGE();
1562
- } else if (parser->content_length > 0) {
1622
+ parser->state = NEW_MESSAGE();
1623
+ CALLBACK_NOTIFY(message_complete);
1624
+ } else if (parser->content_length != ULLONG_MAX) {
1563
1625
  /* Content-Length header given and non-zero */
1564
- state = s_body_identity;
1626
+ parser->state = s_body_identity;
1565
1627
  } else {
1566
- if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {
1628
+ if (parser->type == HTTP_REQUEST ||
1629
+ !http_message_needs_eof(parser)) {
1567
1630
  /* Assume content-length 0 - read the next */
1568
- CALLBACK2(message_complete);
1569
- state = NEW_MESSAGE();
1631
+ parser->state = NEW_MESSAGE();
1632
+ CALLBACK_NOTIFY(message_complete);
1570
1633
  } else {
1571
1634
  /* Read body until EOF */
1572
- state = s_body_identity_eof;
1635
+ parser->state = s_body_identity_eof;
1573
1636
  }
1574
1637
  }
1575
1638
  }
@@ -1578,30 +1641,56 @@ size_t http_parser_execute (http_parser *parser,
1578
1641
  }
1579
1642
 
1580
1643
  case s_body_identity:
1581
- to_read = (size_t)MIN(pe - p, parser->content_length);
1582
- if (to_read > 0) {
1583
- if (settings->on_body) settings->on_body(parser, p, to_read);
1584
- p += to_read - 1;
1585
- parser->content_length -= to_read;
1586
- if (parser->content_length == 0) {
1587
- CALLBACK2(message_complete);
1588
- state = NEW_MESSAGE();
1589
- }
1644
+ {
1645
+ uint64_t to_read = MIN(parser->content_length,
1646
+ (uint64_t) ((data + len) - p));
1647
+
1648
+ assert(parser->content_length != 0
1649
+ && parser->content_length != ULLONG_MAX);
1650
+
1651
+ /* The difference between advancing content_length and p is because
1652
+ * the latter will automaticaly advance on the next loop iteration.
1653
+ * Further, if content_length ends up at 0, we want to see the last
1654
+ * byte again for our message complete callback.
1655
+ */
1656
+ MARK(body);
1657
+ parser->content_length -= to_read;
1658
+ p += to_read - 1;
1659
+
1660
+ if (parser->content_length == 0) {
1661
+ parser->state = s_message_done;
1662
+
1663
+ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
1664
+ *
1665
+ * The alternative to doing this is to wait for the next byte to
1666
+ * trigger the data callback, just as in every other case. The
1667
+ * problem with this is that this makes it difficult for the test
1668
+ * harness to distinguish between complete-on-EOF and
1669
+ * complete-on-length. It's not clear that this distinction is
1670
+ * important for applications, but let's keep it for now.
1671
+ */
1672
+ CALLBACK_DATA_(body, p - body_mark + 1, p - data);
1673
+ goto reexecute_byte;
1590
1674
  }
1675
+
1591
1676
  break;
1677
+ }
1592
1678
 
1593
1679
  /* read until EOF */
1594
1680
  case s_body_identity_eof:
1595
- to_read = pe - p;
1596
- if (to_read > 0) {
1597
- if (settings->on_body) settings->on_body(parser, p, to_read);
1598
- p += to_read - 1;
1599
- }
1681
+ MARK(body);
1682
+ p = data + len - 1;
1683
+
1684
+ break;
1685
+
1686
+ case s_message_done:
1687
+ parser->state = NEW_MESSAGE();
1688
+ CALLBACK_NOTIFY(message_complete);
1600
1689
  break;
1601
1690
 
1602
1691
  case s_chunk_size_start:
1603
1692
  {
1604
- assert(nread == 1);
1693
+ assert(parser->nread == 1);
1605
1694
  assert(parser->flags & F_CHUNKED);
1606
1695
 
1607
1696
  unhex_val = unhex[(unsigned char)ch];
@@ -1611,16 +1700,18 @@ size_t http_parser_execute (http_parser *parser,
1611
1700
  }
1612
1701
 
1613
1702
  parser->content_length = unhex_val;
1614
- state = s_chunk_size;
1703
+ parser->state = s_chunk_size;
1615
1704
  break;
1616
1705
  }
1617
1706
 
1618
1707
  case s_chunk_size:
1619
1708
  {
1709
+ uint64_t t;
1710
+
1620
1711
  assert(parser->flags & F_CHUNKED);
1621
1712
 
1622
1713
  if (ch == CR) {
1623
- state = s_chunk_size_almost_done;
1714
+ parser->state = s_chunk_size_almost_done;
1624
1715
  break;
1625
1716
  }
1626
1717
 
@@ -1628,7 +1719,7 @@ size_t http_parser_execute (http_parser *parser,
1628
1719
 
1629
1720
  if (unhex_val == -1) {
1630
1721
  if (ch == ';' || ch == ' ') {
1631
- state = s_chunk_parameters;
1722
+ parser->state = s_chunk_parameters;
1632
1723
  break;
1633
1724
  }
1634
1725
 
@@ -1636,17 +1727,26 @@ size_t http_parser_execute (http_parser *parser,
1636
1727
  goto error;
1637
1728
  }
1638
1729
 
1639
- parser->content_length *= 16;
1640
- parser->content_length += unhex_val;
1730
+ t = parser->content_length;
1731
+ t *= 16;
1732
+ t += unhex_val;
1733
+
1734
+ /* Overflow? */
1735
+ if (t < parser->content_length || t == ULLONG_MAX) {
1736
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1737
+ goto error;
1738
+ }
1739
+
1740
+ parser->content_length = t;
1641
1741
  break;
1642
1742
  }
1643
1743
 
1644
1744
  case s_chunk_parameters:
1645
1745
  {
1646
1746
  assert(parser->flags & F_CHUNKED);
1647
- /* just ignore this shit. TODO check for overflow */
1747
+ /* just ignore this. TODO check for overflow */
1648
1748
  if (ch == CR) {
1649
- state = s_chunk_size_almost_done;
1749
+ parser->state = s_chunk_size_almost_done;
1650
1750
  break;
1651
1751
  }
1652
1752
  break;
@@ -1657,46 +1757,53 @@ size_t http_parser_execute (http_parser *parser,
1657
1757
  assert(parser->flags & F_CHUNKED);
1658
1758
  STRICT_CHECK(ch != LF);
1659
1759
 
1660
- nread = 0;
1760
+ parser->nread = 0;
1661
1761
 
1662
1762
  if (parser->content_length == 0) {
1663
1763
  parser->flags |= F_TRAILING;
1664
- state = s_header_field_start;
1764
+ parser->state = s_header_field_start;
1665
1765
  } else {
1666
- state = s_chunk_data;
1766
+ parser->state = s_chunk_data;
1667
1767
  }
1668
1768
  break;
1669
1769
  }
1670
1770
 
1671
1771
  case s_chunk_data:
1672
1772
  {
1673
- assert(parser->flags & F_CHUNKED);
1773
+ uint64_t to_read = MIN(parser->content_length,
1774
+ (uint64_t) ((data + len) - p));
1674
1775
 
1675
- to_read = (size_t)MIN(pe - p, parser->content_length);
1776
+ assert(parser->flags & F_CHUNKED);
1777
+ assert(parser->content_length != 0
1778
+ && parser->content_length != ULLONG_MAX);
1676
1779
 
1677
- if (to_read > 0) {
1678
- if (settings->on_body) settings->on_body(parser, p, to_read);
1679
- p += to_read - 1;
1680
- }
1780
+ /* See the explanation in s_body_identity for why the content
1781
+ * length and data pointers are managed this way.
1782
+ */
1783
+ MARK(body);
1784
+ parser->content_length -= to_read;
1785
+ p += to_read - 1;
1681
1786
 
1682
- if ((signed)to_read == parser->content_length) {
1683
- state = s_chunk_data_almost_done;
1787
+ if (parser->content_length == 0) {
1788
+ parser->state = s_chunk_data_almost_done;
1684
1789
  }
1685
1790
 
1686
- parser->content_length -= to_read;
1687
1791
  break;
1688
1792
  }
1689
1793
 
1690
1794
  case s_chunk_data_almost_done:
1691
1795
  assert(parser->flags & F_CHUNKED);
1796
+ assert(parser->content_length == 0);
1692
1797
  STRICT_CHECK(ch != CR);
1693
- state = s_chunk_data_done;
1798
+ parser->state = s_chunk_data_done;
1799
+ CALLBACK_DATA(body);
1694
1800
  break;
1695
1801
 
1696
1802
  case s_chunk_data_done:
1697
1803
  assert(parser->flags & F_CHUNKED);
1698
1804
  STRICT_CHECK(ch != LF);
1699
- state = s_chunk_size_start;
1805
+ parser->nread = 0;
1806
+ parser->state = s_chunk_size_start;
1700
1807
  break;
1701
1808
 
1702
1809
  default:
@@ -1706,14 +1813,25 @@ size_t http_parser_execute (http_parser *parser,
1706
1813
  }
1707
1814
  }
1708
1815
 
1709
- CALLBACK(header_field);
1710
- CALLBACK(header_value);
1711
- CALLBACK(url);
1816
+ /* Run callbacks for any marks that we have leftover after we ran our of
1817
+ * bytes. There should be at most one of these set, so it's OK to invoke
1818
+ * them in series (unset marks will not result in callbacks).
1819
+ *
1820
+ * We use the NOADVANCE() variety of callbacks here because 'p' has already
1821
+ * overflowed 'data' and this allows us to correct for the off-by-one that
1822
+ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
1823
+ * value that's in-bounds).
1824
+ */
1825
+
1826
+ assert(((header_field_mark ? 1 : 0) +
1827
+ (header_value_mark ? 1 : 0) +
1828
+ (url_mark ? 1 : 0) +
1829
+ (body_mark ? 1 : 0)) <= 1);
1712
1830
 
1713
- parser->state = state;
1714
- parser->header_state = header_state;
1715
- parser->index = (unsigned char) index;
1716
- parser->nread = nread;
1831
+ CALLBACK_DATA_NOADVANCE(header_field);
1832
+ CALLBACK_DATA_NOADVANCE(header_value);
1833
+ CALLBACK_DATA_NOADVANCE(url);
1834
+ CALLBACK_DATA_NOADVANCE(body);
1717
1835
 
1718
1836
  return len;
1719
1837
 
@@ -1726,43 +1844,65 @@ error:
1726
1844
  }
1727
1845
 
1728
1846
 
1847
+ /* Does the parser need to see an EOF to find the end of the message? */
1729
1848
  int
1730
- http_should_keep_alive (http_parser *parser)
1849
+ http_message_needs_eof (const http_parser *parser)
1850
+ {
1851
+ if (parser->type == HTTP_REQUEST) {
1852
+ return 0;
1853
+ }
1854
+
1855
+ /* See RFC 2616 section 4.4 */
1856
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
1857
+ parser->status_code == 204 || /* No Content */
1858
+ parser->status_code == 304 || /* Not Modified */
1859
+ parser->flags & F_SKIPBODY) { /* response to a HEAD request */
1860
+ return 0;
1861
+ }
1862
+
1863
+ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
1864
+ return 0;
1865
+ }
1866
+
1867
+ return 1;
1868
+ }
1869
+
1870
+
1871
+ int
1872
+ http_should_keep_alive (const http_parser *parser)
1731
1873
  {
1732
1874
  if (parser->http_major > 0 && parser->http_minor > 0) {
1733
1875
  /* HTTP/1.1 */
1734
1876
  if (parser->flags & F_CONNECTION_CLOSE) {
1735
1877
  return 0;
1736
- } else {
1737
- return 1;
1738
1878
  }
1739
1879
  } else {
1740
1880
  /* HTTP/1.0 or earlier */
1741
- if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
1742
- return 1;
1743
- } else {
1881
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
1744
1882
  return 0;
1745
1883
  }
1746
1884
  }
1885
+
1886
+ return !http_message_needs_eof(parser);
1747
1887
  }
1748
1888
 
1749
1889
 
1750
- const char * http_method_str (enum http_method m)
1890
+ const char *
1891
+ http_method_str (enum http_method m)
1751
1892
  {
1752
- return method_strings[m];
1893
+ return ELEM_AT(method_strings, m, "<unknown>");
1753
1894
  }
1754
1895
 
1755
1896
 
1756
1897
  void
1757
1898
  http_parser_init (http_parser *parser, enum http_parser_type t)
1758
1899
  {
1900
+ void *data = parser->data; /* preserve application data */
1901
+ memset(parser, 0, sizeof(*parser));
1902
+ parser->data = data;
1759
1903
  parser->type = t;
1760
1904
  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
1761
- parser->nread = 0;
1762
- parser->upgrade = 0;
1763
- parser->flags = 0;
1764
- parser->method = 0;
1765
- parser->http_errno = 0;
1905
+ parser->http_errno = HPE_OK;
1766
1906
  }
1767
1907
 
1768
1908
  const char *
@@ -1776,3 +1916,259 @@ http_errno_description(enum http_errno err) {
1776
1916
  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
1777
1917
  return http_strerror_tab[err].description;
1778
1918
  }
1919
+
1920
+ static enum http_host_state
1921
+ http_parse_host_char(enum http_host_state s, const char ch) {
1922
+ switch(s) {
1923
+ case s_http_userinfo:
1924
+ case s_http_userinfo_start:
1925
+ if (ch == '@') {
1926
+ return s_http_host_start;
1927
+ }
1928
+
1929
+ if (IS_USERINFO_CHAR(ch)) {
1930
+ return s_http_userinfo;
1931
+ }
1932
+ break;
1933
+
1934
+ case s_http_host_start:
1935
+ if (ch == '[') {
1936
+ return s_http_host_v6_start;
1937
+ }
1938
+
1939
+ if (IS_HOST_CHAR(ch)) {
1940
+ return s_http_host;
1941
+ }
1942
+
1943
+ break;
1944
+
1945
+ case s_http_host:
1946
+ if (IS_HOST_CHAR(ch)) {
1947
+ return s_http_host;
1948
+ }
1949
+
1950
+ /* FALLTHROUGH */
1951
+ case s_http_host_v6_end:
1952
+ if (ch == ':') {
1953
+ return s_http_host_port_start;
1954
+ }
1955
+
1956
+ break;
1957
+
1958
+ case s_http_host_v6:
1959
+ if (ch == ']') {
1960
+ return s_http_host_v6_end;
1961
+ }
1962
+
1963
+ /* FALLTHROUGH */
1964
+ case s_http_host_v6_start:
1965
+ if (IS_HEX(ch) || ch == ':') {
1966
+ return s_http_host_v6;
1967
+ }
1968
+
1969
+ break;
1970
+
1971
+ case s_http_host_port:
1972
+ case s_http_host_port_start:
1973
+ if (IS_NUM(ch)) {
1974
+ return s_http_host_port;
1975
+ }
1976
+
1977
+ break;
1978
+
1979
+ default:
1980
+ break;
1981
+ }
1982
+ return s_http_host_dead;
1983
+ }
1984
+
1985
+ static int
1986
+ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
1987
+ enum http_host_state s;
1988
+
1989
+ const char *p;
1990
+ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
1991
+
1992
+ u->field_data[UF_HOST].len = 0;
1993
+
1994
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
1995
+
1996
+ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
1997
+ enum http_host_state new_s = http_parse_host_char(s, *p);
1998
+
1999
+ if (new_s == s_http_host_dead) {
2000
+ return 1;
2001
+ }
2002
+
2003
+ switch(new_s) {
2004
+ case s_http_host:
2005
+ if (s != s_http_host) {
2006
+ u->field_data[UF_HOST].off = p - buf;
2007
+ }
2008
+ u->field_data[UF_HOST].len++;
2009
+ break;
2010
+
2011
+ case s_http_host_v6:
2012
+ if (s != s_http_host_v6) {
2013
+ u->field_data[UF_HOST].off = p - buf;
2014
+ }
2015
+ u->field_data[UF_HOST].len++;
2016
+ break;
2017
+
2018
+ case s_http_host_port:
2019
+ if (s != s_http_host_port) {
2020
+ u->field_data[UF_PORT].off = p - buf;
2021
+ u->field_data[UF_PORT].len = 0;
2022
+ u->field_set |= (1 << UF_PORT);
2023
+ }
2024
+ u->field_data[UF_PORT].len++;
2025
+ break;
2026
+
2027
+ case s_http_userinfo:
2028
+ if (s != s_http_userinfo) {
2029
+ u->field_data[UF_USERINFO].off = p - buf ;
2030
+ u->field_data[UF_USERINFO].len = 0;
2031
+ u->field_set |= (1 << UF_USERINFO);
2032
+ }
2033
+ u->field_data[UF_USERINFO].len++;
2034
+ break;
2035
+
2036
+ default:
2037
+ break;
2038
+ }
2039
+ s = new_s;
2040
+ }
2041
+
2042
+ /* Make sure we don't end somewhere unexpected */
2043
+ switch (s) {
2044
+ case s_http_host_start:
2045
+ case s_http_host_v6_start:
2046
+ case s_http_host_v6:
2047
+ case s_http_host_port_start:
2048
+ case s_http_userinfo:
2049
+ case s_http_userinfo_start:
2050
+ return 1;
2051
+ default:
2052
+ break;
2053
+ }
2054
+
2055
+ return 0;
2056
+ }
2057
+
2058
+ int
2059
+ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
2060
+ struct http_parser_url *u)
2061
+ {
2062
+ enum state s;
2063
+ const char *p;
2064
+ enum http_parser_url_fields uf, old_uf;
2065
+ int found_at = 0;
2066
+
2067
+ u->port = u->field_set = 0;
2068
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
2069
+ uf = old_uf = UF_MAX;
2070
+
2071
+ for (p = buf; p < buf + buflen; p++) {
2072
+ s = parse_url_char(s, *p);
2073
+
2074
+ /* Figure out the next field that we're operating on */
2075
+ switch (s) {
2076
+ case s_dead:
2077
+ return 1;
2078
+
2079
+ /* Skip delimeters */
2080
+ case s_req_schema_slash:
2081
+ case s_req_schema_slash_slash:
2082
+ case s_req_server_start:
2083
+ case s_req_query_string_start:
2084
+ case s_req_fragment_start:
2085
+ continue;
2086
+
2087
+ case s_req_schema:
2088
+ uf = UF_SCHEMA;
2089
+ break;
2090
+
2091
+ case s_req_server_with_at:
2092
+ found_at = 1;
2093
+
2094
+ /* FALLTROUGH */
2095
+ case s_req_server:
2096
+ uf = UF_HOST;
2097
+ break;
2098
+
2099
+ case s_req_path:
2100
+ uf = UF_PATH;
2101
+ break;
2102
+
2103
+ case s_req_query_string:
2104
+ uf = UF_QUERY;
2105
+ break;
2106
+
2107
+ case s_req_fragment:
2108
+ uf = UF_FRAGMENT;
2109
+ break;
2110
+
2111
+ default:
2112
+ assert(!"Unexpected state");
2113
+ return 1;
2114
+ }
2115
+
2116
+ /* Nothing's changed; soldier on */
2117
+ if (uf == old_uf) {
2118
+ u->field_data[uf].len++;
2119
+ continue;
2120
+ }
2121
+
2122
+ u->field_data[uf].off = p - buf;
2123
+ u->field_data[uf].len = 1;
2124
+
2125
+ u->field_set |= (1 << uf);
2126
+ old_uf = uf;
2127
+ }
2128
+
2129
+ /* host must be present if there is a schema */
2130
+ /* parsing http:///toto will fail */
2131
+ if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
2132
+ if (http_parse_host(buf, u, found_at) != 0) {
2133
+ return 1;
2134
+ }
2135
+ }
2136
+
2137
+ /* CONNECT requests can only contain "hostname:port" */
2138
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
2139
+ return 1;
2140
+ }
2141
+
2142
+ if (u->field_set & (1 << UF_PORT)) {
2143
+ /* Don't bother with endp; we've already validated the string */
2144
+ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
2145
+
2146
+ /* Ports have a max value of 2^16 */
2147
+ if (v > 0xffff) {
2148
+ return 1;
2149
+ }
2150
+
2151
+ u->port = (uint16_t) v;
2152
+ }
2153
+
2154
+ return 0;
2155
+ }
2156
+
2157
+ void
2158
+ http_parser_pause(http_parser *parser, int paused) {
2159
+ /* Users should only be pausing/unpausing a parser that is not in an error
2160
+ * state. In non-debug builds, there's not much that we can do about this
2161
+ * other than ignore it.
2162
+ */
2163
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
2164
+ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
2165
+ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
2166
+ } else {
2167
+ assert(0 && "Attempting to pause parser in error state");
2168
+ }
2169
+ }
2170
+
2171
+ int
2172
+ http_body_is_final(const struct http_parser *parser) {
2173
+ return parser->state == s_message_done;
2174
+ }