// OpenTween - Client of Twitter
// Copyright (c) 2016 kim_upsilon (@kim_upsilon)
// All rights reserved.
//
// This file is part of OpenTween.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see , or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using OpenTween.Api;
using OpenTween.Api.DataModel;
namespace OpenTween.Connection
{
public class TwitterApiConnection : IApiConnection, IDisposable
{
public Uri RestApiBase { get; set; } = new Uri("https://api.twitter.com/1.1/");
public bool IsDisposed { get; private set; } = false;
public string AccessToken { get; }
public string AccessSecret { get; }
internal HttpClient http;
public TwitterApiConnection(string accessToken, string accessSecret)
{
this.AccessToken = accessToken;
this.AccessSecret = accessSecret;
this.InitializeHttpClient(accessToken, accessSecret);
Networking.WebProxyChanged += this.Networking_WebProxyChanged;
}
public Task GetAsync(Uri uri, IDictionary param)
=> this.GetAsync(uri, param, null);
public async Task GetAsync(Uri uri, IDictionary param, string endpointName)
{
var requestUri = new Uri(this.RestApiBase, uri);
if (param != null)
requestUri = new Uri(requestUri, "?" + MyCommon.BuildQueryString(param));
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
try
{
using (var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false))
{
await this.CheckStatusCode(response)
.ConfigureAwait(false);
if (endpointName != null)
MyCommon.TwitterApiInfo.UpdateFromHeader(response.Headers, endpointName);
using (var content = response.Content)
{
var responseText = await content.ReadAsStringAsync()
.ConfigureAwait(false);
try
{
return MyCommon.CreateDataFromJson(responseText);
}
catch (SerializationException ex)
{
throw TwitterApiException.CreateFromException(ex, responseText);
}
}
}
}
catch (OperationCanceledException ex)
{
throw TwitterApiException.CreateFromException(ex);
}
}
public async Task> PostLazyAsync(Uri uri, IDictionary param)
{
var requestUri = new Uri(this.RestApiBase, uri);
var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
using (var postContent = new FormUrlEncodedContent(param))
{
request.Content = postContent;
HttpResponseMessage response = null;
try
{
response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false);
await this.CheckStatusCode(response)
.ConfigureAwait(false);
var result = new LazyJson(response);
response = null;
return result;
}
catch (OperationCanceledException ex)
{
throw TwitterApiException.CreateFromException(ex);
}
finally
{
response?.Dispose();
}
}
}
public async Task> PostLazyAsync(Uri uri, IDictionary param, IDictionary media)
{
var requestUri = new Uri(this.RestApiBase, uri);
var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
using (var postContent = new MultipartFormDataContent())
{
foreach (var kv in param)
postContent.Add(new StringContent(kv.Value), kv.Key);
foreach (var kv in media)
postContent.Add(new StreamContent(kv.Value.OpenRead()), kv.Key);
request.Content = postContent;
HttpResponseMessage response = null;
try
{
response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false);
await this.CheckStatusCode(response)
.ConfigureAwait(false);
var result = new LazyJson(response);
response = null;
return result;
}
catch (OperationCanceledException ex)
{
throw TwitterApiException.CreateFromException(ex);
}
finally
{
response?.Dispose();
}
}
}
protected async Task CheckStatusCode(HttpResponseMessage response)
{
var statusCode = response.StatusCode;
if (statusCode == HttpStatusCode.OK)
{
Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
return;
}
string responseText;
using (var content = response.Content)
{
responseText = await content.ReadAsStringAsync()
.ConfigureAwait(false);
}
if (string.IsNullOrWhiteSpace(responseText))
{
if (statusCode == HttpStatusCode.Unauthorized)
Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
throw new TwitterApiException(statusCode, responseText);
}
try
{
var error = TwitterError.ParseJson(responseText);
if (error?.Errors == null || error.Errors.Length == 0)
throw new TwitterApiException(statusCode, responseText);
var errorCodes = error.Errors.Select(x => x.Code);
if (errorCodes.Any(x => x == TwitterErrorCode.InternalError || x == TwitterErrorCode.SuspendedAccount))
{
Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
}
throw new TwitterApiException(error, responseText);
}
catch (SerializationException)
{
throw new TwitterApiException(statusCode, responseText);
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (this.IsDisposed)
return;
this.IsDisposed = true;
if (disposing)
{
Networking.WebProxyChanged -= this.Networking_WebProxyChanged;
this.http.Dispose();
}
}
~TwitterApiConnection()
{
this.Dispose(false);
}
private void InitializeHttpClient(string accessToken, string accessSecret)
{
var handler = new OAuthHandler(Networking.CreateHttpClientHandler(),
ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
accessToken, accessSecret);
this.http = Networking.CreateHttpClient(handler);
}
private void Networking_WebProxyChanged(object sender, EventArgs e)
{
this.InitializeHttpClient(this.AccessToken, this.AccessSecret);
}
}
}