本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
2.7KB

  1. package packp
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "gopkg.in/src-d/go-git.v4/plumbing"
  9. "gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
  10. )
  11. const ackLineLen = 44
  12. // ServerResponse object acknowledgement from upload-pack service
  13. type ServerResponse struct {
  14. ACKs []plumbing.Hash
  15. }
  16. // Decode decodes the response into the struct, isMultiACK should be true, if
  17. // the request was done with multi_ack or multi_ack_detailed capabilities.
  18. func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error {
  19. // TODO: implement support for multi_ack or multi_ack_detailed responses
  20. if isMultiACK {
  21. return errors.New("multi_ack and multi_ack_detailed are not supported")
  22. }
  23. s := pktline.NewScanner(reader)
  24. for s.Scan() {
  25. line := s.Bytes()
  26. if err := r.decodeLine(line); err != nil {
  27. return err
  28. }
  29. // we need to detect when the end of a response header and the beginning
  30. // of a packfile header happened, some requests to the git daemon
  31. // produces a duplicate ACK header even when multi_ack is not supported.
  32. stop, err := r.stopReading(reader)
  33. if err != nil {
  34. return err
  35. }
  36. if stop {
  37. break
  38. }
  39. }
  40. return s.Err()
  41. }
  42. // stopReading detects when a valid command such as ACK or NAK is found to be
  43. // read in the buffer without moving the read pointer.
  44. func (r *ServerResponse) stopReading(reader *bufio.Reader) (bool, error) {
  45. ahead, err := reader.Peek(7)
  46. if err == io.EOF {
  47. return true, nil
  48. }
  49. if err != nil {
  50. return false, err
  51. }
  52. if len(ahead) > 4 && r.isValidCommand(ahead[0:3]) {
  53. return false, nil
  54. }
  55. if len(ahead) == 7 && r.isValidCommand(ahead[4:]) {
  56. return false, nil
  57. }
  58. return true, nil
  59. }
  60. func (r *ServerResponse) isValidCommand(b []byte) bool {
  61. commands := [][]byte{ack, nak}
  62. for _, c := range commands {
  63. if bytes.Equal(b, c) {
  64. return true
  65. }
  66. }
  67. return false
  68. }
  69. func (r *ServerResponse) decodeLine(line []byte) error {
  70. if len(line) == 0 {
  71. return fmt.Errorf("unexpected flush")
  72. }
  73. if bytes.Equal(line[0:3], ack) {
  74. return r.decodeACKLine(line)
  75. }
  76. if bytes.Equal(line[0:3], nak) {
  77. return nil
  78. }
  79. return fmt.Errorf("unexpected content %q", string(line))
  80. }
  81. func (r *ServerResponse) decodeACKLine(line []byte) error {
  82. if len(line) < ackLineLen {
  83. return fmt.Errorf("malformed ACK %q", line)
  84. }
  85. sp := bytes.Index(line, []byte(" "))
  86. h := plumbing.NewHash(string(line[sp+1 : sp+41]))
  87. r.ACKs = append(r.ACKs, h)
  88. return nil
  89. }
  90. // Encode encodes the ServerResponse into a writer.
  91. func (r *ServerResponse) Encode(w io.Writer) error {
  92. if len(r.ACKs) > 1 {
  93. return errors.New("multi_ack and multi_ack_detailed are not supported")
  94. }
  95. e := pktline.NewEncoder(w)
  96. if len(r.ACKs) == 0 {
  97. return e.Encodef("%s\n", nak)
  98. }
  99. return e.Encodef("%s %s\n", ack, r.ACKs[0].String())
  100. }
上海开阖软件有限公司 沪ICP备12045867号-1