/// <summary>
        /// Upload file stream to the server
        /// </summary>
        /// <param name="apiSession">api session</param>
        /// <param name="inputStream">stream for read from</param>
        /// <param name="fileName">file name</param>
        /// <param name="fileSize">file size in bytes</param>
        /// <param name="destFolderPath">destination folder like /path/to/folder </param>
        /// <param name="cancellationToken">cancellation tokern</param>
        /// <param name="overwriteFileifExists"></param>
        /// <returns></returns>
        public async Task UploadFileAsync(ApiSession apiSession, Stream inputStream, string fileName, long fileSize, string destFolderPath, CancellationToken cancellationToken, bool overwriteFileifExists = false)
        {
            if (apiSession == null)
            {
                throw new ArgumentNullException(nameof(apiSession));
            }

            try
            {
                var    spaceName = apiSession.SpaceName;
                string boundary  = "EasyMorphCommandClient--------" + Guid.NewGuid().ToString("N");
                string url       = UrlHelper.JoinUrl("space", spaceName, "files", destFolderPath);

                using (var content = new MultipartFormDataContent(boundary))
                {
                    var downloadProgress = new FileProgress(fileName, fileSize);
                    downloadProgress.StateChanged += DownloadProgress_StateChanged;
                    using (cancellationToken.Register(() => downloadProgress.ChangeState(FileProgressState.Cancelled)))
                    {
                        using (var streamContent = new ProgressStreamContent(inputStream, downloadProgress))
                        {
                            content.Add(streamContent, "files", Path.GetFileName(fileName));

                            var requestMessage = BuildHttpRequestMessage(overwriteFileifExists ? HttpMethod.Put : HttpMethod.Post, url, content, apiSession);
                            using (requestMessage)
                            {
                                using (var response = await GetHttpClient().SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
                                {
                                    await HandleResponse(response);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
                when(ex.InnerException != null && ex.InnerException is WebException)
                {
                    var einner = ex.InnerException as WebException;

                    if (einner.Status == WebExceptionStatus.ConnectionClosed)
                    {
                        throw new MorphApiNotFoundException("Specified folder not found");
                    }
                }
        }
        /// <summary>
        /// Download file from server
        /// </summary>
        /// <param name="apiSession">api session</param>
        /// <param name="remoteFilePath"> Path to the remote file. Like /some/folder/file.txt </param>
        /// <param name="handleFile">delegate to check file info before accessing to the file stream</param>
        /// <param name="streamToWriteTo">stream for writing. Writing will be executed only if handleFile delegate returns true</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task DownloadFileAsync(ApiSession apiSession, string remoteFilePath, Func <DownloadFileInfo, bool> handleFile, Stream streamToWriteTo, CancellationToken cancellationToken)
        {
            if (apiSession == null)
            {
                throw new ArgumentNullException(nameof(apiSession));
            }

            var spaceName = apiSession.SpaceName;
            var nvc       = new NameValueCollection();

            nvc.Add("_", DateTime.Now.Ticks.ToString());
            var url = UrlHelper.JoinUrl("space", spaceName, "files", remoteFilePath) + nvc.ToQueryString();

            // it's necessary to add HttpCompletionOption.ResponseHeadersRead to disable caching
            using (HttpResponseMessage response = await GetHttpClient()
                                                  .SendAsync(BuildHttpRequestMessage(HttpMethod.Get, url, null, apiSession), HttpCompletionOption.ResponseHeadersRead, cancellationToken))
                if (response.IsSuccessStatusCode)
                {
                    using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
                    {
                        var contentDisposition = response.Content.Headers.ContentDisposition;
                        DownloadFileInfo dfi   = null;
                        if (contentDisposition != null)
                        {
                            dfi = new DownloadFileInfo
                            {
                                // need to fix double quotes, that may come from server response
                                // FileNameStar contains file name encoded in UTF8
                                FileName = (contentDisposition.FileNameStar ?? contentDisposition.FileName).TrimStart('\"').TrimEnd('\"')
                            };
                        }
                        var contentLength = response.Content.Headers.ContentLength;
                        var fileProgress  = new FileProgress(dfi.FileName, contentLength.Value);
                        fileProgress.StateChanged += DownloadProgress_StateChanged;

                        var bufferSize = 4096;
                        if (handleFile(dfi))
                        {
                            var buffer     = new byte[bufferSize];
                            var size       = contentLength.Value;
                            var processed  = 0;
                            var lastUpdate = DateTime.MinValue;

                            fileProgress.ChangeState(FileProgressState.Starting);

                            while (true)
                            {
                                // cancel download if cancellation token triggered
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    fileProgress.ChangeState(FileProgressState.Cancelled);
                                    throw new OperationCanceledException();
                                }

                                var length = await streamToReadFrom.ReadAsync(buffer, 0, buffer.Length);

                                if (length <= 0)
                                {
                                    break;
                                }
                                await streamToWriteTo.WriteAsync(buffer, 0, length);

                                processed += length;
                                if (DateTime.Now - lastUpdate > TimeSpan.FromMilliseconds(250))
                                {
                                    fileProgress.SetProcessedBytes(processed);
                                    fileProgress.ChangeState(FileProgressState.Processing);
                                    lastUpdate = DateTime.Now;
                                }
                            }

                            fileProgress.ChangeState(FileProgressState.Finishing);
                        }
                    }
                }
                else
                {
                    // TODO: check
                    await HandleErrorResponse(response);
                }
        }