/// <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); } } }