func (app *GitchainApp) OnDeliver(msg wendy.Message) { log := app.log var err error if msg.Purpose&MSG_BROADCAST != 0 { log.Debug("received a broadcast") if msg.Sender.ID == app.cluster.ID() { log.Error("received own broadcast", "bugtrap", "true") } var envelope broadcastEnvelope dec := gob.NewDecoder(bytes.NewBuffer(msg.Value)) dec.Decode(&envelope) if err != nil { log.Error("error while decoding an incoming message", "err", err) } else { var txe *transaction.Envelope if msg.Purpose&MSG_TRANSACTION != 0 { if txe, err = transaction.DecodeEnvelope(envelope.Content); err != nil { log.Error("error while decoding transaction", "err", err) } else { app.srv.Router.Pub(txe, "/transaction") log.Debug("announced transaction locally", "txn", txe) } } var newLimit wendy.NodeID nodes := app.cluster.RoutingTableNodes() if len(nodes) > 1 { for i := range nodes[0 : len(nodes)-1] { var buf bytes.Buffer enc := gob.NewEncoder(&buf) if nodes[i].ID.Less(envelope.Limit) { if nodes[i+1].ID.Less(envelope.Limit) { newLimit = nodes[i+1].ID } else { newLimit = envelope.Limit } if err = enc.Encode(broadcastEnvelope{Content: envelope.Content, Limit: newLimit}); err != nil { return } wmsg := app.cluster.NewMessage(msg.Purpose, nodes[i].ID, buf.Bytes()) if err = app.cluster.Send(wmsg); err != nil { log.Error("error sending message", "err", err) } else { log.Debug("forwarded transaction", "txn", txe) } } else { break } } } if nodes[len(nodes)-1].ID.Less(envelope.Limit) { var buf bytes.Buffer enc := gob.NewEncoder(&buf) if err = enc.Encode(broadcastEnvelope{Content: envelope.Content, Limit: app.cluster.ID()}); err != nil { return } wmsg := app.cluster.NewMessage(msg.Purpose, nodes[len(nodes)-1].ID, buf.Bytes()) if err = app.cluster.Send(wmsg); err != nil { log.Error("error sending message", "err", err) } else { log.Debug("forwarded transaction", "txn", txe) } } } } else { switch { case msg.Purpose&MSG_OBJECT != 0: obj := git.DecodeObject(msg.Value) err = git.WriteObject(obj, path.Join(app.srv.Config.General.DataPath, "objects")) if err != nil { log.Error("error while writing object", "obj", obj, "err", err) } } } }
func SetupGitRoutes(r *mux.Router, srv *context.T, log log15.Logger) { log = log.New("cmp", "git") // Git Server r.Methods("POST").Path("/{repository:.+}/git-upload-pack").HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { log := log.New("cmp", "git-upload-pack") dec := pktline.NewDecoder(req.Body) resp.Header().Add("Cache-Control", "no-cache") resp.Header().Add("Content-Type", "application/x-git-upload-pack-result") enc := pktline.NewEncoder(resp) var wants, haves, common []git.Hash var objects []git.Object wantsRcvd := false for { var pktline []byte if err := dec.Decode(&pktline); err != nil { log.Error("error while decoding pkt-line", "err", err) return } switch { case pktline == nil: switch { case !wantsRcvd: wantsRcvd = true case wantsRcvd: for i := range haves { _, err := readObject(srv, haves[i]) if err == nil { enc.Encode([]byte(fmt.Sprintf("ACK %x common\n", haves[i]))) common = append(common, haves[i]) } else { enc.Encode([]byte("NAK\n")) } } haves = make([]git.Hash, 0) } case bytes.Compare(pktline, []byte("done\n")) == 0: if len(common) == 0 { enc.Encode([]byte("NAK\n")) } goto done default: line := bytes.Split(pktline, []byte{' '}) h := bytes.TrimSuffix(line[1], []byte{10}) hash, err := hex.DecodeString(string(h)) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("error parsing hash %s: %v\n", line[1], err))...)) return } if string(line[0]) == "want" { wants = append(wants, hash) } if string(line[0]) == "have" { haves = append(haves, hash) } } } done: var err error for i := range wants { var objs []git.Object objs, err = processCommit(srv, wants[i], common) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("%s", err))...)) return } objects = append(objects, objs...) } // filter out duplicates seen := make(map[string]bool) filteredObjects := make([]git.Object, 0) for i := range objects { hash := string(objects[i].Hash()) if !seen[hash] { seen[hash] = true filteredObjects = append(filteredObjects, objects[i]) } } // packfile := git.NewPackfile(filteredObjects) err = git.WritePackfile(&sideband64Writer{writer: &pktlineWriter{encoder: enc}, band: 1}, packfile) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("%s", err))...)) return } enc.Encode(append([]byte{1}, pktlineToBytes(nil)...)) enc.Encode(nil) }) r.Methods("POST").Path("/{repository:.+}/git-receive-pack").HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { reponame := mux.Vars(req)["repository"] var lines [][]byte dec := pktline.NewDecoder(req.Body) dec.DecodeUntilFlush(&lines) resp.Header().Add("Cache-Control", "no-cache") resp.Header().Add("Content-Type", "application/x-git-receive-pack-result") enc := pktline.NewEncoder(resp) packfile, err := git.ReadPackfile(req.Body) if err != nil { enc.Encode(append([]byte{1}, pktlineToBytes([]byte(fmt.Sprintf("unpack %v\n", err)))...)) } else { enc.Encode(append([]byte{1}, pktlineToBytes([]byte("unpack ok"))...)) for i := range packfile.Objects { err = git.WriteObject(packfile.Objects[i], path.Join(srv.Config.General.DataPath, "objects")) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("Error while writing object: %v\n", err))...)) } else { srv.Router.Pub(packfile.Objects[i], "/git/object") } } for i := range lines { split := strings.Split(string(lines[i]), " ") old := split[0] new := split[1] ref := strings.TrimRight(split[2], string([]byte{0})) oldHash, err := hex.DecodeString(old) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("Malformed hash %s\n", old))...)) return } newHash, err := hex.DecodeString(new) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("Malformed hash %s\n", new))...)) return } tx := transaction.NewReferenceUpdate(reponame, ref, oldHash, newHash) key, err := srv.DB.GetMainKey() if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("Errow while retrieving main key: %v", err))...)) return } if key == nil { enc.Encode(append([]byte{3}, []byte("No main private key to sign the transaction")...)) return } hash, err := srv.DB.GetPreviousEnvelopeHashForPublicKey(&key.PublicKey) if err != nil { enc.Encode(append([]byte{3}, []byte(fmt.Sprintf("Error while preparing transaction: %v", err))...)) return } txe := transaction.NewEnvelope(hash, tx) txe.Sign(key) enc.Encode(append([]byte{2}, []byte(fmt.Sprintf("[gitchain] Transaction %s\n", txe.Hash()))...)) srv.Router.Pub(txe, "/transaction") enc.Encode(append([]byte{1}, pktlineToBytes([]byte(fmt.Sprintf("ok %s\n", ref)))...)) } } enc.Encode(append([]byte{1}, pktlineToBytes(nil)...)) enc.Encode(nil) }) r.Methods("GET").Path("/{repository:.+}/info/refs").HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { req.ParseForm() service := req.Form["service"][0] reponame := mux.Vars(req)["repository"] repo, err := srv.DB.GetRepository(reponame) if err != nil { log.Error("error while retrieving repository", "repo", reponame, "err", err) resp.WriteHeader(500) return } if repo == nil || repo.Status == repository.PENDING { resp.WriteHeader(404) return } refs, err := srv.DB.ListRefs(reponame) if err != nil { log.Error("error listing refs", "repo", reponame, "err", err) resp.WriteHeader(500) return } reflines := make([][]byte, len(refs)) for i := range refs { ref, err := srv.DB.GetRef(reponame, refs[i]) if err != nil { log.Error("error getting ref", "repo", reponame, "err", err) resp.WriteHeader(500) return } refline := append(append([]byte(hex.EncodeToString(ref)), 32), []byte(refs[i])...) if i == 0 { // append capabilities refline = append(append(refline, 0), capabilities()...) } refline = append(refline, 10) // LF reflines[i] = refline } ref, err := srv.DB.GetRef(reponame, "refs/heads/master") if bytes.Compare(ref, make([]byte, 20)) != 0 { reflines = append(reflines, append(append(append([]byte(hex.EncodeToString(ref)), 32), []byte("HEAD")...), 10)) } resp.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", service)) resp.Header().Add("Cache-Control", "no-cache") enc := pktline.NewEncoder(resp) enc.Encode([]byte(fmt.Sprintf("# service=%s\n", service))) enc.Encode(nil) if len(reflines) == 0 { enc.Encode(append(append(append(append([]byte("0000000000000000000000000000000000000000"), 32), nulCapabilities()...), []byte{0, 32, 10}...))) } else { for i := range reflines { enc.Encode(reflines[i]) } } enc.Encode(nil) }) r.Methods("GET").Path("/{repository:.+}/HEAD").HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { reponame := mux.Vars(req)["repository"] ref, err := srv.DB.GetRef(reponame, "refs/heads/master") if err != nil { log.Error("error while retrieving repository HEAD", "repo", reponame, "err", err) resp.WriteHeader(500) return } resp.Header().Add("Content-Type", "text/plain") resp.Header().Add("Cache-Control", "no-cache") resp.Write([]byte(hex.EncodeToString(ref))) }) r.Methods("GET").Path("/{repository:.+}/objects/{hash:.+}").HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { }) }