(HTTP2+HTTP3)対応cURLを自前ビルドしてpythonから使う その2

pipでpycurlのインストールを試みたら、いろいろと足りないものがあったりしたので自前でビルドしました。

前回からの続きです。

pycurlのインストール

必要なライブラリをインストールできたらpipを使ってpycurlをインストールします。 一緒にルート証明書のコレクションcertifiもインストールしておきましょう。

(develop)$ pip install pycurl certifi

これで環境構築ができました。ただし共有ライブラリなどがvirtualenvの中にあるので、Pythonから使えるようにしなければなりません。

一般的には環境変数LD_LIBRARY_PATH/etc/ld.so.confにパスを設定すると思うのですが、今回は「ホスト環境をできるだけ汚さない」方針のため別の方法で解決をしています。

ctypesによるライブラリのロード

なにもせずにpycurlをインポートすると「共有ライブラリが見つからない」と言われます。次のようになるはずです。 エラーにならない場合はホスト環境のlibcurlがロードされているかもしれません。

(develop) $ python
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pycurl
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: libssl.so.81.3: cannot open shared object file: No such file or directory

これを回避するためpycurlのインポート前に手動で共有ライブラリをロードします。 ライブラリが配置されている場所は$VIRTUAL_ENV/lib/ですので次のようにします。

import os
import ctypes
venvdir = os.environ.get('VIRTUAL_ENV')
if venvdir:
  ctypes.cdll.LoadLibrary(venvdir+'/lib/libcurl.so')

ここでlibcurl.soを明示的にロードしてあげると、ほかのライブラリはビルド時に設定したLDFLAGSの効力で勝手に検索してくれるようです。

HTTP/2の動作確認

virtualenvのPythonを使って以下のスクリプトを実行してみます。

import os
import ctypes
venvdir = os.environ.get('VIRTUAL_ENV')
if venvdir:
  ctypes.cdll.LoadLibrary(venvdir+'/lib/libcurl.so')
import pycurl
import certifi
from io import BytesIO

url='https://nghttp2.org/'
def header_function(header_line):
  print(header_line)

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_2)
c.setopt(c.URL, url)
c.setopt(c.HEADERFUNCTION, header_function)
c.setopt(c.WRITEDATA, buffer)
c.perform()

実行するとHTTPレスポンスの内容が表示されます。先頭に

b'HTTP/2 200 \r\n'

の行があればHTTP/2で通信できているはずです。

HTTP/3の動作確認

先ほどと同じく、virtualenvのPythonを使って以下のスクリプトを実行してみます。

import os
import ctypes
venvdir = os.environ.get('VIRTUAL_ENV')
if venvdir:
  ctypes.cdll.LoadLibrary(venvdir+'/lib/libcurl.so')
import pycurl
import certifi
from io import BytesIO

url='https://nghttp2.org:4433/'
def header_function(header_line):
  print(header_line)

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_3)
c.setopt(c.URL, url)
c.setopt(c.HEADERFUNCTION, header_function)
c.setopt(c.WRITEDATA, buffer)
c.perform()

実行するとHTTPレスポンスの内容が表示されます。先頭に

b'HTTP/3 200 \r\n'

の行があればHTTP/3で通信できているはずです。

共有ライブラリのロード方法について

今回はできるだけvirtualenv環境に必要なものを押し込んだため、pycurlモジュールの読み込み前にひと手間必要でした。

もしかしたらvirtualenvのactivateスクリプトでLD_LIBRARY_PATHを設定したほうが使いやすいかもしれません。

[おわり]