git-fetch-pack.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. "use strict";
  2. var makeChannel = require('culvert');
  3. var wrapHandler = require('../lib/wrap-handler');
  4. var bodec = require('bodec');
  5. module.exports = fetchPack;
  6. function fetchPack(transport, onError) {
  7. if (!onError) onError = throwIt;
  8. // Wrap our handler functions to route errors properly.
  9. onRef = wrapHandler(onRef, onError);
  10. onWant = wrapHandler(onWant, onError);
  11. onNak = wrapHandler(onNak, onError);
  12. onMore = wrapHandler(onMore, onError);
  13. onReady = wrapHandler(onReady, onError);
  14. var caps = null;
  15. var capsSent = false;
  16. var refs = {};
  17. var haves = {};
  18. var havesCount = 0;
  19. // Create a duplex channel for talking with the agent.
  20. var libraryChannel = makeChannel();
  21. var agentChannel = makeChannel();
  22. var api = {
  23. put: libraryChannel.put,
  24. drain: libraryChannel.drain,
  25. take: agentChannel.take
  26. };
  27. // Start the connection and listen for the response.
  28. var socket = transport("git-upload-pack", onError);
  29. socket.take(onRef);
  30. // Return the other half of the duplex API channel.
  31. return {
  32. put: agentChannel.put,
  33. drain: agentChannel.drain,
  34. take: libraryChannel.take
  35. };
  36. function onRef(line) {
  37. if (line === undefined) {
  38. throw new Error("Socket disconnected");
  39. }
  40. if (line === null) {
  41. api.put(refs);
  42. api.take(onWant);
  43. return;
  44. }
  45. else if (!caps) {
  46. caps = {};
  47. Object.defineProperty(refs, "caps", {value: caps});
  48. Object.defineProperty(refs, "shallows", {value:[]});
  49. var index = line.indexOf("\0");
  50. if (index >= 0) {
  51. line.substring(index + 1).split(" ").forEach(function (cap) {
  52. var i = cap.indexOf("=");
  53. if (i >= 0) {
  54. caps[cap.substring(0, i)] = cap.substring(i + 1);
  55. }
  56. else {
  57. caps[cap] = true;
  58. }
  59. });
  60. line = line.substring(0, index);
  61. }
  62. }
  63. var match = line.match(/(^[0-9a-f]{40}) (.*)$/);
  64. if (!match) {
  65. if (typeof line === "string" && /^ERR/i.test(line)) {
  66. throw new Error(line);
  67. }
  68. throw new Error("Invalid line: " + JSON.stringify(line));
  69. }
  70. refs[match[2]] = match[1];
  71. socket.take(onRef);
  72. }
  73. var packChannel;
  74. var progressChannel;
  75. var errorChannel;
  76. function onWant(line) {
  77. if (line === undefined) return socket.put();
  78. if (line === null) {
  79. socket.put(null);
  80. return api.take(onWant);
  81. }
  82. if (line.deepen) {
  83. socket.put("deepen " + line.deepen + "\n");
  84. return api.take(onWant);
  85. }
  86. if (line.have) {
  87. haves[line.have] = true;
  88. havesCount++;
  89. socket.put("have " + line.have + "\n");
  90. return api.take(onWant);
  91. }
  92. if (line.want) {
  93. var extra = "";
  94. if (!capsSent) {
  95. capsSent = true;
  96. if (caps["ofs-delta"]) extra += " ofs-delta";
  97. if (caps["thin-pack"]) extra += " thin-pack";
  98. // if (caps["multi_ack_detailed"]) extra += " multi_ack_detailed";
  99. // else if (caps["multi_ack"]) extra +=" multi_ack";
  100. if (caps["side-band-64k"]) extra += " side-band-64k";
  101. else if (caps["side-band"]) extra += " side-band";
  102. // if (caps["agent"]) extra += " agent=" + agent;
  103. if (caps.agent) extra += " agent=" + caps.agent;
  104. }
  105. extra += "\n";
  106. socket.put("want " + line.want + extra);
  107. return api.take(onWant);
  108. }
  109. if (line.done) {
  110. socket.put("done\n");
  111. return socket.take(onNak);
  112. }
  113. throw new Error("Invalid have/want command");
  114. }
  115. function onNak(line) {
  116. if (line === undefined) return api.put();
  117. if (line === null) return socket.take(onNak);
  118. if (bodec.isBinary(line) || line.progress || line.error) {
  119. packChannel = makeChannel();
  120. progressChannel = makeChannel();
  121. errorChannel = makeChannel();
  122. api.put({
  123. pack: { take: packChannel.take },
  124. progress: { take: progressChannel.take },
  125. error: { take: errorChannel.take },
  126. });
  127. return onMore(null, line);
  128. }
  129. var match = line.match(/^shallow ([0-9a-f]{40})$/);
  130. if (match) {
  131. refs.shallows.push(match[1]);
  132. return socket.take(onNak);
  133. }
  134. match = line.match(/^ACK ([0-9a-f]{40})$/);
  135. if (match) {
  136. return socket.take(onNak);
  137. }
  138. if (line === "NAK") {
  139. return socket.take(onNak);
  140. }
  141. throw new Error("Expected NAK, but got " + JSON.stringify(line));
  142. }
  143. function onMore(line) {
  144. if (line === undefined) {
  145. packChannel.put();
  146. progressChannel.put();
  147. errorChannel.put();
  148. return api.put();
  149. }
  150. if (line === null) {
  151. api.put(line);
  152. }
  153. else {
  154. if (line.progress) {
  155. progressChannel.put(line.progress);
  156. }
  157. else if (line.error) {
  158. errorChannel.put(line.error);
  159. }
  160. else {
  161. if (!packChannel.put(line)) {
  162. return packChannel.drain(onReady);
  163. }
  164. }
  165. }
  166. socket.take(onMore);
  167. }
  168. function onReady() {
  169. socket.take(onMore);
  170. }
  171. }
  172. var defer = require('js-git/lib/defer');
  173. function throwIt(err) {
  174. defer(function () {
  175. throw err;
  176. });
  177. // throw err;
  178. }