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
@@ -1,31 +1,35 @@
1
1
  /*
2
- * Copyright (C) 2009-2012 the libgit2 contributors
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
3
  *
4
4
  * This file is part of libgit2, distributed under the GNU GPL v2 with
5
5
  * a Linking Exception. For full terms see the included COPYING file.
6
6
  */
7
- #include <stdlib.h>
7
+ #ifndef GIT_WINHTTP
8
+
8
9
  #include "git2.h"
9
10
  #include "http_parser.h"
10
-
11
- #include "transport.h"
12
- #include "common.h"
13
- #include "netops.h"
14
11
  #include "buffer.h"
15
- #include "pkt.h"
16
- #include "refs.h"
17
- #include "pack.h"
18
- #include "fetch.h"
19
- #include "filebuf.h"
20
- #include "repository.h"
21
- #include "protocol.h"
22
- #if GIT_WINHTTP
23
- # include <winhttp.h>
24
- # pragma comment(lib, "winhttp.lib")
25
- #endif
26
-
27
- #define WIDEN2(s) L ## s
28
- #define WIDEN(s) WIDEN2(s)
12
+ #include "netops.h"
13
+ #include "smart.h"
14
+
15
+ static const char *prefix_http = "http://";
16
+ static const char *prefix_https = "https://";
17
+ static const char *upload_pack_service = "upload-pack";
18
+ static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
19
+ static const char *upload_pack_service_url = "/git-upload-pack";
20
+ static const char *receive_pack_service = "receive-pack";
21
+ static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
22
+ static const char *receive_pack_service_url = "/git-receive-pack";
23
+ static const char *get_verb = "GET";
24
+ static const char *post_verb = "POST";
25
+ static const char *basic_authtype = "Basic";
26
+
27
+ #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
28
+
29
+ #define PARSE_ERROR_GENERIC -1
30
+ #define PARSE_ERROR_REPLAY -2
31
+
32
+ #define CHUNK_SIZE 4096
29
33
 
30
34
  enum last_cb {
31
35
  NONE,
@@ -33,53 +37,132 @@ enum last_cb {
33
37
  VALUE
34
38
  };
35
39
 
40
+ typedef enum {
41
+ GIT_HTTP_AUTH_BASIC = 1,
42
+ } http_authmechanism_t;
43
+
36
44
  typedef struct {
37
- git_transport parent;
38
- http_parser_settings settings;
39
- git_buf buf;
40
- int error;
41
- int transfer_finished :1,
42
- ct_found :1,
43
- ct_finished :1,
44
- pack_ready :1;
45
- enum last_cb last_cb;
46
- http_parser parser;
47
- char *content_type;
48
- char *path;
45
+ git_smart_subtransport_stream parent;
46
+ const char *service;
47
+ const char *service_url;
48
+ char *redirect_url;
49
+ const char *verb;
50
+ char *chunk_buffer;
51
+ unsigned chunk_buffer_len;
52
+ unsigned sent_request : 1,
53
+ received_response : 1,
54
+ chunked : 1,
55
+ redirect_count : 3;
56
+ } http_stream;
57
+
58
+ typedef struct {
59
+ git_smart_subtransport parent;
60
+ transport_smart *owner;
61
+ gitno_socket socket;
62
+ const char *path;
49
63
  char *host;
50
64
  char *port;
51
- char *service;
52
- char buffer[65536];
53
- #ifdef GIT_WIN32
54
- WSADATA wsd;
55
- #endif
56
- #ifdef GIT_WINHTTP
57
- HINTERNET session;
58
- HINTERNET connection;
59
- HINTERNET request;
60
- #endif
61
- } transport_http;
62
-
63
- static int gen_request(git_buf *buf, const char *path, const char *host, const char *op,
64
- const char *service, ssize_t content_length, int ls)
65
+ char *user_from_url;
66
+ char *pass_from_url;
67
+ git_cred *cred;
68
+ git_cred *url_cred;
69
+ http_authmechanism_t auth_mechanism;
70
+ unsigned connected : 1,
71
+ use_ssl : 1;
72
+
73
+ /* Parser structures */
74
+ http_parser parser;
75
+ http_parser_settings settings;
76
+ gitno_buffer parse_buffer;
77
+ git_buf parse_header_name;
78
+ git_buf parse_header_value;
79
+ char parse_buffer_data[2048];
80
+ char *content_type;
81
+ char *location;
82
+ git_vector www_authenticate;
83
+ enum last_cb last_cb;
84
+ int parse_error;
85
+ unsigned parse_finished : 1;
86
+ } http_subtransport;
87
+
88
+ typedef struct {
89
+ http_stream *s;
90
+ http_subtransport *t;
91
+
92
+ /* Target buffer details from read() */
93
+ char *buffer;
94
+ size_t buf_size;
95
+ size_t *bytes_read;
96
+ } parser_context;
97
+
98
+ static int apply_basic_credential(git_buf *buf, git_cred *cred)
65
99
  {
66
- if (path == NULL) /* Is 'git fetch http://host.com/' valid? */
67
- path = "/";
100
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
101
+ git_buf raw = GIT_BUF_INIT;
102
+ int error = -1;
103
+
104
+ git_buf_printf(&raw, "%s:%s", c->username, c->password);
105
+
106
+ if (git_buf_oom(&raw) ||
107
+ git_buf_puts(buf, "Authorization: Basic ") < 0 ||
108
+ git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 ||
109
+ git_buf_puts(buf, "\r\n") < 0)
110
+ goto on_error;
111
+
112
+ error = 0;
113
+
114
+ on_error:
115
+ if (raw.size)
116
+ memset(raw.ptr, 0x0, raw.size);
117
+
118
+ git_buf_free(&raw);
119
+ return error;
120
+ }
121
+
122
+ static int gen_request(
123
+ git_buf *buf,
124
+ http_stream *s,
125
+ size_t content_length)
126
+ {
127
+ http_subtransport *t = OWNING_SUBTRANSPORT(s);
128
+
129
+ if (!t->path)
130
+ t->path = "/";
131
+
132
+ /* If we were redirected, make sure to respect that here */
133
+ if (s->redirect_url)
134
+ git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
135
+ else
136
+ git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
68
137
 
69
- if (ls) {
70
- git_buf_printf(buf, "%s %s/info/refs?service=git-%s HTTP/1.1\r\n", op, path, service);
71
- } else {
72
- git_buf_printf(buf, "%s %s/git-%s HTTP/1.1\r\n", op, path, service);
73
- }
74
138
  git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
75
- git_buf_printf(buf, "Host: %s\r\n", host);
76
- if (content_length > 0) {
77
- git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", service);
78
- git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", service);
79
- git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
80
- } else {
139
+ git_buf_printf(buf, "Host: %s\r\n", t->host);
140
+
141
+ if (s->chunked || content_length > 0) {
142
+ git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
143
+ git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", s->service);
144
+
145
+ if (s->chunked)
146
+ git_buf_puts(buf, "Transfer-Encoding: chunked\r\n");
147
+ else
148
+ git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
149
+ } else
81
150
  git_buf_puts(buf, "Accept: */*\r\n");
151
+
152
+ /* Apply credentials to the request */
153
+ if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
154
+ t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
155
+ apply_basic_credential(buf, t->cred) < 0)
156
+ return -1;
157
+
158
+ /* Use url-parsed basic auth if username and password are both provided */
159
+ if (!t->cred && t->user_from_url && t->pass_from_url) {
160
+ if (!t->url_cred &&
161
+ git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
162
+ return -1;
163
+ if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
82
164
  }
165
+
83
166
  git_buf_puts(buf, "\r\n");
84
167
 
85
168
  if (git_buf_oom(buf))
@@ -88,538 +171,783 @@ static int gen_request(git_buf *buf, const char *path, const char *host, const c
88
171
  return 0;
89
172
  }
90
173
 
91
- static int send_request(transport_http *t, const char *service, void *data, ssize_t content_length, int ls)
174
+ static int parse_unauthorized_response(
175
+ git_vector *www_authenticate,
176
+ int *allowed_types,
177
+ http_authmechanism_t *auth_mechanism)
92
178
  {
93
- #ifndef GIT_WINHTTP
94
- git_buf request = GIT_BUF_INIT;
95
- const char *verb;
96
- int error = -1;
97
-
98
- verb = ls ? "GET" : "POST";
99
- /* Generate and send the HTTP request */
100
- if (gen_request(&request, t->path, t->host, verb, service, content_length, ls) < 0) {
101
- giterr_set(GITERR_NET, "Failed to generate request");
102
- return -1;
179
+ unsigned i;
180
+ char *entry;
181
+
182
+ git_vector_foreach(www_authenticate, i, entry) {
183
+ if (!strncmp(entry, basic_authtype, 5) &&
184
+ (entry[5] == '\0' || entry[5] == ' ')) {
185
+ *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
186
+ *auth_mechanism = GIT_HTTP_AUTH_BASIC;
187
+ }
103
188
  }
104
189
 
190
+ return 0;
191
+ }
105
192
 
106
- if (gitno_send((git_transport *) t, request.ptr, request.size, 0) < 0)
107
- goto cleanup;
193
+ static int on_header_ready(http_subtransport *t)
194
+ {
195
+ git_buf *name = &t->parse_header_name;
196
+ git_buf *value = &t->parse_header_value;
108
197
 
109
- if (content_length) {
110
- if (gitno_send((git_transport *) t, data, content_length, 0) < 0)
111
- goto cleanup;
198
+ if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
199
+ if (!t->content_type) {
200
+ t->content_type = git__strdup(git_buf_cstr(value));
201
+ GITERR_CHECK_ALLOC(t->content_type);
202
+ }
112
203
  }
204
+ else if (!strcmp("WWW-Authenticate", git_buf_cstr(name))) {
205
+ char *dup = git__strdup(git_buf_cstr(value));
206
+ GITERR_CHECK_ALLOC(dup);
113
207
 
114
- error = 0;
208
+ git_vector_insert(&t->www_authenticate, dup);
209
+ }
210
+ else if (!strcasecmp("Location", git_buf_cstr(name))) {
211
+ if (!t->location) {
212
+ t->location= git__strdup(git_buf_cstr(value));
213
+ GITERR_CHECK_ALLOC(t->location);
214
+ }
215
+ }
115
216
 
116
- cleanup:
117
- git_buf_free(&request);
118
- return error;
217
+ return 0;
218
+ }
219
+
220
+ static int on_header_field(http_parser *parser, const char *str, size_t len)
221
+ {
222
+ parser_context *ctx = (parser_context *) parser->data;
223
+ http_subtransport *t = ctx->t;
224
+
225
+ /* Both parse_header_name and parse_header_value are populated
226
+ * and ready for consumption */
227
+ if (VALUE == t->last_cb)
228
+ if (on_header_ready(t) < 0)
229
+ return t->parse_error = PARSE_ERROR_GENERIC;
230
+
231
+ if (NONE == t->last_cb || VALUE == t->last_cb)
232
+ git_buf_clear(&t->parse_header_name);
233
+
234
+ if (git_buf_put(&t->parse_header_name, str, len) < 0)
235
+ return t->parse_error = PARSE_ERROR_GENERIC;
236
+
237
+ t->last_cb = FIELD;
238
+ return 0;
239
+ }
240
+
241
+ static int on_header_value(http_parser *parser, const char *str, size_t len)
242
+ {
243
+ parser_context *ctx = (parser_context *) parser->data;
244
+ http_subtransport *t = ctx->t;
119
245
 
120
- #else
121
- wchar_t *verb;
122
- wchar_t url[GIT_WIN_PATH], ct[GIT_WIN_PATH];
246
+ assert(NONE != t->last_cb);
247
+
248
+ if (FIELD == t->last_cb)
249
+ git_buf_clear(&t->parse_header_value);
250
+
251
+ if (git_buf_put(&t->parse_header_value, str, len) < 0)
252
+ return t->parse_error = PARSE_ERROR_GENERIC;
253
+
254
+ t->last_cb = VALUE;
255
+ return 0;
256
+ }
257
+
258
+ static int on_headers_complete(http_parser *parser)
259
+ {
260
+ parser_context *ctx = (parser_context *) parser->data;
261
+ http_subtransport *t = ctx->t;
262
+ http_stream *s = ctx->s;
123
263
  git_buf buf = GIT_BUF_INIT;
124
- BOOL ret;
125
- DWORD flags;
126
- void *buffer;
127
- wchar_t *types[] = {
128
- L"*/*",
129
- NULL,
130
- };
131
-
132
- verb = ls ? L"GET" : L"POST";
133
- buffer = data ? data : WINHTTP_NO_REQUEST_DATA;
134
- flags = t->parent.use_ssl ? WINHTTP_FLAG_SECURE : 0;
135
-
136
- if (ls)
137
- git_buf_printf(&buf, "%s/info/refs?service=git-%s", t->path, service);
138
- else
139
- git_buf_printf(&buf, "%s/git-%s", t->path, service);
140
264
 
141
- if (git_buf_oom(&buf))
142
- return -1;
265
+ /* Both parse_header_name and parse_header_value are populated
266
+ * and ready for consumption. */
267
+ if (VALUE == t->last_cb)
268
+ if (on_header_ready(t) < 0)
269
+ return t->parse_error = PARSE_ERROR_GENERIC;
143
270
 
144
- git__utf8_to_16(url, GIT_WIN_PATH, git_buf_cstr(&buf));
271
+ /* Check for an authentication failure. */
272
+ if (parser->status_code == 401 &&
273
+ get_verb == s->verb &&
274
+ t->owner->cred_acquire_cb) {
275
+ int allowed_types = 0;
145
276
 
146
- t->request = WinHttpOpenRequest(t->connection, verb, url, NULL, WINHTTP_NO_REFERER, types, flags);
147
- if (t->request == NULL) {
148
- git_buf_free(&buf);
149
- giterr_set(GITERR_OS, "Failed to open request");
150
- return -1;
151
- }
277
+ if (parse_unauthorized_response(&t->www_authenticate,
278
+ &allowed_types, &t->auth_mechanism) < 0)
279
+ return t->parse_error = PARSE_ERROR_GENERIC;
152
280
 
153
- git_buf_clear(&buf);
154
- if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", service) < 0)
155
- goto on_error;
281
+ if (allowed_types &&
282
+ (!t->cred || 0 == (t->cred->credtype & allowed_types))) {
156
283
 
157
- git__utf8_to_16(ct, GIT_WIN_PATH, git_buf_cstr(&buf));
284
+ if (t->owner->cred_acquire_cb(&t->cred,
285
+ t->owner->url,
286
+ t->user_from_url,
287
+ allowed_types,
288
+ t->owner->cred_acquire_payload) < 0)
289
+ return PARSE_ERROR_GENERIC;
158
290
 
159
- if (WinHttpAddRequestHeaders(t->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD) == FALSE) {
160
- giterr_set(GITERR_OS, "Failed to add a header to the request");
161
- goto on_error;
291
+ assert(t->cred);
292
+
293
+ /* Successfully acquired a credential. */
294
+ return t->parse_error = PARSE_ERROR_REPLAY;
295
+ }
162
296
  }
163
297
 
164
- if (!t->parent.check_cert) {
165
- int flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA;
166
- if (WinHttpSetOption(t->request, WINHTTP_OPTION_SECURITY_FLAGS, &flags, sizeof(flags)) == FALSE) {
167
- giterr_set(GITERR_OS, "Failed to set options to ignore cert errors");
168
- goto on_error;
298
+ /* Check for a redirect.
299
+ * Right now we only permit a redirect to the same hostname. */
300
+ if ((parser->status_code == 301 ||
301
+ parser->status_code == 302 ||
302
+ (parser->status_code == 303 && get_verb == s->verb) ||
303
+ parser->status_code == 307) &&
304
+ t->location) {
305
+
306
+ if (s->redirect_count >= 7) {
307
+ giterr_set(GITERR_NET, "Too many redirects");
308
+ return t->parse_error = PARSE_ERROR_GENERIC;
309
+ }
310
+
311
+ if (t->location[0] != '/') {
312
+ giterr_set(GITERR_NET, "Only relative redirects are supported");
313
+ return t->parse_error = PARSE_ERROR_GENERIC;
169
314
  }
315
+
316
+ /* Set the redirect URL on the stream. This is a transfer of
317
+ * ownership of the memory. */
318
+ if (s->redirect_url)
319
+ git__free(s->redirect_url);
320
+
321
+ s->redirect_url = t->location;
322
+ t->location = NULL;
323
+
324
+ t->connected = 0;
325
+ s->redirect_count++;
326
+
327
+ return t->parse_error = PARSE_ERROR_REPLAY;
170
328
  }
171
329
 
172
- if (WinHttpSendRequest(t->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
173
- data, (DWORD)content_length, (DWORD)content_length, 0) == FALSE) {
174
- giterr_set(GITERR_OS, "Failed to send request");
175
- goto on_error;
330
+ /* Check for a 200 HTTP status code. */
331
+ if (parser->status_code != 200) {
332
+ giterr_set(GITERR_NET,
333
+ "Unexpected HTTP status code: %d",
334
+ parser->status_code);
335
+ return t->parse_error = PARSE_ERROR_GENERIC;
176
336
  }
177
337
 
178
- ret = WinHttpReceiveResponse(t->request, NULL);
179
- if (ret == FALSE) {
180
- giterr_set(GITERR_OS, "Failed to receive response");
181
- goto on_error;
338
+ /* The response must contain a Content-Type header. */
339
+ if (!t->content_type) {
340
+ giterr_set(GITERR_NET, "No Content-Type header in response");
341
+ return t->parse_error = PARSE_ERROR_GENERIC;
182
342
  }
183
343
 
184
- return 0;
344
+ /* The Content-Type header must match our expectation. */
345
+ if (get_verb == s->verb)
346
+ git_buf_printf(&buf,
347
+ "application/x-git-%s-advertisement",
348
+ ctx->s->service);
349
+ else
350
+ git_buf_printf(&buf,
351
+ "application/x-git-%s-result",
352
+ ctx->s->service);
353
+
354
+ if (git_buf_oom(&buf))
355
+ return t->parse_error = PARSE_ERROR_GENERIC;
356
+
357
+ if (strcmp(t->content_type, git_buf_cstr(&buf))) {
358
+ git_buf_free(&buf);
359
+ giterr_set(GITERR_NET,
360
+ "Invalid Content-Type: %s",
361
+ t->content_type);
362
+ return t->parse_error = PARSE_ERROR_GENERIC;
363
+ }
185
364
 
186
- on_error:
187
365
  git_buf_free(&buf);
188
- if (t->request)
189
- WinHttpCloseHandle(t->request);
190
- t->request = NULL;
191
- return -1;
192
- #endif
366
+
367
+ return 0;
193
368
  }
194
369
 
195
- static int do_connect(transport_http *t)
370
+ static int on_message_complete(http_parser *parser)
196
371
  {
197
- #ifndef GIT_WINHTTP
198
- if (t->parent.connected && http_should_keep_alive(&t->parser))
199
- return 0;
372
+ parser_context *ctx = (parser_context *) parser->data;
373
+ http_subtransport *t = ctx->t;
200
374
 
201
- if (gitno_connect((git_transport *) t, t->host, t->port) < 0)
202
- return -1;
203
-
204
- t->parent.connected = 1;
375
+ t->parse_finished = 1;
205
376
 
206
377
  return 0;
207
- #else
208
- wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
209
- wchar_t host[GIT_WIN_PATH];
210
- int32_t port;
378
+ }
211
379
 
212
- t->session = WinHttpOpen(ua, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
213
- WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
380
+ static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
381
+ {
382
+ parser_context *ctx = (parser_context *) parser->data;
383
+ http_subtransport *t = ctx->t;
214
384
 
215
- if (t->session == NULL) {
216
- giterr_set(GITERR_OS, "Failed to init WinHTTP");
217
- goto on_error;
385
+ if (ctx->buf_size < len) {
386
+ giterr_set(GITERR_NET, "Can't fit data in the buffer");
387
+ return t->parse_error = PARSE_ERROR_GENERIC;
218
388
  }
219
389
 
220
- git__utf8_to_16(host, GIT_WIN_PATH, t->host);
390
+ memcpy(ctx->buffer, str, len);
391
+ *(ctx->bytes_read) += len;
392
+ ctx->buffer += len;
393
+ ctx->buf_size -= len;
221
394
 
222
- if (git__strtol32(&port, t->port, NULL, 10) < 0)
223
- goto on_error;
395
+ return 0;
396
+ }
224
397
 
225
- t->connection = WinHttpConnect(t->session, host, port, 0);
226
- if (t->connection == NULL) {
227
- giterr_set(GITERR_OS, "Failed to connect to host");
228
- goto on_error;
229
- }
398
+ static void clear_parser_state(http_subtransport *t)
399
+ {
400
+ unsigned i;
401
+ char *entry;
230
402
 
231
- t->parent.connected = 1;
232
- return 0;
403
+ http_parser_init(&t->parser, HTTP_RESPONSE);
404
+ gitno_buffer_setup(&t->socket,
405
+ &t->parse_buffer,
406
+ t->parse_buffer_data,
407
+ sizeof(t->parse_buffer_data));
233
408
 
234
- on_error:
235
- if (t->session) {
236
- WinHttpCloseHandle(t->session);
237
- t->session = NULL;
238
- }
239
- return -1;
240
- #endif
241
- }
409
+ t->last_cb = NONE;
410
+ t->parse_error = 0;
411
+ t->parse_finished = 0;
242
412
 
243
- /*
244
- * The HTTP parser is streaming, so we need to wait until we're in the
245
- * field handler before we can be sure that we can store the previous
246
- * value. Right now, we only care about the
247
- * Content-Type. on_header_{field,value} should be kept generic enough
248
- * to work for any request.
249
- */
413
+ git_buf_free(&t->parse_header_name);
414
+ git_buf_init(&t->parse_header_name, 0);
250
415
 
251
- static const char *typestr = "Content-Type";
416
+ git_buf_free(&t->parse_header_value);
417
+ git_buf_init(&t->parse_header_value, 0);
252
418
 
253
- static int on_header_field(http_parser *parser, const char *str, size_t len)
419
+ git__free(t->content_type);
420
+ t->content_type = NULL;
421
+
422
+ git__free(t->location);
423
+ t->location = NULL;
424
+
425
+ git_vector_foreach(&t->www_authenticate, i, entry)
426
+ git__free(entry);
427
+
428
+ git_vector_free(&t->www_authenticate);
429
+ }
430
+
431
+ static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
254
432
  {
255
- transport_http *t = (transport_http *) parser->data;
256
- git_buf *buf = &t->buf;
257
-
258
- if (t->last_cb == VALUE && t->ct_found) {
259
- t->ct_finished = 1;
260
- t->ct_found = 0;
261
- t->content_type = git__strdup(git_buf_cstr(buf));
262
- GITERR_CHECK_ALLOC(t->content_type);
263
- git_buf_clear(buf);
264
- }
433
+ git_buf buf = GIT_BUF_INIT;
265
434
 
266
- if (t->ct_found) {
267
- t->last_cb = FIELD;
268
- return 0;
435
+ /* Chunk header */
436
+ git_buf_printf(&buf, "%X\r\n", (unsigned)len);
437
+
438
+ if (git_buf_oom(&buf))
439
+ return -1;
440
+
441
+ if (gitno_send(socket, buf.ptr, buf.size, 0) < 0) {
442
+ git_buf_free(&buf);
443
+ return -1;
269
444
  }
270
445
 
271
- if (t->last_cb != FIELD)
272
- git_buf_clear(buf);
446
+ git_buf_free(&buf);
273
447
 
274
- git_buf_put(buf, str, len);
275
- t->last_cb = FIELD;
448
+ /* Chunk body */
449
+ if (len > 0 && gitno_send(socket, buffer, len, 0) < 0)
450
+ return -1;
276
451
 
277
- return git_buf_oom(buf);
452
+ /* Chunk footer */
453
+ if (gitno_send(socket, "\r\n", 2, 0) < 0)
454
+ return -1;
455
+
456
+ return 0;
278
457
  }
279
458
 
280
- static int on_header_value(http_parser *parser, const char *str, size_t len)
459
+ static int http_connect(http_subtransport *t)
281
460
  {
282
- transport_http *t = (transport_http *) parser->data;
283
- git_buf *buf = &t->buf;
461
+ int flags = 0;
284
462
 
285
- if (t->ct_finished) {
286
- t->last_cb = VALUE;
463
+ if (t->connected &&
464
+ http_should_keep_alive(&t->parser) &&
465
+ http_body_is_final(&t->parser))
287
466
  return 0;
288
- }
289
467
 
290
- if (t->last_cb == VALUE)
291
- git_buf_put(buf, str, len);
468
+ if (t->socket.socket)
469
+ gitno_close(&t->socket);
470
+
471
+ if (t->use_ssl) {
472
+ int tflags;
292
473
 
293
- if (t->last_cb == FIELD && !strcmp(git_buf_cstr(buf), typestr)) {
294
- t->ct_found = 1;
295
- git_buf_clear(buf);
296
- git_buf_put(buf, str, len);
474
+ if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
475
+ return -1;
476
+
477
+ flags |= GITNO_CONNECT_SSL;
478
+
479
+ if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags)
480
+ flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
297
481
  }
298
482
 
299
- t->last_cb = VALUE;
483
+ if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
484
+ return -1;
300
485
 
301
- return git_buf_oom(buf);
486
+ t->connected = 1;
487
+ return 0;
302
488
  }
303
489
 
304
- static int on_headers_complete(http_parser *parser)
490
+ static int http_stream_read(
491
+ git_smart_subtransport_stream *stream,
492
+ char *buffer,
493
+ size_t buf_size,
494
+ size_t *bytes_read)
305
495
  {
306
- transport_http *t = (transport_http *) parser->data;
307
- git_buf *buf = &t->buf;
496
+ http_stream *s = (http_stream *)stream;
497
+ http_subtransport *t = OWNING_SUBTRANSPORT(s);
498
+ parser_context ctx;
499
+ size_t bytes_parsed;
308
500
 
309
- /* The content-type is text/plain for 404, so don't validate */
310
- if (parser->status_code == 404) {
311
- git_buf_clear(buf);
312
- return 0;
501
+ replay:
502
+ *bytes_read = 0;
503
+
504
+ assert(t->connected);
505
+
506
+ if (!s->sent_request) {
507
+ git_buf request = GIT_BUF_INIT;
508
+
509
+ clear_parser_state(t);
510
+
511
+ if (gen_request(&request, s, 0) < 0) {
512
+ giterr_set(GITERR_NET, "Failed to generate request");
513
+ return -1;
514
+ }
515
+
516
+ if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
517
+ git_buf_free(&request);
518
+ return -1;
519
+ }
520
+
521
+ git_buf_free(&request);
522
+
523
+ s->sent_request = 1;
313
524
  }
314
525
 
315
- if (t->content_type == NULL) {
316
- t->content_type = git__strdup(git_buf_cstr(buf));
317
- if (t->content_type == NULL)
318
- return t->error = -1;
526
+ if (!s->received_response) {
527
+ if (s->chunked) {
528
+ assert(s->verb == post_verb);
529
+
530
+ /* Flush, if necessary */
531
+ if (s->chunk_buffer_len > 0 &&
532
+ write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
533
+ return -1;
534
+
535
+ s->chunk_buffer_len = 0;
536
+
537
+ /* Write the final chunk. */
538
+ if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0)
539
+ return -1;
540
+ }
541
+
542
+ s->received_response = 1;
319
543
  }
320
544
 
321
- git_buf_clear(buf);
322
- git_buf_printf(buf, "application/x-git-%s-advertisement", t->service);
323
- if (git_buf_oom(buf))
324
- return t->error = -1;
545
+ while (!*bytes_read && !t->parse_finished) {
546
+ t->parse_buffer.offset = 0;
325
547
 
326
- if (strcmp(t->content_type, git_buf_cstr(buf)))
327
- return t->error = -1;
548
+ if (gitno_recv(&t->parse_buffer) < 0)
549
+ return -1;
328
550
 
329
- git_buf_clear(buf);
330
- return 0;
331
- }
551
+ /* This call to http_parser_execute will result in invocations of the
552
+ * on_* family of callbacks. The most interesting of these is
553
+ * on_body_fill_buffer, which is called when data is ready to be copied
554
+ * into the target buffer. We need to marshal the buffer, buf_size, and
555
+ * bytes_read parameters to this callback. */
556
+ ctx.t = t;
557
+ ctx.s = s;
558
+ ctx.buffer = buffer;
559
+ ctx.buf_size = buf_size;
560
+ ctx.bytes_read = bytes_read;
332
561
 
333
- static int on_message_complete(http_parser *parser)
334
- {
335
- transport_http *t = (transport_http *) parser->data;
562
+ /* Set the context, call the parser, then unset the context. */
563
+ t->parser.data = &ctx;
564
+
565
+ bytes_parsed = http_parser_execute(&t->parser,
566
+ &t->settings,
567
+ t->parse_buffer.data,
568
+ t->parse_buffer.offset);
569
+
570
+ t->parser.data = NULL;
571
+
572
+ /* If there was a handled authentication failure, then parse_error
573
+ * will have signaled us that we should replay the request. */
574
+ if (PARSE_ERROR_REPLAY == t->parse_error) {
575
+ s->sent_request = 0;
576
+
577
+ if (http_connect(t) < 0)
578
+ return -1;
336
579
 
337
- t->transfer_finished = 1;
580
+ goto replay;
581
+ }
582
+
583
+ if (t->parse_error < 0)
584
+ return -1;
338
585
 
339
- if (parser->status_code == 404) {
340
- giterr_set(GITERR_NET, "Remote error: %s", git_buf_cstr(&t->buf));
341
- t->error = -1;
586
+ if (bytes_parsed != t->parse_buffer.offset) {
587
+ giterr_set(GITERR_NET,
588
+ "HTTP parser error: %s",
589
+ http_errno_description((enum http_errno)t->parser.http_errno));
590
+ return -1;
591
+ }
342
592
  }
343
593
 
344
594
  return 0;
345
595
  }
346
596
 
347
- static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
597
+ static int http_stream_write_chunked(
598
+ git_smart_subtransport_stream *stream,
599
+ const char *buffer,
600
+ size_t len)
348
601
  {
349
- git_transport *transport = (git_transport *) parser->data;
350
- transport_http *t = (transport_http *) parser->data;
351
- gitno_buffer *buf = &transport->buffer;
602
+ http_stream *s = (http_stream *)stream;
603
+ http_subtransport *t = OWNING_SUBTRANSPORT(s);
352
604
 
353
- if (buf->len - buf->offset < len) {
354
- giterr_set(GITERR_NET, "Can't fit data in the buffer");
355
- return t->error = -1;
605
+ assert(t->connected);
606
+
607
+ /* Send the request, if necessary */
608
+ if (!s->sent_request) {
609
+ git_buf request = GIT_BUF_INIT;
610
+
611
+ clear_parser_state(t);
612
+
613
+ if (gen_request(&request, s, 0) < 0) {
614
+ giterr_set(GITERR_NET, "Failed to generate request");
615
+ return -1;
616
+ }
617
+
618
+ if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
619
+ git_buf_free(&request);
620
+ return -1;
621
+ }
622
+
623
+ git_buf_free(&request);
624
+
625
+ s->sent_request = 1;
626
+ }
627
+
628
+ if (len > CHUNK_SIZE) {
629
+ /* Flush, if necessary */
630
+ if (s->chunk_buffer_len > 0) {
631
+ if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
632
+ return -1;
633
+
634
+ s->chunk_buffer_len = 0;
635
+ }
636
+
637
+ /* Write chunk directly */
638
+ if (write_chunk(&t->socket, buffer, len) < 0)
639
+ return -1;
356
640
  }
641
+ else {
642
+ /* Append as much to the buffer as we can */
643
+ int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
644
+
645
+ if (!s->chunk_buffer)
646
+ s->chunk_buffer = git__malloc(CHUNK_SIZE);
647
+
648
+ memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
649
+ s->chunk_buffer_len += count;
650
+ buffer += count;
651
+ len -= count;
357
652
 
358
- memcpy(buf->data + buf->offset, str, len);
359
- buf->offset += len;
653
+ /* Is the buffer full? If so, then flush */
654
+ if (CHUNK_SIZE == s->chunk_buffer_len) {
655
+ if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
656
+ return -1;
657
+
658
+ s->chunk_buffer_len = 0;
659
+
660
+ if (len > 0) {
661
+ memcpy(s->chunk_buffer, buffer, len);
662
+ s->chunk_buffer_len = len;
663
+ }
664
+ }
665
+ }
360
666
 
361
667
  return 0;
362
668
  }
363
669
 
364
- static int http_recv_cb(gitno_buffer *buf)
670
+ static int http_stream_write_single(
671
+ git_smart_subtransport_stream *stream,
672
+ const char *buffer,
673
+ size_t len)
365
674
  {
366
- git_transport *transport = (git_transport *) buf->cb_data;
367
- transport_http *t = (transport_http *) transport;
368
- size_t old_len;
369
- char buffer[2048];
370
- #ifdef GIT_WINHTTP
371
- DWORD recvd;
372
- #else
373
- gitno_buffer inner;
374
- int error;
375
- #endif
376
-
377
- if (t->transfer_finished)
378
- return 0;
675
+ http_stream *s = (http_stream *)stream;
676
+ http_subtransport *t = OWNING_SUBTRANSPORT(s);
677
+ git_buf request = GIT_BUF_INIT;
379
678
 
380
- #ifndef GIT_WINHTTP
381
- gitno_buffer_setup(transport, &inner, buffer, sizeof(buffer));
679
+ assert(t->connected);
382
680
 
383
- if ((error = gitno_recv(&inner)) < 0)
681
+ if (s->sent_request) {
682
+ giterr_set(GITERR_NET, "Subtransport configured for only one write");
384
683
  return -1;
385
-
386
- old_len = buf->offset;
387
- http_parser_execute(&t->parser, &t->settings, inner.data, inner.offset);
388
- if (t->error < 0)
389
- return t->error;
390
- #else
391
- old_len = buf->offset;
392
- if (WinHttpReadData(t->request, buffer, sizeof(buffer), &recvd) == FALSE) {
393
- giterr_set(GITERR_OS, "Failed to read data from the network");
394
- return t->error = -1;
395
684
  }
396
685
 
397
- if (buf->len - buf->offset < recvd) {
398
- giterr_set(GITERR_NET, "Can't fit data in the buffer");
399
- return t->error = -1;
686
+ clear_parser_state(t);
687
+
688
+ if (gen_request(&request, s, len) < 0) {
689
+ giterr_set(GITERR_NET, "Failed to generate request");
690
+ return -1;
400
691
  }
401
692
 
402
- memcpy(buf->data + buf->offset, buffer, recvd);
403
- buf->offset += recvd;
404
- #endif
693
+ if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
694
+ goto on_error;
695
+
696
+ if (len && gitno_send(&t->socket, buffer, len, 0) < 0)
697
+ goto on_error;
698
+
699
+ git_buf_free(&request);
700
+ s->sent_request = 1;
405
701
 
406
- return (int)(buf->offset - old_len);
702
+ return 0;
703
+
704
+ on_error:
705
+ git_buf_free(&request);
706
+ return -1;
407
707
  }
408
708
 
409
- /* Set up the gitno_buffer so calling gitno_recv() grabs data from the HTTP response */
410
- static void setup_gitno_buffer(git_transport *transport)
709
+ static void http_stream_free(git_smart_subtransport_stream *stream)
411
710
  {
412
- transport_http *t = (transport_http *) transport;
711
+ http_stream *s = (http_stream *)stream;
413
712
 
414
- /* WinHTTP takes care of this for us */
415
- #ifndef GIT_WINHTTP
416
- http_parser_init(&t->parser, HTTP_RESPONSE);
417
- t->parser.data = t;
418
- t->transfer_finished = 0;
419
- memset(&t->settings, 0x0, sizeof(http_parser_settings));
420
- t->settings.on_header_field = on_header_field;
421
- t->settings.on_header_value = on_header_value;
422
- t->settings.on_headers_complete = on_headers_complete;
423
- t->settings.on_body = on_body_fill_buffer;
424
- t->settings.on_message_complete = on_message_complete;
425
- #endif
713
+ if (s->chunk_buffer)
714
+ git__free(s->chunk_buffer);
715
+
716
+ if (s->redirect_url)
717
+ git__free(s->redirect_url);
426
718
 
427
- gitno_buffer_setup_callback(transport, &transport->buffer, t->buffer, sizeof(t->buffer), http_recv_cb, t);
719
+ git__free(s);
428
720
  }
429
721
 
430
- static int http_connect(git_transport *transport, int direction)
722
+ static int http_stream_alloc(http_subtransport *t,
723
+ git_smart_subtransport_stream **stream)
431
724
  {
432
- transport_http *t = (transport_http *) transport;
433
- int ret;
434
- git_buf request = GIT_BUF_INIT;
435
- const char *service = "upload-pack";
436
- const char *url = t->parent.url, *prefix_http = "http://", *prefix_https = "https://";
437
- const char *default_port;
438
- git_pkt *pkt;
725
+ http_stream *s;
439
726
 
440
- if (direction == GIT_DIR_PUSH) {
441
- giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
727
+ if (!stream)
442
728
  return -1;
443
- }
444
729
 
445
- t->parent.direction = direction;
730
+ s = git__calloc(sizeof(http_stream), 1);
731
+ GITERR_CHECK_ALLOC(s);
446
732
 
447
- if (!git__prefixcmp(url, prefix_http)) {
448
- url = t->parent.url + strlen(prefix_http);
449
- default_port = "80";
450
- }
733
+ s->parent.subtransport = &t->parent;
734
+ s->parent.read = http_stream_read;
735
+ s->parent.write = http_stream_write_single;
736
+ s->parent.free = http_stream_free;
451
737
 
452
- if (!git__prefixcmp(url, prefix_https)) {
453
- url += strlen(prefix_https);
454
- default_port = "443";
455
- }
738
+ *stream = (git_smart_subtransport_stream *)s;
739
+ return 0;
740
+ }
456
741
 
457
- t->path = strchr(url, '/');
742
+ static int http_uploadpack_ls(
743
+ http_subtransport *t,
744
+ git_smart_subtransport_stream **stream)
745
+ {
746
+ http_stream *s;
458
747
 
459
- if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, default_port)) < 0)
460
- goto cleanup;
748
+ if (http_stream_alloc(t, stream) < 0)
749
+ return -1;
461
750
 
462
- t->service = git__strdup(service);
463
- GITERR_CHECK_ALLOC(t->service);
751
+ s = (http_stream *)*stream;
464
752
 
465
- if ((ret = do_connect(t)) < 0)
466
- goto cleanup;
753
+ s->service = upload_pack_service;
754
+ s->service_url = upload_pack_ls_service_url;
755
+ s->verb = get_verb;
467
756
 
468
- if ((ret = send_request(t, "upload-pack", NULL, 0, 1)) < 0)
469
- goto cleanup;
757
+ return 0;
758
+ }
470
759
 
471
- setup_gitno_buffer(transport);
472
- if ((ret = git_protocol_store_refs(transport, 2)) < 0)
473
- goto cleanup;
760
+ static int http_uploadpack(
761
+ http_subtransport *t,
762
+ git_smart_subtransport_stream **stream)
763
+ {
764
+ http_stream *s;
474
765
 
475
- pkt = git_vector_get(&transport->refs, 0);
476
- if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) {
477
- giterr_set(GITERR_NET, "Invalid HTTP response");
478
- return t->error = -1;
479
- } else {
480
- /* Remove the comment pkt from the list */
481
- git_vector_remove(&transport->refs, 0);
482
- git__free(pkt);
483
- }
766
+ if (http_stream_alloc(t, stream) < 0)
767
+ return -1;
484
768
 
485
- if (git_protocol_detect_caps(git_vector_get(&transport->refs, 0), &transport->caps) < 0)
486
- return t->error = -1;
769
+ s = (http_stream *)*stream;
487
770
 
488
- cleanup:
489
- git_buf_free(&request);
490
- git_buf_clear(&t->buf);
771
+ s->service = upload_pack_service;
772
+ s->service_url = upload_pack_service_url;
773
+ s->verb = post_verb;
491
774
 
492
- return ret;
775
+ return 0;
493
776
  }
494
777
 
495
- static int http_negotiation_step(struct git_transport *transport, void *data, size_t len)
778
+ static int http_receivepack_ls(
779
+ http_subtransport *t,
780
+ git_smart_subtransport_stream **stream)
496
781
  {
497
- transport_http *t = (transport_http *) transport;
498
- int ret;
782
+ http_stream *s;
499
783
 
500
- /* First, send the data as a HTTP POST request */
501
- if ((ret = do_connect(t)) < 0)
784
+ if (http_stream_alloc(t, stream) < 0)
502
785
  return -1;
503
786
 
504
- if (send_request(t, "upload-pack", data, len, 0) < 0)
505
- return -1;
787
+ s = (http_stream *)*stream;
506
788
 
507
- /* Then we need to set up the buffer to grab data from the HTTP response */
508
- setup_gitno_buffer(transport);
789
+ s->service = receive_pack_service;
790
+ s->service_url = receive_pack_ls_service_url;
791
+ s->verb = get_verb;
509
792
 
510
793
  return 0;
511
794
  }
512
795
 
513
- static int http_close(git_transport *transport)
796
+ static int http_receivepack(
797
+ http_subtransport *t,
798
+ git_smart_subtransport_stream **stream)
514
799
  {
515
- #ifndef GIT_WINHTTP
516
- if (gitno_ssl_teardown(transport) < 0)
517
- return -1;
800
+ http_stream *s;
518
801
 
519
- if (gitno_close(transport->socket) < 0) {
520
- giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
802
+ if (http_stream_alloc(t, stream) < 0)
521
803
  return -1;
522
- }
523
- #else
524
- transport_http *t = (transport_http *) transport;
525
804
 
526
- if (t->request)
527
- WinHttpCloseHandle(t->request);
528
- if (t->connection)
529
- WinHttpCloseHandle(t->connection);
530
- if (t->session)
531
- WinHttpCloseHandle(t->session);
532
- #endif
805
+ s = (http_stream *)*stream;
533
806
 
534
- transport->connected = 0;
807
+ /* Use Transfer-Encoding: chunked for this request */
808
+ s->chunked = 1;
809
+ s->parent.write = http_stream_write_chunked;
810
+
811
+ s->service = receive_pack_service;
812
+ s->service_url = receive_pack_service_url;
813
+ s->verb = post_verb;
535
814
 
536
815
  return 0;
537
816
  }
538
817
 
539
-
540
- static void http_free(git_transport *transport)
818
+ static int http_action(
819
+ git_smart_subtransport_stream **stream,
820
+ git_smart_subtransport *subtransport,
821
+ const char *url,
822
+ git_smart_service_t action)
541
823
  {
542
- transport_http *t = (transport_http *) transport;
543
- git_vector *refs = &transport->refs;
544
- git_vector *common = &transport->common;
545
- unsigned int i;
546
- git_pkt *p;
547
-
548
- #ifdef GIT_WIN32
549
- /* cleanup the WSA context. note that this context
550
- * can be initialized more than once with WSAStartup(),
551
- * and needs to be cleaned one time for each init call
552
- */
553
- WSACleanup();
554
- #endif
555
-
556
- git_vector_foreach(refs, i, p) {
557
- git_pkt_free(p);
824
+ http_subtransport *t = (http_subtransport *)subtransport;
825
+ const char *default_port = NULL;
826
+ int ret;
827
+
828
+ if (!stream)
829
+ return -1;
830
+
831
+ if (!t->host || !t->port || !t->path) {
832
+ if (!git__prefixcmp(url, prefix_http)) {
833
+ url = url + strlen(prefix_http);
834
+ default_port = "80";
835
+ }
836
+
837
+ if (!git__prefixcmp(url, prefix_https)) {
838
+ url += strlen(prefix_https);
839
+ default_port = "443";
840
+ t->use_ssl = 1;
841
+ }
842
+
843
+ if (!default_port)
844
+ return -1;
845
+
846
+ if ((ret = gitno_extract_url_parts(&t->host, &t->port,
847
+ &t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
848
+ return ret;
849
+
850
+ t->path = strchr(url, '/');
558
851
  }
559
- git_vector_free(refs);
560
- git_vector_foreach(common, i, p) {
561
- git_pkt_free(p);
852
+
853
+ if (http_connect(t) < 0)
854
+ return -1;
855
+
856
+ switch (action)
857
+ {
858
+ case GIT_SERVICE_UPLOADPACK_LS:
859
+ return http_uploadpack_ls(t, stream);
860
+
861
+ case GIT_SERVICE_UPLOADPACK:
862
+ return http_uploadpack(t, stream);
863
+
864
+ case GIT_SERVICE_RECEIVEPACK_LS:
865
+ return http_receivepack_ls(t, stream);
866
+
867
+ case GIT_SERVICE_RECEIVEPACK:
868
+ return http_receivepack(t, stream);
562
869
  }
563
- git_vector_free(common);
564
- git_buf_free(&t->buf);
565
- git__free(t->content_type);
566
- git__free(t->host);
567
- git__free(t->port);
568
- git__free(t->service);
569
- git__free(t->parent.url);
570
- git__free(t);
870
+
871
+ *stream = NULL;
872
+ return -1;
571
873
  }
572
874
 
573
- int git_transport_http(git_transport **out)
875
+ static int http_close(git_smart_subtransport *subtransport)
574
876
  {
575
- transport_http *t;
877
+ http_subtransport *t = (http_subtransport *) subtransport;
576
878
 
577
- t = git__malloc(sizeof(transport_http));
578
- GITERR_CHECK_ALLOC(t);
879
+ clear_parser_state(t);
579
880
 
580
- memset(t, 0x0, sizeof(transport_http));
881
+ if (t->socket.socket) {
882
+ gitno_close(&t->socket);
883
+ memset(&t->socket, 0x0, sizeof(gitno_socket));
884
+ }
581
885
 
582
- t->parent.connect = http_connect;
583
- t->parent.negotiation_step = http_negotiation_step;
584
- t->parent.close = http_close;
585
- t->parent.free = http_free;
586
- t->parent.rpc = 1;
886
+ if (t->cred) {
887
+ t->cred->free(t->cred);
888
+ t->cred = NULL;
889
+ }
587
890
 
588
- if (git_vector_init(&t->parent.refs, 16, NULL) < 0) {
589
- git__free(t);
590
- return -1;
891
+ if (t->url_cred) {
892
+ t->url_cred->free(t->url_cred);
893
+ t->url_cred = NULL;
591
894
  }
592
895
 
593
- #ifdef GIT_WIN32
594
- /* on win32, the WSA context needs to be initialized
595
- * before any socket calls can be performed */
596
- if (WSAStartup(MAKEWORD(2,2), &t->wsd) != 0) {
597
- http_free((git_transport *) t);
598
- giterr_set(GITERR_OS, "Winsock init failed");
599
- return -1;
896
+ if (t->host) {
897
+ git__free(t->host);
898
+ t->host = NULL;
899
+ }
900
+
901
+ if (t->port) {
902
+ git__free(t->port);
903
+ t->port = NULL;
904
+ }
905
+
906
+ if (t->user_from_url) {
907
+ git__free(t->user_from_url);
908
+ t->user_from_url = NULL;
909
+ }
910
+
911
+ if (t->pass_from_url) {
912
+ git__free(t->pass_from_url);
913
+ t->pass_from_url = NULL;
600
914
  }
601
- #endif
602
915
 
603
- *out = (git_transport *) t;
604
916
  return 0;
605
917
  }
606
918
 
607
- int git_transport_https(git_transport **out)
919
+ static void http_free(git_smart_subtransport *subtransport)
608
920
  {
609
- #if defined(GIT_SSL) || defined(GIT_WINHTTP)
610
- transport_http *t;
611
- if (git_transport_http((git_transport **)&t) < 0)
921
+ http_subtransport *t = (http_subtransport *) subtransport;
922
+
923
+ http_close(subtransport);
924
+
925
+ git__free(t);
926
+ }
927
+
928
+ int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner)
929
+ {
930
+ http_subtransport *t;
931
+
932
+ if (!out)
612
933
  return -1;
613
934
 
614
- t->parent.use_ssl = 1;
615
- t->parent.check_cert = 1;
616
- *out = (git_transport *) t;
935
+ t = git__calloc(sizeof(http_subtransport), 1);
936
+ GITERR_CHECK_ALLOC(t);
617
937
 
618
- return 0;
619
- #else
620
- GIT_UNUSED(out);
938
+ t->owner = (transport_smart *)owner;
939
+ t->parent.action = http_action;
940
+ t->parent.close = http_close;
941
+ t->parent.free = http_free;
621
942
 
622
- giterr_set(GITERR_NET, "HTTPS support not available");
623
- return -1;
624
- #endif
943
+ t->settings.on_header_field = on_header_field;
944
+ t->settings.on_header_value = on_header_value;
945
+ t->settings.on_headers_complete = on_headers_complete;
946
+ t->settings.on_body = on_body_fill_buffer;
947
+ t->settings.on_message_complete = on_message_complete;
948
+
949
+ *out = (git_smart_subtransport *) t;
950
+ return 0;
625
951
  }
952
+
953
+ #endif /* !GIT_WINHTTP */