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 <[email protected]>
+
+- 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/[email protected]
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 [email protected]: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'))
-
-
[email protected](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',)
+
[email protected](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='[email protected]',
@@ -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: