protected virtual void ApiAsync(HttpMethod httpMethod, string path, object parameters, Type resultType, object userState)
        {
            Stream input;
            bool containsEtag;
            IList<int> batchEtags = null;
            var httpHelper = PrepareRequest(httpMethod, path, parameters, resultType, out input, out containsEtag, out batchEtags);
            _httpWebRequest = httpHelper.HttpWebRequest;

#if FLUENTHTTP_CORE_TPL
            if (HttpWebRequestWrapperCreated != null)
                HttpWebRequestWrapperCreated(this, new HttpWebRequestCreatedEventArgs(userState, httpHelper.HttpWebRequest));
#endif

            var uploadProgressChanged = UploadProgressChanged;
            bool notifyUploadProgressChanged = uploadProgressChanged != null && httpHelper.HttpWebRequest.Method == "POST";

            httpHelper.OpenReadCompleted +=
                (o, e) =>
                {
                    FuntownApiEventArgs args;
                    if (e.Cancelled)
                    {
                        args = new FuntownApiEventArgs(e.Error, true, userState, null);
                    }
                    else if (e.Error == null)
                    {
                        string responseString = null;

                        try
                        {
                            using (var stream = e.Result)
                            {
#if NETFX_CORE
                                bool compressed = false;
                                
                                var contentEncoding = httpHelper.HttpWebResponse.Headers.AllKeys.Contains("Content-Encoding") ? httpHelper.HttpWebResponse.Headers["Content-Encoding"] : null;
                                if (contentEncoding != null)
                                {
                                    if (contentEncoding.IndexOf("gzip") != -1)
                                    {
                                        using (var uncompressedStream = new System.IO.Compression.GZipStream(stream, System.IO.Compression.CompressionMode.Decompress))
                                        {
                                            using (var reader = new StreamReader(uncompressedStream))
                                            {
                                                responseString = reader.ReadToEnd();
                                            }
                                        }

                                        compressed = true;
                                    }
                                    else if (contentEncoding.IndexOf("deflate") != -1)
                                    {
                                        using (var uncompressedStream = new System.IO.Compression.DeflateStream(stream, System.IO.Compression.CompressionMode.Decompress))
                                        {
                                            using (var reader = new StreamReader(uncompressedStream))
                                            {
                                                responseString = reader.ReadToEnd();
                                            }
                                        }

                                        compressed = true;
                                    }
                                }

                                if (!compressed)
                                {
                                    using (var reader = new StreamReader(stream))
                                    {
                                        responseString = reader.ReadToEnd();
                                    }
                                }
#else
                                var response = httpHelper.HttpWebResponse;
                                if (response != null && response.StatusCode == HttpStatusCode.NotModified)
                                {
                                    var jsonObject = new JsonObject();
                                    var headers = new JsonObject();

                                    foreach (var headerName in response.Headers.AllKeys)
                                        headers[headerName] = response.Headers[headerName];

                                    jsonObject["headers"] = headers;
                                    args = new FuntownApiEventArgs(null, false, userState, jsonObject);
                                    OnCompleted(httpMethod, args);
                                    return;
                                }

                                using (var reader = new StreamReader(stream))
                                {
                                    responseString = reader.ReadToEnd();
                                }
#endif
                            }

                            try
                            {
                                object result = ProcessResponse(httpHelper, responseString, resultType, containsEtag, batchEtags);
                                args = new FuntownApiEventArgs(null, false, userState, result);
                            }
                            catch (Exception ex)
                            {
                                args = new FuntownApiEventArgs(ex, false, userState, null);
                            }
                        }
                        catch (Exception ex)
                        {
                            args = httpHelper.HttpWebRequest.IsCancelled ? new FuntownApiEventArgs(ex, true, userState, null) : new FuntownApiEventArgs(ex, false, userState, null);
                        }
                    }
                    else
                    {
                        var webEx = e.Error as WebExceptionWrapper;
                        if (webEx == null)
                        {
                            args = new FuntownApiEventArgs(e.Error, httpHelper.HttpWebRequest.IsCancelled, userState, null);
                        }
                        else
                        {
                            if (webEx.GetResponse() == null)
                            {
                                args = new FuntownApiEventArgs(webEx, false, userState, null);
                            }
                            else
                            {
                                var response = httpHelper.HttpWebResponse;
                                if (response.StatusCode == HttpStatusCode.NotModified)
                                {
                                    var jsonObject = new JsonObject();
                                    var headers = new JsonObject();

                                    foreach (var headerName in response.Headers.AllKeys)
                                        headers[headerName] = response.Headers[headerName];

                                    jsonObject["headers"] = headers;
                                    args = new FuntownApiEventArgs(null, false, userState, jsonObject);
                                }
                                else
                                {
                                    httpHelper.OpenReadAsync();
                                    return;
                                }
                            }
                        }
                    }

                    OnCompleted(httpMethod, args);
                };

            if (input == null)
            {
                httpHelper.OpenReadAsync();
            }
            else
            {
                // we have a request body so write
                httpHelper.OpenWriteCompleted +=
                    (o, e) =>
                    {
                        FuntownApiEventArgs args;
                        if (e.Cancelled)
                        {
                            input.Dispose();
                            args = new FuntownApiEventArgs(e.Error, true, userState, null);
                        }
                        else if (e.Error == null)
                        {
                            try
                            {
                                using (var stream = e.Result)
                                {
                                    // write input to requestStream
                                    var buffer = new byte[BufferSize];
                                    int nread;

                                    if (notifyUploadProgressChanged)
                                    {
                                        long totalBytesToSend = input.Length;
                                        long bytesSent = 0;

                                        while ((nread = input.Read(buffer, 0, buffer.Length)) != 0)
                                        {
                                            stream.Write(buffer, 0, nread);
                                            stream.Flush();

                                            // notify upload progress changed
                                            bytesSent += nread;
                                            OnUploadProgressChanged(new FuntownUploadProgressChangedEventArgs(0, 0, bytesSent, totalBytesToSend, ((int)(bytesSent * 100 / totalBytesToSend)), userState));
                                        }
                                    }
                                    else
                                    {
                                        while ((nread = input.Read(buffer, 0, buffer.Length)) != 0)
                                        {
                                            stream.Write(buffer, 0, nread);
                                            stream.Flush();
                                        }
                                    }
                                }

                                httpHelper.OpenReadAsync();
                                return;
                            }
                            catch (Exception ex)
                            {
                                args = new FuntownApiEventArgs(ex, httpHelper.HttpWebRequest.IsCancelled, userState, null);
                            }
                            finally
                            {
                                input.Dispose();
                            }
                        }
                        else
                        {
                            input.Dispose();
                            var webExceptionWrapper = e.Error as WebExceptionWrapper;
                            if (webExceptionWrapper != null)
                            {
                                var ex = webExceptionWrapper;
                                if (ex.GetResponse() != null)
                                {
                                    httpHelper.OpenReadAsync();
                                    return;
                                }
                            }

                            args = new FuntownApiEventArgs(e.Error, false, userState, null);
                        }

                        OnCompleted(httpMethod, args);
                    };

                httpHelper.OpenWriteAsync();
            }
        }
 protected virtual void OnDeleteCompleted(FuntownApiEventArgs args)
 {
     if (DeleteCompleted != null)
         DeleteCompleted(this, args);
 }
 private void OnCompleted(HttpMethod httpMethod, FuntownApiEventArgs args)
 {
     switch (httpMethod)
     {
         case HttpMethod.Get:
             OnGetCompleted(args);
             break;
         case HttpMethod.Post:
             OnPostCompleted(args);
             break;
         case HttpMethod.Delete:
             OnDeleteCompleted(args);
             break;
         default:
             throw new ArgumentOutOfRangeException("httpMethod");
     }
 }
 protected virtual void OnPostCompleted(FuntownApiEventArgs args)
 {
     if (PostCompleted != null)
         PostCompleted(this, args);
 }