/// <summary> /// Remote API method execution using HTTP "post" method. /// </summary> private DataType ExecutePostHttpRequest <ContractType, DataType>(ViddlerMethodAttribute methodAttribute, StringDictionary parameters) { StringBuilder requestPath = new StringBuilder(); StringBuilder requestData = new StringBuilder(); requestPath.Append((this.EnableSsl && methodAttribute.IsSecure) ? this.SecureBaseUrl : this.BaseUrl); requestPath.Append(methodAttribute.MethodName); requestPath.Append(".xml"); requestData.Append("key="); requestData.Append(this.ApiKey); if (methodAttribute.IsSessionRequired) { requestData.Append("&sessionid="); requestData.Append(this.SessionId); } if (parameters != null && parameters.Keys.Count > 0) { foreach (string key in parameters.Keys) { requestData.Append("&"); requestData.Append(key); requestData.Append("="); requestData.Append(ViddlerHelper.EncodeRequestData(parameters[key])); } } byte[] postDataBytes = System.Text.Encoding.UTF8.GetBytes(requestData.ToString()); try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestPath.ToString()); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.UserAgent = string.Concat("Microsoft .NET, ", this.GetType().Assembly.FullName); request.AllowWriteStreamBuffering = true; request.Accept = "text/xml"; request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; request.KeepAlive = true; request.Timeout = int.MaxValue; request.ReadWriteTimeout = int.MaxValue; using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(postDataBytes, 0, postDataBytes.Length); requestStream.Flush(); } DataType responseObject = ViddlerService.HandleHttpResponse <ContractType, DataType>(request, methodAttribute, this.DumpFolder); return(responseObject); } catch (System.Net.WebException exception) { throw ViddlerService.HandleHttpError(exception); } }
/// <summary> /// Analyzes target response data object and collects properties of promitive type. /// </summary> internal static Dictionary <string, Type> GetExpectedPrimitiveTypeNodes(Type type) { Dictionary <string, Type> nodeNameCollection = new Dictionary <string, Type>(); ViddlerMethodAttribute methodAttribute = ViddlerHelper.GetMethodAttribute(type); if (methodAttribute != null) { string xpath = string.Concat("/", methodAttribute.ElementName, "/"); ViddlerHelper.CollectExpectedPrimitiveTypeNodes(xpath, type, nodeNameCollection, 0); } return(nodeNameCollection); }
/// <summary> /// Handles a web response from the remote server. /// </summary> private static DataType HandleHttpResponse <ContractType, DataType>(HttpWebRequest request, ViddlerMethodAttribute methodAttribute, string dumpPath) { DataType responseObject = default(DataType); using (WebResponse response = request.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { using (MemoryStream stream = new MemoryStream()) { int count; byte[] buffer = new byte[1024]; do { count = responseStream.Read(buffer, 0, 1024); stream.Write(buffer, 0, count); } while (count > 0); // copy response stream to "seek-enabled" stream if (!string.IsNullOrEmpty(dumpPath)) { ViddlerService.DumpResposeToFile(dumpPath, stream, methodAttribute.MethodName, request.RequestUri.ToString()); } stream.Position = 0; string messageType = string.Empty; XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.CloseInput = false; readerSettings.IgnoreComments = true; readerSettings.IgnoreProcessingInstructions = true; using (XmlReader reader = XmlReader.Create(stream, readerSettings)) { if (reader.MoveToContent() == XmlNodeType.Element) { messageType = reader.LocalName; } } stream.Position = 0; if (messageType.Equals(methodAttribute.ElementName, StringComparison.Ordinal)) { try { XmlSerializer serializer = new XmlSerializer(typeof(ContractType)); responseObject = (DataType)serializer.Deserialize(stream); } catch (Exception exception) { if (exception.InnerException != null && exception.InnerException.GetType() == typeof(System.FormatException)) // XmlSerializer has problems with parsing primitive types - removing empty nodes, which are deserialized to properties of primitive type { stream.Position = 0; Dictionary <string, Type> nodesToCheck = ViddlerHelper.GetExpectedPrimitiveTypeNodes(typeof(ContractType)); XmlDocument responseXml = new XmlDocument(); responseXml.PreserveWhitespace = true; responseXml.Load(stream); foreach (string node in nodesToCheck.Keys) { foreach (XmlNode xmlNode in responseXml.SelectNodes(node)) { if (string.IsNullOrEmpty(xmlNode.InnerText.Trim())) { xmlNode.ParentNode.RemoveChild(xmlNode); } } } using (MemoryStream xmlStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(responseXml.OuterXml))) { XmlSerializer serializer = new XmlSerializer(typeof(ContractType)); responseObject = (DataType)serializer.Deserialize(xmlStream); } } else { throw; } } } else if (messageType.Equals("error", StringComparison.Ordinal)) { XmlSerializer serializer = new XmlSerializer(typeof(ViddlerResponseError)); ViddlerResponseError errorObject = (ViddlerResponseError)serializer.Deserialize(stream); throw new ViddlerRequestException(errorObject, null); } else { XmlDocument responseXml = new XmlDocument(); responseXml.PreserveWhitespace = true; responseXml.Load(stream); throw new System.InvalidOperationException(responseXml.OuterXml); } } } } return(responseObject); }
/// <summary> /// Remote API method execution using HTTP "post" method with content type set to "multipart/form-data". /// </summary> private DataType ExecuteMultipartHttpRequest <ContractType, DataType>(ViddlerMethodAttribute methodAttribute, StringDictionary parameters, string fileName, Stream fileStream, Data.UploadEndPoint endPoint) { StringBuilder requestPath = new StringBuilder(); if (endPoint != null && !string.IsNullOrEmpty(endPoint.Url)) { requestPath.Append(endPoint.Url); } else { requestPath.Append((this.EnableSsl && methodAttribute.IsSecure) ? this.SecureBaseUrl : this.BaseUrl); requestPath.Append(methodAttribute.MethodName); requestPath.Append(".xml"); } string boundary = string.Concat("-------------------------", DateTime.Now.Ticks.ToString("x", CultureInfo.InvariantCulture)); byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(string.Concat("\r\n--", boundary, "\r\n")); byte[] endBoundaryBytes = System.Text.Encoding.UTF8.GetBytes(string.Concat("\r\n--", boundary, "--\r\n")); List <byte> requestData = new List <byte>(); // If we have endpoint uploadtoken that is ALL we send and we don't send session/apikey // If we send session/apikey and NOT uploadtoken, Viddler will do a prepareupload behind the scenes and use that uploadtoken. // This is why allow_replace fails w/out an uploadtoken because their prepareupload does NOT have allow_replace set. if (endPoint != null && !string.IsNullOrEmpty(endPoint.Token)) { requestData.AddRange(boundaryBytes); requestData.AddRange(System.Text.Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "Content-Disposition: form-data; name=\"uploadtoken\"\r\n\r\n{0}", endPoint.Token))); } else { requestData.AddRange(boundaryBytes); requestData.AddRange(System.Text.Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "Content-Disposition: form-data; name=\"key\"\r\n\r\n{0}", this.ApiKey))); if (methodAttribute.IsSessionRequired) { requestData.AddRange(boundaryBytes); requestData.AddRange(System.Text.Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "Content-Disposition: form-data; name=\"sessionid\"\r\n\r\n{0}", this.SessionId))); } } if (parameters != null && parameters.Keys.Count > 0) { foreach (string key in parameters.Keys) { //string queryData = string.Format(CultureInfo.InvariantCulture, "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}", key, ViddlerHelper.EncodeRequestData(parameters[key])); string queryData = string.Format(CultureInfo.InvariantCulture, "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}", key, parameters[key]); requestData.AddRange(boundaryBytes); requestData.AddRange(System.Text.Encoding.UTF8.GetBytes(queryData)); } } long fileSize = endBoundaryBytes.Length; if (fileStream != null) { fileStream.Position = 0; if (string.IsNullOrEmpty(fileName)) { fileName = DateTime.Now.Ticks.ToString("x", CultureInfo.InvariantCulture); } requestData.AddRange(boundaryBytes); requestData.AddRange(System.Text.Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "Content-Disposition: form-data; name=\"file\"; filename=\"{0}\"\r\nContent-Type: application/octet-stream\r\n\r\n", fileName))); fileSize += fileStream.Length; } try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestPath.ToString()); request.Method = "POST"; request.ContentType = string.Concat("multipart/form-data; boundary=", boundary); request.UserAgent = string.Concat("Microsoft .NET, ", this.GetType().Assembly.FullName); request.AllowWriteStreamBuffering = false; request.Accept = "text/xml"; request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; request.KeepAlive = true; request.Timeout = int.MaxValue; request.ReadWriteTimeout = int.MaxValue; request.ContentLength = requestData.Count + fileSize; Stream requestStream = request.GetRequestStream(); bool isCancel = false; try { requestStream.Write(requestData.ToArray(), 0, requestData.Count); requestStream.Flush(); if (fileStream != null) { byte[] buffer = new byte[32768]; int bytesRead = 0; while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) { requestStream.Write(buffer, 0, bytesRead); requestStream.Flush(); if (this.Uploading != null) { ViddlerRequestUploadEventArgs uploadEventArgs = new ViddlerRequestUploadEventArgs(typeof(ContractType), fileStream.Length, fileStream.Position); this.Uploading(this, uploadEventArgs); if (uploadEventArgs.Cancel) { isCancel = true; request.Abort(); break; } } } } if (!isCancel) { requestStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length); requestStream.Flush(); } } finally { if (!isCancel) { requestStream.Dispose(); } } if (isCancel) { return(default(DataType)); } else { DataType responseObject = ViddlerService.HandleHttpResponse <ContractType, DataType>(request, methodAttribute, this.DumpFolder); return(responseObject); } } catch (System.Net.WebException exception) { throw ViddlerService.HandleHttpError(exception); } }
/// <summary> /// Remote API method execution. /// </summary> internal DataType ExecuteHttpRequest <ContractType, DataType>(StringDictionary parameters, string fileName, Stream fileStream, Data.UploadEndPoint endPoint) { ViddlerMethodAttribute methodAttribute = ViddlerHelper.GetMethodAttribute(typeof(ContractType)); DataType responseObject = default(DataType); try { if (methodAttribute == null) { throw new System.InvalidOperationException(string.Concat("ViddlerMethodAttribute missing for type \"", typeof(ContractType).ToString(), "\".")); } if (this.BeginRequest != null) { this.BeginRequest(this, new ViddlerRequestEventArgs(typeof(ContractType), parameters, (fileStream != null))); } if (methodAttribute.IsSessionRequired && !this.IsAuthenticated) { throw new System.InvalidOperationException("The current method requires authenticated user."); } if (fileStream != null && !fileStream.CanSeek) { throw new System.ArgumentException("The file stream does not support seeking.", "fileStream"); } switch (methodAttribute.RequestType) { case ViddlerRequestType.Post: { responseObject = this.ExecutePostHttpRequest <ContractType, DataType>(methodAttribute, parameters); break; } case ViddlerRequestType.Multipart: { responseObject = this.ExecuteMultipartHttpRequest <ContractType, DataType>(methodAttribute, parameters, fileName, fileStream, endPoint); break; } default: { responseObject = this.ExecuteGetHttpRequest <ContractType, DataType>(methodAttribute, parameters); break; } } if (this.EndRequest != null) { this.EndRequest(this, new ViddlerRequestEventArgs(typeof(ContractType), parameters, (fileStream != null))); } } catch (Exception exception) { if (this.Error != null) { this.Error(this, new ViddlerRequestErrorEventArgs(typeof(ContractType), parameters, (fileStream != null), exception)); } else { throw; } } return(responseObject); }