Example #1
0
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)
			}
		}
	}
}
Example #2
0
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) {
	})

}