Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-httptools for openSUSE:Factory checked in at 2022-01-10 23:54:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-httptools (Old) and /work/SRC/openSUSE:Factory/.python-httptools.new.1892 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-httptools" Mon Jan 10 23:54:00 2022 rev:2 rq:945406 version:0.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-httptools/python-httptools.changes 2020-10-13 15:46:53.389481929 +0200 +++ /work/SRC/openSUSE:Factory/.python-httptools.new.1892/python-httptools.changes 2022-01-10 23:54:37.676858224 +0100 @@ -1,0 +2,18 @@ +Sun Jan 9 08:53:15 UTC 2022 - Torsten Gruner <simmpho...@opensuse.org> + +- update to version 0.3.0 + * Use cibuildwheel to build release wheels (by @elprans in 2f57b6b) +- version 0.2.0 + * Swap http-parse to llhttp + (by @victoraugustolls and @fantix in 63b5de2 for #56) + * Fix httptools.__all__ + (by @elprans in 9340d32 for #52) + * Add Python 3.9 in the build/test matrix + (by @b0g3r in e2d1a46 for #62) +- version 0.1.2 + * Fix httptools.__all__ + (by @elprans in 9340d32 for #52) + * Add Python 3.9 in the build/test matrix + (by @b0g3r in e2d1a46 for #62) + +------------------------------------------------------------------- Old: ---- httptools-0.1.1.tar.gz New: ---- http-parser-2.9.4.tar.gz httptools-0.3.0.tar.gz llhttp-release-v6.0.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-httptools.spec ++++++ --- /var/tmp/diff_new_pack.3Bu9pG/_old 2022-01-10 23:54:38.064858564 +0100 +++ /var/tmp/diff_new_pack.3Bu9pG/_new 2022-01-10 23:54:38.068858568 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-httptools # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,18 +19,19 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-httptools -Version: 0.1.1 +Version: 0.3.0 Release: 0 Summary: Python framework independent HTTP protocol utils License: MIT Group: Development/Languages/Python URL: https://github.com/MagicStack/httptools -Source: https://github.com/MagicStack/httptools/archive/v%{version}.tar.gz#/httptools-%{version}.tar.gz -BuildRequires: %{python_module Cython} +Source0: https://github.com/MagicStack/httptools/archive/v%{version}.tar.gz#/httptools-%{version}.tar.gz +Source1: https://github.com/nodejs/llhttp/archive/refs/tags/release/v6.0.6.tar.gz#/llhttp-release-v6.0.6.tar.gz +Source2: https://github.com/nodejs/http-parser/archive/refs/tags/v2.9.4.tar.gz#/http-parser-2.9.4.tar.gz +BuildRequires: %{python_module Cython >= 0.29.24} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} BuildRequires: fdupes -BuildRequires: http-parser-devel BuildRequires: python-rpm-macros %python_subpackages @@ -39,23 +40,26 @@ %prep %setup -q -n httptools-%{version} -# unpin Cython -sed -i 's/Cython==/Cython>=/' setup.py -cp %{_includedir}/http_parser.h vendor/http-parser/ +rm -df vendor/llhttp/ +tar -xzf '%{SOURCE1}' -C vendor +mv vendor/llhttp-release*/ vendor/llhttp/ +rm -df vendor/http-parser/ +tar -xzf '%{SOURCE2}' -C vendor +mv vendor/http-parser*/ vendor/http-parser/ %build -%python_build build_ext --use-system-http-parser +%python_build %install %python_install -%{python_expand rm %{buildroot}%{$python_sitearch}/httptools/parser/parser.c +%{python_expand rm %{buildroot}%{$python_sitearch}/httptools/parser/*parser.c; %fdupes %{buildroot}%{$python_sitearch} } +%if 0%{?python_version_nodots} > 36 %check -mv httptools .httptools -%pytest_arch -k 'not (test_parser_response_1 or test_parser_url_2)' -mv .httptools httptools +%pytest_arch -k 'not test_parser_response_1' +%endif %files %{python_files} %doc README.md ++++++ httptools-0.1.1.tar.gz -> httptools-0.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/.github/workflows/build-manylinux-wheels.sh new/httptools-0.3.0/.github/workflows/build-manylinux-wheels.sh --- old/httptools-0.1.1/.github/workflows/build-manylinux-wheels.sh 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/.github/workflows/build-manylinux-wheels.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e -x - -PY_MAJOR=${PYTHON_VERSION%%.*} -PY_MINOR=${PYTHON_VERSION#*.} - -ML_PYTHON_VERSION="cp${PY_MAJOR}${PY_MINOR}-cp${PY_MAJOR}${PY_MINOR}" -if [ "${PY_MAJOR}" -lt "4" -a "${PY_MINOR}" -lt "8" ]; then - ML_PYTHON_VERSION+="m" -fi - -# Compile wheels -PYTHON="/opt/python/${ML_PYTHON_VERSION}/bin/python" -PIP="/opt/python/${ML_PYTHON_VERSION}/bin/pip" -"${PIP}" install --upgrade setuptools pip wheel~=0.31.1 -cd "${GITHUB_WORKSPACE}" -make clean -"${PYTHON}" setup.py bdist_wheel - -# Bundle external shared libraries into the wheels. -for whl in "${GITHUB_WORKSPACE}"/dist/*.whl; do - auditwheel repair $whl -w "${GITHUB_WORKSPACE}"/dist/ - rm "${GITHUB_WORKSPACE}"/dist/*-linux_*.whl -done diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/.github/workflows/release-trigger.yml new/httptools-0.3.0/.github/workflows/release-trigger.yml --- old/httptools-0.1.1/.github/workflows/release-trigger.yml 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/.github/workflows/release-trigger.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,25 +0,0 @@ -name: Trigger Release - -on: - pull_request_review: - types: [submitted] - -jobs: - check-review: - runs-on: ubuntu-latest - steps: - - name: Validate release PR - uses: edgedb/action-release/validate-pr@master - id: release - continue-on-error: true - with: - github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} - version_file: httptools/_version.py - version_line_pattern: | - __version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"]) - - name: Trigger release - uses: edgedb/action-release/trigger@master - if: steps.release.outputs.version != 0 - with: - github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} - release_validation_check: "validate-release-request" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/.github/workflows/release.yml new/httptools-0.3.0/.github/workflows/release.yml --- old/httptools-0.1.1/.github/workflows/release.yml 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/.github/workflows/release.yml 2021-08-10 20:09:32.000000000 +0200 @@ -17,6 +17,8 @@ uses: edgedb/action-release/validate-pr@master id: checkver with: + require_team: Release Managers + require_approval: no github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} version_file: httptools/_version.py version_line_pattern: | @@ -35,7 +37,7 @@ mkdir -p dist/ echo "${VERSION}" > dist/VERSION - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2 with: name: dist path: dist/ @@ -45,93 +47,98 @@ runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: fetch-depth: 50 submodules: true - - name: Set up Python 3.7 - uses: actions/setup-python@v1 - with: - python-version: 3.7 + - name: Set up Python + uses: actions/setup-python@v2 - name: Build source distribution run: | - pip install -U setuptools wheel pip + python -m pip install -U setuptools wheel pip python setup.py sdist - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2 with: name: dist - path: dist/ + path: dist/*.tar.* build-wheels: needs: validate-release-request runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8] - os: [ubuntu-16.04, macos-latest, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] + cibw_arch: ["auto64", "aarch64", "universal2"] + cibw_python: + - "cp36-*" + - "cp37-*" + - "cp38-*" + - "cp39-*" + - "cp310-*" exclude: - # Python 3.5 is unable to properly - # find the recent VS tooling - # https://bugs.python.org/issue30389 + - os: ubuntu-latest + cibw_arch: universal2 + - os: macos-latest + cibw_arch: aarch64 + - os: macos-latest + cibw_python: "cp36-*" + cibw_arch: universal2 + - os: macos-latest + cibw_python: "cp37-*" + cibw_arch: universal2 - os: windows-latest - python-version: 3.5 + cibw_arch: universal2 + - os: windows-latest + cibw_arch: aarch64 + + defaults: + run: + shell: bash + + env: + PIP_DISABLE_PIP_VERSION_CHECK: 1 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: fetch-depth: 50 submodules: true - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + - name: Set up QEMU + if: matrix.os == 'ubuntu-latest' && matrix.cibw_arch == 'aarch64' + uses: docker/setup-qemu-action@v1 with: - python-version: ${{ matrix.python-version }} - - - name: Install Python Deps - run: | - python -m pip install --upgrade setuptools pip wheel + platforms: arm64 - - name: Build Wheels (linux) - if: startsWith(matrix.os, 'ubuntu') - uses: docker://quay.io/pypa/manylinux1_x86_64 + - uses: pypa/cibuildwheel@v2.1.1 env: - PYTHON_VERSION: ${{ matrix.python-version }} - with: - entrypoint: /github/workspace/.github/workflows/build-manylinux-wheels.sh - - - name: Build Wheels (non-linux) - if: "!startsWith(matrix.os, 'ubuntu')" - run: | - make clean - python setup.py bdist_wheel + CIBW_BUILD_VERBOSITY: 1 + CIBW_BUILD: ${{ matrix.cibw_python }} + CIBW_ARCHS: ${{ matrix.cibw_arch }} + CIBW_TEST_EXTRAS: "test" + CIBW_TEST_COMMAND: "python {project}/tests/__init__.py" + CIBW_TEST_COMMAND_WINDOWS: "python {project}\\tests\\__init__.py" + CIBW_TEST_SKIP: "*universal2:arm64" - - name: Test Wheels - if: | - !startsWith(matrix.os, 'windows') - && !contains(github.event.pull_request.labels.*.name, 'skip wheel tests') - run: | - pip install --pre httptools -f "file:///${GITHUB_WORKSPACE}/dist" - make -C "${GITHUB_WORKSPACE}" testinstalled - - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2 with: name: dist - path: dist/ + path: wheelhouse/*.whl publish: needs: [build-sdist, build-wheels] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: fetch-depth: 5 submodules: false - - uses: actions/download-artifact@v1 + - uses: actions/download-artifact@v2 with: name: dist path: dist/ @@ -161,7 +168,7 @@ release_name: v${{ steps.relver.outputs.version }} target: ${{ github.event.pull_request.base.ref }} body: ${{ github.event.pull_request.body }} - draft: true + draft: false - run: | ls -al dist/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/.github/workflows/tests.yml new/httptools-0.3.0/.github/workflows/tests.yml --- old/httptools-0.1.1/.github/workflows/tests.yml 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/.github/workflows/tests.yml 2021-08-10 20:09:32.000000000 +0200 @@ -13,10 +13,9 @@ build: runs-on: ${{ matrix.os }} strategy: - max-parallel: 4 matrix: - python-version: [3.5, 3.6, 3.7, 3.8] - os: [windows-latest, ubuntu-18.04, macos-latest] + python-version: [3.5, 3.6, 3.7, 3.8, 3.9, 3.10.0-rc.1] + os: [windows-latest, ubuntu-latest, macos-latest] exclude: # Python 3.5 is unable to properly # find the recent VS tooling @@ -24,24 +23,27 @@ - os: windows-latest python-version: 3.5 + env: + PIP_DISABLE_PIP_VERSION_CHECK: 1 + steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 with: fetch-depth: 50 submodules: true - name: Check if release PR. uses: edgedb/action-release/validate-pr@master - continue-on-error: true id: release with: github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} + missing_version_ok: yes version_file: httptools/_version.py version_line_pattern: | __version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"]) - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 if: steps.release.outputs.version == 0 with: python-version: ${{ matrix.python-version }} @@ -49,5 +51,6 @@ - name: Test if: steps.release.outputs.version == 0 run: | - pip install -e .[test] - python setup.py test + python -m pip install -U pip setuptools wheel + python -m pip install -e .[test] + python -m unittest -v tests.suite diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/.gitignore new/httptools-0.3.0/.gitignore --- old/httptools-0.1.1/.gitignore 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/.gitignore 2021-08-10 20:09:32.000000000 +0200 @@ -29,3 +29,5 @@ /.pytest_cache /.mypy_cache /.vscode +.eggs +.venv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/.gitmodules new/httptools-0.3.0/.gitmodules --- old/httptools-0.1.1/.gitmodules 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/.gitmodules 2021-08-10 20:09:32.000000000 +0200 @@ -1,3 +1,6 @@ [submodule "vendor/http-parser"] path = vendor/http-parser url = https://github.com/nodejs/http-parser.git +[submodule "vendor/llhttp"] + path = vendor/llhttp + url = https://github.com/nodejs/llhttp.git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/Makefile new/httptools-0.3.0/Makefile --- old/httptools-0.1.1/Makefile 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/Makefile 2021-08-10 20:09:32.000000000 +0200 @@ -13,17 +13,16 @@ python3 setup.py sdist upload -test: - python3 setup.py test +test: compile + python3 -m unittest -v clean: find $(ROOT)/httptools/parser -name '*.c' | xargs rm -f find $(ROOT)/httptools/parser -name '*.html' | xargs rm -f -distclean: +distclean: clean git --git-dir="$(ROOT)/vendor/http-parser/.git" clean -dfx - find $(ROOT)/httptools/parser -name '*.c' | xargs rm -f - find $(ROOT)/httptools/parser -name '*.html' | xargs rm -f + git --git-dir="$(ROOT)/vendor/llhttp/.git" clean -dfx testinstalled: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/README.md new/httptools-0.3.0/README.md --- old/httptools-0.1.1/README.md 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/README.md 2021-08-10 20:09:32.000000000 +0200 @@ -8,8 +8,11 @@ # APIs httptools contains two classes `httptools.HttpRequestParser`, -`httptools.HttpResponseParser` and a function for parsing URLs -`httptools.parse_url`. See unittests for examples. +`httptools.HttpResponseParser` (fulfilled through +[llhttp](https://github.com/nodejs/llhttp)) and a function for +parsing URLs `httptools.parse_url` (through +[http-parse](https://github.com/nodejs/http-parser) for now). +See unittests for examples. ```python @@ -91,12 +94,12 @@ 1. Clone this repository with `git clone --recursive g...@github.com:MagicStack/httptools.git` -2. Create a virtual environment with Python 3.5: - `python3.5 -m venv envname` +2. Create a virtual environment with Python 3: + `python3 -m venv envname` 3. Activate the environment with `source envname/bin/activate` -4. Install Cython with `pip install cython` +4. Install development requirements with `pip install -e .[test]` 5. Run `make` and `make test`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/__init__.py new/httptools-0.3.0/httptools/__init__.py --- old/httptools-0.1.1/httptools/__init__.py 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/httptools/__init__.py 2021-08-10 20:09:32.000000000 +0200 @@ -1,6 +1,6 @@ -from .parser import parser +from . import parser from .parser import * # NOQA from ._version import __version__ # NOQA -__all__ = parser.__all__ # NOQA +__all__ = parser.__all__ + ('__version__',) # NOQA diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/_version.py new/httptools-0.3.0/httptools/_version.py --- old/httptools-0.1.1/httptools/_version.py 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/httptools/_version.py 2021-08-10 20:09:32.000000000 +0200 @@ -10,4 +10,4 @@ # supported platforms, publish the packages on PyPI, merge the PR # to the target branch, create a Git tag pointing to the commit. -__version__ = '0.1.1' +__version__ = '0.3.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/parser/__init__.py new/httptools-0.3.0/httptools/parser/__init__.py --- old/httptools-0.1.1/httptools/parser/__init__.py 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/httptools/parser/__init__.py 2021-08-10 20:09:32.000000000 +0200 @@ -1,5 +1,5 @@ -from .parser import * -from .errors import * +from .parser import * # NoQA +from .errors import * # NoQA +from .url_parser import * # NoQA - -__all__ = parser.__all__ + errors.__all__ +__all__ = parser.__all__ + errors.__all__ + url_parser.__all__ # NoQA diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/parser/cparser.pxd new/httptools-0.3.0/httptools/parser/cparser.pxd --- old/httptools-0.1.1/httptools/parser/cparser.pxd 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/httptools/parser/cparser.pxd 2021-08-10 20:09:32.000000000 +0200 @@ -1,139 +1,156 @@ -from libc.stdint cimport uint16_t, uint32_t, uint64_t +from libc.stdint cimport int32_t, uint8_t, uint16_t, uint64_t -cdef extern from "../../vendor/http-parser/http_parser.h": - ctypedef int (*http_data_cb) (http_parser*, - const char *at, - size_t length) except -1 - - ctypedef int (*http_cb) (http_parser*) except -1 - - struct http_parser: - unsigned int type - unsigned int flags - unsigned int state - unsigned int header_state - unsigned int index - - uint32_t nread +cdef extern from "llhttp.h": + struct llhttp__internal_s: + int32_t _index + void *_span_pos0 + void *_span_cb0 + int32_t error + const char *reason + const char *error_pos + void *data + void *_current uint64_t content_length + uint8_t type + uint8_t method + uint8_t http_major + uint8_t http_minor + uint8_t header_state + uint16_t flags + uint8_t upgrade + uint16_t status_code + uint8_t finish + void *settings + ctypedef llhttp__internal_s llhttp__internal_t + ctypedef llhttp__internal_t llhttp_t - unsigned short http_major - unsigned short http_minor - unsigned int status_code - unsigned int method - unsigned int http_errno - - unsigned int upgrade + ctypedef int (*llhttp_data_cb) (llhttp_t*, + const char *at, + size_t length) except -1 - void *data + ctypedef int (*llhttp_cb) (llhttp_t*) except -1 - struct http_parser_settings: - http_cb on_message_begin - http_data_cb on_url - http_data_cb on_status - http_data_cb on_header_field - http_data_cb on_header_value - http_cb on_headers_complete - http_data_cb on_body - http_cb on_message_complete - http_cb on_chunk_header - http_cb on_chunk_complete + struct llhttp_settings_s: + llhttp_cb on_message_begin + llhttp_data_cb on_url + llhttp_data_cb on_status + llhttp_data_cb on_header_field + llhttp_data_cb on_header_value + llhttp_cb on_headers_complete + llhttp_data_cb on_body + llhttp_cb on_message_complete + llhttp_cb on_chunk_header + llhttp_cb on_chunk_complete + ctypedef llhttp_settings_s llhttp_settings_t - enum http_parser_type: + enum llhttp_type: + HTTP_BOTH, HTTP_REQUEST, - HTTP_RESPONSE, - HTTP_BOTH + HTTP_RESPONSE + ctypedef llhttp_type llhttp_type_t - enum http_errno: + enum llhttp_errno: HPE_OK, - HPE_CB_message_begin, - HPE_CB_url, - HPE_CB_header_field, - HPE_CB_header_value, - HPE_CB_headers_complete, - HPE_CB_body, - HPE_CB_message_complete, - HPE_CB_status, - HPE_CB_chunk_header, - HPE_CB_chunk_complete, - HPE_INVALID_EOF_STATE, - HPE_HEADER_OVERFLOW, + HPE_INTERNAL, + HPE_STRICT, + HPE_LF_EXPECTED, + HPE_UNEXPECTED_CONTENT_LENGTH, HPE_CLOSED_CONNECTION, - HPE_INVALID_VERSION, - HPE_INVALID_STATUS, HPE_INVALID_METHOD, HPE_INVALID_URL, - HPE_INVALID_HOST, - HPE_INVALID_PORT, - HPE_INVALID_PATH, - HPE_INVALID_QUERY_STRING, - HPE_INVALID_FRAGMENT, - HPE_LF_EXPECTED, + HPE_INVALID_CONSTANT, + HPE_INVALID_VERSION, HPE_INVALID_HEADER_TOKEN, HPE_INVALID_CONTENT_LENGTH, HPE_INVALID_CHUNK_SIZE, - HPE_INVALID_CONSTANT, - HPE_INVALID_INTERNAL_STATE, - HPE_STRICT, + HPE_INVALID_STATUS, + HPE_INVALID_EOF_STATE, + HPE_INVALID_TRANSFER_ENCODING, + HPE_CB_MESSAGE_BEGIN, + HPE_CB_HEADERS_COMPLETE, + HPE_CB_MESSAGE_COMPLETE, + HPE_CB_CHUNK_HEADER, + HPE_CB_CHUNK_COMPLETE, HPE_PAUSED, - HPE_UNKNOWN + HPE_PAUSED_UPGRADE, + HPE_USER + ctypedef llhttp_errno llhttp_errno_t - enum flags: - F_CHUNKED, + enum llhttp_flags: F_CONNECTION_KEEP_ALIVE, F_CONNECTION_CLOSE, F_CONNECTION_UPGRADE, - F_TRAILING, + F_CHUNKED, F_UPGRADE, - F_SKIPBODY + F_CONTENT_LENGTH, + F_SKIPBODY, + F_TRAILING, + F_LENIENT, + F_TRANSFER_ENCODING + ctypedef llhttp_flags llhttp_flags_t + + enum llhttp_method: + HTTP_DELETE, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_CONNECT, + HTTP_OPTIONS, + HTTP_TRACE, + HTTP_COPY, + HTTP_LOCK, + HTTP_MKCOL, + HTTP_MOVE, + HTTP_PROPFIND, + HTTP_PROPPATCH, + HTTP_SEARCH, + HTTP_UNLOCK, + HTTP_BIND, + HTTP_REBIND, + HTTP_UNBIND, + HTTP_ACL, + HTTP_REPORT, + HTTP_MKACTIVITY, + HTTP_CHECKOUT, + HTTP_MERGE, + HTTP_MSEARCH, + HTTP_NOTIFY, + HTTP_SUBSCRIBE, + HTTP_UNSUBSCRIBE, + HTTP_PATCH, + HTTP_PURGE, + HTTP_MKCALENDAR, + HTTP_LINK, + HTTP_UNLINK, + HTTP_SOURCE, + HTTP_PRI, + HTTP_DESCRIBE, + HTTP_ANNOUNCE, + HTTP_SETUP, + HTTP_PLAY, + HTTP_PAUSE, + HTTP_TEARDOWN, + HTTP_GET_PARAMETER, + HTTP_SET_PARAMETER, + HTTP_REDIRECT, + HTTP_RECORD, + HTTP_FLUSH + ctypedef llhttp_method llhttp_method_t + + void llhttp_init(llhttp_t* parser, llhttp_type_t type, const llhttp_settings_t* settings) + + void llhttp_settings_init(llhttp_settings_t* settings) + + llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) + + void llhttp_resume_after_upgrade(llhttp_t* parser) + + int llhttp_should_keep_alive(const llhttp_t* parser) + + const char* llhttp_get_error_pos(const llhttp_t* parser) + const char* llhttp_get_error_reason(const llhttp_t* parser) + const char* llhttp_method_name(llhttp_method_t method) - enum http_method: - DELETE, GET, HEAD, POST, PUT, CONNECT, OPTIONS, TRACE, COPY, - LOCK, MKCOL, MOVE, PROPFIND, PROPPATCH, SEARCH, UNLOCK, BIND, - REBIND, UNBIND, ACL, REPORT, MKACTIVITY, CHECKOUT, MERGE, - MSEARCH, NOTIFY, SUBSCRIBE, UNSUBSCRIBE, PATCH, PURGE, MKCALENDAR, - LINK, UNLINK - - void http_parser_init(http_parser *parser, http_parser_type type) - - size_t http_parser_execute(http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len) - - int http_should_keep_alive(const http_parser *parser) - - void http_parser_settings_init(http_parser_settings *settings) - - const char *http_errno_name(http_errno err) - const char *http_errno_description(http_errno err) - const char *http_method_str(http_method m) - - # URL Parser - - enum http_parser_url_fields: - UF_SCHEMA = 0, - UF_HOST = 1, - UF_PORT = 2, - UF_PATH = 3, - UF_QUERY = 4, - UF_FRAGMENT = 5, - UF_USERINFO = 6, - UF_MAX = 7 - - struct http_parser_url_field_data: - uint16_t off - uint16_t len - - struct http_parser_url: - uint16_t field_set - uint16_t port - http_parser_url_field_data[<int>UF_MAX] field_data - - void http_parser_url_init(http_parser_url *u) - - int http_parser_parse_url(const char *buf, - size_t buflen, - int is_connect, - http_parser_url *u) + void llhttp_set_error_reason(llhttp_t* parser, const char* reason); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/parser/parser.pyx new/httptools-0.3.0/httptools/parser/parser.pyx --- old/httptools-0.1.1/httptools/parser/parser.pyx 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/httptools/parser/parser.pyx 2021-08-10 20:09:32.000000000 +0200 @@ -19,15 +19,15 @@ from . cimport cparser -__all__ = ('HttpRequestParser', 'HttpResponseParser', 'parse_url') +__all__ = ('HttpRequestParser', 'HttpResponseParser') @cython.internal cdef class HttpParser: cdef: - cparser.http_parser* _cparser - cparser.http_parser_settings* _csettings + cparser.llhttp_t* _cparser + cparser.llhttp_settings_t* _csettings bytes _current_header_name bytes _current_header_value @@ -42,13 +42,13 @@ Py_buffer py_buf def __cinit__(self): - self._cparser = <cparser.http_parser*> \ - PyMem_Malloc(sizeof(cparser.http_parser)) + self._cparser = <cparser.llhttp_t*> \ + PyMem_Malloc(sizeof(cparser.llhttp_t)) if self._cparser is NULL: raise MemoryError() - self._csettings = <cparser.http_parser_settings*> \ - PyMem_Malloc(sizeof(cparser.http_parser_settings)) + self._csettings = <cparser.llhttp_settings_t*> \ + PyMem_Malloc(sizeof(cparser.llhttp_settings_t)) if self._csettings is NULL: raise MemoryError() @@ -56,11 +56,11 @@ PyMem_Free(self._cparser) PyMem_Free(self._csettings) - cdef _init(self, protocol, cparser.http_parser_type mode): - cparser.http_parser_init(self._cparser, mode) - self._cparser.data = <void*>self + cdef _init(self, protocol, cparser.llhttp_type_t mode): + cparser.llhttp_settings_init(self._csettings) - cparser.http_parser_settings_init(self._csettings) + cparser.llhttp_init(self._cparser, mode, self._csettings) + self._cparser.data = <void*>self self._current_header_name = None self._current_header_value = None @@ -145,59 +145,72 @@ ### Public API ### def get_http_version(self): - cdef cparser.http_parser* parser = self._cparser + cdef cparser.llhttp_t* parser = self._cparser return '{}.{}'.format(parser.http_major, parser.http_minor) def should_keep_alive(self): - return bool(cparser.http_should_keep_alive(self._cparser)) + return bool(cparser.llhttp_should_keep_alive(self._cparser)) def should_upgrade(self): - cdef cparser.http_parser* parser = self._cparser + cdef cparser.llhttp_t* parser = self._cparser return bool(parser.upgrade) def feed_data(self, data): cdef: size_t data_len - size_t nb + cparser.llhttp_errno_t err Py_buffer *buf + bint owning_buf = False + char* err_pos if PyMemoryView_Check(data): buf = PyMemoryView_GET_BUFFER(data) data_len = <size_t>buf.len - nb = cparser.http_parser_execute( + err = cparser.llhttp_execute( self._cparser, - self._csettings, <char*>buf.buf, data_len) else: buf = &self.py_buf PyObject_GetBuffer(data, buf, PyBUF_SIMPLE) + owning_buf = True data_len = <size_t>buf.len - nb = cparser.http_parser_execute( + err = cparser.llhttp_execute( self._cparser, - self._csettings, <char*>buf.buf, data_len) - PyBuffer_Release(buf) + try: + if self._cparser.upgrade == 1 and err == cparser.HPE_PAUSED_UPGRADE: + err_pos = cparser.llhttp_get_error_pos(self._cparser) + + # Immediately free the parser from "error" state, simulating + # http-parser behavior here because 1) we never had the API to + # allow users manually "resume after upgrade", and 2) the use + # case for resuming parsing is very rare. + cparser.llhttp_resume_after_upgrade(self._cparser) + + # The err_pos here is specific for the input buf. So if we ever + # switch to the llhttp behavior (re-raise HttpParserUpgrade for + # successive calls to feed_data() until resume_after_upgrade is + # called), we have to store the result and keep our own state. + raise HttpParserUpgrade(err_pos - <char*>buf.buf) + finally: + if owning_buf: + PyBuffer_Release(buf) - if self._cparser.http_errno != cparser.HPE_OK: - ex = parser_error_from_errno( - <cparser.http_errno> self._cparser.http_errno) + if err != cparser.HPE_OK: + ex = parser_error_from_errno( + self._cparser, + <cparser.llhttp_errno_t> self._cparser.error) if isinstance(ex, HttpParserCallbackError): if self._last_error is not None: ex.__context__ = self._last_error self._last_error = None raise ex - if self._cparser.upgrade: - raise HttpParserUpgrade(nb) - - if nb != data_len: - raise HttpParserError('not all of the data was parsed') - cdef class HttpRequestParser(HttpParser): @@ -209,8 +222,8 @@ self._csettings.on_url = cb_on_url def get_method(self): - cdef cparser.http_parser* parser = self._cparser - return cparser.http_method_str(<cparser.http_method> parser.method) + cdef cparser.llhttp_t* parser = self._cparser + return cparser.llhttp_method_name(<cparser.llhttp_method_t> parser.method) cdef class HttpResponseParser(HttpParser): @@ -223,11 +236,11 @@ self._csettings.on_status = cb_on_status def get_status_code(self): - cdef cparser.http_parser* parser = self._cparser + cdef cparser.llhttp_t* parser = self._cparser return parser.status_code -cdef int cb_on_message_begin(cparser.http_parser* parser) except -1: +cdef int cb_on_message_begin(cparser.llhttp_t* parser) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._proto_on_message_begin() @@ -238,55 +251,59 @@ return 0 -cdef int cb_on_url(cparser.http_parser* parser, +cdef int cb_on_url(cparser.llhttp_t* parser, const char *at, size_t length) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._proto_on_url(at[:length]) except BaseException as ex: + cparser.llhttp_set_error_reason(parser, "`on_url` callback error") pyparser._last_error = ex - return -1 + return cparser.HPE_USER else: return 0 -cdef int cb_on_status(cparser.http_parser* parser, +cdef int cb_on_status(cparser.llhttp_t* parser, const char *at, size_t length) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._proto_on_status(at[:length]) except BaseException as ex: + cparser.llhttp_set_error_reason(parser, "`on_status` callback error") pyparser._last_error = ex - return -1 + return cparser.HPE_USER else: return 0 -cdef int cb_on_header_field(cparser.http_parser* parser, +cdef int cb_on_header_field(cparser.llhttp_t* parser, const char *at, size_t length) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._on_header_field(at[:length]) except BaseException as ex: + cparser.llhttp_set_error_reason(parser, "`on_header_field` callback error") pyparser._last_error = ex - return -1 + return cparser.HPE_USER else: return 0 -cdef int cb_on_header_value(cparser.http_parser* parser, +cdef int cb_on_header_value(cparser.llhttp_t* parser, const char *at, size_t length) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._on_header_value(at[:length]) except BaseException as ex: + cparser.llhttp_set_error_reason(parser, "`on_header_value` callback error") pyparser._last_error = ex - return -1 + return cparser.HPE_USER else: return 0 -cdef int cb_on_headers_complete(cparser.http_parser* parser) except -1: +cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._on_headers_complete() @@ -300,19 +317,20 @@ return 0 -cdef int cb_on_body(cparser.http_parser* parser, +cdef int cb_on_body(cparser.llhttp_t* parser, const char *at, size_t length) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._proto_on_body(at[:length]) except BaseException as ex: + cparser.llhttp_set_error_reason(parser, "`on_body` callback error") pyparser._last_error = ex - return -1 + return cparser.HPE_USER else: return 0 -cdef int cb_on_message_complete(cparser.http_parser* parser) except -1: +cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._proto_on_message_complete() @@ -323,7 +341,7 @@ return 0 -cdef int cb_on_chunk_header(cparser.http_parser* parser) except -1: +cdef int cb_on_chunk_header(cparser.llhttp_t* parser) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._on_chunk_header() @@ -334,7 +352,7 @@ return 0 -cdef int cb_on_chunk_complete(cparser.http_parser* parser) except -1: +cdef int cb_on_chunk_complete(cparser.llhttp_t* parser) except -1: cdef HttpParser pyparser = <HttpParser>parser.data try: pyparser._on_chunk_complete() @@ -345,19 +363,15 @@ return 0 -cdef parser_error_from_errno(cparser.http_errno errno): - cdef bytes desc = cparser.http_errno_description(errno) +cdef parser_error_from_errno(cparser.llhttp_t* parser, cparser.llhttp_errno_t errno): + cdef bytes reason = cparser.llhttp_get_error_reason(parser) - if errno in (cparser.HPE_CB_message_begin, - cparser.HPE_CB_url, - cparser.HPE_CB_header_field, - cparser.HPE_CB_header_value, - cparser.HPE_CB_headers_complete, - cparser.HPE_CB_body, - cparser.HPE_CB_message_complete, - cparser.HPE_CB_status, - cparser.HPE_CB_chunk_header, - cparser.HPE_CB_chunk_complete): + if errno in (cparser.HPE_CB_MESSAGE_BEGIN, + cparser.HPE_CB_HEADERS_COMPLETE, + cparser.HPE_CB_MESSAGE_COMPLETE, + cparser.HPE_CB_CHUNK_HEADER, + cparser.HPE_CB_CHUNK_COMPLETE, + cparser.HPE_USER): cls = HttpParserCallbackError elif errno == cparser.HPE_INVALID_STATUS: @@ -372,100 +386,4 @@ else: cls = HttpParserError - return cls(desc.decode('latin-1')) - - -@cython.freelist(250) -cdef class URL: - cdef readonly bytes schema - cdef readonly bytes host - cdef readonly object port - cdef readonly bytes path - cdef readonly bytes query - cdef readonly bytes fragment - cdef readonly bytes userinfo - - def __cinit__(self, bytes schema, bytes host, object port, bytes path, - bytes query, bytes fragment, bytes userinfo): - - self.schema = schema - self.host = host - self.port = port - self.path = path - self.query = query - self.fragment = fragment - self.userinfo = userinfo - - def __repr__(self): - return ('<URL schema: {!r}, host: {!r}, port: {!r}, path: {!r}, ' - 'query: {!r}, fragment: {!r}, userinfo: {!r}>' - .format(self.schema, self.host, self.port, self.path, - self.query, self.fragment, self.userinfo)) - - -def parse_url(url): - cdef: - Py_buffer py_buf - char* buf_data - cparser.http_parser_url* parsed - int res - bytes schema = None - bytes host = None - object port = None - bytes path = None - bytes query = None - bytes fragment = None - bytes userinfo = None - object result = None - int off - int ln - - parsed = <cparser.http_parser_url*> \ - PyMem_Malloc(sizeof(cparser.http_parser_url)) - cparser.http_parser_url_init(parsed) - - PyObject_GetBuffer(url, &py_buf, PyBUF_SIMPLE) - try: - buf_data = <char*>py_buf.buf - res = cparser.http_parser_parse_url(buf_data, py_buf.len, 0, parsed) - - if res == 0: - if parsed.field_set & (1 << cparser.UF_SCHEMA): - off = parsed.field_data[<int>cparser.UF_SCHEMA].off - ln = parsed.field_data[<int>cparser.UF_SCHEMA].len - schema = buf_data[off:off+ln] - - if parsed.field_set & (1 << cparser.UF_HOST): - off = parsed.field_data[<int>cparser.UF_HOST].off - ln = parsed.field_data[<int>cparser.UF_HOST].len - host = buf_data[off:off+ln] - - if parsed.field_set & (1 << cparser.UF_PORT): - port = parsed.port - - if parsed.field_set & (1 << cparser.UF_PATH): - off = parsed.field_data[<int>cparser.UF_PATH].off - ln = parsed.field_data[<int>cparser.UF_PATH].len - path = buf_data[off:off+ln] - - if parsed.field_set & (1 << cparser.UF_QUERY): - off = parsed.field_data[<int>cparser.UF_QUERY].off - ln = parsed.field_data[<int>cparser.UF_QUERY].len - query = buf_data[off:off+ln] - - if parsed.field_set & (1 << cparser.UF_FRAGMENT): - off = parsed.field_data[<int>cparser.UF_FRAGMENT].off - ln = parsed.field_data[<int>cparser.UF_FRAGMENT].len - fragment = buf_data[off:off+ln] - - if parsed.field_set & (1 << cparser.UF_USERINFO): - off = parsed.field_data[<int>cparser.UF_USERINFO].off - ln = parsed.field_data[<int>cparser.UF_USERINFO].len - userinfo = buf_data[off:off+ln] - - return URL(schema, host, port, path, query, fragment, userinfo) - else: - raise HttpParserInvalidURLError("invalid url {!r}".format(url)) - finally: - PyBuffer_Release(&py_buf) - PyMem_Free(parsed) + return cls(reason.decode('latin-1')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/parser/url_cparser.pxd new/httptools-0.3.0/httptools/parser/url_cparser.pxd --- old/httptools-0.1.1/httptools/parser/url_cparser.pxd 1970-01-01 01:00:00.000000000 +0100 +++ new/httptools-0.3.0/httptools/parser/url_cparser.pxd 2021-08-10 20:09:32.000000000 +0200 @@ -0,0 +1,31 @@ +from libc.stdint cimport uint16_t + + +cdef extern from "http_parser.h": + # URL Parser + + enum http_parser_url_fields: + UF_SCHEMA = 0, + UF_HOST = 1, + UF_PORT = 2, + UF_PATH = 3, + UF_QUERY = 4, + UF_FRAGMENT = 5, + UF_USERINFO = 6, + UF_MAX = 7 + + struct http_parser_url_field_data: + uint16_t off + uint16_t len + + struct http_parser_url: + uint16_t field_set + uint16_t port + http_parser_url_field_data[<int>UF_MAX] field_data + + void http_parser_url_init(http_parser_url *u) + + int http_parser_parse_url(const char *buf, + size_t buflen, + int is_connect, + http_parser_url *u) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/httptools/parser/url_parser.pyx new/httptools-0.3.0/httptools/parser/url_parser.pyx --- old/httptools-0.1.1/httptools/parser/url_parser.pyx 1970-01-01 01:00:00.000000000 +0100 +++ new/httptools-0.3.0/httptools/parser/url_parser.pyx 2021-08-10 20:09:32.000000000 +0200 @@ -0,0 +1,108 @@ +#cython: language_level=3 + +from __future__ import print_function +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from cpython cimport PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE, \ + Py_buffer + +from .errors import HttpParserInvalidURLError + +cimport cython +from . cimport url_cparser as uparser + +__all__ = ('parse_url',) + +@cython.freelist(250) +cdef class URL: + cdef readonly bytes schema + cdef readonly bytes host + cdef readonly object port + cdef readonly bytes path + cdef readonly bytes query + cdef readonly bytes fragment + cdef readonly bytes userinfo + + def __cinit__(self, bytes schema, bytes host, object port, bytes path, + bytes query, bytes fragment, bytes userinfo): + + self.schema = schema + self.host = host + self.port = port + self.path = path + self.query = query + self.fragment = fragment + self.userinfo = userinfo + + def __repr__(self): + return ('<URL schema: {!r}, host: {!r}, port: {!r}, path: {!r}, ' + 'query: {!r}, fragment: {!r}, userinfo: {!r}>' + .format(self.schema, self.host, self.port, self.path, + self.query, self.fragment, self.userinfo)) + + +def parse_url(url): + cdef: + Py_buffer py_buf + char* buf_data + uparser.http_parser_url* parsed + int res + bytes schema = None + bytes host = None + object port = None + bytes path = None + bytes query = None + bytes fragment = None + bytes userinfo = None + object result = None + int off + int ln + + parsed = <uparser.http_parser_url*> \ + PyMem_Malloc(sizeof(uparser.http_parser_url)) + uparser.http_parser_url_init(parsed) + + PyObject_GetBuffer(url, &py_buf, PyBUF_SIMPLE) + try: + buf_data = <char*>py_buf.buf + res = uparser.http_parser_parse_url(buf_data, py_buf.len, 0, parsed) + + if res == 0: + if parsed.field_set & (1 << uparser.UF_SCHEMA): + off = parsed.field_data[<int>uparser.UF_SCHEMA].off + ln = parsed.field_data[<int>uparser.UF_SCHEMA].len + schema = buf_data[off:off+ln] + + if parsed.field_set & (1 << uparser.UF_HOST): + off = parsed.field_data[<int>uparser.UF_HOST].off + ln = parsed.field_data[<int>uparser.UF_HOST].len + host = buf_data[off:off+ln] + + if parsed.field_set & (1 << uparser.UF_PORT): + port = parsed.port + + if parsed.field_set & (1 << uparser.UF_PATH): + off = parsed.field_data[<int>uparser.UF_PATH].off + ln = parsed.field_data[<int>uparser.UF_PATH].len + path = buf_data[off:off+ln] + + if parsed.field_set & (1 << uparser.UF_QUERY): + off = parsed.field_data[<int>uparser.UF_QUERY].off + ln = parsed.field_data[<int>uparser.UF_QUERY].len + query = buf_data[off:off+ln] + + if parsed.field_set & (1 << uparser.UF_FRAGMENT): + off = parsed.field_data[<int>uparser.UF_FRAGMENT].off + ln = parsed.field_data[<int>uparser.UF_FRAGMENT].len + fragment = buf_data[off:off+ln] + + if parsed.field_set & (1 << uparser.UF_USERINFO): + off = parsed.field_data[<int>uparser.UF_USERINFO].off + ln = parsed.field_data[<int>uparser.UF_USERINFO].len + userinfo = buf_data[off:off+ln] + + return URL(schema, host, port, path, query, fragment, userinfo) + else: + raise HttpParserInvalidURLError("invalid url {!r}".format(url)) + finally: + PyBuffer_Release(&py_buf) + PyMem_Free(parsed) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/pytest.ini new/httptools-0.3.0/pytest.ini --- old/httptools-0.1.1/pytest.ini 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/pytest.ini 2021-08-10 20:09:32.000000000 +0200 @@ -1,3 +1,3 @@ [pytest] -addopts = --capture=no --assert=plain --strict --tb native +addopts = --capture=no --assert=plain --strict-markers --tb=native --import-mode=importlib testpaths = tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/setup.py new/httptools-0.3.0/setup.py --- old/httptools-0.1.1/setup.py 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/setup.py 2021-08-10 20:09:32.000000000 +0200 @@ -15,7 +15,7 @@ ROOT = pathlib.Path(__file__).parent -CYTHON_DEPENDENCY = 'Cython==0.29.14' +CYTHON_DEPENDENCY = 'Cython(>=0.29.24,<0.30.0)' class httptools_build_ext(build_ext): @@ -26,6 +26,8 @@ 'Produce a colorized HTML version of the Cython source.'), ('cython-directives=', None, 'Cythion compiler directives'), + ('use-system-llhttp', None, + 'Use the system provided llhttp, instead of the bundled one'), ('use-system-http-parser', None, 'Use the system provided http-parser, instead of the bundled one'), ] @@ -33,6 +35,7 @@ boolean_options = build_ext.boolean_options + [ 'cython-always', 'cython-annotate', + 'use-system-llhttp', 'use-system-http-parser', ] @@ -44,6 +47,7 @@ return super().initialize_options() + self.use_system_llhttp = False self.use_system_http_parser = False self.cython_always = False self.cython_annotate = None @@ -108,16 +112,34 @@ self._initialized = True def build_extensions(self): + mod_parser, mod_url_parser = self.distribution.ext_modules + if self.use_system_llhttp: + mod_parser.libraries.append('llhttp') + + if sys.platform == 'darwin' and \ + os.path.exists('/opt/local/include'): + # Support macports on Mac OS X. + mod_parser.include_dirs.append('/opt/local/include') + else: + mod_parser.include_dirs.append( + str(ROOT / 'vendor' / 'llhttp' / 'include')) + mod_parser.include_dirs.append( + str(ROOT / 'vendor' / 'llhttp' / 'src')) + mod_parser.sources.append('vendor/llhttp/src/api.c') + mod_parser.sources.append('vendor/llhttp/src/http.c') + mod_parser.sources.append('vendor/llhttp/src/llhttp.c') + if self.use_system_http_parser: - self.compiler.add_library('http_parser') + mod_url_parser.libraries.append('http_parser') if sys.platform == 'darwin' and \ os.path.exists('/opt/local/include'): # Support macports on Mac OS X. - self.compiler.add_include_dir('/opt/local/include') + mod_url_parser.include_dirs.append('/opt/local/include') else: - self.compiler.add_include_dir(str(ROOT / 'vendor' / 'http-parser')) - self.distribution.ext_modules[0].sources.append( + mod_url_parser.include_dirs.append( + str(ROOT / 'vendor' / 'http-parser')) + mod_url_parser.sources.append( 'vendor/http-parser/http_parser.c') super().build_extensions() @@ -163,6 +185,7 @@ 'Development Status :: 5 - Production/Stable', ], platforms=['macOS', 'POSIX', 'Windows'], + python_requires='>=3.5.0', zip_safe=False, author='Yury Selivanov', author_email='y...@magic.io', @@ -179,6 +202,13 @@ ], extra_compile_args=CFLAGS, ), + Extension( + "httptools.parser.url_parser", + sources=[ + "httptools/parser/url_parser.pyx", + ], + extra_compile_args=CFLAGS, + ), ], include_package_data=True, test_suite='tests.suite', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httptools-0.1.1/tests/test_parser.py new/httptools-0.3.0/tests/test_parser.py --- old/httptools-0.1.1/tests/test_parser.py 2020-02-08 00:00:33.000000000 +0100 +++ new/httptools-0.3.0/tests/test_parser.py 2021-08-10 20:09:32.000000000 +0200 @@ -103,7 +103,7 @@ with self.assertRaisesRegex( httptools.HttpParserError, - 'data received after completed connection'): + 'Expected HTTP/'): p.feed_data(b'12123123') def test_parser_response_2(self): @@ -117,7 +117,7 @@ for cbname in callbacks: with self.subTest('{} callback fails correctly'.format(cbname)): with self.assertRaisesRegex(httptools.HttpParserCallbackError, - 'callback failed'): + 'callback error'): m = mock.Mock() getattr(m, cbname).side_effect = Exception() @@ -225,7 +225,6 @@ p = httptools.HttpRequestParser(m) p.feed_data(CHUNKED_REQUEST1_1) - self.assertEqual(p.get_method(), b'POST') m.on_message_begin.assert_called_once_with() @@ -348,6 +347,11 @@ b'Host': b'example.com', b'Upgrade': b'WebSocket'}) + # The parser can be used again for further parsing - this is a legacy + # behavior from the time we were still using http-parser. + p.feed_data(CHUNKED_REQUEST1_1) + self.assertEqual(p.get_method(), b'POST') + def test_parser_request_upgrade_flag(self): class Protocol: