From 6bcc23bea1c5eabcbc383196887d6406fb5c7105 Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 01:24:24 +0900
Subject: [PATCH 1/7] =?UTF-8?q?IApiConnectionLegacy.PostJsonAsync=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E7=AE=87?=
 =?UTF-8?q?=E6=89=80=E3=82=92PostJsonRequest=E3=81=AB=E7=A7=BB=E8=A1=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 OpenTween.Tests/Api/TwitterApiTest.cs | 27 ++++++++++++++++---------
 OpenTween/Api/TwitterApi.cs           | 29 ++++++++++++++++++---------
 OpenTween/Connection/ApiResponse.cs   |  9 +++++++++
 3 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/OpenTween.Tests/Api/TwitterApiTest.cs b/OpenTween.Tests/Api/TwitterApiTest.cs
index 1fde2e592..87df7119d 100644
--- a/OpenTween.Tests/Api/TwitterApiTest.cs
+++ b/OpenTween.Tests/Api/TwitterApiTest.cs
@@ -23,6 +23,7 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Net;
 using System.Net.Http;
 using System.Reflection;
 using System.Runtime.InteropServices;
@@ -674,8 +675,9 @@ public async Task DirectMessagesEventsList_Test()
         [Fact]
         public async Task DirectMessagesEventsNew_Test()
         {
+            using var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
             var mock = new Mock<IApiConnectionLegacy>();
-            var responseText = """
+            var requestJson = """
                 {
                   "event": {
                     "type": "message_create",
@@ -697,11 +699,14 @@ public async Task DirectMessagesEventsNew_Test()
                 }
                 """;
             mock.Setup(x =>
-                x.PostJsonAsync<TwitterMessageEventSingle>(
-                    new Uri("direct_messages/events/new.json", UriKind.Relative),
-                    responseText)
+                x.SendAsync(
+                    It.Is<PostJsonRequest>(r =>
+                        r.RequestUri == new Uri("direct_messages/events/new.json", UriKind.Relative) &&
+                        r.JsonString == requestJson
+                    )
+                )
             )
-            .ReturnsAsync(LazyJson.Create(new TwitterMessageEventSingle()));
+            .ReturnsAsync(new ApiResponse(responseMessage));
 
             using var twitterApi = new TwitterApi();
             twitterApi.ApiConnection = mock.Object;
@@ -1304,11 +1309,13 @@ public async Task MediaMetadataCreate_Test()
         {
             var mock = new Mock<IApiConnectionLegacy>();
             mock.Setup(x =>
-                x.PostJsonAsync(
-                    new Uri("https://upload.twitter.com/1.1/media/metadata/create.json", UriKind.Absolute),
-                    """{"media_id": "12345", "alt_text": {"text": "hogehoge"}}""")
-            )
-            .ReturnsAsync("");
+                x.SendAsync(
+                    It.Is<PostJsonRequest>(r =>
+                        r.RequestUri == new Uri("https://upload.twitter.com/1.1/media/metadata/create.json") &&
+                        r.JsonString == """{"media_id": "12345", "alt_text": {"text": "hogehoge"}}"""
+                    )
+                )
+            );
 
             using var twitterApi = new TwitterApi();
             twitterApi.ApiConnection = mock.Object;
diff --git a/OpenTween/Api/TwitterApi.cs b/OpenTween/Api/TwitterApi.cs
index 6614555e7..e3d3be4f0 100644
--- a/OpenTween/Api/TwitterApi.cs
+++ b/OpenTween/Api/TwitterApi.cs
@@ -425,10 +425,8 @@ public Task<TwitterMessageEventList> DirectMessagesEventsList(int? count = null,
             return this.Connection.GetAsync<TwitterMessageEventList>(endpoint, param, "/direct_messages/events/list");
         }
 
-        public Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(long recipientId, string text, long? mediaId = null)
+        public async Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(long recipientId, string text, long? mediaId = null)
         {
-            var endpoint = new Uri("direct_messages/events/new.json", UriKind.Relative);
-
             var attachment = "";
             if (mediaId != null)
             {
@@ -458,7 +456,16 @@ public Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(long re
                 }
                 """;
 
-            return this.Connection.PostJsonAsync<TwitterMessageEventSingle>(endpoint, json);
+            var request = new PostJsonRequest
+            {
+                RequestUri = new("direct_messages/events/new.json", UriKind.Relative),
+                JsonString = json,
+            };
+
+            var response = await this.Connection.SendAsync(request)
+                .ConfigureAwait(false);
+
+            return response.ReadAsLazyJson<TwitterMessageEventSingle>();
         }
 
         public Task DirectMessagesEventsDestroy(TwitterDirectMessageId eventId)
@@ -792,14 +799,18 @@ public Task<TwitterUploadMediaResult> MediaUploadStatus(long mediaId)
             return this.Connection.GetAsync<TwitterUploadMediaResult>(endpoint, param, endpointName: null);
         }
 
-        public Task MediaMetadataCreate(long mediaId, string altText)
+        public async Task MediaMetadataCreate(long mediaId, string altText)
         {
-            var endpoint = new Uri("https://upload.twitter.com/1.1/media/metadata/create.json");
-
             var escapedAltText = JsonUtils.EscapeJsonString(altText);
-            var json = $$$"""{"media_id": "{{{mediaId}}}", "alt_text": {"text": "{{{escapedAltText}}}"}}""";
+            var request = new PostJsonRequest
+            {
+                RequestUri = new("https://upload.twitter.com/1.1/media/metadata/create.json"),
+                JsonString = $$$"""{"media_id": "{{{mediaId}}}", "alt_text": {"text": "{{{escapedAltText}}}"}}""",
+            };
 
-            return this.Connection.PostJsonAsync(endpoint, json);
+            await this.Connection.SendAsync(request)
+                .IgnoreResponse()
+                .ConfigureAwait(false);
         }
 
         public OAuthEchoHandler CreateOAuthEchoHandler(HttpMessageHandler innerHandler, Uri authServiceProvider, Uri? realm = null)
diff --git a/OpenTween/Connection/ApiResponse.cs b/OpenTween/Connection/ApiResponse.cs
index ee1560058..da03230e9 100644
--- a/OpenTween/Connection/ApiResponse.cs
+++ b/OpenTween/Connection/ApiResponse.cs
@@ -114,4 +114,13 @@ public async Task<string> ReadAsString()
                 .ConfigureAwait(false);
         }
     }
+
+    public static class ApiResponseTaskExtension
+    {
+        public static async Task IgnoreResponse(this Task<ApiResponse> task)
+        {
+            using var response = await task.ConfigureAwait(false);
+            // レスポンスボディを読み込まず破棄する
+        }
+    }
 }

From 666676ab362b431014b9a11e1e41f5d1d95555b2 Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 01:27:32 +0900
Subject: [PATCH 2/7] =?UTF-8?q?TwitterApiConnection.PostJsonAsync=E3=83=A1?=
 =?UTF-8?q?=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E5=89=8A=E9=99=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Connection/TwitterApiConnectionTest.cs    | 69 -------------------
 OpenTween/Connection/IApiConnectionLegacy.cs  |  4 --
 OpenTween/Connection/TwitterApiConnection.cs  | 29 --------
 3 files changed, 102 deletions(-)

diff --git a/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs b/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
index c646f07bb..b793548e3 100644
--- a/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
+++ b/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
@@ -481,75 +481,6 @@ public async Task PostLazyAsync_Multipart_NullTest()
             Assert.Equal(0, mockHandler.QueueCount);
         }
 
-        [Fact]
-        public async Task PostJsonAsync_Test()
-        {
-            using var mockHandler = new HttpMessageHandlerMock();
-            using var http = new HttpClient(mockHandler);
-            using var apiConnection = new TwitterApiConnection();
-            apiConnection.Http = http;
-
-            mockHandler.Enqueue(async x =>
-            {
-                Assert.Equal(HttpMethod.Post, x.Method);
-                Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
-                    x.RequestUri.AbsoluteUri);
-
-                Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
-
-                var body = await x.Content.ReadAsStringAsync();
-
-                Assert.Equal("""{"aaaa": 1111}""", body);
-
-                return new HttpResponseMessage(HttpStatusCode.OK)
-                {
-                    Content = new StringContent(@"{""ok"":true}"),
-                };
-            });
-
-            var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
-
-            var response = await apiConnection.PostJsonAsync(endpoint, """{"aaaa": 1111}""");
-
-            Assert.Equal(@"{""ok"":true}", response);
-            Assert.Equal(0, mockHandler.QueueCount);
-        }
-
-        [Fact]
-        public async Task PostJsonAsync_T_Test()
-        {
-            using var mockHandler = new HttpMessageHandlerMock();
-            using var http = new HttpClient(mockHandler);
-            using var apiConnection = new TwitterApiConnection();
-            apiConnection.Http = http;
-
-            mockHandler.Enqueue(async x =>
-            {
-                Assert.Equal(HttpMethod.Post, x.Method);
-                Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
-                    x.RequestUri.AbsoluteUri);
-
-                Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
-
-                var body = await x.Content.ReadAsStringAsync();
-
-                Assert.Equal("""{"aaaa": 1111}""", body);
-
-                return new HttpResponseMessage(HttpStatusCode.OK)
-                {
-                    Content = new StringContent("\"hogehoge\""),
-                };
-            });
-
-            var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
-            var response = await apiConnection.PostJsonAsync<string>(endpoint, """{"aaaa": 1111}""");
-            var result = await response.LoadJsonAsync();
-
-            Assert.Equal("hogehoge", result);
-
-            Assert.Equal(0, mockHandler.QueueCount);
-        }
-
         [Fact]
         public async Task DeleteAsync_Test()
         {
diff --git a/OpenTween/Connection/IApiConnectionLegacy.cs b/OpenTween/Connection/IApiConnectionLegacy.cs
index 4175e589d..b72dd5072 100644
--- a/OpenTween/Connection/IApiConnectionLegacy.cs
+++ b/OpenTween/Connection/IApiConnectionLegacy.cs
@@ -46,10 +46,6 @@ public interface IApiConnectionLegacy : IApiConnection, IDisposable
 
         Task PostAsync(Uri uri, IDictionary<string, string>? param, IDictionary<string, IMediaItem>? media);
 
-        Task<string> PostJsonAsync(Uri uri, string json);
-
-        Task<LazyJson<T>> PostJsonAsync<T>(Uri uri, string json);
-
         Task DeleteAsync(Uri uri);
     }
 }
diff --git a/OpenTween/Connection/TwitterApiConnection.cs b/OpenTween/Connection/TwitterApiConnection.cs
index bfcc99b0d..2b3f651bc 100644
--- a/OpenTween/Connection/TwitterApiConnection.cs
+++ b/OpenTween/Connection/TwitterApiConnection.cs
@@ -351,35 +351,6 @@ await TwitterApiConnection.CheckStatusCode(response)
             }
         }
 
-        public async Task<string> PostJsonAsync(Uri uri, string json)
-        {
-            var request = new PostJsonRequest
-            {
-                RequestUri = uri,
-                JsonString = json,
-            };
-
-            using var response = await this.SendAsync(request)
-                .ConfigureAwait(false);
-
-            return await response.ReadAsString()
-                .ConfigureAwait(false);
-        }
-
-        public async Task<LazyJson<T>> PostJsonAsync<T>(Uri uri, string json)
-        {
-            var request = new PostJsonRequest
-            {
-                RequestUri = uri,
-                JsonString = json,
-            };
-
-            using var response = await this.SendAsync(request)
-                .ConfigureAwait(false);
-
-            return response.ReadAsLazyJson<T>();
-        }
-
         public async Task DeleteAsync(Uri uri)
         {
             var requestUri = new Uri(RestApiBase, uri);

From 0cf3ada054f61714f7acf56a69e49d215e8c13fa Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 01:50:26 +0900
Subject: [PATCH 3/7] =?UTF-8?q?DeleteRequest=E3=82=AF=E3=83=A9=E3=82=B9?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Connection/DeleteRequestTest.cs           | 50 +++++++++++++++++++
 OpenTween/Connection/DeleteRequest.cs         | 45 +++++++++++++++++
 OpenTween/Connection/TwitterApiConnection.cs  | 23 +++------
 3 files changed, 101 insertions(+), 17 deletions(-)
 create mode 100644 OpenTween.Tests/Connection/DeleteRequestTest.cs
 create mode 100644 OpenTween/Connection/DeleteRequest.cs

diff --git a/OpenTween.Tests/Connection/DeleteRequestTest.cs b/OpenTween.Tests/Connection/DeleteRequestTest.cs
new file mode 100644
index 000000000..3b72c5b0d
--- /dev/null
+++ b/OpenTween.Tests/Connection/DeleteRequestTest.cs
@@ -0,0 +1,50 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2023 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 Xunit;
+
+namespace OpenTween.Connection
+{
+    public class DeleteRequestTest
+    {
+        [Fact]
+        public void CreateMessage_Test()
+        {
+            var request = new DeleteRequest
+            {
+                RequestUri = new("hoge/aaa.json", UriKind.Relative),
+                Query = new Dictionary<string, string>
+                {
+                    ["id"] = "12345",
+                },
+            };
+
+            var baseUri = new Uri("https://example.com/v1/");
+            using var requestMessage = request.CreateMessage(baseUri);
+
+            Assert.Equal(HttpMethod.Delete, requestMessage.Method);
+            Assert.Equal(new("https://example.com/v1/hoge/aaa.json?id=12345"), requestMessage.RequestUri);
+        }
+    }
+}
diff --git a/OpenTween/Connection/DeleteRequest.cs b/OpenTween/Connection/DeleteRequest.cs
new file mode 100644
index 000000000..f15502814
--- /dev/null
+++ b/OpenTween/Connection/DeleteRequest.cs
@@ -0,0 +1,45 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2023 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.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+
+namespace OpenTween.Connection
+{
+    public class DeleteRequest : IHttpRequest
+    {
+        public required Uri RequestUri { get; set; }
+
+        public IDictionary<string, string>? Query { get; set; }
+
+        public string? EndpointName { get; set; }
+
+        public HttpRequestMessage CreateMessage(Uri baseUri)
+            => new()
+            {
+                Method = HttpMethod.Delete,
+                RequestUri = GetRequest.BuildUriWithQuery(new(baseUri, this.RequestUri), this.Query),
+            };
+    }
+}
diff --git a/OpenTween/Connection/TwitterApiConnection.cs b/OpenTween/Connection/TwitterApiConnection.cs
index 2b3f651bc..93db05aaa 100644
--- a/OpenTween/Connection/TwitterApiConnection.cs
+++ b/OpenTween/Connection/TwitterApiConnection.cs
@@ -353,25 +353,14 @@ await TwitterApiConnection.CheckStatusCode(response)
 
         public async Task DeleteAsync(Uri uri)
         {
-            var requestUri = new Uri(RestApiBase, uri);
-            using var request = new HttpRequestMessage(HttpMethod.Delete, requestUri);
-
-            try
+            var request = new DeleteRequest
             {
-                using var response = await this.Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
-                    .ConfigureAwait(false);
+                RequestUri = uri,
+            };
 
-                await TwitterApiConnection.CheckStatusCode(response)
-                    .ConfigureAwait(false);
-            }
-            catch (HttpRequestException ex)
-            {
-                throw TwitterApiException.CreateFromException(ex);
-            }
-            catch (OperationCanceledException ex)
-            {
-                throw TwitterApiException.CreateFromException(ex);
-            }
+            await this.SendAsync(request)
+                .IgnoreResponse()
+                .ConfigureAwait(false);
         }
 
         public static async Task<T> HandleTimeout<T>(Func<CancellationToken, Task<T>> func, TimeSpan timeout)

From c79d992c15612c7db85c899d1d355e7f22907d41 Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 02:05:06 +0900
Subject: [PATCH 4/7] =?UTF-8?q?IApiConnectionLegacy.DeleteAsync=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E7=AE=87?=
 =?UTF-8?q?=E6=89=80=E3=82=92DeleteRequest=E3=81=AB=E7=A7=BB=E8=A1=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 OpenTween.Tests/Api/TwitterApiTest.cs | 13 +++++++++----
 OpenTween/Api/TwitterApi.cs           | 18 ++++++++++--------
 2 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/OpenTween.Tests/Api/TwitterApiTest.cs b/OpenTween.Tests/Api/TwitterApiTest.cs
index 87df7119d..c660337a3 100644
--- a/OpenTween.Tests/Api/TwitterApiTest.cs
+++ b/OpenTween.Tests/Api/TwitterApiTest.cs
@@ -721,10 +721,15 @@ public async Task DirectMessagesEventsDestroy_Test()
         {
             var mock = new Mock<IApiConnectionLegacy>();
             mock.Setup(x =>
-                x.DeleteAsync(
-                    new Uri("direct_messages/events/destroy.json?id=100", UriKind.Relative))
-            )
-            .Returns(Task.CompletedTask);
+                x.SendAsync(
+                    It.Is<DeleteRequest>(r =>
+                        r.RequestUri == new Uri("direct_messages/events/destroy.json", UriKind.Relative) &&
+                        r.Query != null &&
+                        r.Query.Count == 1 &&
+                        r.Query["id"] == "100"
+                    )
+                )
+            );
 
             using var twitterApi = new TwitterApi();
             twitterApi.ApiConnection = mock.Object;
diff --git a/OpenTween/Api/TwitterApi.cs b/OpenTween/Api/TwitterApi.cs
index e3d3be4f0..342122287 100644
--- a/OpenTween/Api/TwitterApi.cs
+++ b/OpenTween/Api/TwitterApi.cs
@@ -468,18 +468,20 @@ public async Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(l
             return response.ReadAsLazyJson<TwitterMessageEventSingle>();
         }
 
-        public Task DirectMessagesEventsDestroy(TwitterDirectMessageId eventId)
+        public async Task DirectMessagesEventsDestroy(TwitterDirectMessageId eventId)
         {
-            var endpoint = new Uri("direct_messages/events/destroy.json", UriKind.Relative);
-            var param = new Dictionary<string, string>
+            var request = new DeleteRequest
             {
-                ["id"] = eventId.Id,
+                RequestUri = new("direct_messages/events/destroy.json", UriKind.Relative),
+                Query = new Dictionary<string, string>
+                {
+                    ["id"] = eventId.Id,
+                },
             };
 
-            // なぜか application/x-www-form-urlencoded でパラメーターを送ると Bad Request になる謎仕様
-            endpoint = new Uri(endpoint.OriginalString + "?" + MyCommon.BuildQueryString(param), UriKind.Relative);
-
-            return this.Connection.DeleteAsync(endpoint);
+            await this.Connection.SendAsync(request)
+                .IgnoreResponse()
+                .ConfigureAwait(false);
         }
 
         public Task<TwitterUser> UsersShow(string screenName)

From 76f1ff02d3592e13e9c9e4ba2bed11c0ab6f2ba0 Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 02:06:29 +0900
Subject: [PATCH 5/7] =?UTF-8?q?TwitterApiConnection.DeleteAsync=E3=83=A1?=
 =?UTF-8?q?=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E5=89=8A=E9=99=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Connection/TwitterApiConnectionTest.cs    | 24 -------------------
 OpenTween/Connection/IApiConnectionLegacy.cs  |  2 --
 OpenTween/Connection/TwitterApiConnection.cs  | 12 ----------
 3 files changed, 38 deletions(-)

diff --git a/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs b/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
index b793548e3..bfb4b5f96 100644
--- a/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
+++ b/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
@@ -481,30 +481,6 @@ public async Task PostLazyAsync_Multipart_NullTest()
             Assert.Equal(0, mockHandler.QueueCount);
         }
 
-        [Fact]
-        public async Task DeleteAsync_Test()
-        {
-            using var mockHandler = new HttpMessageHandlerMock();
-            using var http = new HttpClient(mockHandler);
-            using var apiConnection = new TwitterApiConnection();
-            apiConnection.Http = http;
-
-            mockHandler.Enqueue(x =>
-            {
-                Assert.Equal(HttpMethod.Delete, x.Method);
-                Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
-                    x.RequestUri.AbsoluteUri);
-
-                return new HttpResponseMessage(HttpStatusCode.NoContent);
-            });
-
-            var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
-
-            await apiConnection.DeleteAsync(endpoint);
-
-            Assert.Equal(0, mockHandler.QueueCount);
-        }
-
         [Fact]
         public async Task HandleTimeout_SuccessTest()
         {
diff --git a/OpenTween/Connection/IApiConnectionLegacy.cs b/OpenTween/Connection/IApiConnectionLegacy.cs
index b72dd5072..7f364f6ac 100644
--- a/OpenTween/Connection/IApiConnectionLegacy.cs
+++ b/OpenTween/Connection/IApiConnectionLegacy.cs
@@ -45,7 +45,5 @@ public interface IApiConnectionLegacy : IApiConnection, IDisposable
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string>? param, IDictionary<string, IMediaItem>? media);
 
         Task PostAsync(Uri uri, IDictionary<string, string>? param, IDictionary<string, IMediaItem>? media);
-
-        Task DeleteAsync(Uri uri);
     }
 }
diff --git a/OpenTween/Connection/TwitterApiConnection.cs b/OpenTween/Connection/TwitterApiConnection.cs
index 93db05aaa..7c7e578f6 100644
--- a/OpenTween/Connection/TwitterApiConnection.cs
+++ b/OpenTween/Connection/TwitterApiConnection.cs
@@ -351,18 +351,6 @@ await TwitterApiConnection.CheckStatusCode(response)
             }
         }
 
-        public async Task DeleteAsync(Uri uri)
-        {
-            var request = new DeleteRequest
-            {
-                RequestUri = uri,
-            };
-
-            await this.SendAsync(request)
-                .IgnoreResponse()
-                .ConfigureAwait(false);
-        }
-
         public static async Task<T> HandleTimeout<T>(Func<CancellationToken, Task<T>> func, TimeSpan timeout)
         {
             using var cts = new CancellationTokenSource();

From 1668176ee22fd71149bd25d900af36f9213f0de7 Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 01:32:28 +0900
Subject: [PATCH 6/7] =?UTF-8?q?TwitterApiConnection.GetStreamAsync?=
 =?UTF-8?q?=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E5=89=8A=E9=99=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Connection/TwitterApiConnectionTest.cs    | 46 -------------------
 OpenTween/Connection/IApiConnectionLegacy.cs  |  4 --
 OpenTween/Connection/TwitterApiConnection.cs  | 38 ---------------
 OpenTween/ErrorReportHandler.cs               | 12 -----
 4 files changed, 100 deletions(-)

diff --git a/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs b/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
index bfb4b5f96..cc8935625 100644
--- a/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
+++ b/OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
@@ -280,52 +280,6 @@ public async Task SendAsync_ErrorJsonTest()
             Assert.Equal(0, mockHandler.QueueCount);
         }
 
-        [Fact]
-        public async Task GetStreamAsync_Test()
-        {
-            using var mockHandler = new HttpMessageHandlerMock();
-            using var http = new HttpClient(mockHandler);
-            using var apiConnection = new TwitterApiConnection();
-            using var image = TestUtils.CreateDummyImage();
-            apiConnection.Http = http;
-
-            mockHandler.Enqueue(x =>
-            {
-                Assert.Equal(HttpMethod.Get, x.Method);
-                Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
-                    x.RequestUri.GetLeftPart(UriPartial.Path));
-
-                var query = HttpUtility.ParseQueryString(x.RequestUri.Query);
-
-                Assert.Equal("1111", query["aaaa"]);
-                Assert.Equal("2222", query["bbbb"]);
-
-                return new HttpResponseMessage(HttpStatusCode.OK)
-                {
-                    Content = new ByteArrayContent(image.Stream.ToArray()),
-                };
-            });
-
-            var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
-            var param = new Dictionary<string, string>
-            {
-                ["aaaa"] = "1111",
-                ["bbbb"] = "2222",
-            };
-
-            var stream = await apiConnection.GetStreamAsync(endpoint, param);
-
-            using (var memoryStream = new MemoryStream())
-            {
-                // 内容の比較のために MemoryStream にコピー
-                await stream.CopyToAsync(memoryStream);
-
-                Assert.Equal(image.Stream.ToArray(), memoryStream.ToArray());
-            }
-
-            Assert.Equal(0, mockHandler.QueueCount);
-        }
-
         [Fact]
         public async Task PostLazyAsync_Test()
         {
diff --git a/OpenTween/Connection/IApiConnectionLegacy.cs b/OpenTween/Connection/IApiConnectionLegacy.cs
index 7f364f6ac..9b9e7d8c0 100644
--- a/OpenTween/Connection/IApiConnectionLegacy.cs
+++ b/OpenTween/Connection/IApiConnectionLegacy.cs
@@ -34,10 +34,6 @@ public interface IApiConnectionLegacy : IApiConnection, IDisposable
     {
         Task<T> GetAsync<T>(Uri uri, IDictionary<string, string>? param, string? endpointName);
 
-        Task<Stream> GetStreamAsync(Uri uri, IDictionary<string, string>? param);
-
-        Task<Stream> GetStreamAsync(Uri uri, IDictionary<string, string>? param, string? endpointName);
-
         Task<Stream> GetStreamingStreamAsync(Uri uri, IDictionary<string, string>? param);
 
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string>? param);
diff --git a/OpenTween/Connection/TwitterApiConnection.cs b/OpenTween/Connection/TwitterApiConnection.cs
index 7c7e578f6..bf81c2363 100644
--- a/OpenTween/Connection/TwitterApiConnection.cs
+++ b/OpenTween/Connection/TwitterApiConnection.cs
@@ -164,44 +164,6 @@ private void ThrowIfRateLimitExceeded(string endpointName)
             }
         }
 
-        public Task<Stream> GetStreamAsync(Uri uri, IDictionary<string, string>? param)
-            => this.GetStreamAsync(uri, param, null);
-
-        public async Task<Stream> GetStreamAsync(Uri uri, IDictionary<string, string>? param, string? endpointName)
-        {
-            // レートリミット規制中はAPIリクエストを送信せずに直ちにエラーを発生させる
-            if (endpointName != null)
-                this.ThrowIfRateLimitExceeded(endpointName);
-
-            var requestUri = new Uri(RestApiBase, uri);
-
-            if (param != null)
-                requestUri = new Uri(requestUri, "?" + MyCommon.BuildQueryString(param));
-
-            try
-            {
-                var response = await this.Http.GetAsync(requestUri)
-                    .ConfigureAwait(false);
-
-                if (endpointName != null)
-                    MyCommon.TwitterApiInfo.UpdateFromHeader(response.Headers, endpointName);
-
-                await TwitterApiConnection.CheckStatusCode(response)
-                    .ConfigureAwait(false);
-
-                return await response.Content.ReadAsStreamAsync()
-                    .ConfigureAwait(false);
-            }
-            catch (HttpRequestException ex)
-            {
-                throw TwitterApiException.CreateFromException(ex);
-            }
-            catch (OperationCanceledException ex)
-            {
-                throw TwitterApiException.CreateFromException(ex);
-            }
-        }
-
         public async Task<Stream> GetStreamingStreamAsync(Uri uri, IDictionary<string, string>? param)
         {
             var requestUri = new Uri(RestApiBase, uri);
diff --git a/OpenTween/ErrorReportHandler.cs b/OpenTween/ErrorReportHandler.cs
index af9d572f6..166b1727b 100644
--- a/OpenTween/ErrorReportHandler.cs
+++ b/OpenTween/ErrorReportHandler.cs
@@ -106,18 +106,6 @@ public static bool IsExceptionIgnorable(Exception ex)
                     return true;
             }
 
-            if (ex is TaskCanceledException cancelEx)
-            {
-                // ton.twitter.com の画像でタイムアウトした場合、try-catch で例外がキャッチできない
-                // https://osdn.net/ticket/browse.php?group_id=6526&tid=37433
-                var stackTrace = new StackTrace(cancelEx);
-                var lastFrameMethod = stackTrace.GetFrame(stackTrace.FrameCount - 1).GetMethod();
-                var matchClass = lastFrameMethod.ReflectedType == typeof(TwitterApiConnection);
-                var matchMethod = lastFrameMethod.Name == nameof(TwitterApiConnection.GetStreamAsync);
-                if (matchClass && matchMethod)
-                    return true;
-            }
-
             return false;
         }
     }

From a5614fe46cf642c88084098da4e5364c7511db7a Mon Sep 17 00:00:00 2001
From: Kimura Youichi <kim.upsilon@bucyou.net>
Date: Tue, 12 Dec 2023 01:39:40 +0900
Subject: [PATCH 7/7] =?UTF-8?q?TwitterApiConnection.GetStreamingStreamAsyn?=
 =?UTF-8?q?c=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E5=89=8A?=
 =?UTF-8?q?=E9=99=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 OpenTween/Connection/IApiConnectionLegacy.cs |  2 -
 OpenTween/Connection/TwitterApiConnection.cs | 48 ++------------------
 2 files changed, 5 insertions(+), 45 deletions(-)

diff --git a/OpenTween/Connection/IApiConnectionLegacy.cs b/OpenTween/Connection/IApiConnectionLegacy.cs
index 9b9e7d8c0..839ad462e 100644
--- a/OpenTween/Connection/IApiConnectionLegacy.cs
+++ b/OpenTween/Connection/IApiConnectionLegacy.cs
@@ -34,8 +34,6 @@ public interface IApiConnectionLegacy : IApiConnection, IDisposable
     {
         Task<T> GetAsync<T>(Uri uri, IDictionary<string, string>? param, string? endpointName);
 
-        Task<Stream> GetStreamingStreamAsync(Uri uri, IDictionary<string, string>? param);
-
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string>? param);
 
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string>? param, IDictionary<string, IMediaItem>? media);
diff --git a/OpenTween/Connection/TwitterApiConnection.cs b/OpenTween/Connection/TwitterApiConnection.cs
index bf81c2363..0e860607c 100644
--- a/OpenTween/Connection/TwitterApiConnection.cs
+++ b/OpenTween/Connection/TwitterApiConnection.cs
@@ -54,7 +54,6 @@ public static string RestApiHost
 
         internal HttpClient Http;
         internal HttpClient HttpUpload;
-        internal HttpClient HttpStreaming;
 
         internal ITwitterCredential Credential { get; }
 
@@ -71,16 +70,13 @@ public TwitterApiConnection(ITwitterCredential credential)
             Networking.WebProxyChanged += this.Networking_WebProxyChanged;
         }
 
-        [MemberNotNull(nameof(Http), nameof(HttpUpload), nameof(HttpStreaming))]
+        [MemberNotNull(nameof(Http), nameof(HttpUpload))]
         private void InitializeHttpClients()
         {
             this.Http = InitializeHttpClient(this.Credential);
 
             this.HttpUpload = InitializeHttpClient(this.Credential);
             this.HttpUpload.Timeout = Networking.UploadImageTimeout;
-
-            this.HttpStreaming = InitializeHttpClient(this.Credential, disableGzip: true);
-            this.HttpStreaming.Timeout = Timeout.InfiniteTimeSpan;
         }
 
         public async Task<ApiResponse> SendAsync(IHttpRequest request)
@@ -164,35 +160,6 @@ private void ThrowIfRateLimitExceeded(string endpointName)
             }
         }
 
-        public async Task<Stream> GetStreamingStreamAsync(Uri uri, IDictionary<string, string>? param)
-        {
-            var requestUri = new Uri(RestApiBase, uri);
-
-            if (param != null)
-                requestUri = new Uri(requestUri, "?" + MyCommon.BuildQueryString(param));
-
-            try
-            {
-                var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
-                var response = await this.HttpStreaming.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
-                    .ConfigureAwait(false);
-
-                await TwitterApiConnection.CheckStatusCode(response)
-                    .ConfigureAwait(false);
-
-                return await response.Content.ReadAsStreamAsync()
-                    .ConfigureAwait(false);
-            }
-            catch (HttpRequestException ex)
-            {
-                throw TwitterApiException.CreateFromException(ex);
-            }
-            catch (OperationCanceledException ex)
-            {
-                throw TwitterApiException.CreateFromException(ex);
-            }
-        }
-
         public async Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string>? param)
         {
             var requestUri = new Uri(RestApiBase, uri);
@@ -438,7 +405,6 @@ protected virtual void Dispose(bool disposing)
                 Networking.WebProxyChanged -= this.Networking_WebProxyChanged;
                 this.Http.Dispose();
                 this.HttpUpload.Dispose();
-                this.HttpStreaming.Dispose();
             }
         }
 
@@ -524,17 +490,13 @@ await TwitterApiConnection.CheckStatusCode(response)
             }
         }
 
-        private static HttpClient InitializeHttpClient(ITwitterCredential credential, bool disableGzip = false)
+        private static HttpClient InitializeHttpClient(ITwitterCredential credential)
         {
             var builder = Networking.CreateHttpClientBuilder();
 
-            builder.SetupHttpClientHandler(x =>
-            {
-                x.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
-
-                if (disableGzip)
-                    x.AutomaticDecompression = DecompressionMethods.None;
-            });
+            builder.SetupHttpClientHandler(
+                x => x.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache)
+            );
 
             builder.AddHandler(x => credential.CreateHttpHandler(x));