protected internal virtual async Task <JToken> GetResponseSafeInBatch(IUntypedCall call, HandlingContext context)
        {
            try
            {
                var response = await SafeNext(call, context, false);

                switch (response)
                {
                case JsonServerResponseWrapper jsonResponseWrapper:
                    return(jsonResponseWrapper.Value);

                case RawServerResponseWrapper rawResponseWrapper:
                    throw new JsonRpcInternalException("Raw responses are not supported in batches", null, null, rawResponseWrapper.Source.StatusCode);

                case null:
                    throw new ArgumentNullException(nameof(response));

                default:
                    throw new ArgumentOutOfRangeException(nameof(response), response.GetType().Name);
                }
            }
            catch (Exception e)
            {
                if (!(e is JsonRpcErrorResponseException))
                {
                    log.LogWarning(e, $"{nameof(GetResponseSafeInBatch)} failed: converting exception to json response");
                }
                return(errorFactory.ConvertExceptionToResponse(e, headerJsonRpcSerializer));
            }
        }
        protected internal virtual async Task <IServerResponseWrapper> SafeNext(IUntypedCall call, HandlingContext context, bool allowRawResponses)
        {
            log.LogTrace($"{nameof(SafeNext)}: Started. {nameof(allowRawResponses)} is [{allowRawResponses}]");
            IHeaderDictionary nestedHeaders = null;

            try
            {
                context.OriginalHttpContext.RequestAborted.ThrowIfCancellationRequested();
                var nestedHttpContext = nestedContextFactory.Create(context.OriginalHttpContext, call, context.RequestEncoding);
                log.LogTrace($"{nameof(SafeNext)}: invoking pipeline on nested context");
                await context.Next(nestedHttpContext);

                nestedHeaders = nestedHttpContext.Response.Headers;
                var result = await responseReader.GetResponse(nestedHttpContext, call, allowRawResponses, context.OriginalHttpContext.RequestAborted);

                if (result == null)
                {
                    throw new JsonRpcInternalException($"{nameof(ResponseReader)} returned null");
                }
                log.LogTrace($"{nameof(SafeNext)}: Completed");
                return(result);
            }
            catch (Exception e)
            {
                log.LogWarning(e, $"{nameof(SafeNext)} failed: converting exception to json response");
                var response = errorFactory.ConvertExceptionToResponse(e, headerJsonRpcSerializer);
                return(new JsonServerResponseWrapper(response, call, nestedHeaders));
            }
        }
Example #3
0
        internal static JToken SetId(JToken value, IUntypedCall call)
        {
            if (call is UntypedRequest request)
            {
                value[JsonRpcConstants.IdProperty] = request.RawId;
            }

            return(value);
        }
Example #4
0
        public JsonServerResponseWrapper(JToken value, IUntypedCall call, IHeaderDictionary headers)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            Value   = SetId(value, call);
            Headers = headers;
        }
Example #5
0
        public HttpContext Create(HttpContext context, IUntypedCall call, Encoding requestEncoding)
        {
            var newFeatures = new FeatureCollection(context.Features);

            OverrideRequestDataFeatures(newFeatures, context.Features, call, requestEncoding);
            // NOTE: factory breaks HttpContextAccessor
            var result = httpContextFactory.Create(newFeatures);

            log.LogTrace($"Created HttpContext [{result}]");
            return(result);
        }
Example #6
0
        protected virtual IHttpRequestFeature CreateRequest(IHttpRequestFeature originalRequest, IUntypedCall call, Encoding requestEncoding)
        {
            var headers = new Dictionary <string, StringValues>(originalRequest.Headers, StringComparer.Ordinal);
            var result  = new HttpRequestFeature
            {
                Protocol    = originalRequest.Protocol,
                Method      = originalRequest.Method,
                Scheme      = originalRequest.Scheme,
                Path        = originalRequest.Path,
                PathBase    = originalRequest.PathBase,
                QueryString = originalRequest.QueryString,
                RawTarget   = originalRequest.RawTarget,
                Headers     = new HeaderDictionary(headers),
                Body        = new MemoryStream(requestEncoding.GetBytes(call.RawJson))
            };

            log.LogTrace($"Copied request: [{result.Protocol}] [{result.Method}] [{result.Scheme}] [{result.Path}] [{result.PathBase}] [{result.QueryString}] [{result.RawTarget}], [{headers.Count}] headers");
            return(result);
        }
Example #7
0
        protected virtual void OverrideRequestDataFeatures(IFeatureCollection newFeatures, IFeatureCollection originalFeatures, IUntypedCall call, Encoding requestEncoding)
        {
            var itemsFeature = CreateItems(originalFeatures.Get <IItemsFeature>());

            itemsFeature.Items[JsonRpcConstants.RequestItemKey]        = call;
            itemsFeature.Items[JsonRpcConstants.NestedPipelineItemKey] = true;
            newFeatures.Set(itemsFeature);
            var requestFeature = CreateRequest(originalFeatures.Get <IHttpRequestFeature>(), call, requestEncoding);

            newFeatures.Set(requestFeature);
            var responseFeature = new StreamResponseBodyFeature(new MemoryStream());

            newFeatures.Set <IHttpResponseBodyFeature>(responseFeature);
        }
Example #8
0
 public void WithSingle(IUntypedCall singleCall)
 {
     SingleCall = singleCall;
 }
        /// <summary>
        /// Normal result of action when it returns object
        /// </summary>
        /// <param name="response"></param>
        /// <param name="call"></param>
        /// <param name="responseBody"></param>
        /// <param name="encoding"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        protected internal virtual async Task <IServerResponseWrapper> HandleObjectResult(HttpResponse response, IUntypedCall call, MemoryStream responseBody, Encoding encoding, CancellationToken token)
        {
            log.LogTrace($"{nameof(HandleObjectResult)}");
            var jToken = await ReadJsonResponse(responseBody, encoding, token);

            return(new JsonServerResponseWrapper(jToken, call, response.Headers));
        }
        protected internal virtual async Task <IServerResponseWrapper> HandleResultType(HttpResponse response, Type actionResultType, IUntypedCall call, MemoryStream responseBody, Encoding encoding, CancellationToken token)
        {
            switch (actionResultType)
            {
            case Type objectResult when objectResult == typeof(ObjectResult):
                return(await HandleObjectResult(response, call, responseBody, encoding, token));

            case null:
                return(await HandleNullResult(response, call, responseBody, encoding, token));

            default:
                return(await HandleUnknownResult(response, call, responseBody, encoding, token));
            }
        }
        protected internal virtual async Task <IServerResponseWrapper> GetResponseWrapper(HttpResponse response, Type actionResultType, IUntypedCall call, CancellationToken token)
        {
            if (!(response.Body is MemoryStream ms))
            {
                throw new JsonRpcInternalException("Expected MemoryStream to read response");
            }

            var contentType = response.GetTypedHeaders().ContentType;
            // TODO not sure if this will be right encoding
            var encoding = contentType?.Encoding ?? Encoding.UTF8;

            log.LogTrace($"{nameof(GetResponseWrapper)}: {nameof(encoding)} is [{encoding.EncodingName}]");
            ms.Seek(0, SeekOrigin.Begin);
            return(await HandleResultType(response, actionResultType, call, ms, encoding, token));
        }
        public virtual async Task <IServerResponseWrapper> GetResponse(HttpContext nestedHttpContext, IUntypedCall call, bool allowRawResponses, CancellationToken token)
        {
            var actionResultType = Utils.GetActionResultType(nestedHttpContext);

            log.LogTrace($"{nameof(GetResponse)}: {nameof(allowRawResponses)} is [{allowRawResponses}], {nameof(actionResultType)} is [{actionResultType}]");
            var responseWrapper = await GetResponseWrapper(nestedHttpContext.Response, actionResultType, call, token);

            switch (responseWrapper)
            {
            case RawServerResponseWrapper _ when !allowRawResponses:
                throw new JsonRpcInternalException($"Raw responses are not allowed by default and not supported in batches, check {nameof(JsonRpcOptions)}");

            case null:
                throw new ArgumentNullException(nameof(responseWrapper));

            default:
                return(responseWrapper);
            }
        }
 /// <summary>
 /// Unknown action results are treated as raw responses
 /// </summary>
 /// <param name="response"></param>
 /// <param name="call"></param>
 /// <param name="responseBody"></param>
 /// <param name="encoding"></param>
 /// <param name="token"></param>
 /// <returns></returns>
 protected internal virtual Task <IServerResponseWrapper> HandleUnknownResult(HttpResponse response, IUntypedCall call, MemoryStream responseBody, Encoding encoding, CancellationToken token)
 {
     log.LogTrace($"{nameof(GetResponseWrapper)}: return raw response");
     return(Task.FromResult <IServerResponseWrapper>(new RawServerResponseWrapper(response)));
 }
        /// <summary>
        /// Framework decided to reject our request, eg. 404 when routing failed
        /// </summary>
        /// <param name="response"></param>
        /// <param name="call"></param>
        /// <param name="responseBody"></param>
        /// <param name="encoding"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        protected internal virtual Task <IServerResponseWrapper> HandleNullResult(HttpResponse response, IUntypedCall call, MemoryStream responseBody, Encoding encoding, CancellationToken token)
        {
            log.LogTrace($"{nameof(HandleNullResult)}");
            var body   = ReadTextBody(responseBody, encoding);
            var jToken = FormatHttpErrorResponse(response, body);

            return(Task.FromResult <IServerResponseWrapper>(new JsonServerResponseWrapper(jToken, call, response.Headers)));
        }