Skip to content

Commit

Permalink
Mastodon APIに対するリクエストを送信するMastodonApiConnectionを実装
Browse files Browse the repository at this point in the history
  • Loading branch information
upsilon committed May 4, 2017
1 parent 54aaa6e commit 46d555a
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 0 deletions.
35 changes: 35 additions & 0 deletions OpenTween/Connection/IMastodonApiConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// OpenTween - Client of Twitter
// Copyright (c) 2017 kim_upsilon (@kim_upsilon) <https://upsilo.net/~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 <http://www.gnu.org/licenses/>, 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.Net.Http;
using System.Net.WebSockets;
using System.Threading.Tasks;

namespace OpenTween.Connection
{
public interface IMastodonApiConnection : IDisposable
{
Task<T> SendAsync<T>(HttpMethod method, Uri uri, IEnumerable<KeyValuePair<string, string>> param);
Task<WebSocket> GetWebSocketAsync(Uri uri, IEnumerable<KeyValuePair<string, string>> param);
}
}
194 changes: 194 additions & 0 deletions OpenTween/Connection/MastodonApiConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// OpenTween - Client of Twitter
// Copyright (c) 2017 kim_upsilon (@kim_upsilon) <https://upsilo.net/~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 <http://www.gnu.org/licenses/>, 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.Cache;
using System.Net.Http;
using System.Net.WebSockets;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace OpenTween.Connection
{
public sealed class MastodonApiConnection : IMastodonApiConnection
{
public Uri InstanceUri { get; }
public Uri WebsocketUri { get; }
public string AccessToken { get; }

internal HttpClient http;

public MastodonApiConnection(Uri instanceUri, string accessToken)
{
this.InstanceUri = InstanceUri;
this.AccessToken = accessToken;

var websocketUri = new UriBuilder(this.InstanceUri);
websocketUri.Scheme = websocketUri.Scheme == "https" ? "wss" : "ws";
this.WebsocketUri = websocketUri.Uri;

this.InitializeHttpClient();
Networking.WebProxyChanged += this.Networking_WebProxyChanged;
}

public Task<T> SendAsync<T>(HttpMethod method, Uri uri, IEnumerable<KeyValuePair<string, string>> param)
{
if (method == HttpMethod.Get)
return this.GetAsync<T>(uri, param);

return this.PostAsync<T>(method, uri, param);
}

public async Task<WebSocket> GetWebSocketAsync(Uri uri, IEnumerable<KeyValuePair<string, string>> param)
{
var socket = new ClientWebSocket();

Networking.SetWebSocketOptions(socket.Options);

var requestParams = this.GetAccessTokenParams();
if (param != null)
requestParams = requestParams.Concat(param);

var requestUri = new Uri(new Uri(this.WebsocketUri, uri), "?" + MyCommon.BuildQueryString(requestParams));

await socket.ConnectAsync(requestUri, CancellationToken.None);

return socket;
}

public void Dispose()
{
Networking.WebProxyChanged -= this.Networking_WebProxyChanged;
this.http.Dispose();
}

private async Task<T> GetAsync<T>(Uri uri, IEnumerable<KeyValuePair<string, string>> param)
{
var requestParams = this.GetAccessTokenParams();
if (param != null)
requestParams = requestParams.Concat(param);

var requestUri = new Uri(new Uri(this.InstanceUri, uri), "?" + MyCommon.BuildQueryString(requestParams));

try
{
using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
using (var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();

using (var content = response.Content)
{
var responseText = await content.ReadAsStringAsync()
.ConfigureAwait(false);

try
{
return MyCommon.CreateDataFromJson<T>(responseText);
}
catch (SerializationException ex)
{
throw new WebApiException("Invalid Response", responseText, ex);
}
}
}
}
catch (HttpRequestException ex)
{
throw new WebApiException(ex.InnerException?.Message ?? ex.Message, ex);
}
catch (OperationCanceledException ex)
{
throw new WebApiException("Timeout", ex);
}
}

private async Task<T> PostAsync<T>(HttpMethod method, Uri uri, IEnumerable<KeyValuePair<string, string>> param)
{
var requestUri = new Uri(this.InstanceUri, uri);

var requestParams = this.GetAccessTokenParams();
if (param != null)
requestParams = requestParams.Concat(param);

try
{
using (var request = new HttpRequestMessage(method, requestUri))
using (var postContent = new FormUrlEncodedContent(requestParams))
{
request.Content = postContent;

using (var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();

using (var content = response.Content)
{
var responseText = await content.ReadAsStringAsync()
.ConfigureAwait(false);

try
{
return MyCommon.CreateDataFromJson<T>(responseText);
}
catch (SerializationException ex)
{
throw new WebApiException("Invalid Response", responseText, ex);
}
}
}
}
}
catch (HttpRequestException ex)
{
throw new WebApiException(ex.InnerException?.Message ?? ex.Message, ex);
}
catch (OperationCanceledException ex)
{
throw new WebApiException("Timeout", ex);
}
}

private IEnumerable<KeyValuePair<string, string>> GetAccessTokenParams()
{
return new[]
{
new KeyValuePair<string, string>("access_token", this.AccessToken),
};
}

private void InitializeHttpClient()
{
var innerHandler = Networking.CreateHttpClientHandler();
innerHandler.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);

this.http = Networking.CreateHttpClient(innerHandler);
}

private void Networking_WebProxyChanged(object sender, EventArgs e)
=> this.InitializeHttpClient();
}
}
9 changes: 9 additions & 0 deletions OpenTween/Connection/Networking.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -182,6 +183,14 @@ public static HttpClient CreateHttpClient(HttpMessageHandler handler)
return client;
}

public static void SetWebSocketOptions(ClientWebSocketOptions options)
{
if (Networking.Proxy != null)
options.Proxy = Networking.Proxy;

options.SetRequestHeader("User-Agent", Networking.GetUserAgentString());
}

public static string GetUserAgentString(bool fakeMSIE = false)
{
if (fakeMSIE)
Expand Down
2 changes: 2 additions & 0 deletions OpenTween/OpenTween.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@
</Compile>
<Compile Include="Bing.cs" />
<Compile Include="Connection\IApiConnection.cs" />
<Compile Include="Connection\IMastodonApiConnection.cs" />
<Compile Include="Connection\IMediaUploadService.cs" />
<Compile Include="Connection\imgly.cs" />
<Compile Include="Connection\Imgur.cs" />
<Compile Include="Connection\LazyJson.cs" />
<Compile Include="Connection\MastodonApiConnection.cs" />
<Compile Include="Connection\Mobypicture.cs" />
<Compile Include="Connection\Networking.cs" />
<Compile Include="Connection\OAuthEchoHandler.cs" />
Expand Down

0 comments on commit 46d555a

Please sign in to comment.