Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using Invidious through a HTTP Proxy #4270

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@ https_only: false
##
#force_resolve:

##
## Configuration for using a HTTP proxy
##
## If unset, then no HTTP proxy will be used.
##
http_proxy:
user:
password:
host:
port:


##
## Use Innertube's transcripts API instead of timedtext for closed captions
Expand Down
10 changes: 7 additions & 3 deletions shard.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ shards:

athena-negotiation:
git: https://github.com/athena-framework/negotiation.git
version: 0.1.1
version: 0.1.3

backtracer:
git: https://github.com/sija/backtracer.cr.git
version: 1.2.1
version: 1.2.2

db:
git: https://github.com/crystal-lang/crystal-db.git
Expand All @@ -20,6 +20,10 @@ shards:
git: https://github.com/crystal-loot/exception_page.git
version: 0.2.2

http_proxy:
git: https://github.com/mamantoha/http_proxy.git
version: 0.10.1

kemal:
git: https://github.com/kemalcr/kemal.git
version: 1.1.2
Expand All @@ -42,7 +46,7 @@ shards:

spectator:
git: https://github.com/icy-arctic-fox/spectator.git
version: 0.10.4
version: 0.10.6

sqlite3:
git: https://github.com/crystal-lang/crystal-sqlite3.git
Expand Down
3 changes: 3 additions & 0 deletions shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ dependencies:
athena-negotiation:
github: athena-framework/negotiation
version: ~> 0.1.1
http_proxy:
github: mamantoha/http_proxy
version: ~> 0.10.1

development_dependencies:
spectator:
Expand Down
1 change: 1 addition & 0 deletions src/invidious.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require "./ext/kemal_content_for.cr"
require "./ext/kemal_static_file_handler.cr"

require "http_proxy"
require "athena-negotiation"
require "openssl/hmac"
require "option_parser"
Expand Down Expand Up @@ -90,7 +91,7 @@
"branch" => "#{CURRENT_BRANCH}",
}

YT_POOL = YoutubeConnectionPool.new(YT_URL, capacity: CONFIG.pool_size)

Check failure on line 94 in src/invidious.cr

View workflow job for this annotation

GitHub Actions / build - crystal: nightly, stable: false

instantiating 'YoutubeConnectionPool.new(URI, capacity: Int32)'

# CLI
Kemal.config.extra_options do |parser|
Expand Down Expand Up @@ -185,7 +186,7 @@

Invidious::Jobs.register Invidious::Jobs::ClearExpiredItemsJob.new

Invidious::Jobs.start_all

Check failure on line 189 in src/invidious.cr

View workflow job for this annotation

GitHub Actions / build - crystal: nightly, stable: false

instantiating 'Invidious::Jobs.start_all()'

def popular_videos
Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get
Expand Down
11 changes: 11 additions & 0 deletions src/invidious/config.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ struct ConfigPreferences
end
end

struct HTTPProxyConfig
include YAML::Serializable

property user : String
property password : String
property host : String
property port : Int32
end

class Config
include YAML::Serializable

Expand Down Expand Up @@ -126,6 +135,8 @@ class Config
property host_binding : String = "0.0.0.0"
# Pool size for HTTP requests to youtube.com and ytimg.com (each domain has a separate pool of `pool_size`)
property pool_size : Int32 = 100
# HTTP Proxy configuration
property http_proxy : HTTPProxyConfig? = nil

# Use Innertube's transcripts API instead of timedtext for closed captions
property use_innertube_for_captions : Bool = false
Expand Down
34 changes: 34 additions & 0 deletions src/invidious/helpers/crystal_class_overrides.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,40 @@ end
class HTTP::Client
property family : Socket::Family = Socket::Family::UNSPEC

# Override stdlib to automatically initialize proxy if configured
syeopite marked this conversation as resolved.
Show resolved Hide resolved
#
# Accurate as of crystal 1.10.1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it work with other crystal versions? (at least with the version matrix used in our CI pipeline)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep the initialize function wasn't touched


def initialize(@host : String, port = nil, tls : TLSContext = nil)
check_host_only(@host)

{% if flag?(:without_openssl) %}
if tls
raise "HTTP::Client TLS is disabled because `-D without_openssl` was passed at compile time"
end
@tls = nil
{% else %}
@tls = case tls
when true
OpenSSL::SSL::Context::Client.new
when OpenSSL::SSL::Context::Client
tls
when false, nil
nil
end
{% end %}

@port = (port || (@tls ? 443 : 80)).to_i

self.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
end

def initialize(@io : IO, @host = "", @port = 80)
@reconnect = false

self.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
end

private def io
io = @io
return io if io
Expand Down
19 changes: 18 additions & 1 deletion src/invidious/yt_backend/connection_pool.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,21 @@

def initialize(url : URI, @capacity = 5, @timeout = 5.0)
@url = url
@pool = build_pool()

Check failure on line 24 in src/invidious/yt_backend/connection_pool.cr

View workflow job for this annotation

GitHub Actions / build - crystal: nightly, stable: false

instantiating 'build_pool()'
end

def client(&block)
conn = pool.checkout
# Proxy needs to be reinstated every time we get a client from the pool
conn.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy

begin
response = yield conn
rescue ex
conn.close
conn = HTTP::Client.new(url)

conn = HTTP::Client.new(url)
conn.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
conn.family = CONFIG.force_resolve
conn.family = Socket::Family::INET if conn.family == Socket::Family::UNSPEC
conn.before_request { |r| add_yt_headers(r) } if url.host == "www.youtube.com"
Expand All @@ -45,7 +49,7 @@

private def build_pool
DB::Pool(HTTP::Client).new(initial_pool_size: 0, max_pool_size: capacity, max_idle_pool_size: capacity, checkout_timeout: timeout) do
conn = HTTP::Client.new(url)

Check failure on line 52 in src/invidious/yt_backend/connection_pool.cr

View workflow job for this annotation

GitHub Actions / build - crystal: nightly, stable: false

instantiating 'HTTP::Client.new(URI)'
conn.family = CONFIG.force_resolve
conn.family = Socket::Family::INET if conn.family == Socket::Family::UNSPEC
conn.before_request { |r| add_yt_headers(r) } if url.host == "www.youtube.com"
Expand Down Expand Up @@ -77,3 +81,16 @@
client.close
end
end

def make_configured_http_proxy_client
# This method is only called when configuration for an HTTP proxy are set
config_proxy = CONFIG.http_proxy.not_nil!

return HTTP::Proxy::Client.new(
config_proxy.host,
config_proxy.port,

username: config_proxy.user,
password: config_proxy.password,
)
end