private HttpHelper PrepareRequest(HttpMethod httpMethod, string path, object parameters, Type resultType, out Stream input, out bool containsEtag, out IList <int> batchEtags, string uriOverride = "") { input = null; containsEtag = false; batchEtags = null; IDictionary <string, FacebookMediaObject> mediaObjects; IDictionary <string, FacebookMediaStream> mediaStreams; IDictionary <string, object> parametersWithoutMediaObjects = ToDictionary(parameters, out mediaObjects, out mediaStreams) ?? new Dictionary <string, object>(); if (!parametersWithoutMediaObjects.ContainsKey("access_token") && !string.IsNullOrEmpty(AccessToken)) { parametersWithoutMediaObjects["access_token"] = AccessToken; } if (!parametersWithoutMediaObjects.ContainsKey("return_ssl_resources") && IsSecureConnection) { parametersWithoutMediaObjects["return_ssl_resources"] = true; } string etag = null; if (parametersWithoutMediaObjects.ContainsKey(ETagKey)) { etag = (string)parametersWithoutMediaObjects[ETagKey]; parametersWithoutMediaObjects.Remove(ETagKey); containsEtag = true; } Uri uri; bool isLegacyRestApi = false; path = ParseUrlQueryString(path, parametersWithoutMediaObjects, false, out uri, out isLegacyRestApi); if (parametersWithoutMediaObjects.ContainsKey("format")) { parametersWithoutMediaObjects["format"] = "json-strings"; } string restMethod = null; if (parametersWithoutMediaObjects.ContainsKey("method")) { restMethod = (string)parametersWithoutMediaObjects["method"]; if (restMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Parameter cannot contain method=delete. Use Delete or DeleteAsync or DeleteTaskAsync methods instead.", "parameters"); } parametersWithoutMediaObjects.Remove("method"); isLegacyRestApi = true; } else if (isLegacyRestApi) { throw new ArgumentException("Parameters should contain rest 'method' name", "parameters"); } UriBuilder uriBuilder; if (uri == null) { uriBuilder = new UriBuilder { Scheme = "https" }; if (isLegacyRestApi) { if (string.IsNullOrEmpty(restMethod)) { throw new InvalidOperationException("Legacy rest api 'method' in parameters is null or empty."); } path = string.Concat("method/", restMethod); parametersWithoutMediaObjects["format"] = "json-strings"; if (restMethod.Equals("video.upload")) { uriBuilder.Host = UseFacebookBeta ? "api-video.beta.facebook.com" : "api-video.facebook.com"; } else if (LegacyRestApiReadOnlyCalls.Contains(restMethod)) { uriBuilder.Host = UseFacebookBeta ? "api-read.beta.facebook.com" : "api-read.facebook.com"; } else { uriBuilder.Host = UseFacebookBeta ? "api.beta.facebook.com" : "api.facebook.com"; } } else { if (parametersWithoutMediaObjects.ContainsKey("batch")) { var processBatchResponse = !parametersWithoutMediaObjects.ContainsKey("_process_batch_response_") || (bool)parametersWithoutMediaObjects["_process_batch_response_"]; if (processBatchResponse) { batchEtags = new List <int>(); var batch = parametersWithoutMediaObjects["batch"] as IList <object>; if (batch != null) { int i; for (i = 0; i < batch.Count; i++) { var batchParameter = batch[i] as IDictionary <string, object>; if (batchParameter != null) { IDictionary <string, object> headers = null; if (batchParameter.ContainsKey("headers")) { headers = (IDictionary <string, object>)batchParameter["headers"]; } bool containsBatchEtag = batchParameter.ContainsKey(ETagKey); if (containsBatchEtag) { if (string.IsNullOrEmpty((string)batchParameter[ETagKey])) { batchEtags.Add(i); batchParameter.Remove(ETagKey); continue; } else if (headers == null) { headers = new Dictionary <string, object>(); batchParameter["headers"] = headers; } } if (containsBatchEtag) { if (!headers.ContainsKey("If-None-Match")) { headers["If-None-Match"] = string.Concat('"', batchParameter[ETagKey], '"'); } batchParameter.Remove(ETagKey); batchEtags.Add(i); } else { if (headers != null && headers.ContainsKey("If-None-Match")) { batchEtags.Add(i); } } } } } } } path = path ?? string.Empty; if (httpMethod == HttpMethod.Post && path.EndsWith("/videos", StringComparison.OrdinalIgnoreCase)) { uriBuilder.Host = UseFacebookBeta ? "graph-video.beta.facebook.com" : "graph-video.facebook.com"; } else { uriBuilder.Host = UseFacebookBeta ? "graph.beta.facebook.com" : "graph.facebook.com"; } } } else { uriBuilder = new UriBuilder { Host = uri.Host, Scheme = uri.Scheme }; } uriBuilder.Path = path; string contentType = null; long? contentLength = null; var queryString = new StringBuilder(); SerializeParameters(parametersWithoutMediaObjects); if (parametersWithoutMediaObjects.ContainsKey("access_token")) { var accessToken = parametersWithoutMediaObjects["access_token"] as string; if (!string.IsNullOrEmpty(accessToken) && accessToken != "null") { queryString.AppendFormat("access_token={0}&", accessToken); } parametersWithoutMediaObjects.Remove("access_token"); } if (httpMethod != HttpMethod.Post) { if (containsEtag && httpMethod != HttpMethod.Get) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "{0} is only supported for http get method.", ETagKey), "httpMethod"); } // for GET,DELETE if (mediaObjects.Count > 0 && mediaStreams.Count > 0) { throw new InvalidOperationException("Attachments (FacebookMediaObject/FacebookMediaStream) are valid only in POST requests."); } #if SILVERLIGHT && !WINDOWS_PHONE if (httpMethod == HttpMethod.Delete) { queryString.Append("method=delete&"); } #endif foreach (var kvp in parametersWithoutMediaObjects) { queryString.AppendFormat("{0}={1}&", HttpHelper.UrlEncode(kvp.Key), HttpHelper.UrlEncode(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode))); } } else { if (mediaObjects.Count == 0 && mediaStreams.Count == 0) { contentType = "application/x-www-form-urlencoded"; var sb = new StringBuilder(); foreach (var kvp in parametersWithoutMediaObjects) { sb.AppendFormat("{0}={1}&", HttpHelper.UrlEncode(kvp.Key), HttpHelper.UrlEncode(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode))); } if (sb.Length > 0) { sb.Length--; } input = sb.Length == 0 ? null : new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString())); } else { string boundary = Boundary == null ? DateTime.UtcNow.Ticks.ToString("x", CultureInfo.InvariantCulture) // for unit testing : Boundary(); contentType = string.Concat("multipart/form-data; boundary=", boundary); var streams = new List <Stream>(); var indexOfDisposableStreams = new List <int>(); // Build up the post message header var sb = new StringBuilder(); foreach (var kvp in parametersWithoutMediaObjects) { sb.Append(MultiPartFormPrefix).Append(boundary).Append(MultiPartNewLine); sb.Append("Content-Disposition: form-data; name=\"").Append(kvp.Key).Append("\""); sb.Append(MultiPartNewLine).Append(MultiPartNewLine); sb.Append(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode)); sb.Append(MultiPartNewLine); } indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()))); foreach (var facebookMediaObject in mediaObjects) { var sbMediaObject = new StringBuilder(); var mediaObject = facebookMediaObject.Value; if (mediaObject.ContentType == null || mediaObject.GetValue() == null || string.IsNullOrEmpty(mediaObject.FileName)) { throw new InvalidOperationException(AttachmentMustHavePropertiesSetError); } sbMediaObject.Append(MultiPartFormPrefix).Append(boundary).Append(MultiPartNewLine); sbMediaObject.Append("Content-Disposition: form-data; name=\"").Append(facebookMediaObject.Key).Append("\"; filename=\"").Append(mediaObject.FileName).Append("\"").Append(MultiPartNewLine); sbMediaObject.Append("Content-Type: ").Append(mediaObject.ContentType).Append(MultiPartNewLine).Append(MultiPartNewLine); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(sbMediaObject.ToString()))); byte[] fileData = mediaObject.GetValue(); if (fileData == null) { throw new InvalidOperationException(AttachmentValueIsNull); } indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(fileData)); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(MultiPartNewLine))); } foreach (var facebookMediaStream in mediaStreams) { var sbMediaStream = new StringBuilder(); var mediaStream = facebookMediaStream.Value; if (mediaStream.ContentType == null || mediaStream.GetValue() == null || string.IsNullOrEmpty(mediaStream.FileName)) { throw new InvalidOperationException(AttachmentMustHavePropertiesSetError); } sbMediaStream.Append(MultiPartFormPrefix).Append(boundary).Append(MultiPartNewLine); sbMediaStream.Append("Content-Disposition: form-data; name=\"").Append(facebookMediaStream.Key).Append("\"; filename=\"").Append(mediaStream.FileName).Append("\"").Append(MultiPartNewLine); sbMediaStream.Append("Content-Type: ").Append(mediaStream.ContentType).Append(MultiPartNewLine).Append(MultiPartNewLine); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(sbMediaStream.ToString()))); var stream = mediaStream.GetValue(); if (stream == null) { throw new InvalidOperationException(AttachmentValueIsNull); } streams.Add(stream); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(MultiPartNewLine))); } indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(string.Concat(MultiPartNewLine, MultiPartFormPrefix, boundary, MultiPartFormPrefix, MultiPartNewLine)))); input = new CombinationStream(streams, indexOfDisposableStreams); } contentLength = input == null ? 0 : input.Length; } if (queryString.Length > 0) { queryString.Length--; } uriBuilder.Query = queryString.ToString(); Uri finalUri = uriBuilder.Uri; if (uriOverride != "") { finalUri = new Uri(uriOverride); } var request = HttpWebRequestFactory == null ? new HttpWebRequestWrapper((HttpWebRequest)WebRequest.Create(finalUri)) : HttpWebRequestFactory(uriBuilder.Uri); switch (httpMethod) { case HttpMethod.Get: request.Method = "GET"; break; case HttpMethod.Delete: #if !(SILVERLIGHT && !WINDOWS_PHONE) request.Method = "DELETE"; request.TrySetContentLength(0); break; #endif case HttpMethod.Post: request.Method = "POST"; break; default: throw new ArgumentOutOfRangeException("httpMethod"); } request.ContentType = contentType; if (!string.IsNullOrEmpty(etag)) { request.Headers[HttpRequestHeader.IfNoneMatch] = string.Concat('"', etag, '"'); } #if !(SILVERLIGHT || NETFX_CORE) request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; request.AllowWriteStreamBuffering = false; #endif #if NETFX_CORE request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip,deflate"; #endif if (contentLength.HasValue) { request.TrySetContentLength(contentLength.Value); } request.TrySetUserAgent("Facebook C# SDK"); return(new HttpHelper(request)); }
private HttpHelper PrepareRequest(HttpMethod httpMethod, string path, object parameters, Type resultType, out Stream input, out bool containsEtag, out IList<int> batchEtags) { input = null; containsEtag = false; batchEtags = null; IDictionary<string, FacebookMediaObject> mediaObjects; IDictionary<string, FacebookMediaStream> mediaStreams; IDictionary<string, object> parametersWithoutMediaObjects = ToDictionary(parameters, out mediaObjects, out mediaStreams) ?? new Dictionary<string, object>(); if (!parametersWithoutMediaObjects.ContainsKey("access_token") && !string.IsNullOrEmpty(AccessToken)) parametersWithoutMediaObjects["access_token"] = AccessToken; if (!parametersWithoutMediaObjects.ContainsKey("return_ssl_resources") && IsSecureConnection) parametersWithoutMediaObjects["return_ssl_resources"] = true; string etag = null; if (parametersWithoutMediaObjects.ContainsKey(ETagKey)) { etag = (string)parametersWithoutMediaObjects[ETagKey]; parametersWithoutMediaObjects.Remove(ETagKey); containsEtag = true; } Uri uri; bool isLegacyRestApi = false; path = ParseUrlQueryString(path, parametersWithoutMediaObjects, false, out uri, out isLegacyRestApi); if (parametersWithoutMediaObjects.ContainsKey("format")) parametersWithoutMediaObjects["format"] = "json-strings"; string restMethod = null; if (parametersWithoutMediaObjects.ContainsKey("method")) { restMethod = (string)parametersWithoutMediaObjects["method"]; if (restMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("Parameter cannot contain method=delete. Use Delete or DeleteAsync or DeleteTaskAsync methods instead.", "parameters"); parametersWithoutMediaObjects.Remove("method"); isLegacyRestApi = true; } else if (isLegacyRestApi) { throw new ArgumentException("Parameters should contain rest 'method' name", "parameters"); } UriBuilder uriBuilder; if (uri == null) { uriBuilder = new UriBuilder { Scheme = "https" }; if (isLegacyRestApi) { if (string.IsNullOrEmpty(restMethod)) throw new InvalidOperationException("Legacy rest api 'method' in parameters is null or empty."); path = string.Concat("method/", restMethod); parametersWithoutMediaObjects["format"] = "json-strings"; if (restMethod.Equals("video.upload")) uriBuilder.Host = UseFacebookBeta ? "api-video.beta.facebook.com" : "api-video.facebook.com"; else if (LegacyRestApiReadOnlyCalls.Contains(restMethod)) uriBuilder.Host = UseFacebookBeta ? "api-read.beta.facebook.com" : "api-read.facebook.com"; else uriBuilder.Host = UseFacebookBeta ? "api.beta.facebook.com" : "api.facebook.com"; } else { if (parametersWithoutMediaObjects.ContainsKey("batch")) { var processBatchResponse = !parametersWithoutMediaObjects.ContainsKey("_process_batch_response_") || (bool)parametersWithoutMediaObjects["_process_batch_response_"]; if (processBatchResponse) { batchEtags = new List<int>(); var batch = parametersWithoutMediaObjects["batch"] as IList<object>; if (batch != null) { int i; for (i = 0; i < batch.Count; i++) { var batchParameter = batch[i] as IDictionary<string, object>; if (batchParameter != null) { IDictionary<string, object> headers = null; if (batchParameter.ContainsKey("headers")) headers = (IDictionary<string, object>)batchParameter["headers"]; bool containsBatchEtag = batchParameter.ContainsKey(ETagKey); if (containsBatchEtag) { if (string.IsNullOrEmpty((string)batchParameter[ETagKey])) { batchEtags.Add(i); batchParameter.Remove(ETagKey); continue; } else if (headers == null) { headers = new Dictionary<string, object>(); batchParameter["headers"] = headers; } } if (containsBatchEtag) { if (!headers.ContainsKey("If-None-Match")) headers["If-None-Match"] = string.Concat('"', batchParameter[ETagKey], '"'); batchParameter.Remove(ETagKey); batchEtags.Add(i); } else { if (headers != null && headers.ContainsKey("If-None-Match")) batchEtags.Add(i); } } } } } } else if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } if (httpMethod == HttpMethod.Post && path.EndsWith("/videos")) uriBuilder.Host = UseFacebookBeta ? "graph-video.beta.facebook.com" : "graph-video.facebook.com"; else uriBuilder.Host = UseFacebookBeta ? "graph.beta.facebook.com" : "graph.facebook.com"; } } else { uriBuilder = new UriBuilder { Host = uri.Host, Scheme = uri.Scheme }; } uriBuilder.Path = path; string contentType = null; var queryString = new StringBuilder(); SerializeParameters(parametersWithoutMediaObjects); if (parametersWithoutMediaObjects.ContainsKey("access_token")) { var accessToken = parametersWithoutMediaObjects["access_token"]; if (accessToken != null && (!(accessToken is string) || (!string.IsNullOrEmpty((string)accessToken)))) queryString.AppendFormat("access_token={0}&", accessToken); parametersWithoutMediaObjects.Remove("access_token"); } if (httpMethod != HttpMethod.Post) { if (containsEtag && httpMethod != HttpMethod.Get) throw new ArgumentException(string.Format("{0} is only supported for http get method.", ETagKey), "httpMethod"); // for GET,DELETE if (mediaObjects.Count > 0 && mediaStreams.Count > 0) throw new InvalidOperationException("Attachments (FacebookMediaObject/FacebookMediaStream) are valid only in POST requests."); #if SILVERLIGHT && !WINDOWS_PHONE if (httpMethod == HttpMethod.Delete) queryString.Append("method=delete&"); #endif foreach (var kvp in parametersWithoutMediaObjects) queryString.AppendFormat("{0}={1}&", HttpHelper.UrlEncode(kvp.Key), HttpHelper.UrlEncode(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode))); } else { if (mediaObjects.Count == 0 && mediaStreams.Count == 0) { contentType = "application/x-www-form-urlencoded"; var sb = new StringBuilder(); foreach (var kvp in parametersWithoutMediaObjects) sb.AppendFormat("{0}={1}&", HttpHelper.UrlEncode(kvp.Key), HttpHelper.UrlEncode(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode))); if (sb.Length > 0) sb.Length--; input = sb.Length == 0 ? null : new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString())); } else { string boundary = Boundary == null ? DateTime.UtcNow.Ticks.ToString("x", CultureInfo.InvariantCulture) // for unit testing : Boundary(); contentType = string.Concat("multipart/form-data; boundary=", boundary); var streams = new List<Stream>(); var indexOfDisposableStreams = new List<int>(); // Build up the post message header var sb = new StringBuilder(); foreach (var kvp in parametersWithoutMediaObjects) { sb.Append(MultiPartFormPrefix).Append(boundary).Append(MultiPartNewLine); sb.Append("Content-Disposition: form-data; name=\"").Append(kvp.Key).Append("\""); sb.Append(MultiPartNewLine).Append(MultiPartNewLine); sb.Append(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode)); sb.Append(MultiPartNewLine); } indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()))); foreach (var facebookMediaObject in mediaObjects) { var sbMediaObject = new StringBuilder(); var mediaObject = facebookMediaObject.Value; if (mediaObject.ContentType == null || mediaObject.GetValue() == null || string.IsNullOrEmpty(mediaObject.FileName)) throw new InvalidOperationException(AttachmentMustHavePropertiesSetError); sbMediaObject.Append(MultiPartFormPrefix).Append(boundary).Append(MultiPartNewLine); sbMediaObject.Append("Content-Disposition: form-data; name=\"").Append(facebookMediaObject.Key).Append("\"; filename=\"").Append(mediaObject.FileName).Append("\"").Append(MultiPartNewLine); sbMediaObject.Append("Content-Type: ").Append(mediaObject.ContentType).Append(MultiPartNewLine).Append(MultiPartNewLine); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(sbMediaObject.ToString()))); byte[] fileData = mediaObject.GetValue(); if (fileData == null) throw new InvalidOperationException(AttachmentValueIsNull); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(fileData)); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(MultiPartNewLine))); } foreach (var facebookMediaStream in mediaStreams) { var sbMediaStream = new StringBuilder(); var mediaStream = facebookMediaStream.Value; if (mediaStream.ContentType == null || mediaStream.GetValue() == null || string.IsNullOrEmpty(mediaStream.FileName)) throw new InvalidOperationException(AttachmentMustHavePropertiesSetError); sbMediaStream.Append(MultiPartFormPrefix).Append(boundary).Append(MultiPartNewLine); sbMediaStream.Append("Content-Disposition: form-data; name=\"").Append(facebookMediaStream.Key).Append("\"; filename=\"").Append(mediaStream.FileName).Append("\"").Append(MultiPartNewLine); sbMediaStream.Append("Content-Type: ").Append(mediaStream.ContentType).Append(MultiPartNewLine).Append(MultiPartNewLine); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(sbMediaStream.ToString()))); var stream = mediaStream.GetValue(); if (stream == null) throw new InvalidOperationException(AttachmentValueIsNull); streams.Add(stream); indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(MultiPartNewLine))); } indexOfDisposableStreams.Add(streams.Count); streams.Add(new MemoryStream(Encoding.UTF8.GetBytes(string.Concat(MultiPartNewLine, MultiPartFormPrefix, boundary, MultiPartFormPrefix, MultiPartNewLine)))); input = new CombinationStream(streams, indexOfDisposableStreams); } } if (queryString.Length > 0) queryString.Length--; uriBuilder.Query = queryString.ToString(); var request = HttpWebRequestFactory == null ? new HttpWebRequestWrapper((HttpWebRequest)WebRequest.Create(uriBuilder.Uri)) : HttpWebRequestFactory(uriBuilder.Uri); switch (httpMethod) { case HttpMethod.Get: request.Method = "GET"; break; case HttpMethod.Delete: #if !(SILVERLIGHT && !WINDOWS_PHONE) request.Method = "DELETE"; request.TrySetContentLength(0); break; #endif case HttpMethod.Post: request.Method = "POST"; break; default: throw new ArgumentOutOfRangeException("httpMethod"); } request.ContentType = contentType; if (!string.IsNullOrEmpty(etag)) request.Headers[HttpRequestHeader.IfNoneMatch] = string.Concat('"', etag, '"'); #if !(SILVERLIGHT || NETFX_CORE) request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; request.AllowWriteStreamBuffering = false; #endif #if NETFX_CORE request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip,deflate"; #endif if (input != null) request.TrySetContentLength(input.Length); request.TrySetUserAgent("Facebook C# SDK"); return new HttpHelper(request); }