/// <summary> /// Initializes a new instance of the <see cref="Proxy"/> class. /// </summary> /// <param name="proxyType">The <see cref="Type"/> of the remote object for which to create a proxy.</param> /// <param name="configuration">Proxy configuration information.</param> internal Proxy(Type proxyType, ProxyConfiguration configuration) : base(proxyType) { this.Configuration = configuration; this.Context = new ProxyContext(); this.m_jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings() { DateFormatHandling = DateFormatHandling.IsoDateFormat, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, NullValueHandling = NullValueHandling.Include, ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, ContractResolver = new ProxyContractResolver() }); ResourceAttribute resourceUrlAttribute = proxyType.GetCustomAttribute <ResourceAttribute>(); if (null == this.Configuration.Relative) { if (null != resourceUrlAttribute) { this.Configuration.Relative = resourceUrlAttribute.Relative; if (this.Configuration.Relative.Length != 0 && !this.Configuration.Relative.EndsWith("/", StringComparison.OrdinalIgnoreCase)) { this.Configuration.Relative += "/"; } } else { var invariantCulture = System.Globalization.CultureInfo.InvariantCulture; this.Configuration.Relative = string.Format(invariantCulture, "api/{0}/", proxyType.Name.ToLower(invariantCulture)); } } if (!this.Configuration.Root.EndsWith("/", StringComparison.OrdinalIgnoreCase)) { this.Configuration.Root += "/"; } }
public override IMessage Invoke(IMessage msg) { IMethodCallMessage methodCall = msg as IMethodCallMessage; var invariantCulture = System.Globalization.CultureInfo.InvariantCulture; if (null != methodCall) { try { HttpMethod httpMethod = HttpMethod.Get; HttpMethodAttribute httpMethodAttribute = methodCall.MethodBase.GetCustomAttribute <HttpMethodAttribute>(true); if (null != httpMethodAttribute) { httpMethod = httpMethodAttribute.HttpMethod; } ResourceAttribute resourceUrlAttribute = methodCall.MethodBase.GetCustomAttribute <ResourceAttribute>(true); string actionUrl = string.Empty; if (null != resourceUrlAttribute) { actionUrl = resourceUrlAttribute.Relative; } Type expectedReturn = ((MethodInfo)methodCall.MethodBase).ReturnType; object retVal = null; object[] outArgs = null; Dictionary <string, object> toSerialize = new Dictionary <string, object>(); Dictionary <string, string> toQueryString = new Dictionary <string, string>(); // Determines which arguments should be used in the querystring parameters, and which arguments will be serialized and posted. for (int argIndex = 0, argCount = methodCall.InArgCount; argIndex < argCount; argIndex++) { object value = methodCall.GetInArg(argIndex); if (null == value || value.GetType().IsPrimitiveOrBasicStruct()) { if (null != value && value is DateTime) { value = ((DateTime)value).ToLocalTime(); StringBuilder sb = new StringBuilder(); using (TextWriter writer = new StringWriter(sb, invariantCulture)) { this.m_jsonSerializer.Serialize(writer, value); } value = sb.ToString(); value = ((string)value).Substring(1, ((string)value).Length - 2); toQueryString.Add(methodCall.GetInArgName(argIndex), (value != null) ? value.ToString() : string.Empty); } else { toQueryString.Add(methodCall.GetInArgName(argIndex), (value != null) ? HttpUtility.UrlEncode(value.ToString()) : string.Empty); } } else { toSerialize.Add(methodCall.GetInArgName(argIndex), value); } } { // Prepare the post content. byte[] postData = null; string responseData; if (toSerialize.Count > 0) { StringBuilder sb = new StringBuilder(); using (TextWriter writer = new StringWriter(sb, invariantCulture)) { if (toSerialize.Count == 1) { this.m_jsonSerializer.Serialize(writer, toSerialize.Values.First()); } else { this.m_jsonSerializer.Serialize(writer, toSerialize); } } postData = Encoding.UTF8.GetBytes(sb.ToString()); } if ((postData == null || postData.Length == 0) && httpMethod != HttpMethod.Get) { postData = Encoding.UTF8.GetBytes("{}"); } // Assemble the querystring. object[] urlParameters = null; if (null != resourceUrlAttribute && resourceUrlAttribute.ParameterNames.Count() > 0) { urlParameters = new object[resourceUrlAttribute.ParameterNames.Count()]; for (int i = 0, total = resourceUrlAttribute.ParameterNames.Count(); i < total; i++) { string parameterName = resourceUrlAttribute.ParameterNames[i]; if (toQueryString.ContainsKey(parameterName)) { urlParameters[i] = toQueryString[parameterName]; toQueryString.Remove(parameterName); } else if (toSerialize.ContainsKey(parameterName)) { urlParameters[i] = toSerialize[parameterName]; toSerialize.Remove(parameterName); } } } var queryString = string.Empty; if (toQueryString.Count > 0) { queryString = "?" + string.Join("&", (from kvp in toQueryString select kvp.Key + "=" + kvp.Value).ToArray()); } // Determine the final url if (null != urlParameters) { actionUrl = string.Format(invariantCulture, actionUrl, urlParameters); } string serviceUrl = this.Configuration.Root + this.Configuration.Relative + actionUrl + queryString; // Create the request context RequestContext context = this.Context.CreateRequestContext(); context.RequestUrl = new Uri(serviceUrl); // Create and configure the HTTP request HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(context.RequestUrl); { webRequest.Method = httpMethod.ToString().ToUpper(); webRequest.Timeout = this.Configuration.RequestTimeout ?? (5 * 60 * 1000); if (null != this.Configuration.RequestHeaders) { foreach (var kvp in this.Configuration.RequestHeaders) { webRequest.Headers[kvp.Key] = kvp.Value; } } if (null != postData && postData.Length > 0) { webRequest.ContentLength = postData.Length; webRequest.ContentType = "application/json; charset=UTF-8"; Stream postStream = webRequest.GetRequestStream(); postStream.Write(postData, 0, postData.Length); postStream.Close(); } } HttpWebResponse response = null; { Stopwatch timer = Stopwatch.StartNew(); try { // Add a HTTP header with a random number to help tracking the request here and on the remote server int randomNumber = new Random().Next(int.MaxValue); webRequest.Headers["X-SP-Unique-Identifier"] = randomNumber.ToString(invariantCulture); // Send the request response = (HttpWebResponse)webRequest.GetResponse(); } catch (WebException ex) { string errorContent = null; if (ex.Response != null) { context.ResponseStatus = (int)((HttpWebResponse)ex.Response).StatusCode; if (ex.Response.ContentLength != 0) { using (var stream = ex.Response.GetResponseStream()) { if (null != stream) { using (var reader = new StreamReader(stream)) { errorContent = reader.ReadToEnd(); context.ResponseSize = errorContent.Length; } } } string errorResult = null; if (null != errorContent) { using (StringReader r = new StringReader(errorContent)) { errorResult = r.ReadToEnd(); } } if (!string.IsNullOrWhiteSpace(errorResult)) { context.ResponseContent = errorResult; throw new ProxyException(errorResult, (HttpWebResponse)ex.Response); } } } throw new ProxyException(string.Format(invariantCulture, "Exception caught while performing a {0} request to url '{1}'. {2}: {3}", httpMethod.ToString(), serviceUrl, ex.GetType().Name, ex.Message), ex); } finally { var elapsedMilliseconds = timer.ElapsedMilliseconds; timer.Stop(); context.RequestElapsedMilliseconds = elapsedMilliseconds; context.RequestElapsedTime = new TimeSpan(0, 0, 0, 0, (int)elapsedMilliseconds); } context.ResponseHeaders.Clear(); // Add the response information to the context for (int index = 0, total = response.Headers.Count; index < total; index++) { context.ResponseHeaders[response.Headers.GetKey(index)] = response.Headers.Get(index); } context.ResponseStatus = (int)response.StatusCode; if ((int)response.StatusCode >= 400) { throw new ProxyException("Invalid HTTP status code.", response); } // Read the response contents Stream responseStream = response.GetResponseStream(); using (StreamReader responseReader = new StreamReader(responseStream)) { responseData = responseReader.ReadToEnd(); // note: context.ResponseSize should be in bytes, not string length // ContentLength should be length reported in the Content-Length header (number of octets) context.ResponseSize = response.ContentLength; context.ResponseContent = responseData; responseReader.Close(); } responseStream.Close(); response.Close(); } // Attempt to deserialize the response contents if (context.ResponseHeaders.ContainsKey("Content-Type")) { string contentType = (context.ResponseHeaders["Content-Type"] ?? string.Empty).Split(';').FirstOrDefault(h => !h.Trim().StartsWith("charset", StringComparison.OrdinalIgnoreCase)); string temp = contentType.Split('/').Last(); if (temp.EndsWith("json", StringComparison.OrdinalIgnoreCase) || temp.EndsWith("javascript", StringComparison.OrdinalIgnoreCase)) { using (StringReader r = new StringReader(responseData)) { using (JsonReader r2 = new JsonTextReader(r)) { r2.DateTimeZoneHandling = DateTimeZoneHandling.Utc; if (expectedReturn != typeof(void)) { retVal = this.m_jsonSerializer.Deserialize(r2, expectedReturn); } } } } else { throw new ProxyException("Unknown response content type: " + contentType, response); } } } int outArgsCount = null != outArgs ? outArgs.Length : 0; IMethodReturnMessage callResult = new ReturnMessage(retVal, outArgs, outArgsCount, methodCall.LogicalCallContext, methodCall); return(callResult); } catch (Exception e) { return(new ReturnMessage(e, methodCall)); } } else { throw new NotSupportedException(); } }