Defines an operation to peform during a request.
        private RequestOperation CreateBatchOperation()
        {
            var uri = new SDataUri(Uri);

            if (uri.PathSegments.Length != 4)
            {
                throw new InvalidOperationException("Batch requests can only be made on collection end points");
            }

            var feed    = new AtomFeed();
            var batchOp = new RequestOperation(HttpMethod.Post, feed);

            foreach (var op in _operations)
            {
                AtomEntry entry;

                if (op.Resource == null)
                {
                    if (op.Method != HttpMethod.Post)
                    {
                        throw new InvalidOperationException("A predicate must be specified for GET, PUT and DELETE batch requests");
                    }

                    var entryUri = new SDataUri(uri)
                    {
                        CollectionPredicate = op.Predicate
                    };
                    entry = new AtomEntry {
                        Id = new AtomId(entryUri.Uri)
                    };
                }
                else
                {
                    entry = op.Resource as AtomEntry;

                    if (entry == null)
                    {
                        throw new InvalidOperationException("Only atom entry resources can be submitted in batch requests");
                    }
                }

                entry.SetSDataHttpMethod(op.Method);

                if (!string.IsNullOrEmpty(op.ETag))
                {
                    entry.SetSDataHttpIfMatch(op.ETag);
                }

                feed.AddEntry(entry);

                foreach (var file in op.Files)
                {
                    batchOp.Files.Add(file);
                }
            }

            return(batchOp);
        }
        /// <summary>
        /// Reads resource information from the data source based on the URL and the ETag of the specified entry.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="entry"></param>
        /// <returns></returns>
        public virtual AtomEntry ReadEntry(SDataBaseRequest request, AtomEntry entry)
        {
            Guard.ArgumentNotNull(request, "request");

            try
            {
                var requestUrl = request.ToString();
                var eTag = entry != null ? entry.GetSDataHttpETag() : null;
                var batchItem = new SDataBatchRequestItem
                                {
                                    Url = requestUrl,
                                    Method = HttpMethod.Get,
                                    ETag = eTag
                                };

                if (BatchProcess.Instance.AddToBatch(batchItem))
                {
                    return null;
                }

                var operation = new RequestOperation(HttpMethod.Get) {ETag = eTag};
                var response = ExecuteRequest(requestUrl, operation, MediaType.AtomEntry, MediaType.Xml);
                entry = (AtomEntry) response.Content;

                if (!string.IsNullOrEmpty(response.ETag))
                {
                    entry.SetSDataHttpETag(response.ETag);
                }

                return entry;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// generic read from the specified url
        /// </summary>
        /// <param name="url">url to read from </param>
        /// <returns>string response from server</returns>
        public virtual object Read(string url)
        {
            Guard.ArgumentNotNull(url, "url");

            try
            {
                var operation = new RequestOperation(HttpMethod.Get);
                var response = ExecuteRequest(url, operation);

                if (response.Content is string && response.ContentType == MediaType.Xml)
                {
                    try
                    {
                        return ReadSchema(response);
                    }
                    catch (XmlException)
                    {
                    }
                    catch (InvalidOperationException)
                    {
                    }
                }

                return response.Content;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Generic delete from server
        /// </summary>
        /// <param name="url">the url for the operation</param>
        /// <returns><b>true</b> returns true if the operation was successful</returns>
        public virtual bool Delete(string url)
        {
            Guard.ArgumentNotNull(url, "url");

            try
            {
                var operation = new RequestOperation(HttpMethod.Delete);
                var response = ExecuteRequest(url, operation, MediaType.Xml);
                return response.StatusCode == HttpStatusCode.OK;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        private RequestOperation CreateBatchOperation()
        {
            var uri = new SDataUri(Uri);

            if (uri.PathSegments.Length != 4)
            {
                throw new InvalidOperationException("Batch requests can only be made on collection end points");
            }

            var feed = new AtomFeed();
            var batchOp = new RequestOperation(HttpMethod.Post, feed);

            foreach (var op in _operations)
            {
                AtomEntry entry;

                if (op.Resource == null)
                {
                    if (op.Method != HttpMethod.Post)
                    {
                        throw new InvalidOperationException("A predicate must be specified for GET, PUT and DELETE batch requests");
                    }

                    var entryUri = new SDataUri(uri) {CollectionPredicate = op.Predicate};
                    entry = new AtomEntry {Id = new AtomId(entryUri.Uri)};
                }
                else
                {
                    entry = op.Resource as AtomEntry;

                    if (entry == null)
                    {
                        throw new InvalidOperationException("Only atom entry resources can be submitted in batch requests");
                    }
                }

                entry.SetSDataHttpMethod(op.Method);

                if (!string.IsNullOrEmpty(op.ETag))
                {
                    entry.SetSDataHttpIfMatch(op.ETag);
                }

                feed.AddEntry(entry);

                foreach (var file in op.Files)
                {
                    batchOp.Files.Add(file);
                }
            }

            return batchOp;
        }
 protected virtual ISDataResponse ExecuteRequest(string url, RequestOperation operation, params MediaType[] accept)
 {
     var request = new SDataRequest(url, operation)
                   {
                       Accept = accept,
                       UserName = UserName,
                       Password = Password,
                       Timeout = Timeout,
                       Cookies = Cookies,
                       UserAgent = UserAgent
                   };
     return request.GetResponse();
 }
        private bool DeleteEntry(string url, AtomEntry entry)
        {
            try
            {
                var eTag = entry != null ? entry.GetSDataHttpETag() : null;
                var batchItem = new SDataBatchRequestItem
                                {
                                    Url = url,
                                    Method = HttpMethod.Delete,
                                    ETag = eTag
                                };

                if (BatchProcess.Instance.AddToBatch(batchItem))
                {
                    return true;
                }

                var operation = new RequestOperation(HttpMethod.Delete) {ETag = eTag};
                var response = ExecuteRequest(url, operation, MediaType.AtomEntry, MediaType.Xml);
                return response.StatusCode == HttpStatusCode.OK;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Updates information about a syndication resource in the data source.
        /// </summary>
        /// <param name="request">The url from the syndication data source for the resource to be updated.</param>
        /// <param name="entry">
        ///     An object that implements the <see cref="ISyndicationResource"/> interface that represents the updated information for the resource.
        /// </param>
        public virtual AtomEntry UpdateEntry(SDataBaseRequest request, AtomEntry entry)
        {
            Guard.ArgumentNotNull(request, "request");
            Guard.ArgumentNotNull(entry, "entry");

            try
            {
                var url = request.ToString();
                var eTag = entry.GetSDataHttpETag();
                var batchItem = new SDataBatchRequestItem
                                {
                                    Url = url,
                                    Method = HttpMethod.Put,
                                    Entry = entry,
                                    ETag = eTag
                                };

                if (BatchProcess.Instance.AddToBatch(batchItem))
                {
                    return null;
                }

                var operation = new RequestOperation(HttpMethod.Put, entry) {ETag = eTag};
                return ExecuteEntryRequest(url, operation);
            }
            catch (SDataClientException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        private AtomEntry ExecuteEntryRequest(string url, RequestOperation operation)
        {
            var response = ExecuteRequest(url, operation, MediaType.AtomEntry, MediaType.Xml);
            var result = response.Content as AtomEntry;
            if (result == null)
            {
                var feedResult = response.Content as AtomFeed;
                if (feedResult == null)
                {
                    throw new SDataClientException("Unexpected content: " + response.Content);
                }

                result = feedResult.Entries.FirstOrDefault();
            }

            if (!string.IsNullOrEmpty(response.ETag))
            {
                result.SetSDataHttpETag(response.ETag);
            }

            return result;
        }
        /// <summary>
        /// Removes a resource from the syndication data source.
        /// </summary>
        /// <param name="request">The request from the syndication data source for the resource to be removed.</param>
        /// <param name="entry">the resource that is being deleted</param>
        /// <returns><b>true</b> if the syndication resource was successfully deleted; otherwise, <b>false</b>.</returns>
        public virtual bool DeleteEntry(SDataBaseRequest request, AtomEntry entry)
        {
            Guard.ArgumentNotNull(request, "request");

            try
            {
                var url = request.ToString();
                var eTag = entry != null ? entry.GetSDataHttpETag() : null;
                var batchItem = new SDataBatchRequestItem
                                {
                                    Url = url,
                                    Method = HttpMethod.Delete,
                                    ETag = eTag
                                };

                if (BatchProcess.Instance.AddToBatch(batchItem))
                {
                    return true;
                }

                var operation = new RequestOperation(HttpMethod.Delete) {ETag = eTag};
                var response = ExecuteRequest(url, operation, MediaType.AtomEntry, MediaType.Xml);
                return response.StatusCode == HttpStatusCode.OK;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Reads resource information from the data source based on the URL and the specified ETag.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="eTag"></param>
        /// <returns></returns>
        public virtual AtomFeed ReadFeed(SDataBaseRequest request, ref string eTag)
        {
            Guard.ArgumentNotNull(request, "request");

            try
            {
                var url = request.ToString();
                var operation = new RequestOperation(HttpMethod.Get) {ETag = eTag};
                return ExecuteFeedRequest(url, operation, out eTag);
            }
            catch (SDataClientException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Adds a new syndication resource to the data source.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="feed"></param>
        /// <param name="eTag"></param>
        /// <returns></returns>
        public virtual AtomFeed CreateFeed(SDataBaseRequest request, AtomFeed feed, out string eTag)
        {
            Guard.ArgumentNotNull(request, "request");
            Guard.ArgumentNotNull(feed, "feed");

            try
            {
                var url = request.ToString();
                var operation = new RequestOperation(HttpMethod.Post, feed);
                return ExecuteFeedRequest(url, operation, out eTag);
            }
            catch (SDataClientException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Reads xsd from a $schema request
        /// </summary>
        /// <param name="request">url for the syndication resource to get information for.</param>
        /// <returns>SDataSchema</returns>
        public virtual SDataSchema ReadSchema(SDataResourceSchemaRequest request)
        {
            Guard.ArgumentNotNull(request, "request");

            try
            {
                var requestUrl = request.ToString();
                var operation = new RequestOperation(HttpMethod.Get);
                var response = ExecuteRequest(requestUrl, operation, MediaType.Xml);
                var targetElementName = !string.IsNullOrEmpty(response.Location)
                                            ? new Uri(response.Location).Fragment.TrimStart('#')
                                            : null;

                using (var reader = new StringReader((string) response.Content))
                {
                    return SDataSchema.Read(reader, targetElementName);
                }
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// generic read from the specified url
        /// </summary>
        /// <param name="url">url to read from </param>
        /// <returns>string response from server</returns>
        public virtual object Read(string url)
        {
            Guard.ArgumentNotNull(url, "url");

            try
            {
                var operation = new RequestOperation(HttpMethod.Get);
                var response = ExecuteRequest(url, operation, MediaType.Xml);
                var text = response.Content as string;

                if (text != null && response.ContentType == MediaType.Xml)
                {
                    var targetElementName = !string.IsNullOrEmpty(response.Location)
                                                ? new Uri(response.Location).Fragment
                                                : null;

                    using (var reader = new StringReader(text))
                    {
                        try
                        {
                            return SDataSchema.Read(reader, targetElementName);
                        }
                        catch (XmlException)
                        {
                        }
                        catch (InvalidOperationException)
                        {
                        }
                    }
                }

                return response.Content;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Reads resource information from the data source based on the URL and the specified ETag.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="eTag"></param>
        /// <returns></returns>
        public virtual AtomFeed ReadFeed(SDataBaseRequest request, ref string eTag)
        {
            Guard.ArgumentNotNull(request, "request");

            try
            {
                var requestUrl = request.ToString();
                var operation = new RequestOperation(HttpMethod.Get) {ETag = eTag};
                var response = ExecuteRequest(requestUrl, operation, MediaType.Atom, MediaType.Xml);
                eTag = response.ETag;
                return (AtomFeed) response.Content;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        private AtomFeed ExecuteFeedRequest(string url, RequestOperation operation, out string eTag)
        {
            var response = ExecuteRequest(url, operation, MediaType.Atom, MediaType.Xml);
            var result = response.Content as AtomFeed;
            if (result == null)
            {
                throw new SDataClientException("Unexpected content: " + response.Content);
            }

            eTag = response.ETag;
            return result;
        }
        /// <summary>
        /// Reads xsd from a $schema request
        /// </summary>
        /// <param name="request">url for the syndication resource to get information for.</param>
        /// <returns>SDataSchema</returns>
        public virtual SDataSchemaObject ReadSchema(SDataResourceSchemaRequest request)
        {
            Guard.ArgumentNotNull(request, "request");

            try
            {
                var requestUrl = request.ToString();
                var operation = new RequestOperation(HttpMethod.Get);
                var response = ExecuteRequest(requestUrl, operation, MediaType.Xml);
                return ReadSchema(response);
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        public void ReadAsync(string url, object userState)
        {
            Guard.ArgumentNotNull(url, "url");

            var operation = new RequestOperation(HttpMethod.Get);
            var request = new SDataRequest(url, operation)
                          {
                              UserName = UserName,
                              Password = Password,
                              Timeout = Timeout,
                              Cookies = Cookies,
                              UserAgent = UserAgent
                          };
            request.BeginGetResponse(
                asyncResult =>
                {
                    try
                    {
                        var response = request.EndGetResponse(asyncResult);

                        if (ReadCompleted != null)
                        {
                            var content = response.Content;

                            if (content is string && response.ContentType == MediaType.Xml)
                            {
                                try
                                {
                                    content = ReadSchema(response);
                                }
                                catch (XmlException)
                                {
                                }
                                catch (InvalidOperationException)
                                {
                                }
                            }

                            ReadCompleted(this, new ReadCompletedEventArgs(content, null, false, userState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ReadCompleted != null)
                        {
                            ReadCompleted(this, new ReadCompletedEventArgs(null, ex, false, userState));
                        }
                    }
                }, null);
        }
        private AtomEntry CreateEntry(string url, AtomEntry entry)
        {
            Guard.ArgumentNotNull(entry, "entry");

            try
            {
                var batchItem = new SDataBatchRequestItem
                                {
                                    Url = url,
                                    Method = HttpMethod.Post,
                                    Entry = entry
                                };

                if (BatchProcess.Instance.AddToBatch(batchItem))
                {
                    return null;
                }

                var operation = new RequestOperation(HttpMethod.Post, entry);
                var response = ExecuteRequest(url, operation, MediaType.AtomEntry, MediaType.Xml);
                var result = response.Content as AtomEntry;

                if (result == null)
                {
                    var feedResult = response.Content as AtomFeed;
                    if (feedResult != null)
                    {
                        result = feedResult.Entries.FirstOrDefault();
                    }
                }

                if (!string.IsNullOrEmpty(response.ETag) && result != null)
                {
                    result.SetSDataHttpETag(response.ETag);
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        private WebRequest CreateRequest(string uri, RequestOperation op)
        {
            var request = WebRequest.Create(uri);

            request.Method          = op.Method.ToString().ToUpper();
            request.Timeout         = Timeout;
            request.PreAuthenticate = true;

            if (_proxySet)
            {
                request.Proxy = _proxy;
            }

            var httpRequest = request as HttpWebRequest;

            if (httpRequest != null)
            {
                httpRequest.AllowAutoRedirect = false;
                httpRequest.ReadWriteTimeout  = Timeout;
                httpRequest.KeepAlive         = false;
                httpRequest.ProtocolVersion   = HttpVersion.Version10;

                if (Accept != null)
                {
                    httpRequest.Accept = string.Join(",", Array.ConvertAll(Accept, type => MediaTypeNames.GetMediaType(type)));
                }

                if (Cookies != null)
                {
                    httpRequest.CookieContainer = Cookies;
                }

                if (!string.IsNullOrEmpty(UserAgent))
                {
                    httpRequest.UserAgent = UserAgent;
                }
            }

            if (Credentials != null)
            {
                request.Credentials = Credentials;
            }
            else if (!string.IsNullOrEmpty(UserName) || !string.IsNullOrEmpty(Password))
            {
                var uriPrefix = new Uri(uri);
                var cred      = new NetworkCredential(UserName, Password);
                request.Credentials = new CredentialCache
                {
                    { uriPrefix, "Basic", cred },
                    { uriPrefix, "Digest", cred },
                    { uriPrefix, "NTLM", cred },
                    { uriPrefix, "Kerberos", cred },
                    { uriPrefix, "Negotiate", cred }
                };
            }
            else
            {
                request.Credentials = CredentialCache.DefaultCredentials;
            }

            if (!string.IsNullOrEmpty(op.ETag))
            {
                var header = op.Method == HttpMethod.Get
                                 ? HttpRequestHeader.IfNoneMatch
                                 : HttpRequestHeader.IfMatch;
                request.Headers[header] = op.ETag;
            }

            var isMultipart = op.Form.Count > 0 || op.Files.Count > 0;

            if (op.Resource != null || isMultipart)
            {
                using (var stream = request.GetRequestStream())
                {
                    var       requestStream = isMultipart ? new MemoryStream() : stream;
                    MediaType?mediaType;

                    if (op.Resource is ISyndicationResource)
                    {
                        mediaType = op.Resource is AtomFeed ? MediaType.Atom : MediaType.AtomEntry;
                        ((ISyndicationResource)op.Resource).Save(requestStream);
                    }
                    else if (op.Resource is IXmlSerializable)
                    {
                        mediaType = MediaType.Xml;

                        using (var xmlWriter = XmlWriter.Create(requestStream))
                        {
                            ((IXmlSerializable)op.Resource).WriteXml(xmlWriter);
                        }
                    }
                    else if (op.Resource is string)
                    {
                        mediaType = MediaType.Text;

                        using (var writer = new StreamWriter(requestStream))
                        {
                            writer.Write((string)op.Resource);
                        }
                    }
                    else
                    {
                        mediaType = null;
                    }

                    if (op.ContentType != null)
                    {
                        mediaType = op.ContentType.Value;
                    }

                    if (isMultipart)
                    {
                        requestStream.Seek(0, SeekOrigin.Begin);

                        using (var multipart = new MimeMessage())
                        {
                            if (mediaType != null)
                            {
                                var part = new MimePart(requestStream)
                                {
                                    ContentType = MediaTypeNames.GetMediaType(mediaType.Value)
                                };
                                multipart.Add(part);
                            }

                            foreach (var data in op.Form)
                            {
                                var part = new MimePart(new MemoryStream(Encoding.UTF8.GetBytes(data.Value)))
                                {
                                    ContentType             = MediaTypeNames.TextMediaType,
                                    ContentTransferEncoding = "binary",
                                    ContentDisposition      = new ContentDisposition(DispositionTypeNames.Inline)
                                    {
                                        Parameters = { { "name", data.Key } }
                                    }
                                };
                                multipart.Add(part);
                            }

                            foreach (var file in op.Files)
                            {
                                var contentDisposition = new ContentDisposition(DispositionTypeNames.Attachment);
                                if (file.FileName != null)
                                {
                                    if (file.FileName == Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(file.FileName)))
                                    {
                                        contentDisposition.FileName = file.FileName;
                                    }
                                    else
                                    {
                                        contentDisposition.Parameters["filename*"] = string.Format("{0}''{1}", Encoding.UTF8.WebName, HttpUtility.UrlEncode(file.FileName));
                                    }
                                }

                                var part = new MimePart(file.Stream)
                                {
                                    ContentType             = !string.IsNullOrEmpty(file.ContentType) ? file.ContentType : "application/octet-stream",
                                    ContentTransferEncoding = "binary",
                                    ContentDisposition      = contentDisposition
                                };
                                multipart.Add(part);
                            }

                            multipart.WriteTo(stream);
                            request.ContentType = new ContentType("multipart/" + (op.Files.Count > 0 ? "related" : "form-data"))
                            {
                                Boundary = multipart.Boundary
                            }.ToString();
                        }
                    }
                    else if (mediaType != null)
                    {
                        request.ContentType = MediaTypeNames.GetMediaType(mediaType.Value);
                    }
                }
            }

            return(request);
        }
        private AtomEntry UpdateEntry(string url, AtomEntry entry)
        {
            Guard.ArgumentNotNull(entry, "entry");

            try
            {
                var eTag = entry.GetSDataHttpETag();
                var batchItem = new SDataBatchRequestItem
                                {
                                    Url = url,
                                    Method = HttpMethod.Put,
                                    Entry = entry,
                                    ETag = eTag
                                };

                if (BatchProcess.Instance.AddToBatch(batchItem))
                {
                    return null;
                }

                var operation = new RequestOperation(HttpMethod.Put, entry) {ETag = eTag};
                var response = ExecuteRequest(url, operation, MediaType.AtomEntry, MediaType.Xml);
                entry = (AtomEntry) response.Content;

                if (!string.IsNullOrEmpty(response.ETag))
                {
                    entry.SetSDataHttpETag(response.ETag);
                }

                return entry;
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        /// <summary>
        /// Asynchronous PUT to the server
        /// </summary>
        /// <param name="request">The request that identifies the resource within the syndication data source.</param>
        /// <param name="resource">The resource that should be created asynchronously.</param>
        public virtual AsyncRequest CreateAsync(SDataBaseRequest request, ISyndicationResource resource)
        {
            Guard.ArgumentNotNull(request, "request");
            Guard.ArgumentNotNull(resource, "resource");

            try
            {
                var requestUrl = new SDataUri(request.ToString()) {TrackingId = Guid.NewGuid().ToString()}.ToString();
                var operation = new RequestOperation(HttpMethod.Post, resource);
                var response = ExecuteRequest(requestUrl, operation, MediaType.Xml);
                var tracking = (Tracking) response.Content;
                return new AsyncRequest(this, response.Location, tracking);
            }
            catch (Exception ex)
            {
                throw new SDataClientException(ex.Message, ex);
            }
        }
        private WebRequest CreateRequest(string uri, RequestOperation op)
        {
            var request = WebRequest.Create(uri);
            request.Method = op.Method.ToString().ToUpper();
            request.Timeout = Timeout;
            request.PreAuthenticate = true;

            if (_proxySet)
            {
                request.Proxy = _proxy;
            }

            var httpRequest = request as HttpWebRequest;
            if (httpRequest != null)
            {
                httpRequest.AllowAutoRedirect = false;
                httpRequest.ReadWriteTimeout = Timeout;
                httpRequest.KeepAlive = false;
                httpRequest.ProtocolVersion = HttpVersion.Version10;

                if (Accept != null)
                {
                    httpRequest.Accept = string.Join(",", Array.ConvertAll(Accept, type => MediaTypeNames.GetMediaType(type)));
                }

                if (Cookies != null)
                {
                    httpRequest.CookieContainer = Cookies;
                }

                if (!string.IsNullOrEmpty(UserAgent))
                {
                    httpRequest.UserAgent = UserAgent;
                }
            }

            if (Credentials != null)
            {
                request.Credentials = Credentials;
            }
            else if (!string.IsNullOrEmpty(UserName) || !string.IsNullOrEmpty(Password))
            {
                var uriPrefix = new Uri(uri);
                var cred = new NetworkCredential(UserName, Password);
                request.Credentials = new CredentialCache
                                      {
                                          {uriPrefix, "Basic", cred},
                                          {uriPrefix, "Digest", cred},
                                          {uriPrefix, "NTLM", cred},
                                          {uriPrefix, "Kerberos", cred},
                                          {uriPrefix, "Negotiate", cred}
                                      };
            }
            else
            {
                request.Credentials = CredentialCache.DefaultCredentials;
            }

            if (!string.IsNullOrEmpty(op.ETag))
            {
                var header = op.Method == HttpMethod.Get
                                 ? HttpRequestHeader.IfNoneMatch
                                 : HttpRequestHeader.IfMatch;
                request.Headers[header] = op.ETag;
            }

            if (op.Resource != null)
            {
                using (var stream = request.GetRequestStream())
                {
                    var requestStream = op.Files.Count > 0 ? new MemoryStream() : stream;
                    MediaType mediaType;

                    if (op.Resource is ISyndicationResource)
                    {
                        mediaType = op.Resource is AtomFeed ? MediaType.Atom : MediaType.AtomEntry;
                        ((ISyndicationResource) op.Resource).Save(requestStream);
                    }
                    else if (op.Resource is IXmlSerializable)
                    {
                        mediaType = MediaType.Xml;

                        using (var xmlWriter = XmlWriter.Create(requestStream))
                        {
                            ((IXmlSerializable) op.Resource).WriteXml(xmlWriter);
                        }
                    }
                    else if (op.Resource is string)
                    {
                        mediaType = MediaType.Text;

                        using (var writer = new StreamWriter(requestStream))
                        {
                            writer.Write((string) op.Resource);
                        }
                    }
                    else
                    {
                        throw new Exception();
                    }

                    if (op.ContentType != null)
                    {
                        mediaType = op.ContentType.Value;
                    }

                    var contentType = MediaTypeNames.GetMediaType(mediaType);

                    if (op.Files.Count > 0)
                    {
                        requestStream.Seek(0, SeekOrigin.Begin);
                        var part = new MimePart(requestStream) {ContentType = contentType};

                        using (var multipart = new MimeMessage(part))
                        {
                            contentType = new ContentType("multipart/related") {Boundary = multipart.Boundary}.ToString();

                            foreach (var file in op.Files)
                            {
                                var type = !string.IsNullOrEmpty(file.ContentType) ? file.ContentType : "application/octet-stream";
                                var disposition = new ContentDisposition(DispositionTypeNames.Attachment) {FileName = file.FileName};
                                part = new MimePart(file.Stream)
                                       {
                                           ContentType = type,
                                           ContentTransferEncoding = "binary",
                                           ContentDisposition = disposition
                                       };
                                multipart.Add(part);
                            }

                            multipart.WriteTo(stream);
                        }
                    }

                    request.ContentType = contentType;
                }
            }

            return request;
        }
        private WebRequest CreateRequest(string uri, RequestOperation op)
        {
            var request = WebRequest.Create(uri);
            request.Method = op.Method.ToString().ToUpper();
            request.Timeout = Timeout;
            request.Proxy = Proxy;
            request.PreAuthenticate = false;

            var httpRequest = request as HttpWebRequest;
            if (httpRequest != null)
            {
                httpRequest.AllowAutoRedirect = false;
                httpRequest.ReadWriteTimeout = Timeout;
                httpRequest.KeepAlive = false;
                httpRequest.ProtocolVersion = HttpVersion.Version10;

                if (Accept != null)
                {
                    httpRequest.Accept = string.Join(",", Array.ConvertAll(Accept, type => MediaTypeNames.GetMediaType(type)));
                }

                if (Cookies != null)
                {
                    httpRequest.CookieContainer = Cookies;
                }

                if (!string.IsNullOrEmpty(UserAgent))
                {
                    httpRequest.UserAgent = UserAgent;
                }
            }

            if (!string.IsNullOrEmpty(UserName) || !string.IsNullOrEmpty(Password))
            {
                var cred = new NetworkCredential(UserName, Password);
                request.Credentials = new CredentialCache
                                      {
                                          {new Uri(uri), "Digest", cred},
                                          {new Uri(uri), "Basic", cred}
                                      };
            }

            if (!string.IsNullOrEmpty(op.ETag))
            {
                var header = op.Method == HttpMethod.Get
                                 ? HttpRequestHeader.IfNoneMatch
                                 : HttpRequestHeader.IfMatch;
                request.Headers[header] = op.ETag;
            }

            if (op.Resource != null)
            {
                request.ContentType = op.Resource is AtomFeed
                                          ? MediaTypeNames.AtomFeedMediaType
                                          : MediaTypeNames.AtomEntryMediaType;

                using (var stream = request.GetRequestStream())
                {
                    op.Resource.Save(stream);
                }
            }

            return request;
        }