Add feed option to disable HTTP/2 to avoid fingerprinting

This commit is contained in:
Frédéric Guillot 2024-02-24 22:08:23 -08:00
parent 420a3d4d95
commit eae4cb1417
36 changed files with 90 additions and 10 deletions

View file

@ -140,6 +140,7 @@ type Feed struct {
Password string `json:"password"` Password string `json:"password"`
Category *Category `json:"category,omitempty"` Category *Category `json:"category,omitempty"`
HideGlobally bool `json:"hide_globally"` HideGlobally bool `json:"hide_globally"`
DisableHTTP2 bool `json:"disable_http2"`
} }
// FeedCreationRequest represents the request to create a feed. // FeedCreationRequest represents the request to create a feed.
@ -160,6 +161,7 @@ type FeedCreationRequest struct {
BlocklistRules string `json:"blocklist_rules"` BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"` KeeplistRules string `json:"keeplist_rules"`
HideGlobally bool `json:"hide_globally"` HideGlobally bool `json:"hide_globally"`
DisableHTTP2 bool `json:"disable_http2"`
} }
// FeedModificationRequest represents the request to update a feed. // FeedModificationRequest represents the request to update a feed.
@ -182,6 +184,7 @@ type FeedModificationRequest struct {
AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"` AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"`
FetchViaProxy *bool `json:"fetch_via_proxy"` FetchViaProxy *bool `json:"fetch_via_proxy"`
HideGlobally *bool `json:"hide_globally"` HideGlobally *bool `json:"hide_globally"`
DisableHTTP2 *bool `json:"disable_http2"`
} }
// FeedIcon represents the feed icon. // FeedIcon represents the feed icon.

View file

@ -42,6 +42,7 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
requestBuilder.WithUsernameAndPassword(subscriptionDiscoveryRequest.Username, subscriptionDiscoveryRequest.Password) requestBuilder.WithUsernameAndPassword(subscriptionDiscoveryRequest.Username, subscriptionDiscoveryRequest.Password)
requestBuilder.UseProxy(subscriptionDiscoveryRequest.FetchViaProxy) requestBuilder.UseProxy(subscriptionDiscoveryRequest.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(subscriptionDiscoveryRequest.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(subscriptionDiscoveryRequest.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(subscriptionDiscoveryRequest.DisableHTTP2)
subscriptions, localizedError := subscription.NewSubscriptionFinder(requestBuilder).FindSubscriptions( subscriptions, localizedError := subscription.NewSubscriptionFinder(requestBuilder).FindSubscriptions(
subscriptionDiscoveryRequest.URL, subscriptionDiscoveryRequest.URL,

View file

@ -866,4 +866,9 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql) _, err = tx.Exec(sql)
return err return err
}, },
func(tx *sql.Tx) (err error) {
sql := `ALTER TABLE feeds ADD COLUMN disable_http2 bool default 'f'`
_, err = tx.Exec(sql)
return err
},
} }

View file

@ -327,6 +327,7 @@
"form.feed.label.urlrewrite_rules": "Umschreibregeln für URL", "form.feed.label.urlrewrite_rules": "Umschreibregeln für URL",
"form.feed.label.ignore_http_cache": "Ignoriere HTTP-Cache", "form.feed.label.ignore_http_cache": "Ignoriere HTTP-Cache",
"form.feed.label.allow_self_signed_certificates": "Erlaube selbstsignierte oder ungültige Zertifikate", "form.feed.label.allow_self_signed_certificates": "Erlaube selbstsignierte oder ungültige Zertifikate",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Über Proxy abrufen", "form.feed.label.fetch_via_proxy": "Über Proxy abrufen",
"form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren", "form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren",
"form.feed.label.no_media_player": "Kein Media-Player (Audio/Video)", "form.feed.label.no_media_player": "Kein Media-Player (Audio/Video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.keeplist_rules": "Κρατήστε Κανόνες", "form.feed.label.keeplist_rules": "Κρατήστε Κανόνες",
"form.feed.label.ignore_http_cache": "Αγνοήστε την προσωρινή μνήμη HTTP", "form.feed.label.ignore_http_cache": "Αγνοήστε την προσωρινή μνήμη HTTP",
"form.feed.label.allow_self_signed_certificates": "Να επιτρέπονται αυτο-υπογεγραμμένα ή μη έγκυρα πιστοποιητικά", "form.feed.label.allow_self_signed_certificates": "Να επιτρέπονται αυτο-υπογεγραμμένα ή μη έγκυρα πιστοποιητικά",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Λήψη μέσω διακομιστή μεσολάβησης", "form.feed.label.fetch_via_proxy": "Λήψη μέσω διακομιστή μεσολάβησης",
"form.feed.label.disabled": "Μη ανανέωση αυτής της ροής", "form.feed.label.disabled": "Μη ανανέωση αυτής της ροής",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.urlrewrite_rules": "URL Rewrite Rules", "form.feed.label.urlrewrite_rules": "URL Rewrite Rules",
"form.feed.label.ignore_http_cache": "Ignore HTTP cache", "form.feed.label.ignore_http_cache": "Ignore HTTP cache",
"form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates", "form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Fetch via proxy", "form.feed.label.fetch_via_proxy": "Fetch via proxy",
"form.feed.label.disabled": "Do not refresh this feed", "form.feed.label.disabled": "Do not refresh this feed",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.urlrewrite_rules": "Reglas de Filtrado (Reescritura)", "form.feed.label.urlrewrite_rules": "Reglas de Filtrado (Reescritura)",
"form.feed.label.ignore_http_cache": "Ignorar caché HTTP", "form.feed.label.ignore_http_cache": "Ignorar caché HTTP",
"form.feed.label.allow_self_signed_certificates": "Permitir certificados autofirmados o no válidos", "form.feed.label.allow_self_signed_certificates": "Permitir certificados autofirmados o no válidos",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Buscar a través de proxy", "form.feed.label.fetch_via_proxy": "Buscar a través de proxy",
"form.feed.label.disabled": "No actualice este feed", "form.feed.label.disabled": "No actualice este feed",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.keeplist_rules": "Keep-säännöt", "form.feed.label.keeplist_rules": "Keep-säännöt",
"form.feed.label.ignore_http_cache": "Ohita HTTP-välimuisti", "form.feed.label.ignore_http_cache": "Ohita HTTP-välimuisti",
"form.feed.label.allow_self_signed_certificates": "Salli itseallekirjoitetut tai virheelliset varmenteet", "form.feed.label.allow_self_signed_certificates": "Salli itseallekirjoitetut tai virheelliset varmenteet",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Nouda välityspalvelimen kautta", "form.feed.label.fetch_via_proxy": "Nouda välityspalvelimen kautta",
"form.feed.label.disabled": "Älä päivitä tätä syötettä", "form.feed.label.disabled": "Älä päivitä tätä syötettä",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.urlrewrite_rules": "Règles de réécriture d'URL", "form.feed.label.urlrewrite_rules": "Règles de réécriture d'URL",
"form.feed.label.ignore_http_cache": "Ignorer le cache HTTP", "form.feed.label.ignore_http_cache": "Ignorer le cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Autoriser les certificats auto-signés ou non valides", "form.feed.label.allow_self_signed_certificates": "Autoriser les certificats auto-signés ou non valides",
"form.feed.label.disable_http2": "Désactiver HTTP/2",
"form.feed.label.fetch_via_proxy": "Récupérer via proxy", "form.feed.label.fetch_via_proxy": "Récupérer via proxy",
"form.feed.label.disabled": "Ne pas actualiser ce flux", "form.feed.label.disabled": "Ne pas actualiser ce flux",
"form.feed.label.no_media_player": "Pas de lecteur multimedia (audio/vidéo)", "form.feed.label.no_media_player": "Pas de lecteur multimedia (audio/vidéo)",

View file

@ -327,6 +327,7 @@
"form.feed.label.urlrewrite_rules": " यूआरएल पुनर्लेखन नियम", "form.feed.label.urlrewrite_rules": " यूआरएल पुनर्लेखन नियम",
"form.feed.label.ignore_http_cache": "एचटीटीपी कैश पर ध्यान न दें", "form.feed.label.ignore_http_cache": "एचटीटीपी कैश पर ध्यान न दें",
"form.feed.label.allow_self_signed_certificates": "स्व-हस्ताक्षरित या अमान्य प्रमाणपत्रों की अनुमति दें", "form.feed.label.allow_self_signed_certificates": "स्व-हस्ताक्षरित या अमान्य प्रमाणपत्रों की अनुमति दें",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "प्रॉक्सी के माध्यम से प्राप्त करें", "form.feed.label.fetch_via_proxy": "प्रॉक्सी के माध्यम से प्राप्त करें",
"form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें", "form.feed.label.disabled": "इस फ़ीड को रीफ़्रेश न करें",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -324,6 +324,7 @@
"form.feed.label.urlrewrite_rules": "Aturan Tulis Ulang URL", "form.feed.label.urlrewrite_rules": "Aturan Tulis Ulang URL",
"form.feed.label.ignore_http_cache": "Abaikan Tembolok HTTP", "form.feed.label.ignore_http_cache": "Abaikan Tembolok HTTP",
"form.feed.label.allow_self_signed_certificates": "Perbolehkan sertifikat web tidak valid atau sertifikasi sendiri", "form.feed.label.allow_self_signed_certificates": "Perbolehkan sertifikat web tidak valid atau sertifikasi sendiri",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Ambil via Proksi", "form.feed.label.fetch_via_proxy": "Ambil via Proksi",
"form.feed.label.disabled": "Jangan perbarui umpan ini", "form.feed.label.disabled": "Jangan perbarui umpan ini",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.urlrewrite_rules": "Regole di riscrittura URL", "form.feed.label.urlrewrite_rules": "Regole di riscrittura URL",
"form.feed.label.ignore_http_cache": "Ignora cache HTTP", "form.feed.label.ignore_http_cache": "Ignora cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Consenti certificati autofirmati o non validi", "form.feed.label.allow_self_signed_certificates": "Consenti certificati autofirmati o non validi",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Recuperare tramite proxy", "form.feed.label.fetch_via_proxy": "Recuperare tramite proxy",
"form.feed.label.disabled": "Non aggiornare questo feed", "form.feed.label.disabled": "Non aggiornare questo feed",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "HTTPキャッシュを無視", "form.feed.label.ignore_http_cache": "HTTPキャッシュを無視",
"form.feed.label.allow_self_signed_certificates": "自己署名証明書または無効な証明書を許可する", "form.feed.label.allow_self_signed_certificates": "自己署名証明書または無効な証明書を許可する",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "プロキシ経由で取得", "form.feed.label.fetch_via_proxy": "プロキシ経由で取得",
"form.feed.label.disabled": "このフィードを更新しない", "form.feed.label.disabled": "このフィードを更新しない",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "Negeer HTTP-cache", "form.feed.label.ignore_http_cache": "Negeer HTTP-cache",
"form.feed.label.allow_self_signed_certificates": "Sta zelfondertekende of ongeldige certificaten toe", "form.feed.label.allow_self_signed_certificates": "Sta zelfondertekende of ongeldige certificaten toe",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Ophalen via proxy", "form.feed.label.fetch_via_proxy": "Ophalen via proxy",
"form.feed.label.disabled": "Vernieuw deze feed niet", "form.feed.label.disabled": "Vernieuw deze feed niet",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -330,6 +330,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "Zignoruj pamięć podręczną HTTP", "form.feed.label.ignore_http_cache": "Zignoruj pamięć podręczną HTTP",
"form.feed.label.allow_self_signed_certificates": "Zezwalaj na certyfikaty z podpisem własnym lub nieprawidłowe certyfikaty", "form.feed.label.allow_self_signed_certificates": "Zezwalaj na certyfikaty z podpisem własnym lub nieprawidłowe certyfikaty",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Pobierz przez proxy", "form.feed.label.fetch_via_proxy": "Pobierz przez proxy",
"form.feed.label.disabled": "Nie odświeżaj tego kanału", "form.feed.label.disabled": "Nie odświeżaj tego kanału",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -327,6 +327,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "Ignorar cache HTTP", "form.feed.label.ignore_http_cache": "Ignorar cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Permitir certificados autoassinados ou inválidos", "form.feed.label.allow_self_signed_certificates": "Permitir certificados autoassinados ou inválidos",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.disabled": "Não atualizar esta fonte", "form.feed.label.disabled": "Não atualizar esta fonte",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",
"form.feed.label.fetch_via_proxy": "Buscar via proxy", "form.feed.label.fetch_via_proxy": "Buscar via proxy",

View file

@ -330,6 +330,7 @@
"form.feed.label.apprise_service_urls": "Список ссылок сервисов Apprise, разделенный запятой", "form.feed.label.apprise_service_urls": "Список ссылок сервисов Apprise, разделенный запятой",
"form.feed.label.ignore_http_cache": "Игнорировать HTTP кеш", "form.feed.label.ignore_http_cache": "Игнорировать HTTP кеш",
"form.feed.label.allow_self_signed_certificates": "Разрешить самоподписанные или недействительные сертификаты", "form.feed.label.allow_self_signed_certificates": "Разрешить самоподписанные или недействительные сертификаты",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Использовать прокси", "form.feed.label.fetch_via_proxy": "Использовать прокси",
"form.feed.label.disabled": "Не обновлять эту подписку", "form.feed.label.disabled": "Не обновлять эту подписку",
"form.feed.label.no_media_player": "Отключить медиаплеер (аудио и видео)", "form.feed.label.no_media_player": "Отключить медиаплеер (аудио и видео)",

View file

@ -327,6 +327,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "HTTP önbelleğini yoksay", "form.feed.label.ignore_http_cache": "HTTP önbelleğini yoksay",
"form.feed.label.allow_self_signed_certificates": "Kendinden imzalı veya geçersiz sertifikalara izin ver", "form.feed.label.allow_self_signed_certificates": "Kendinden imzalı veya geçersiz sertifikalara izin ver",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Proxy ile çek", "form.feed.label.fetch_via_proxy": "Proxy ile çek",
"form.feed.label.disabled": "Bu beslemeyi yenileme", "form.feed.label.disabled": "Bu beslemeyi yenileme",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -331,6 +331,7 @@
"form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs", "form.feed.label.apprise_service_urls": "Comma separated list of Apprise service URLs",
"form.feed.label.ignore_http_cache": "Ігнорувати кеш HTTP", "form.feed.label.ignore_http_cache": "Ігнорувати кеш HTTP",
"form.feed.label.allow_self_signed_certificates": "Дозволити сертифікати з власним підписом або недійсні", "form.feed.label.allow_self_signed_certificates": "Дозволити сертифікати з власним підписом або недійсні",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "Використати проксі-сервер", "form.feed.label.fetch_via_proxy": "Використати проксі-сервер",
"form.feed.label.disabled": "Не оновлювати цю стрічку", "form.feed.label.disabled": "Не оновлювати цю стрічку",
"form.feed.label.no_media_player": "No media player (audio/video)", "form.feed.label.no_media_player": "No media player (audio/video)",

View file

@ -325,6 +325,7 @@
"form.feed.label.apprise_service_urls": "使用逗号分隔的 Apprise 服务 URL 列表", "form.feed.label.apprise_service_urls": "使用逗号分隔的 Apprise 服务 URL 列表",
"form.feed.label.ignore_http_cache": "忽略 HTTP 缓存", "form.feed.label.ignore_http_cache": "忽略 HTTP 缓存",
"form.feed.label.allow_self_signed_certificates": "允许自签名证书或无效证书", "form.feed.label.allow_self_signed_certificates": "允许自签名证书或无效证书",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "通过代理获取", "form.feed.label.fetch_via_proxy": "通过代理获取",
"form.feed.label.disabled": "请勿刷新此源", "form.feed.label.disabled": "请勿刷新此源",
"form.feed.label.no_media_player": "没有媒体播放器(音频/视频)", "form.feed.label.no_media_player": "没有媒体播放器(音频/视频)",

View file

@ -327,6 +327,7 @@
"form.feed.label.apprise_service_urls": "使用逗號分隔的 Apprise 服務 URL 列表", "form.feed.label.apprise_service_urls": "使用逗號分隔的 Apprise 服務 URL 列表",
"form.feed.label.ignore_http_cache": "忽略 HTTP 快取", "form.feed.label.ignore_http_cache": "忽略 HTTP 快取",
"form.feed.label.allow_self_signed_certificates": "允許自簽章憑證或無效憑證", "form.feed.label.allow_self_signed_certificates": "允許自簽章憑證或無效憑證",
"form.feed.label.disable_http2": "Disable HTTP/2 to avoid fingerprinting",
"form.feed.label.fetch_via_proxy": "透過代理獲取", "form.feed.label.fetch_via_proxy": "透過代理獲取",
"form.feed.label.disabled": "請勿更新此 Feed", "form.feed.label.disabled": "請勿更新此 Feed",
"form.feed.label.no_media_player": "沒有媒體播放器(音訊/視訊)", "form.feed.label.no_media_player": "沒有媒體播放器(音訊/視訊)",

View file

@ -51,6 +51,7 @@ type Feed struct {
FetchViaProxy bool `json:"fetch_via_proxy"` FetchViaProxy bool `json:"fetch_via_proxy"`
HideGlobally bool `json:"hide_globally"` HideGlobally bool `json:"hide_globally"`
AppriseServiceURLs string `json:"apprise_service_urls"` AppriseServiceURLs string `json:"apprise_service_urls"`
DisableHTTP2 bool `json:"disable_http2"`
// Non persisted attributes // Non persisted attributes
Category *Category `json:"category,omitempty"` Category *Category `json:"category,omitempty"`
@ -150,6 +151,7 @@ type FeedCreationRequest struct {
KeeplistRules string `json:"keeplist_rules"` KeeplistRules string `json:"keeplist_rules"`
HideGlobally bool `json:"hide_globally"` HideGlobally bool `json:"hide_globally"`
UrlRewriteRules string `json:"urlrewrite_rules"` UrlRewriteRules string `json:"urlrewrite_rules"`
DisableHTTP2 bool `json:"disable_http2"`
} }
type FeedCreationRequestFromSubscriptionDiscovery struct { type FeedCreationRequestFromSubscriptionDiscovery struct {
@ -175,6 +177,7 @@ type FeedCreationRequestFromSubscriptionDiscovery struct {
KeeplistRules string `json:"keeplist_rules"` KeeplistRules string `json:"keeplist_rules"`
HideGlobally bool `json:"hide_globally"` HideGlobally bool `json:"hide_globally"`
UrlRewriteRules string `json:"urlrewrite_rules"` UrlRewriteRules string `json:"urlrewrite_rules"`
DisableHTTP2 bool `json:"disable_http2"`
} }
// FeedModificationRequest represents the request to update a feed. // FeedModificationRequest represents the request to update a feed.
@ -199,6 +202,7 @@ type FeedModificationRequest struct {
AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"` AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"`
FetchViaProxy *bool `json:"fetch_via_proxy"` FetchViaProxy *bool `json:"fetch_via_proxy"`
HideGlobally *bool `json:"hide_globally"` HideGlobally *bool `json:"hide_globally"`
DisableHTTP2 *bool `json:"disable_http2"`
} }
// Patch updates a feed with modified values. // Patch updates a feed with modified values.
@ -282,6 +286,10 @@ func (f *FeedModificationRequest) Patch(feed *Feed) {
if f.HideGlobally != nil { if f.HideGlobally != nil {
feed.HideGlobally = *f.HideGlobally feed.HideGlobally = *f.HideGlobally
} }
if f.DisableHTTP2 != nil {
feed.DisableHTTP2 = *f.DisableHTTP2
}
} }
// Feeds is a list of feed // Feeds is a list of feed

View file

@ -12,4 +12,5 @@ type SubscriptionDiscoveryRequest struct {
Password string `json:"password"` Password string `json:"password"`
FetchViaProxy bool `json:"fetch_via_proxy"` FetchViaProxy bool `json:"fetch_via_proxy"`
AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"` AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"`
DisableHTTP2 bool `json:"disable_http2"`
} }

View file

@ -26,6 +26,7 @@ type RequestBuilder struct {
clientTimeout int clientTimeout int
withoutRedirects bool withoutRedirects bool
ignoreTLSErrors bool ignoreTLSErrors bool
disableHTTP2 bool
} }
func NewRequestBuilder() *RequestBuilder { func NewRequestBuilder() *RequestBuilder {
@ -97,6 +98,11 @@ func (r *RequestBuilder) WithoutRedirects() *RequestBuilder {
return r return r
} }
func (r *RequestBuilder) DisableHTTP2(value bool) *RequestBuilder {
r.disableHTTP2 = value
return r
}
func (r *RequestBuilder) IgnoreTLSErrors(value bool) *RequestBuilder { func (r *RequestBuilder) IgnoreTLSErrors(value bool) *RequestBuilder {
r.ignoreTLSErrors = value r.ignoreTLSErrors = value
return r return r
@ -126,6 +132,14 @@ func (r *RequestBuilder) ExecuteRequest(requestURL string) (*http.Response, erro
}, },
} }
if r.disableHTTP2 {
transport.ForceAttemptHTTP2 = false
// https://pkg.go.dev/net/http#hdr-HTTP_2
// Programs that must disable HTTP/2 can do so by setting [Transport.TLSNextProto] (for clients) or [Server.TLSNextProto] (for servers) to a non-nil, empty map.
transport.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
}
if r.useClientProxy && r.clientProxyURL != "" { if r.useClientProxy && r.clientProxyURL != "" {
if proxyURL, err := url.Parse(r.clientProxyURL); err != nil { if proxyURL, err := url.Parse(r.clientProxyURL); err != nil {
slog.Warn("Unable to parse proxy URL", slog.Warn("Unable to parse proxy URL",
@ -165,6 +179,8 @@ func (r *RequestBuilder) ExecuteRequest(requestURL string) (*http.Response, erro
slog.Bool("without_redirects", r.withoutRedirects), slog.Bool("without_redirects", r.withoutRedirects),
slog.Bool("with_proxy", r.useClientProxy), slog.Bool("with_proxy", r.useClientProxy),
slog.String("proxy_url", r.clientProxyURL), slog.String("proxy_url", r.clientProxyURL),
slog.Bool("ignore_tls_errors", r.ignoreTLSErrors),
slog.Bool("disable_http2", r.disableHTTP2),
)) ))
return client.Do(req) return client.Do(req)

View file

@ -67,6 +67,7 @@ func CreateFeedFromSubscriptionDiscovery(store *storage.Storage, userID int64, f
subscription.EtagHeader = feedCreationRequest.ETag subscription.EtagHeader = feedCreationRequest.ETag
subscription.LastModifiedHeader = feedCreationRequest.LastModified subscription.LastModifiedHeader = feedCreationRequest.LastModified
subscription.FeedURL = feedCreationRequest.FeedURL subscription.FeedURL = feedCreationRequest.FeedURL
subscription.DisableHTTP2 = feedCreationRequest.DisableHTTP2
subscription.WithCategoryID(feedCreationRequest.CategoryID) subscription.WithCategoryID(feedCreationRequest.CategoryID)
subscription.CheckedNow() subscription.CheckedNow()
@ -90,6 +91,7 @@ func CreateFeedFromSubscriptionDiscovery(store *storage.Storage, userID int64, f
requestBuilder.WithProxy(config.Opts.HTTPClientProxy()) requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
requestBuilder.UseProxy(feedCreationRequest.FetchViaProxy) requestBuilder.UseProxy(feedCreationRequest.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(feedCreationRequest.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(feedCreationRequest.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(feedCreationRequest.DisableHTTP2)
checkFeedIcon( checkFeedIcon(
store, store,
@ -126,6 +128,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
requestBuilder.WithProxy(config.Opts.HTTPClientProxy()) requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
requestBuilder.UseProxy(feedCreationRequest.FetchViaProxy) requestBuilder.UseProxy(feedCreationRequest.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(feedCreationRequest.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(feedCreationRequest.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(feedCreationRequest.DisableHTTP2)
responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(feedCreationRequest.FeedURL)) responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(feedCreationRequest.FeedURL))
defer responseHandler.Close() defer responseHandler.Close()
@ -159,6 +162,7 @@ func CreateFeed(store *storage.Storage, userID int64, feedCreationRequest *model
subscription.Disabled = feedCreationRequest.Disabled subscription.Disabled = feedCreationRequest.Disabled
subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache subscription.IgnoreHTTPCache = feedCreationRequest.IgnoreHTTPCache
subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates subscription.AllowSelfSignedCertificates = feedCreationRequest.AllowSelfSignedCertificates
subscription.DisableHTTP2 = feedCreationRequest.DisableHTTP2
subscription.FetchViaProxy = feedCreationRequest.FetchViaProxy subscription.FetchViaProxy = feedCreationRequest.FetchViaProxy
subscription.ScraperRules = feedCreationRequest.ScraperRules subscription.ScraperRules = feedCreationRequest.ScraperRules
subscription.RewriteRules = feedCreationRequest.RewriteRules subscription.RewriteRules = feedCreationRequest.RewriteRules
@ -238,6 +242,7 @@ func RefreshFeed(store *storage.Storage, userID, feedID int64, forceRefresh bool
requestBuilder.WithProxy(config.Opts.HTTPClientProxy()) requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
requestBuilder.UseProxy(originalFeed.FetchViaProxy) requestBuilder.UseProxy(originalFeed.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(originalFeed.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(originalFeed.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(originalFeed.DisableHTTP2)
responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(originalFeed.FeedURL)) responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(originalFeed.FeedURL))
defer responseHandler.Close() defer responseHandler.Close()

View file

@ -72,6 +72,7 @@ func ProcessFeedEntries(store *storage.Storage, feed *model.Feed, user *model.Us
requestBuilder.WithProxy(config.Opts.HTTPClientProxy()) requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
requestBuilder.UseProxy(feed.FetchViaProxy) requestBuilder.UseProxy(feed.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(feed.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(feed.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(feed.DisableHTTP2)
content, scraperErr := scraper.ScrapeWebsite( content, scraperErr := scraper.ScrapeWebsite(
requestBuilder, requestBuilder,
@ -181,6 +182,7 @@ func ProcessEntryWebPage(feed *model.Feed, entry *model.Entry, user *model.User)
requestBuilder.WithProxy(config.Opts.HTTPClientProxy()) requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
requestBuilder.UseProxy(feed.FetchViaProxy) requestBuilder.UseProxy(feed.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(feed.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(feed.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(feed.DisableHTTP2)
content, scraperErr := scraper.ScrapeWebsite( content, scraperErr := scraper.ScrapeWebsite(
requestBuilder, requestBuilder,

View file

@ -174,8 +174,8 @@ func (s *Storage) WeeklyFeedEntryCount(userID, feedID int64) (int, error) {
FROM FROM
entries entries
WHERE WHERE
entries.user_id=$1 AND entries.user_id=$1 AND
entries.feed_id=$2 AND entries.feed_id=$2 AND
entries.published_at BETWEEN (now() - interval '1 week') AND now(); entries.published_at BETWEEN (now() - interval '1 week') AND now();
` `
@ -235,10 +235,11 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
hide_globally, hide_globally,
url_rewrite_rules, url_rewrite_rules,
no_media_player, no_media_player,
apprise_service_urls apprise_service_urls,
disable_http2
) )
VALUES VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24) ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)
RETURNING RETURNING
id id
` `
@ -268,6 +269,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
feed.UrlRewriteRules, feed.UrlRewriteRules,
feed.NoMediaPlayer, feed.NoMediaPlayer,
feed.AppriseServiceURLs, feed.AppriseServiceURLs,
feed.DisableHTTP2,
).Scan(&feed.ID) ).Scan(&feed.ID)
if err != nil { if err != nil {
return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err) return fmt.Errorf(`store: unable to create feed %q: %v`, feed.FeedURL, err)
@ -339,9 +341,10 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
hide_globally=$24, hide_globally=$24,
url_rewrite_rules=$25, url_rewrite_rules=$25,
no_media_player=$26, no_media_player=$26,
apprise_service_urls=$27 apprise_service_urls=$27,
disable_http2=$28
WHERE WHERE
id=$28 AND user_id=$29 id=$29 AND user_id=$30
` `
_, err = s.db.Exec(query, _, err = s.db.Exec(query,
feed.FeedURL, feed.FeedURL,
@ -371,6 +374,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
feed.UrlRewriteRules, feed.UrlRewriteRules,
feed.NoMediaPlayer, feed.NoMediaPlayer,
feed.AppriseServiceURLs, feed.AppriseServiceURLs,
feed.DisableHTTP2,
feed.ID, feed.ID,
feed.UserID, feed.UserID,
) )

View file

@ -163,7 +163,8 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
c.hide_globally as category_hidden, c.hide_globally as category_hidden,
fi.icon_id, fi.icon_id,
u.timezone, u.timezone,
f.apprise_service_urls f.apprise_service_urls,
f.disable_http2
FROM FROM
feeds f feeds f
LEFT JOIN LEFT JOIN
@ -172,7 +173,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
feed_icons fi ON fi.feed_id=f.id feed_icons fi ON fi.feed_id=f.id
LEFT JOIN LEFT JOIN
users u ON u.id=f.user_id users u ON u.id=f.user_id
WHERE %s WHERE %s
%s %s
` `
@ -230,6 +231,7 @@ func (f *FeedQueryBuilder) GetFeeds() (model.Feeds, error) {
&iconID, &iconID,
&tz, &tz,
&feed.AppriseServiceURLs, &feed.AppriseServiceURLs,
&feed.DisableHTTP2,
) )
if err != nil { if err != nil {
@ -274,9 +276,9 @@ func (f *FeedQueryBuilder) fetchFeedCounter() (unreadCounters map[int64]int, rea
count(*) count(*)
FROM FROM
entries e entries e
%s %s
WHERE WHERE
%s %s
GROUP BY GROUP BY
e.feed_id, e.status e.feed_id, e.status
` `

View file

@ -33,6 +33,7 @@
<div class="details-content"> <div class="details-content">
<label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label> <label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
<label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label> <label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
<label><input type="checkbox" name="disable_http2" value="1" {{ if .form.DisableHTTP2 }}checked{{ end }}> {{ t "form.feed.label.disable_http2" }}</label>
{{ if .hasProxyConfigured }} {{ if .hasProxyConfigured }}
<label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label> <label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label>

View file

@ -29,6 +29,9 @@
{{ if .form.AllowSelfSignedCertificates }} {{ if .form.AllowSelfSignedCertificates }}
<input type="hidden" name="allow_self_signed_certificates" value="1"> <input type="hidden" name="allow_self_signed_certificates" value="1">
{{ end }} {{ end }}
{{ if .form.DisableHTTP2 }}
<input type="hidden" name="disable_http2" value="1">
{{ end }}
<h3>{{ t "page.add_feed.choose_feed" }}</h3> <h3>{{ t "page.add_feed.choose_feed" }}</h3>

View file

@ -100,6 +100,7 @@
<label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label> <label><input type="checkbox" name="crawler" value="1" {{ if .form.Crawler }}checked{{ end }}> {{ t "form.feed.label.crawler" }}</label>
<label><input type="checkbox" name="ignore_http_cache" value="1" {{ if .form.IgnoreHTTPCache }}checked{{ end }}> {{ t "form.feed.label.ignore_http_cache" }}</label> <label><input type="checkbox" name="ignore_http_cache" value="1" {{ if .form.IgnoreHTTPCache }}checked{{ end }}> {{ t "form.feed.label.ignore_http_cache" }}</label>
<label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label> <label><input type="checkbox" name="allow_self_signed_certificates" value="1" {{ if .form.AllowSelfSignedCertificates }}checked{{ end }}> {{ t "form.feed.label.allow_self_signed_certificates" }}</label>
<label><input type="checkbox" name="disable_http2" value="1" {{ if .form.DisableHTTP2 }}checked{{ end }}> {{ t "form.feed.label.disable_http2" }}</label>
{{ if .hasProxyConfigured }} {{ if .hasProxyConfigured }}
<label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label> <label><input type="checkbox" name="fetch_via_proxy" value="1" {{ if .form.FetchViaProxy }}checked{{ end }}> {{ t "form.feed.label.fetch_via_proxy" }}</label>
{{ end }} {{ end }}

View file

@ -62,6 +62,7 @@ func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) {
HideGlobally: feed.HideGlobally, HideGlobally: feed.HideGlobally,
CategoryHidden: feed.Category.HideGlobally, CategoryHidden: feed.Category.HideGlobally,
AppriseServiceURLs: feed.AppriseServiceURLs, AppriseServiceURLs: feed.AppriseServiceURLs,
DisableHTTP2: feed.DisableHTTP2,
} }
sess := session.New(h.store, request.SessionID(r)) sess := session.New(h.store, request.SessionID(r))

View file

@ -34,6 +34,7 @@ type FeedForm struct {
HideGlobally bool HideGlobally bool
CategoryHidden bool // Category has "hide_globally" CategoryHidden bool // Category has "hide_globally"
AppriseServiceURLs string AppriseServiceURLs string
DisableHTTP2 bool
} }
// Merge updates the fields of the given feed. // Merge updates the fields of the given feed.
@ -61,6 +62,7 @@ func (f FeedForm) Merge(feed *model.Feed) *model.Feed {
feed.NoMediaPlayer = f.NoMediaPlayer feed.NoMediaPlayer = f.NoMediaPlayer
feed.HideGlobally = f.HideGlobally feed.HideGlobally = f.HideGlobally
feed.AppriseServiceURLs = f.AppriseServiceURLs feed.AppriseServiceURLs = f.AppriseServiceURLs
feed.DisableHTTP2 = f.DisableHTTP2
return feed return feed
} }
@ -92,5 +94,6 @@ func NewFeedForm(r *http.Request) *FeedForm {
NoMediaPlayer: r.FormValue("no_media_player") == "1", NoMediaPlayer: r.FormValue("no_media_player") == "1",
HideGlobally: r.FormValue("hide_globally") == "1", HideGlobally: r.FormValue("hide_globally") == "1",
AppriseServiceURLs: r.FormValue("apprise_service_urls"), AppriseServiceURLs: r.FormValue("apprise_service_urls"),
DisableHTTP2: r.FormValue("disable_http2") == "1",
} }
} }

View file

@ -27,6 +27,7 @@ type SubscriptionForm struct {
BlocklistRules string BlocklistRules string
KeeplistRules string KeeplistRules string
UrlRewriteRules string UrlRewriteRules string
DisableHTTP2 bool
} }
// Validate makes sure the form values locale.are valid. // Validate makes sure the form values locale.are valid.
@ -76,5 +77,6 @@ func NewSubscriptionForm(r *http.Request) *SubscriptionForm {
BlocklistRules: r.FormValue("blocklist_rules"), BlocklistRules: r.FormValue("blocklist_rules"),
KeeplistRules: r.FormValue("keeplist_rules"), KeeplistRules: r.FormValue("keeplist_rules"),
UrlRewriteRules: r.FormValue("urlrewrite_rules"), UrlRewriteRules: r.FormValue("urlrewrite_rules"),
DisableHTTP2: r.FormValue("disable_http2") == "1",
} }
} }

View file

@ -63,6 +63,7 @@ func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Requ
KeeplistRules: subscriptionForm.KeeplistRules, KeeplistRules: subscriptionForm.KeeplistRules,
UrlRewriteRules: subscriptionForm.UrlRewriteRules, UrlRewriteRules: subscriptionForm.UrlRewriteRules,
FetchViaProxy: subscriptionForm.FetchViaProxy, FetchViaProxy: subscriptionForm.FetchViaProxy,
DisableHTTP2: subscriptionForm.DisableHTTP2,
}) })
if localizedError != nil { if localizedError != nil {
view.Set("form", subscriptionForm) view.Set("form", subscriptionForm)

View file

@ -65,6 +65,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
requestBuilder.WithUsernameAndPassword(subscriptionForm.Username, subscriptionForm.Password) requestBuilder.WithUsernameAndPassword(subscriptionForm.Username, subscriptionForm.Password)
requestBuilder.UseProxy(subscriptionForm.FetchViaProxy) requestBuilder.UseProxy(subscriptionForm.FetchViaProxy)
requestBuilder.IgnoreTLSErrors(subscriptionForm.AllowSelfSignedCertificates) requestBuilder.IgnoreTLSErrors(subscriptionForm.AllowSelfSignedCertificates)
requestBuilder.DisableHTTP2(subscriptionForm.DisableHTTP2)
subscriptionFinder := subscription.NewSubscriptionFinder(requestBuilder) subscriptionFinder := subscription.NewSubscriptionFinder(requestBuilder)
subscriptions, localizedError := subscriptionFinder.FindSubscriptions( subscriptions, localizedError := subscriptionFinder.FindSubscriptions(
@ -103,6 +104,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
KeeplistRules: subscriptionForm.KeeplistRules, KeeplistRules: subscriptionForm.KeeplistRules,
UrlRewriteRules: subscriptionForm.UrlRewriteRules, UrlRewriteRules: subscriptionForm.UrlRewriteRules,
FetchViaProxy: subscriptionForm.FetchViaProxy, FetchViaProxy: subscriptionForm.FetchViaProxy,
DisableHTTP2: subscriptionForm.DisableHTTP2,
}) })
if localizedError != nil { if localizedError != nil {
v.Set("form", subscriptionForm) v.Set("form", subscriptionForm)
@ -128,6 +130,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
KeeplistRules: subscriptionForm.KeeplistRules, KeeplistRules: subscriptionForm.KeeplistRules,
UrlRewriteRules: subscriptionForm.UrlRewriteRules, UrlRewriteRules: subscriptionForm.UrlRewriteRules,
FetchViaProxy: subscriptionForm.FetchViaProxy, FetchViaProxy: subscriptionForm.FetchViaProxy,
DisableHTTP2: subscriptionForm.DisableHTTP2,
}) })
if localizedError != nil { if localizedError != nil {
v.Set("form", subscriptionForm) v.Set("form", subscriptionForm)