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;
bool isAbsolutePath;
path = ParseUrlQueryString(path, parametersWithoutMediaObjects, false, out uri, out isLegacyRestApi, out isAbsolutePath);
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 };
}
if (isAbsolutePath || string.IsNullOrEmpty(Version))
{
uriBuilder.Path = path;
}
else
{
uriBuilder.Path = Version + "/" + 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();
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");
}
if (!string.IsNullOrEmpty(contentType))
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);
}