/// <summary>
        /// Implements the same method as the real service interface allowing the mock to be plugged in
        /// </summary>
        /// <typeparam name="Response"></typeparam>
        /// <typeparam name="Request"></typeparam>
        /// <param name="resource"></param>
        /// <param name="verb"></param>
        /// <param name="request"></param>
        /// <param name="deleteBody"></param>
        /// <param name="session"></param>
        /// <returns></returns>
        public async Task <ShippingApiResponse <Response> > HttpRequest <Response, Request>(string resource, HttpVerb verb, Request request, bool deleteBody, ISession session = null) where Request : IShippingApiRequest
        {
            string fullPath = request.RecordingFullPath(resource, session);
            string cwd      = Directory.GetCurrentDirectory();

            if (File.Exists(fullPath))
            {
                var apiResponse = new ShippingApiResponse <Response> {
                    HttpStatus = HttpStatusCode.OK, Success = true
                };
                long jsonPosition = 0;
                using (var fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
                    using (var fileReader = new StreamReader(fileStream))
                    {
                        for (var line = await fileReader.ReadLineAsync(); line != string.Empty; line = await fileReader.ReadLineAsync())
                        {
                            jsonPosition += line.Length + 2; // + CRLF
                            if (line.IndexOf(':') == -1)
                            {
                                continue;
                            }
                            var headerName  = line.Substring(0, line.IndexOf(':'));
                            var headerValue = line.IndexOf(':') == line.Length? string.Empty : line.Substring(line.IndexOf(':') + 1);
                            apiResponse.ProcessResponseAttribute(headerName, headerValue.Split(','));
                        }
                    }
                using (var fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    using (var recordingStream = new RecordingStream(fileStream, request.RecordingFullPath(resource, session), FileMode.Create))
                    {
                        try
                        {
                            //dont open the record file
                            ShippingApiResponse <Response> .Deserialize(session, recordingStream, apiResponse, jsonPosition);
                        }
                        catch (Exception ex)
                        {
                            session.LogError(string.Format("Mock request {0} got deserialization exception {1}", fullPath, ex.Message));
                            throw ex;
                        }
                    }
                return(apiResponse);
            }

            else
            {
                var apiResponse = new ShippingApiResponse <Response> {
                    HttpStatus = HttpStatusCode.NotFound, Success = false
                };
                session.LogDebug(string.Format("Mock request failed {0}", fullPath));
                apiResponse.Errors.Add(new ErrorDetail()
                {
                    ErrorCode = "Mock 401", Message = "Could not find response file" + fullPath
                });
                return(apiResponse);
            }
        }
        internal async static Task <ShippingApiResponse <Response> > HttpRequestStatic <Response, Request>(string resource, HttpVerb verb, Request request, bool deleteBody = false, ISession session = null) where Request : IShippingApiRequest
        {
            if (session == null)
            {
                session = Globals.DefaultSession;
            }
            var client = Globals.Client(session.EndPoint);

            //            client.Timeout = new TimeSpan(0, 0, 0, 0, session.TimeOutMilliseconds);

            using (var recordingStream = new RecordingStream(null, request.RecordingFullPath(resource, session), FileMode.Create, RecordingStream.RecordType.MultipartMime))
            {
#pragma warning disable CS0618
                recordingStream.OpenRecord(session.Record);
#pragma warning restore CS0618
                string uriBuilder = request.GetUri(resource);

                HttpResponseMessage httpResponseMessage;
                using (HttpRequestMessage requestMessage = new HttpRequestMessage())
                {
                    using (var stream = new MemoryStream())
                    {
                        recordingStream.SetBaseStream(stream, "text/httpRequest");
                        recordingStream.WriteRecordCRLF(string.Format("{0} {1} HTTP/1.1", verb.ToString(), uriBuilder));
                        recordingStream.WriteRecordCRLF(string.Format("{0}: {1}", "Content-Type", request.ContentType));
                        foreach (var h in request.GetHeaders())
                        {
                            AddRequestHeaders(requestMessage, h.Item1, h.Item2, h.Item3, recordingStream);
                        }
                        recordingStream.WriteRecordCRLF("");

                        if (verb == HttpVerb.PUT || verb == HttpVerb.POST || (verb == HttpVerb.DELETE && deleteBody))
                        {
                            using (var writer = new StreamWriter(recordingStream))
                                using (var reqContent = new StreamContent(stream))
                                {
                                    request.SerializeBody(writer, session);
                                    stream.Seek(0, SeekOrigin.Begin);
                                    requestMessage.Content         = reqContent;
                                    reqContent.Headers.ContentType = new MediaTypeHeaderValue(request.ContentType);
                                    recordingStream.WriteRecordCRLF(string.Format("user-agent:{0}", Globals.UserAgent));
                                    recordingStream.WriteRecordCRLF(string.Format("X-PB-UnifiedErrorStructure : true"));

                                    if (verb == HttpVerb.PUT)
                                    {
                                        requestMessage.Method = HttpMethod.Put;
                                    }
                                    else if (verb == HttpVerb.DELETE)
                                    {
                                        requestMessage.Method = HttpMethod.Delete;
                                    }
                                    else
                                    {
                                        requestMessage.Method = HttpMethod.Post;
                                    }
                                    requestMessage.RequestUri = new Uri(client.BaseAddress + uriBuilder);
                                    httpResponseMessage       = await client.SendAsync(requestMessage);
                                }
                        }

                        else if (verb == HttpVerb.DELETE)
                        {
                            requestMessage.Method     = HttpMethod.Delete;
                            requestMessage.RequestUri = new Uri(client.BaseAddress + uriBuilder);
                            httpResponseMessage       = await client.SendAsync(requestMessage);
                        }
                        else
                        {
                            requestMessage.Method     = HttpMethod.Get;
                            requestMessage.RequestUri = new Uri(client.BaseAddress + uriBuilder);
                            httpResponseMessage       = await client.SendAsync(requestMessage);
                        }
                    }
                }

                using (var respStream = await httpResponseMessage.Content.ReadAsStreamAsync())
                {
                    recordingStream.SetBaseStream(respStream, "text/httpResponse");

                    recordingStream.WriteRecordCRLF(string.Format("HTTP/1.1 {0} {1}", (int)httpResponseMessage.StatusCode, httpResponseMessage.ReasonPhrase));
                    recordingStream.WriteRecordCRLF(string.Format("Content-Length: {0}", httpResponseMessage.Content.Headers.ContentLength));
                    recordingStream.WriteRecordCRLF(string.Format("Content-Type: {0}", httpResponseMessage.Content.Headers.ContentType));
                    recordingStream.WriteRecordCRLF("");

                    if (httpResponseMessage.IsSuccessStatusCode)
                    {
                        var apiResponse = new ShippingApiResponse <Response> {
                            HttpStatus = httpResponseMessage.StatusCode, Success = httpResponseMessage.IsSuccessStatusCode
                        };
                        var sb = new StringBuilder();
                        foreach (var h in httpResponseMessage.Headers)
                        {
                            apiResponse.ProcessResponseAttribute(h.Key, h.Value);
                            bool firstValue = true;
                            foreach (var s in h.Value)
                            {
                                if (firstValue)
                                {
                                    firstValue = false;
                                }
                                else
                                {
                                    sb.Append(';');
                                }
                                sb.Append(s);
                            }
                            recordingStream.WriteRecordCRLF(string.Format("{0}:{1}", h.Key, sb.ToString()));
                            sb.Clear();
                        }
                        recordingStream.WriteRecordCRLF("");
                        ShippingApiResponse <Response> .Deserialize(session, recordingStream, apiResponse);

                        return(apiResponse);
                    }
                    else
                    {
                        var apiResponse = new ShippingApiResponse <Response> {
                            HttpStatus = httpResponseMessage.StatusCode, Success = httpResponseMessage.IsSuccessStatusCode
                        };
                        try
                        {
                            ShippingApiResponse <Response> .Deserialize(session, recordingStream, apiResponse);
                        }
                        catch (JsonException)
                        {
                            session.LogWarning(String.Format("http {0} request to {1} failed to deserialize with error {2}", verb.ToString(), uriBuilder, httpResponseMessage.StatusCode));
                            apiResponse.Errors.Add(new ErrorDetail()
                            {
                                ErrorCode = "HTTP " + httpResponseMessage.Version + " " + httpResponseMessage.StatusCode.ToString(), Message = httpResponseMessage.ReasonPhrase
                            });
                        }
                        return(apiResponse);
                    }
                }
            }
        }
        internal static void AddRequestHeaders(HttpRequestMessage request, ShippingApiHeaderAttribute attribute, string propValue, string propName, RecordingStream recordingStream)
        {
            if (attribute.OmitIfEmpty && (propValue == null || propValue.Equals(String.Empty)))
            {
                return;
            }
            switch (propName)
            {
            case "Authorization":
                request.Headers.Authorization = new AuthenticationHeaderValue(attribute.Name, propValue);
                recordingStream.WriteRecordCRLF(string.Format("Authorization: {0} {1}", attribute.Name, propValue));
                break;

            default:
                request.Headers.Add(attribute.Name, propValue);
                recordingStream.WriteRecordCRLF(string.Format("{0}:{1}", attribute.Name, propValue));
                break;
            }
        }