Пример #1
0
        /// <summary>
        /// Asychronously Pad a stream with a sequence of bytes
        /// </summary>
        /// <param name="stream">Target <see cref="Stream"/></param>
        /// <param name="count">Number of bytes to pad</param>
        /// <param name="value">Byte value to use for padding</param>
        /// <param name="result">The <see cref="Result"/> instance to be returned by the call.</param>
        /// <returns>Synchronization handle for completion of padding action.</returns>
        public static Result Pad(this Stream stream, long count, byte value, Result result)
        {
            if (count < 0)
            {
                throw new ArgumentException("count must be non-negative");
            }

            // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation

            if (count == 0)
            {
                result.Return();
            }
            else
            {
                // initialize buffer so we can write in large chunks if need be
                byte[] bytes = new byte[(int)Math.Min(4096L, count)];
                for (int i = 0; i < bytes.Length; ++i)
                {
                    bytes[i] = value;
                }

                // use new task environment so we don't copy the task state over and over again
                TaskEnv.ExecuteNew(() => Coroutine.Invoke(Pad_Helper, stream, count, bytes, result));
            }
            return(result);
        }
Пример #2
0
        /// <summary>
        /// Asynchronous copying of one stream to another.
        /// </summary>
        /// <param name="source">Source <see cref="Stream"/>.</param>
        /// <param name="target">Target <see cref="Stream"/>.</param>
        /// <param name="length">Number of bytes to copy from source to target.</param>
        /// <param name="result">The <see cref="Result"/> instance to be returned by the call.</param>
        /// <returns>Synchronization handle for the number of bytes copied.</returns>
        public static Result <long> CopyToStream(this Stream source, Stream target, long length, Result <long> result)
        {
            if (!SysUtil.UseAsyncIO)
            {
                return(AsyncUtil.Fork(() => CopyToStream(source, target, length), result));
            }

            // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation

            if ((source == Stream.Null) || (length == 0))
            {
                result.Return(0);
            }
            else if (source.IsStreamMemorized() && target.IsStreamMemorized())
            {
                // source & target are memory streams; let's do the copy inline as fast as we can
                result.Return(CopyToStream(source, target, length));
            }
            else
            {
                // use new task environment so we don't copy the task state over and over again
                TaskEnv.ExecuteNew(() => Coroutine.Invoke(CopyTo_Helper, source, target, length, result));
            }
            return(result);
        }
Пример #3
0
        /// <summary>
        /// Invoke an action in the context of a service feature.
        /// </summary>
        /// <remarks>
        /// Assumes that there exists a current <see cref="DreamContext"/> that belongs to a request to another feature of this service.
        /// </remarks>
        /// <param name="verb">Http verb.</param>
        /// <param name="path">Feature path.</param>
        /// <param name="handler">Action to perform in this context.</param>
        /// <returns>Exception thrown by handler or null.</returns>
        public Exception InvokeInServiceContext(string verb, string path, Action handler)
        {
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            if (string.IsNullOrEmpty(verb))
            {
                throw new ArgumentNullException("verb");
            }
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            // create new new environment for execution
            XUri         uri     = Self.AtPath(path);
            DreamContext current = DreamContext.Current;
            Exception    e       = TaskEnv.ExecuteNew(delegate {
                DreamFeatureStage[] stages = new[] {
                    new DreamFeatureStage("InServiceInvokeHandler", InServiceInvokeHandler, DreamAccess.Private)
                };

                // BUGBUGBUG (steveb): when invoking a remote function this way, we're are not running the proloques and epilogues, which leads to errors;
                //  also dream-access attributes are being ignored (i.e. 'public' vs. 'private')
                DreamMessage message = DreamUtil.AppendHeadersToInternallyForwardedMessage(current.Request, DreamMessage.Ok());
                var context          = current.CreateContext(verb, uri, new DreamFeature(this, Self, 0, stages, verb, path), message);
                context.AttachToCurrentTaskEnv();

                // pass along host and public-uri information
                handler();
            }, TimerFactory);

            return(e);
        }
        private static string CachedWebGet(XUri uri, double?ttl, bool?nilIfMissing)
        {
            // fetch message from cache or from the web
            string result;

            lock (_webTextCache) {
                if (_webTextCache.TryGetValue(uri, out result))
                {
                    return(result);
                }
            }

            // do the web request
            Result <DreamMessage> response = new Result <DreamMessage>();

            Plug.New(uri).WithTimeout(DEFAULT_WEB_TIMEOUT).InvokeEx("GET", DreamMessage.Ok(), response);
            DreamMessage message = response.Wait();

            try {
                // check message status
                if (!message.IsSuccessful)
                {
                    if (nilIfMissing.GetValueOrDefault())
                    {
                        return(null);
                    }
                    return(message.Status == DreamStatus.UnableToConnect
                        ? string.Format("(unable to fetch text document from uri [status: {0} ({1}), message: \"{2}\"])", (int)message.Status, message.Status, message.ToDocument()["message"].AsText)
                        : string.Format("(unable to fetch text document from uri [status: {0} ({1})])", (int)message.Status, message.Status));
                }

                // check message size
                Result resMemorize = message.Memorize(InsertTextLimit, new Result()).Block();
                if (resMemorize.HasException)
                {
                    return(nilIfMissing.GetValueOrDefault() ? null : "(text document is too large)");
                }

                // detect encoding and decode response
                var stream   = message.AsStream();
                var encoding = stream.DetectEncoding() ?? message.ContentType.CharSet;
                result = encoding.GetString(stream.ReadBytes(-1));
            } finally {
                message.Close();
            }

            // start timer to clean-up cached result
            lock (_webTextCache) {
                _webTextCache[uri] = result;
            }
            double timeout = Math.Min(60 * 60 * 24, Math.Max(ttl ?? MinCacheTtl, 60));

            TaskEnv.ExecuteNew(() => TaskTimer.New(TimeSpan.FromSeconds(timeout), timer => {
                lock (_webTextCache) {
                    _webTextCache.Remove((XUri)timer.State);
                }
            }, uri, TaskEnv.None));
            return(result);
        }
Пример #5
0
        /// <summary>
        /// Asynchrounously copy a <see cref="Stream"/> to several targets
        /// </summary>
        /// <param name="source">Source <see cref="Stream"/></param>
        /// <param name="targets">Array of target <see cref="Stream"/> objects</param>
        /// <param name="length">Number of bytes to copy from source to targets</param>
        /// <param name="result">The <see cref="Result"/> instance to be returned by the call.</param>
        /// <returns>Synchronization handle for the number of bytes copied to each target.</returns>
        public static Result <long?[]> CopyTo(this Stream source, Stream[] targets, long length, Result <long?[]> result)
        {
            // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation

            if ((source == Stream.Null) || (length == 0))
            {
                long?[] totals = new long?[targets.Length];
                for (int i = 0; i < totals.Length; ++i)
                {
                    totals[i] = 0;
                }
                result.Return(totals);
            }
            else
            {
                // use new task environment so we don't copy the task state over and over again
                TaskEnv.ExecuteNew(() => Coroutine.Invoke(CopyTo_Helper, source, targets, length, result));
            }
            return(result);
        }
Пример #6
0
 private Result <long> CopyStream(Action <string> activity, Stream source, Stream target, long length, Result <long> result)
 {
     // NOTE (steveb): intermediary copy steps already have a timeout operation, no need to limit the duration of the entire copy operation
     if ((source == Stream.Null) || (length == 0))
     {
         activity("return CopyStream 1");
         result.Return(0);
     }
     else if (!SysUtil.UseAsyncIO || (source.IsStreamMemorized() && target.IsStreamMemorized()))
     {
         // source & target are memory streams; let's do the copy inline as fast as we can
         byte[] buffer = new byte[StreamUtil.BUFFER_SIZE];
         long   total  = 0;
         while (length != 0)
         {
             long count = source.Read(buffer, 0, buffer.Length);
             if (count == 0)
             {
                 break;
             }
             target.Write(buffer, 0, (int)count);
             total  += count;
             length -= count;
         }
         activity("return CopyStream 2");
         result.Return(total);
     }
     else
     {
         // use new task environment so we don't copy the task state over and over again
         TaskEnv.ExecuteNew(delegate() {
             activity("pre CopyStream_Handler");
             Coroutine.Invoke(CopyStream_Handler, activity, source, target, length, result);
             activity("post CopyStream_Handler");
         });
     }
     return(result);
 }
Пример #7
0
        //--- Methods ---
        internal void Handler(Result <DreamMessage> result)
        {
            DreamMessage request;

            // check if previous feature handler threw an exception
            try {
                if (result.HasException)
                {
                    DreamAbortException abort = result.Exception as DreamAbortException;
                    if (abort != null)
                    {
                        // extract contained message
                        request = abort.Response;
                    }
                    else
                    {
                        // check if this is a cached response we need to forward
                        DreamCachedResponseException cached = result.Exception as DreamCachedResponseException;
                        if (cached != null)
                        {
                            _response.Throw(cached);
                            return;
                        }

                        // convert exception into message
                        DreamMessage          exceptionMessage = null;
                        ExceptionTranslator[] translators      = _context.Feature.ExceptionTranslators;
                        if (translators != null)
                        {
                            bool locallyAttachedContext = false;
                            try {
                                if (DreamContext.CurrentOrNull == null)
                                {
                                    _context.AttachToCurrentTaskEnv();
                                    locallyAttachedContext = true;
                                }
                                foreach (var translate in translators)
                                {
                                    exceptionMessage = translate(_context, result.Exception);
                                    if (exceptionMessage != null)
                                    {
                                        break;
                                    }
                                }
                            } finally {
                                if (locallyAttachedContext)
                                {
                                    _context.DetachFromTaskEnv();
                                }
                            }
                        }
                        if (exceptionMessage == null)
                        {
                            _log.ErrorExceptionFormat(result.Exception, "handler for {0}:{1} failed ({2})", _context.Verb, _context.Uri.ToString(false), _previousName);
                            exceptionMessage = DreamMessage.InternalError(result.Exception);
                        }
                        request = exceptionMessage;
                    }
                }
                else
                {
                    request = result.Value;
                }
            } catch (Exception e) {
                if (result.HasException)
                {
                    _log.ErrorExceptionFormat(result.Exception, "handler for {0}:{1} failed ({2}), cascading via processing exception '{3}'", _context.Verb, _context.Uri.ToString(false), _previousName, e);
                    request = DreamMessage.InternalError(result.Exception);
                }
                else
                {
                    _log.ErrorExceptionFormat(e, "handler for {0}:{1} failed completing stage '{2}'", _context.Verb, _context.Uri.ToString(false), _previousName);
                    request = DreamMessage.InternalError(e);
                }
            }

            // check if feature handler can handle this message
            if (!MainStage || request.IsSuccessful)
            {
                TaskEnv.ExecuteNew(() => Handler_DreamMessage(request));
            }
            else
            {
                // non-success messages skip the main stage
                _response.Return(request);
            }
        }