private static void ReportFailure(HttpResponseBase response, int?reportBand, Dictionary <ProtocolUtils.UpdateRequest, string> errors, string message) { if (reportBand == ProtocolUtils.ErrorBand) { response.BinaryWrite(ProtocolUtils.Band(ProtocolUtils.MessageBand, ProtocolUtils.DefaultEncoding.GetBytes(string.Format("{0}\n", message)))); foreach (var e in errors) { response.BinaryWrite(ProtocolUtils.Band(ProtocolUtils.MessageBand, ProtocolUtils.DefaultEncoding.GetBytes(string.Format("{0} ({1})\n", e.Key.CanonicalName, e.Value ?? "not created, see other errors")))); } response.BinaryWrite(ProtocolUtils.Band(ProtocolUtils.ErrorBand, ProtocolUtils.DefaultEncoding.GetBytes("code review creation aborted\n"))); } else { var status = new List <byte[]>(); status.Add(ProtocolUtils.PacketLine(string.Format("unpack {0}\n", message))); foreach (var e in errors) { status.Add(ProtocolUtils.PacketLine(string.Format("ng {0} {1}\n", e.Key.CanonicalName, e.Value ?? "not created, see other errors"))); } status.Add(ProtocolUtils.EndMarker); response.BinaryWrite(ProtocolUtils.Band(reportBand, status)); } }
/// <inheritdoc /> public override void ExecuteResult(ControllerContext context) { var response = context.HttpContext.Response; response.StatusCode = 200; response.ContentType = "application/x-" + this.service + "-advertisement"; response.BinaryWrite(ProtocolUtils.PacketLine("# service=" + this.service + "\n")); response.BinaryWrite(ProtocolUtils.EndMarker); var ids = new SortedSet <string>(this.repo.Refs.Select(r => r.TargetIdentifier)); var first = true; foreach (var id in ids) { var line = first ? string.Format("{0} refs/anonymous/{0}\0{1}\n", id, this.GetCapabilities()) : string.Format("{0} refs/anonymous/{0}\n", id); response.BinaryWrite(ProtocolUtils.PacketLine(line)); first = false; } if (first) { var line = string.Format("{0} capabilities^{{}}\0{1}\n", ProtocolUtils.ZeroId, this.GetCapabilities()); response.BinaryWrite(ProtocolUtils.PacketLine(line)); } response.BinaryWrite(ProtocolUtils.EndMarker); response.End(); }
private static void ReportSuccess(HttpResponseBase response, int?reportBand) { response.BinaryWrite(ProtocolUtils.Band(reportBand, ProtocolUtils.PacketLine("unpack ok\n"), ProtocolUtils.PacketLine("ok " + DestinationRefName + "\n"), ProtocolUtils.PacketLine("ok " + SourceRefName + "\n"), ProtocolUtils.EndMarker)); }
private MemoryStream ReadPack(IList <ProtocolUtils.UpdateRequest> commands, HashSet <string> capabilities, Stream input) { var startInfo = new ProcessStartInfo(GitReviewApplication.GitPath, "receive-pack --stateless-rpc .") { WorkingDirectory = GitReviewApplication.RepositoryPath, RedirectStandardInput = true, RedirectStandardOutput = true, StandardOutputEncoding = ProtocolUtils.DefaultEncoding, UseShellExecute = false, }; var output = new MemoryStream(); using (var git = Process.Start(startInfo)) { var outputTask = Task.Factory.StartNew(() => { git.StandardOutput.BaseStream.CopyTo(output); }); var first = true; foreach (var c in commands) { var message = string.Format("{0} {1} {2}", c.SourceIdentifier ?? ProtocolUtils.ZeroId, c.TargetIdentifier ?? ProtocolUtils.ZeroId, c.CanonicalName); if (first) { message += "\0atomic report-status"; } var data = ProtocolUtils.PacketLine(message); git.StandardInput.BaseStream.Write(data, 0, data.Length); first = false; } var marker = ProtocolUtils.EndMarker; git.StandardInput.BaseStream.Write(marker, 0, marker.Length); input.CopyTo(git.StandardInput.BaseStream); git.StandardInput.Close(); git.WaitForExit(); outputTask.Wait(); } output.Seek(0, SeekOrigin.Begin); return(output); }
/// <summary> /// Parses a list of reference update requests from the <see cref="Stream"/>. /// </summary> /// <param name="stream">The <see cref="Stream"/> that contains the requests.</param> /// <param name="capabilities">The capabilities supported by the requester. This will be modified before yielding the first <see cref="UpdateRequest"/> to contain only capabilities that are shared by the client.</param> /// <returns>An enumerable collection of <see cref="UpdateRequest">UpdateRequests</see>.</returns> public static IEnumerable <UpdateRequest> ParseUpdateRequests(Stream stream, HashSet <string> capabilities) { var requests = new List <UpdateRequest>(); var first = true; while (true) { var line = ProtocolUtils.ReadPacketLine(stream); if (line == null) { break; } line = line.TrimEnd('\n'); if (first) { var parts = line.Split(new[] { '\0' }, 2); if (parts.Length < 2) { throw new ProtocolException("Capabilities not specified."); } line = parts[0]; var specified = new HashSet <string>(parts[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); capabilities.IntersectWith(specified); specified.ExceptWith(capabilities); specified.RemoveWhere(s => s.StartsWith("agent=")); if (specified.Count != 0) { throw new ProtocolException("Unrecognized capability."); } } var requestParts = line.Split(new[] { ' ' }, 3); var source = requestParts[0]; var target = requestParts[1]; var name = requestParts[2]; yield return(new UpdateRequest(source == ProtocolUtils.ZeroId ? null : source, target == ProtocolUtils.ZeroId ? null : target, name)); first = false; } if (first) { capabilities.Clear(); } }
private void ReceivePack(ControllerContext context, Stream input, HttpResponseBase response) { var capabilities = new HashSet <string>(Capabilities.Split(' ')); var requests = ProtocolUtils.ParseUpdateRequests(input, capabilities).ToList(); if (requests.Count == 0) { response.BinaryWrite(ProtocolUtils.EndMarker); return; } var reportStatus = capabilities.Contains("report-status"); var useSideBand = capabilities.Contains("side-band-64k"); var reportBand = useSideBand ? ProtocolUtils.PrimaryBand : (int?)null; var failureBand = reportStatus ? reportBand : ProtocolUtils.ErrorBand; try { ProtocolUtils.UpdateRequest source; ProtocolUtils.UpdateRequest destination; var errors = ReadRequests(requests, out source, out destination); if (errors.Any(e => e.Value != null) || source == null || destination == null) { if (reportStatus || useSideBand) { ReportFailure(response, failureBand, errors, "expected source and destination branches to be pushed"); } return; } var refPrefix = Guid.NewGuid().ToString(); source = new ProtocolUtils.UpdateRequest( source.SourceIdentifier, source.TargetIdentifier, RepoFormat.FormatSourceRef(refPrefix, 1)); destination = new ProtocolUtils.UpdateRequest( destination.SourceIdentifier, destination.TargetIdentifier, RepoFormat.FormatDestinationRef(refPrefix, 1)); var output = this.ReadPack(new[] { source, destination }, capabilities, input); var line = ProtocolUtils.ReadPacketLine(output).TrimEnd('\n'); if (line != "unpack ok") { line = line.Substring("unpack ".Length); if (reportStatus || useSideBand) { ReportFailure(response, failureBand, errors, line); } return; } string id; try { using (var ctx = new ReviewContext()) { using (new NoSyncScope()) { id = ctx.GetNextReviewId().Result; } ctx.Reviews.Add(new Review { Id = id, RefPrefix = refPrefix, }); ctx.SaveChanges(); } } catch (DbUpdateException ex) { ReportFailure(response, failureBand, errors, ex.GetBaseException().Message); throw; } if (useSideBand) { var url = new UrlHelper(context.RequestContext).Action("Index", "Home", null, context.HttpContext.Request.Url.Scheme) + "#/" + id; var message = string.Format("code review created:\n\n\t{0}\n\n", url); response.BinaryWrite(ProtocolUtils.Band(ProtocolUtils.MessageBand, Encoding.UTF8.GetBytes(message))); } if (reportStatus) { ReportSuccess(response, reportBand); } } finally { if (useSideBand) { response.BinaryWrite(ProtocolUtils.EndMarker); } } }
private static Dictionary<ProtocolUtils.UpdateRequest, string> ReadRequests(List<ProtocolUtils.UpdateRequest> requests, out ProtocolUtils.UpdateRequest source, out ProtocolUtils.UpdateRequest destination) { var errors = requests.ToDictionary(r => r, r => { if (r.TargetIdentifier == null) { return "delete unsupported"; } else if (r.SourceIdentifier != null) { return "update unsupported"; } else if (r.CanonicalName != SourceRefName && r.CanonicalName != DestinationRefName) { return "ref unsupported"; } return null; }); if (requests.Count == 2) { source = requests.FirstOrDefault(r => r.CanonicalName == SourceRefName); destination = requests.FirstOrDefault(r => r.CanonicalName == DestinationRefName); } else { source = null; destination = null; } return errors; }