diff --git a/.cargo/audit.toml b/.cargo/audit.toml new file mode 100644 index 0000000..e796859 --- /dev/null +++ b/.cargo/audit.toml @@ -0,0 +1,35 @@ +# audit config file +# +# It may be located in the user home (`~/.cargo/audit.toml`) or in the project +# root (`.cargo/audit.toml`). +# +# All of the options which can be passed via CLI arguments can also be +# permanently specified in this file. +# It may be located in the user home (`~/.cargo/audit.toml`) or in the project +# root (`.cargo/audit.toml`). + +[advisories] +ignore = ["RUSTSEC-2020-0071"] + +# # Advisory Database Configuration +# [database] +# path = "~/.cargo/advisory-db" # Path where advisory git repo will be cloned +# url = "https://github.com/RustSec/advisory-db.git" # URL to git repo +# fetch = true # Perform a `git fetch` before auditing (default: true) +# stale = false # Allow stale advisory DB (i.e. no commits for 90 days, default: false) + +# # Output Configuration +# [output] +# deny = ["unmaintained"] # exit on error if unmaintained dependencies are found +# format = "terminal" # "terminal" (human readable report) or "json" +# quiet = false # Only print information on error +# show_tree = true # Show inverse dependency trees along with advisories (default: true) + +# # Target Configuration +# [target] +# arch = "x86_64" # Ignore advisories for CPU architectures other than this one +# os = "linux" # Ignore advisories for operating systems other than this one + +# [yanked] +# enabled = true # Warn for yanked crates in Cargo.lock (default: true) +# update_index = true # Auto-update the crates.io index (default: true) diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..8d1dff8 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,8 @@ +# +# https://stackoverflow.com/questions/28124221/error-linking-with-cc-failed-exit-code-1 +# +[target.x86_64-apple-darwin] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d4fc4b5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +**/.idea +.cargo +.dockerignore +.fossa.yml +.git +.github +CHANGELOG.md +CODE_OF_CONDUCT.md +CONTRIBUTING.md +Containerfile.debian +Dockerfile +GNUmakefile +LICENSE.txt +README.md +SECURITY.md +build + +.vscode/** + +**/target +target + +.cache/ +.nginx/ diff --git a/.fossa.yml b/.fossa.yml new file mode 100644 index 0000000..ef1059f --- /dev/null +++ b/.fossa.yml @@ -0,0 +1,11 @@ +version: 3 + +project: + id: github.com/nginxinc/ngx-rust + name: ngx-rust + url: github.com/nginxinc/ngx-rust + +paths: + exclude: + - ./cache + - ./nginx diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..a85753e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. See my project that depends on this project +2. View logs on '....' +3. Run this test I created +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Your environment** +* Version of the repo - a specific commit or tag +* Version of ngx-rust +* Version of Rust +* Version of NGINX (and all dependencies if modified) +* OS and distribution +* Details about containerization, virtualization, or physical environment + +**Additional context** +Add any other context about the problem here. Any log files you want to share. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6bcce42 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..47781ac --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +### Proposed changes +Describe the use case and detail of the change. If this PR addresses an issue +on GitHub, make sure to include a link to that issue here in this description +(not in the title of the PR). + +### Checklist +Before creating a PR, run through this checklist and mark each as complete. + +- [ ] I have written my commit messages in the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format. +- [ ] I have read the [CONTRIBUTING](/CONTRIBUTING.md) doc +- [ ] I have added tests (when possible) that prove my fix is effective or that my feature works +- [ ] I have checked that all unit tests pass after adding my changes +- [ ] I have updated necessary documentation +- [ ] I have rebased my branch onto master +- [ ] I will ensure my PR is targeting the main branch and pulling from my branch from my own fork diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d5c3ffd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/cargo-audit.yaml b/.github/workflows/cargo-audit.yaml new file mode 100644 index 0000000..4974b63 --- /dev/null +++ b/.github/workflows/cargo-audit.yaml @@ -0,0 +1,14 @@ +name: Security audit +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..31fc6f4 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,128 @@ +name: Rust + +on: [ push, pull_request ] + +env: + CARGO_TERM_COLOR: always + +jobs: + test-linux: + name: Test (Linux) + runs-on: ubuntu-latest + steps: + - name: checkout source + uses: actions/checkout@v3 + - name: set up cargo cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + .cache/cargo/registry/index/ + .cache/cargo/registry/cache/ + .cache/cargo/git/db/ + key: ${{ runner.os }}-cargo + restore-keys: ${{ runner.os }}-cargo + - name: set up nginx source binary cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + .cache/.gnupg + .cache/*.tar.gz + .cache/*.tar.asc + .cache/*.tar.sig + key: nginx-${{ hashFiles('**/nginx-sys/build.rs') }} + restore-keys: nginx- + - name: run tests in container + run: make container-test + + test-macos: + name: Test (MacOS) + runs-on: macos-latest + steps: + - name: install GNU make 4 + run: brew install make openssl + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: set up cargo cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo + restore-keys: ${{ runner.os }}-cargo + - name: set up nginx source binary cache + uses: actions/cache@v3 + continue-on-error: false + with: + path: | + .cache/.gnupg + .cache/*.tar.gz + .cache/*.tar.asc + .cache/*.tar.sig + key: nginx-${{ hashFiles('**/nginx-sys/build.rs') }} + restore-keys: nginx- + - name: run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + +# clippy: +# name: Clippy +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - uses: actions-rs/toolchain@v1 +# with: +# profile: minimal +# toolchain: stable +# override: true +# - run: rustup component add clippy +# - uses: actions-rs/cargo@v1 +# with: +# command: clippy +# args: -- -D warnings + +# docs: +# name: Docs +# runs-on: ubuntu-latest +# steps: +# - name: Checkout repository +# uses: actions/checkout@v3 +# - name: Install Rust +# uses: actions-rs/toolchain@v1 +# with: +# toolchain: stable +# profile: minimal +# override: true +# - name: Check documentation +# env: +# RUSTDOCFLAGS: -D warnings +# uses: actions-rs/cargo@v1 +# with: +# command: doc +# args: --no-deps --document-private-items diff --git a/.github/workflows/fossa.yaml b/.github/workflows/fossa.yaml new file mode 100644 index 0000000..c242914 --- /dev/null +++ b/.github/workflows/fossa.yaml @@ -0,0 +1,18 @@ +name: License Scanning + +on: + - pull_request + - push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Run FOSSA scan and upload build data + uses: fossa-contrib/fossa-action@v1 + with: + fossa-api-key: ${{ secrets.FOSSA_API_KEY }} diff --git a/.gitignore b/.gitignore index fe91bc8..0a75edb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,256 @@ -target -.idea -.vscode -nginx +# Created by https://www.toptal.com/developers/gitignore/api/c,rust,intellij+all,visualstudiocode,vim,emacs +# Edit at https://www.toptal.com/developers/gitignore?templates=c,rust,intellij+all,visualstudiocode,vim,emacs + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/c,rust,intellij+all,visualstudiocode,vim,emacs + +.vscode/c_cpp_properties.json +.cache/ +.nginx/ +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ba81188 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +All notable changes to this project will be documented in this file. + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4547fd8 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,129 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement as listed on the +github project page. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5242d62 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing Guidelines + +The following is a set of guidelines for contributing. We really appreciate that you are considering contributing! + +#### Table Of Contents + +[Ask a Question](#ask-a-question) + +[Contributing](#contributing) + +[Style Guides](#style-guides) + * [Git Style Guide](#git-style-guide) + * [Rust Style Guide](#rust-style-guide) + +[Code of Conduct](CODE_OF_CONDUCT.md) + +## Ask a Question + +Please ask your question on github using discussions. + +## Contributing + +### Report a Bug + +To report a bug, open an issue on GitHub with the label `bug` using the available bug report issue template. Please ensure the issue has not already been reported. + +### Suggest an Enhancement + +To suggest an enhancement, please create an issue on GitHub with the label `enhancement` using the available feature issue template. + +### Open a Pull Request + +* Fork the repo, create a branch, submit a PR when your changes are tested and ready for review. +* Fill in [our pull request template](/.github/PULL_REQUEST_TEMPLATE.md) + +Note: if you’d like to implement a new feature, please consider creating a feature request issue first to start a discussion about the feature. + +## Style Guides + +### Git Style Guide + +* Keep a clean, concise and meaningful git commit history on your branch, rebasing locally and squashing before submitting a PR +* Use the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format when writing a commit message, so that changelogs can be automatically generated +* Follow the guidelines of writing a good commit message as described [here](https://chris.beams.io/posts/git-commit/) and summarised in the next few points + * In the subject line, use the present tense ("Add feature" not "Added feature") + * In the subject line, use the imperative mood ("Move cursor to..." not "Moves cursor to...") + * Limit the subject line to 72 characters or less + * Reference issues and pull requests liberally after the subject line + * Add more detailed description in the body of the git message (`git commit -a` to give you more space and time in your text editor to write a good message instead of `git commit -am`) + +### Rust Style Guide + +* Rust code should be checked in after `cargo fmt` has been run. +* The code style broadly complies with the [official Rust Style Guide](https://doc.rust-lang.org/style-guide/index.html). +* Where feasible, include unit tests. diff --git a/Cargo.lock b/Cargo.lock index 74c5f79..c751c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,748 +1,1078 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] -name = "aho-corasick" -version = "0.6.3" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] -name = "ansi_term" -version = "0.9.0" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "aster" -version = "0.41.0" +name = "aws-sign-v4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93df874cdba37261e8d371d427d8ba77c753febe271b8f484349af6dceea0e48" dependencies = [ - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", + "hex", + "http", + "ring", + "sha256", + "url", ] [[package]] -name = "atty" -version = "0.2.3" +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bindgen" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", ] [[package]] -name = "base64" +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] -name = "bindgen" -version = "0.30.0" +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ - "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", ] [[package]] -name = "bitflags" -version = "0.7.0" +name = "clang-sys" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] [[package]] -name = "bitflags" -version = "0.8.2" +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] -name = "bitflags" -version = "0.9.1" +name = "cpufeatures" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] [[package]] -name = "byteorder" -version = "1.1.0" +name = "crc32fast" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] [[package]] -name = "bytes" -version = "0.4.5" +name = "cxx" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", ] [[package]] -name = "cexpr" -version = "0.2.2" +name = "cxx-build" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ - "nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.13", ] [[package]] -name = "cfg-if" -version = "0.1.2" +name = "cxxbridge-flags" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] -name = "clang-sys" -version = "0.19.0" +name = "cxxbridge-macro" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn 2.0.13", ] [[package]] -name = "clap" -version = "2.27.1" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] -name = "env_logger" -version = "0.4.3" +name = "duct" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ae3fc31835f74c2a7ceda3aeede378b0ae2e74c8f1c36559fcc9ae2a4e7d3e" dependencies = [ - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "once_cell", + "os_pipe", + "shared_child", ] [[package]] -name = "fuchsia-zircon" -version = "0.2.1" +name = "either" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "examples" +version = "0.0.0" dependencies = [ - "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-sign-v4", + "chrono", + "http", + "libc", + "ngx", ] [[package]] -name = "fuchsia-zircon-sys" -version = "0.2.0" +name = "filetime" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast", + "miniz_oxide", ] [[package]] -name = "futures" -version = "0.1.17" +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] [[package]] -name = "futures-cpupool" -version = "0.1.7" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", + "version_check", ] [[package]] name = "glob" -version = "0.2.11" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "httparse" -version = "1.2.3" +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] [[package]] -name = "hyper" -version = "0.11.7" +name = "iana-time-zone" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ - "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", ] [[package]] -name = "iovec" +name = "iana-time-zone-haiku" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cxx", + "cxx-build", ] [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "idna" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi", + "unicode-normalization", ] [[package]] -name = "language-tags" -version = "0.2.2" +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "lazy_static" -version = "0.2.11" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "0.5.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.33" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libloading" -version = "0.4.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "winapi", ] [[package]] -name = "log" -version = "0.3.8" +name = "link-cplusplus" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] [[package]] -name = "memchr" -version = "1.0.2" +name = "log" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] -name = "mime" -version = "0.3.5" +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ - "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "adler", ] [[package]] -name = "mio" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "nginx-sys" +version = "0.1.0" dependencies = [ - "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", + "duct", + "flate2", + "tar", + "ureq", + "which", ] [[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "ngx" +version = "0.3.0-beta" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", + "duct", + "flate2", + "nginx-sys", + "tar", + "ureq", + "which", ] [[package]] -name = "net2" -version = "0.2.31" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", + "minimal-lexical", ] [[package]] -name = "ngx_rust" -version = "0.1.2" +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] -name = "nom" -version = "3.2.1" +name = "num-traits" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] -name = "num_cpus" -version = "1.7.0" +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_pipe" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12" dependencies = [ - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "windows-sys", ] [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "1.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] -name = "quasi" -version = "0.32.0" +name = "proc-macro2" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident", ] [[package]] -name = "quasi_codegen" -version = "0.32.0" +name = "quote" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ - "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] -name = "rand" -version = "0.3.18" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] -name = "redox_syscall" -version = "0.1.31" +name = "regex" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "regex-syntax", +] [[package]] -name = "redox_termios" -version = "0.1.1" +name = "regex-syntax" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", ] [[package]] -name = "regex" -version = "0.2.2" +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustls" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ - "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "ring", + "sct", + "webpki", ] [[package]] -name = "regex-syntax" -version = "0.4.1" +name = "scratch" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] -name = "relay" -version = "0.1.0" +name = "sct" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] -name = "rustc-serialize" -version = "0.3.24" +name = "sha2" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] [[package]] -name = "safemem" -version = "0.2.0" +name = "sha256" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328169f167261957e83d82be47f9e36629e257c62308129033d7f7e7c173d180" +dependencies = [ + "hex", + "sha2", +] [[package]] -name = "scoped-tls" -version = "0.1.0" +name = "shared_child" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi", +] [[package]] -name = "slab" -version = "0.3.0" +name = "shlex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] -name = "slab" -version = "0.4.0" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "smallvec" -version = "0.2.1" +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] -name = "strsim" -version = "0.6.0" +name = "syn" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] -name = "syntex" -version = "0.58.1" +name = "tar" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" dependencies = [ - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime", + "libc", + "xattr", ] [[package]] -name = "syntex_errors" -version = "0.58.1" +name = "termcolor" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] -name = "syntex_pos" -version = "0.58.1" +name = "time" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "wasi", + "winapi", ] [[package]] -name = "syntex_syntax" -version = "0.58.1" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tinyvec_macros", ] [[package]] -name = "take" -version = "0.1.0" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "term" -version = "0.4.6" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tinyvec", ] [[package]] -name = "termion" -version = "1.5.1" +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "ureq" +version = "2.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" dependencies = [ - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "url", + "webpki", + "webpki-roots", ] [[package]] -name = "textwrap" -version = "0.9.0" +name = "url" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "form_urlencoded", + "idna", + "percent-encoding", ] [[package]] -name = "thread_local" -version = "0.3.4" +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "wasm-bindgen-macro", ] [[package]] -name = "time" -version = "0.1.38" +name = "wasm-bindgen-backend" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", ] [[package]] -name = "tokio-core" -version = "0.1.10" +name = "wasm-bindgen-macro" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "tokio-io" -version = "0.1.4" +name = "wasm-bindgen-macro-support" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] -name = "tokio-proto" -version = "0.1.1" +name = "wasm-bindgen-shared" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "tokio-service" -version = "0.1.0" +name = "webpki" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ - "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] -name = "unicase" -version = "2.1.0" +name = "webpki-roots" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki", ] [[package]] -name = "unicode-width" -version = "0.1.4" +name = "which" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] [[package]] -name = "unicode-xid" -version = "0.0.4" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] [[package]] -name = "unreachable" -version = "1.0.0" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] -name = "utf8-ranges" -version = "1.0.0" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "vec_map" -version = "0.8.0" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] [[package]] -name = "version_check" -version = "0.1.3" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] [[package]] -name = "void" -version = "1.0.2" +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] [[package]] -name = "which" -version = "1.0.3" +name = "windows-targets" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] -name = "winapi" -version = "0.2.8" +name = "windows_aarch64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] -name = "winapi-build" -version = "0.1.1" +name = "windows_aarch64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" -"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" -"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0" -"checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" -"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" -"checksum bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33024f55a754d920637461adf87fb485702a69bdf7ac1d307b7e18da93bae505" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" -"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" -"checksum cexpr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cdbb21df6ff3497a61df5059994297f746267020ba38ce237aad9c875f7b4313" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a" -"checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" -"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" -"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" -"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" -"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" -"checksum futures-cpupool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e86f49cc0d92fe1b97a5980ec32d56208272cbb00f15044ea9e2799dde766fdf" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" -"checksum hyper 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4959ca95f55df4265bff2ad63066147255e6fa733682cf6d1cb5eaff6e53324b" -"checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" -"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" -"checksum libloading 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3f92926a9a4ba7aeeb01f5fba3f0d577147243b6e7fa8261c219cd1d6fbe3b1c" -"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" -"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" -"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" -"checksum mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0e8411968194c7b139e9105bc4ae7db0bae232af087147e72f0616ebf5fdb9cb" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" -"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" -"checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" -"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" -"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" -"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" -"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" -"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" -"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" -"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" -"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" -"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" -"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e" -"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c" -"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047" -"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791" -"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" -"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" -"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" -"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" -"checksum tokio-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c843a027f7c1df5f81e7734a0df3f67bf329411781ebf36393ce67beef6071e3" -"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743" -"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" -"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" -"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" -"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4be6cfa54dab45266e98b5d7be2f8ce959ddd49abd141a05d52dce4b07f803bb" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index 1124e3a..1ee7bb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,25 @@ [workspace] members = [ - "ngx-binding" + "nginx-sys", + "examples", ] + +[package] +name = "ngx" +version = "0.3.0-beta" +edition = "2021" +autoexamples = false +license = "Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nginx-sys = { path = "nginx-sys"} + +[build-dependencies] +bindgen = "0.64.0" +which = "4.4.0" +duct = "0.13.6" +ureq = { version = "2.6.2", features = ["tls"] } +flate2 = "1.0.25" +tar = "0.4.38" diff --git a/Containerfile.debian b/Containerfile.debian new file mode 100644 index 0000000..7d8bca4 --- /dev/null +++ b/Containerfile.debian @@ -0,0 +1,16 @@ +FROM rust:slim-bullseye + +RUN set -eux \ + export DEBIAN_FRONTEND=noninteractive; \ + apt-get -qq update; \ + apt-get -qq install --yes --no-install-recommends --no-install-suggests \ + libclang-dev \ + libssl-dev \ + pkg-config \ + git \ + grep \ + gawk \ + gnupg2 \ + sed \ + make; \ + git config --global --add safe.directory /project \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dad9bfe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +ARG NGX_VERSION=1.23.3 +ARG NGX_DEBUG=false + +# --- builder: build all examples +FROM rust:slim-bullseye as build +ARG NGX_VERSION +ARG NGX_DEBUG +WORKDIR /project +RUN --mount=type=cache,target=/var/cache/apt < /dev/null || command -v grep 2> /dev/null) +SED ?= $(shell command -v gsed 2> /dev/null || command -v sed 2> /dev/null) +AWK ?= $(shell command -v gawk 2> /dev/null || command -v awk 2> /dev/null) +VERSION ?= $(shell $(GREP) -Po '^version\s+=\s+"\K.*?(?=")' $(CURDIR)/Cargo.toml) +CARGO ?= cargo +DOCKER ?= docker +DOCKER_BUILD_FLAGS ?= --load +COMMITSAR_DOCKER := $(DOCKER) run --tty --rm --workdir /src -v "$(CURDIR):/src" aevea/commitsar +COMMITSAR ?= $(shell command -v commitsar 2> /dev/null) +PROJECT_NAME ?= ngx-rust +GITHUB_REPOSITORY ?= nginxinc/$(PROJECT_NAME) +SRC_REPO := https://github.com/$(GITHUB_REPOSITORY) + +RELEASE_BUILD_FLAGS ?= --quiet --release + +Q = $(if $(filter 1,$V),,@) +M = $(shell printf "\033[34;1m▶\033[0m") + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + FEATURES += --features=linux +endif + +# Use docker based commitsar if it isn't in the path +ifeq ($(COMMITSAR),) + COMMITSAR = $(COMMITSAR_DOCKER) +endif + +.PHONY: help +help: + @$(GREP) --no-filename -E '^[ a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ + $(AWK) 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-28s\033[0m %s\n", $$1, $$2}' | sort + +.PHONY: clean +clean: clean-cache; $(info $(M) cleaning...) @ ## Cleanup everything + $Q rm -rf $(CURDIR)/target + $Q $(CARGO) clean + +.PHONY: clean-cache +clean-cache: ## Remove all cached dependencies and build artifacts + $(Q) rm -rf $(CACHE_DIR) + +.PHONY: commitsar +commitsar: ## Run git commit linter + $Q $(info $(M) running commitsar...) + $(COMMITSAR) + +target: + $Q mkdir -p $@ + +.PHONY: debug +debug: target/debug ## Build current platform target in debug mode + +target/debug: + $Q echo "$(M) building in debug mode for the current platform" + $Q $(CARGO) build --quiet + +.PHONY: release +release: target/release ## Build current platform target in release mode + +target/release: + $Q echo "$(M) building in release mode for the current platform" + $Q $(CARGO) build $(RELEASE_BUILD_FLAGS) + +.PHONY: test +test: ## Run tests + $Q $(CARGO) test + +.PHONY: format +format: ## Run rustfmt + $Q $(CARGO) fmt + +.PHONY: lint +lint: ## Run clippy + $Q $(CARGO) clippy + +.PHONY: examples-debug +examples-debug: + $Q echo "$(M) building all examples as debug" + $Q $(CARGO) build --quiet --package=examples --examples $(FEATURES) + +include $(CURDIR)/build/container.mk +include $(CURDIR)/build/release.mk +include $(CURDIR)/build/github.mk diff --git a/Makefile b/Makefile deleted file mode 100644 index 47f489a..0000000 --- a/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -clean: - cargo clean - make -C ngx-binding clean diff --git a/README.md b/README.md index 58e8628..4c6c6f8 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,147 @@ -# Rust for NGINX +[![Rust](https://github.com/nginxinc/ngx-rust/actions/workflows/ci.yaml/badge.svg)](https://github.com/nginxinc/ngx-rust/actions/workflows/ci.yaml) -Rust bindings and wrappers for NGINX. Can be used for building dynamic modules and hacking NGINX using rust. +## Project status +This project is still a work in progress and not production ready. -## Production Status +# Description -This version is proof of concept. It has enough binding for building modules for [nginmesh](https://github.com/nginxinc/nginmesh). +This project provides Rust SDK interfaces to the [NGINX](https://nginx.com) proxy allowing the creation of NGINX +dynamic modules completely in Rust. -You still need to write C stub code to build the complete module. Please wait for next version which will remove this restriction. +In short, this SDK allows writing NGINX modules using the Rust language. -## Getting Started +## Build -Add the following dependency to your Cargo manifest... +NGINX modules can be build against a particular version of NGINX. The following environment variables can be used to +specify particular version of NGINX or an NGINX dependency: -```toml -[dependencies] -ngx_rust = "0.1.1" +* `ZLIB_VERSION` (default 1.2.13) - +* `PCRE2_VERSION` (default 10.42) +* `OPENSSL_VERSION` (default 3.0.7) +* `NGX_VERSION` (default 1.23.3) - NGINX OSS version +* `NGX_DEBUG` (default to false)- if set to true, then will compile NGINX `--with-debug` option + +For example, this is how you would compile the [examples](examples) using a specific version of NGINX and enabling +debugging: +``` +NGX_DEBUG=true NGX_VERSION=1.23.0 cargo build --package=examples --examples --release +``` + +To build Linux-only modules, use the "linux" feature: +``` +cargo build --package=examples --examples --features=linux --release ``` -Next, add this to your crate: +After compilation, the modules can be found in the path `target/release/examples/` ( with the `.so` file extension for +Linux or `.dylib` for MacOS). + +Additionally, the folder `.cache/nginx/{NGX_VERSION}/{OS}/` will contain the compiled version of NGINX used to build +the SDK. You can start NGINX directly from this directory if you want to test the module. + +### Mac OS dependencies + +In order to use the optional GNU make build process on MacOS, you will need to install additional tools. This can be +done via [homebrew](https://brew.sh/) with the following command: +``` +brew install make openssl grep +``` + +Additionally, you may need to set up LLVM and clang. Typically, this is done as follows: + +``` +# make sure xcode tools are installed +xcode-select --install +# instal llvm +brew install --with-toolchain llvm +``` + +### Linux dependencies + +See the [Dockerfile](Dockerfile) for dependencies as an example of required packages on Debian Linux. + +### Build example + +Example modules are available in [examples](examples) folder. You can use `cargo build --package=examples --examples` to build these examples. After building, you can find the `.so` or `.dylib` in the `target/debug` folder. Add `--features=linux` to build linux specific modules. **NOTE**: adding the "linux" feature on MacOS will cause a build failure. + +For example (all examples plus linux specific): +`cargo build --packages=examples --examples --features=linux` + +### Docker + +We provide a multistage [Dockerfile](Dockerfile): + + # build all dynamic modules examples and specify NGINX version to use + docker buildx build --build-arg NGX_VERSION=1.23.3 -t ngx-rust . + + # start NGINX using [curl](examples/curl.conf) module example: + docker run --rm -d -p 8000:8000 ngx-rust nginx -c examples/curl.conf + + # test it - you should see 403 Forbidden + curl http://127.0.0.1:8000 -v -H "user-agent: curl" + + + # test it - you should see 404 Not Found + curl http://127.0.0.1:8000 -v -H "user-agent: foo" + +## Usage + +A complete module example using the SDK can be found [here](examples/curl.rs). You can build it with +`cargo build --package=examples --example=curl` then set up NGINX to use it: + +For example: +```nginx +daemon off; +master_process off; + +# unix: +# load_module modules/libcurl.so; + +# error_log logs/error.log debug; +error_log /dev/stdout debug; + +working_directory /tmp/cores/; +worker_rlimit_core 500M; + +events { +} + +http { + access_log /dev/stdout; + server { + listen 8000; + server_name localhost; + location / { + alias /srv/http; + # ... Other config stuff ... -```rust -extern crate ngx_rust; + curl on; + } + } +} ``` -## Building module Example +## Support +This SDK is currently unstable. Right now, our primary goal is collect feedback and stabilize it be before +offering support. Feel free [contributing](CONTRIBUTING.md) by creating issues, PRs, or github discussions. -Please see [istio mixer module](https://github.com/nginxinc/ngx-istio-mixer) for full example. -Currently, it requires much machinery to build the module. +Currently, the only supported platforms are: +* Darwin (Mac OSX) +* Linux platform ## Roadmap +If you have ideas for releases in the future, please suggest them in the github discussions. -Please see [roadmap](https://github.com/nginxinc/ngx-rust/wiki) for future plans. +## Contributing +We welcome pull requests and issues! +Please refer to the [Contributing Guidelines](CONTRIBUTING.md) when doing a PR. -## Limitation +## Authors and acknowledgment +This project uses some great work from [dcoles/nginx-rs](https://github.com/dcoles/nginx-rs), +[arvancloud/nginx-rs](https://github.com/arvancloud/nginx-rs). -Only supports these platforms: -- Darwin (Mac OSX) -- Linux platform +## License +All code in this repository is licensed under the +[Apache License v2 license](LICENSE.txt). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..dd2be6b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,22 @@ +# Security Policy + +## General Guidance + +We advise users to run the most recent release of the NGINX Rust Module SDK, and we issue software updates to the +most recent release. + +## Support + +The NGINX Rust Module SDK is not officially supported. + +## Reporting a Vulnerability + +The F5 Security Incident Response Team (F5 SIRT) has an email alias that makes it easy to report potential security +vulnerabilities. + +- If you’re an F5 customer with an active support contract, please contact + [F5 Technical Support](https://www.f5.com/services/support). +- If you aren’t an F5 customer, please report any potential or current instances of security vulnerabilities with any + F5 product to the F5 Security Incident Response Team at F5SIRT@f5.com + +For more information visit https://www.f5.com/services/support/report-a-vulnerability diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index e69de29..0000000 diff --git a/build/changelog.sh b/build/changelog.sh new file mode 100755 index 0000000..7b81650 --- /dev/null +++ b/build/changelog.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# This script generates a changelog for the current version of the project. + +set -o errexit # abort on nonzero exit status +set -o nounset # abort on unbound variable +set -o pipefail # don't hide errors within pipes + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +EXCLUDED_COMMIT_TYPES="ci|chore" + +pushd . > /dev/null +cd "${SCRIPT_DIR}/.." + +if command -v ggrep > /dev/null; then + GREP=ggrep +else + GREP=grep +fi +if command -v gsed > /dev/null; then + SED=gsed +else + SED=sed +fi + +# if gh is installed, use it to pull the last version number +if command -v gh > /dev/null; then + LAST_RELEASE="$(gh release list --exclude-drafts --exclude-pre-releases --limit 1 | ${GREP} -E 'v[0-9]+\.[0-9]+\.[0-9]+' | cut -f1 | ${GREP} -v "${VERSION}" || true)" +else + LAST_RELEASE="$(git tag --list v* | ${GREP} -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort --version-sort --field-separator=. --reverse | ${GREP} -v "${VERSION}" | head -n1 || true)" +fi + +if [ -z "${LAST_RELEASE}" ]; then + echo "## Initial release ${VERSION}" + git log --format="%s (%h)" | \ + ${GREP} -E -v "^(${EXCLUDED_COMMIT_TYPES}): .*" | \ + ${SED} 's/: /:\t/g1' | \ + column -s " " -t | \ + ${SED} -e 's/^/ * /' +else + LAST_RELEASE_HASH="$(git show --format=%H "${LAST_RELEASE}" | head -n1 | ${SED} -e 's/^tag //')" + + echo "## Changes between ${LAST_RELEASE} [$LAST_RELEASE_HASH] and ${VERSION}:" + git log --format="%s (%h)" "${LAST_RELEASE_HASH}..HEAD" | \ + ${GREP} -E -v "^(${EXCLUDED_COMMIT_TYPES}): .*" | \ + ${SED} 's/: /:\t/g1' | \ + column -s " " -t | \ + ${SED} -e 's/^/ * /' +fi + +echo "" +popd > /dev/null \ No newline at end of file diff --git a/build/container.mk b/build/container.mk new file mode 100644 index 0000000..2fecdcd --- /dev/null +++ b/build/container.mk @@ -0,0 +1,28 @@ +.PHONY: container-debian-build-image +.ONESHELL: container-debian-build-image +container-debian-build-image: +container-debian-build-image: ## Builds a container image for building on Debian Linux + $Q echo "$(M) building debian linux docker build image: $(@)" + $(DOCKER) buildx build $(DOCKER_BUILD_FLAGS) -t debian-ngx-rust-builder -f Containerfile.debian $(CURDIR); + +.PHONY: container-test +container-test: container-debian-build-image ## Run tests inside container + $Q mkdir -p .cache/cargo nginx-sys/.nginx + $(DOCKER) run --rm --volume "$(CURDIR):/project" --workdir /project --env 'CARGO_HOME=/project/.cache/cargo' debian-ngx-rust-builder make test + # Reset permissions on the target directory to the current user + if command -v id > /dev/null; then \ + $(DOCKER) run --rm --volume "$(CURDIR):/project" --workdir /project debian-ngx-rust-builder chown --silent --recursive "$(shell id -u):$(shell id -g)" /project/target /project/.cache /project/nginx-sys/.nginx + fi + +.PHONY: container-shell +container-shell: container-debian-build-image ## Start a shell inside container + $Q mkdir -p .cache/cargo nginx-sys/.nginx + $(DOCKER) run -it --rm --volume "$(CURDIR):/project" --workdir /project --env 'CARGO_HOME=/project/.cache/cargo' debian-ngx-rust-builder bash + # Reset permissions on the target directory to the current user + if command -v id > /dev/null; then \ + $(DOCKER) run --rm --volume "$(CURDIR):/project" --workdir /project debian-ngx-rust-builder chown --silent --recursive "$(shell id -u):$(shell id -g)" /project/target /project/.cache /project/nginx-sys/.nginx + fi + +.PHONY: build-docker +build-docker: ## build docker image with all example modules + $(DOCKER) buildx build $(DOCKER_BUILD_FLAGS) -t $(PROJECT_NAME) . diff --git a/build/github.mk b/build/github.mk new file mode 100644 index 0000000..5d61f4e --- /dev/null +++ b/build/github.mk @@ -0,0 +1,17 @@ +.PHONY: gh-make-release +.ONESHELL: gh-make-release +gh-make-release: +ifndef CI + $(error must be running in CI) +endif +ifneq ($(shell git rev-parse --abbrev-ref HEAD),release-v$(VERSION)) + $(error must be running on release-v$(VERSION) branch) +endif + $(info $(M) updating files with release version [$(GIT_BRANCH)]) @ + git commit -m "ci: update files to version $(VERSION)" Cargo.toml nginx-sys/Cargo.toml + git push origin "release-v$(VERSION)" + git tag -a "v$(VERSION)" -m "ci: tagging v$(VERSION)" + git push origin --tags + gh release create "v$(VERSION)" \ + --title "v$(VERSION)" \ + --notes-file $(CURDIR)/target/dist/release_notes.md \ No newline at end of file diff --git a/build/release.mk b/build/release.mk new file mode 100644 index 0000000..4790a56 --- /dev/null +++ b/build/release.mk @@ -0,0 +1,48 @@ +target/dist: + $Q mkdir -p target/dist + +.PHONY: changelog +.ONESHELL: changelog +changelog: ## Outputs the changes since the last version committed + $Q VERSION="$(VERSION)" $(CURDIR)/build/changelog.sh + +.ONESHELL: target/dist/release_notes.md +target/dist/release_notes.md: target/dist + $(info $(M) building release notes) @ + $Q echo "# Release Notes" > target/dist/release_notes.md + VERSION="$(VERSION)" $(CURDIR)/build/changelog.sh >> target/dist/release_notes.md + +.PHONY: release-notes +release-notes: target/dist/release_notes.md ## Build release notes + +.PHONY: version +version: ## Outputs the current version + $Q echo "Version: $(VERSION)" + +.PHONY: version-update +.ONESHELL: version-update +version-update: ## Prompts for a new version + $(info $(M) updating repository to new version) @ + $Q echo " last committed version: $(LAST_VERSION)" + $Q echo " Cargo.toml file version : $(VERSION)" + read -p " Enter new version in the format (MAJOR.MINOR.PATCH): " version + $Q echo "$$version" | $(GREP) -qE '^[0-9]+\.[0-9]+\.[0-9]+-?.*$$' || \ + (echo "invalid version identifier: $$version" && exit 1) && \ + $(SED) -i "s/^version\s*=.*$$/version = \"$$version\"/" $(CURDIR)/Cargo.toml + $(SED) -i "s/^version\s*=.*$$/version = \"$$version\"/" $(CURDIR)/nginx-sys/Cargo.toml + @ VERSION=$(shell $(GREP) -Po '^version\s+=\s+"\K.*?(?=")' $(CURDIR)/Cargo.toml) + +.PHONY: version-release +.ONESHELL: version-release +version-release: ## Change from a pre-release to full release version + $Q echo "$(VERSION)" | $(GREP) -qE '^[0-9]+\.[0-9]+\.[0-9]+-beta$$' || \ + (echo "invalid version identifier - must contain suffix -beta: $(VERSION)" && exit 1) + export NEW_VERSION="$(shell echo $(VERSION) | $(SED) -e 's/-beta$$//')" + $(SED) -i "s/^version\s*=.*$$/version = \"$$version\"/" $(CURDIR)/Cargo.toml + $(SED) -i "s/^version\s*=.*$$/version = \"$$version\"/" $(CURDIR)/nginx-sys/Cargo.toml + @ VERSION=$(shell $(GREP) -Po '^version\s+=\s+"\K.*?(?=")' $(CURDIR)/Cargo.toml) + +.PHONY: cargo-release +cargo-release: ## Releases a new version to crates.io + $(info $(M) releasing version $(VERSION) to crates.io) @ + $Q $(CARGO) publish \ No newline at end of file diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 0000000..7759359 --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "examples" +version = "0.0.0" +publish = false +edition = "2021" +license = "Apache-2.0" + +[dev-dependencies] +ngx = { path = "../" } +aws-sign-v4 = "0.2.0" +chrono = "0.4.23" +http = "0.2.9" +libc = "0.2.140" + +[[example]] +name = "curl" +path = "curl.rs" +crate-type = ["cdylib"] + +[[example]] +name = "awssig" +path = "awssig.rs" +crate-type = ["cdylib"] + + + +[[example]] +name = "httporigdst" +path = "httporigdst.rs" +crate-type = ["cdylib"] +required-features = ["linux"] + +[features] +linux = [] diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a4d7a0f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,101 @@ +# Examples + +## NGINX Destination IP recovery module for HTTP + +This dynamic module recovers original IP address and port number of the destination packet. It is useful, for example, with container sidecars where all outgoing traffic is redirected to a separate container with iptables before reaching the target. + +This module can only be built with the "linux" feature enabled, and will only successfully build on a Linux OS. + +### Dependencies + +This modules uses the Rust crate libc and Linux **getsockopt** socket API. + +### Example Configuration +#### HTTP + +```nginx configuration +load_module "modules/libhttporigdst.so"; + +http { + server { + # use iptables to capture all outgoing traffic and REDIRECT + # to listening port 15501 + listen 15501; + + # binding variables provided by module will lazily activate it + # and store a context + # variables can be used in config + location / { + # Return if no backend is available or proxy_pass + # return 200 "recv'd: $server_addr:$server_port\n\nproxy_pass http://$server_orig_addr:$server_orig_port\n"; + proxy_pass http://$server_orig_addr:$server_orig_port; + } + } +} +``` + +### Embedded Variables + +The following embedded variables are provided: + +* **server_orig_addr** + * Original IP address +* **server_orig_port** + * Original port + +### Usage + +1. Clone the git repository. + ``` + https://github.com/nginxinc/ngx-rust + ``` + +2. Compile the module from the cloned repo. + ``` + cd ${CLONED_DIRECTORY}/ngx-rust + cargo build --package=examples --example=httporigdst --features=linux + ``` + +3. Copy the shared object to the modules directory, /etc/nginx/modules. + ``` + cp ./target/debug/examples/libhttporigdst.so /etc/nginx/modules + ``` + +4. Add the `load_module` directive to your configuration. + ``` + load_module "modules/libhttporigdst.so"; + ``` + +5. Reload NGINX. + ``` + nginx -t && nginx -s reload + ``` + +6. Redirect traffic outbound. + ``` + iptables -t nat -N NGINX_REDIRECT && \ + iptables -t nat -A NGINX_REDIRECT -p tcp -j REDIRECT --to-port 15501 --dport 15001 && \ + iptables -t nat -N NGINX_OUTPUT && \ + iptables -t nat -A OUTPUT -p tcp -j NGINX_OUTPUT && \ + iptables -t nat -A NGINX_OUTPUT -j NGINX_REDIRECT + ``` + +7. Redirect traffic inbound. + ``` + iptables -t nat -N NGINX_IN_REDIRECT && \ + iptables -t nat -A NGINX_IN_REDIRECT -p tcp -j REDIRECT --to-port 15501 --dport 15001 && \ + iptables -t nat -N NGINX_INBOUND && \ + iptables -t nat -A PREROUTING -p tcp -j NGINX_INBOUND && \ + iptables -t nat -A NGINX_INBOUND -p tcp -j NGINX_IN_REDIRECT + ``` + +8. Test with `curl` (this step assumes you've uncommented the return directive). + ``` + curl --output - ${LISTEN_IP}:15001 + recv'd: ${LISTEN_IP}:15501 + + proxy_pass http://${LISTEN_IP}:15001 + ``` +### Caveats + +This module only supports IPv4. diff --git a/examples/awssig.conf b/examples/awssig.conf new file mode 100644 index 0000000..5ff331e --- /dev/null +++ b/examples/awssig.conf @@ -0,0 +1,50 @@ +daemon off; +master_process off; +# worker_processes 1; + +# on linix load a module: +load_module modules/libawssig.so; + +# error_log /dev/stdout debug; +error_log error.log debug; + +# working_directory /tmp/cores/; +# worker_rlimit_core 500M; + +events { } + +http { + server { + listen *:8000; + server_name localhost; + + awssigv4_access_key my-access-key; + awssigv4_secret_key my-secret-key; + awssigv4_s3_bucket my-bucket; + + location / { + awssigv4 on; + proxy_pass http://localhost:8777; + ## (on | off ) to enable aws sig v4 + location /some { + awssigv4 off; + } + ## awssigv4_s3_endpoint if not set then 's3.amazonaws.com' + # awssigv4_s3_endpoint s3.amazonaws.com; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } + server { + listen 8777; + server_name localhost; + location / { + add_header x-authorization $http_authorization; + add_header x-Amz-Date $http_x_amz_date; + return 204; + } + } +} diff --git a/examples/awssig.rs b/examples/awssig.rs new file mode 100644 index 0000000..e5aa70d --- /dev/null +++ b/examples/awssig.rs @@ -0,0 +1,345 @@ +use http::HeaderMap; +use ngx::ffi::{ + nginx_version, ngx_array_push, ngx_command_t, ngx_conf_t, ngx_http_core_module, ngx_http_handler_pt, + ngx_http_module_t, ngx_http_phases_NGX_HTTP_PRECONTENT_PHASE, ngx_http_request_t, ngx_int_t, ngx_module_t, + ngx_str_t, ngx_uint_t, NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_MODULE, NGX_HTTP_SRV_CONF, + NGX_RS_HTTP_LOC_CONF_OFFSET, NGX_RS_MODULE_SIGNATURE, +}; +use ngx::{core, core::Status, http::*}; +use ngx::{http_request_handler, ngx_log_debug_http, ngx_modules, ngx_null_command, ngx_string}; +use std::os::raw::{c_char, c_void}; + +struct Module; + +impl HTTPModule for Module { + type MainConf = (); + type SrvConf = (); + type LocConf = ModuleConfig; + + unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t { + let cmcf = ngx_http_conf_get_module_main_conf(cf, &ngx_http_core_module); + + let h = ngx_array_push(&mut (*cmcf).phases[ngx_http_phases_NGX_HTTP_PRECONTENT_PHASE as usize].handlers) + as *mut ngx_http_handler_pt; + if h.is_null() { + return core::Status::NGX_ERROR.into(); + } + // set an phase handler + *h = Some(awssigv4_header_handler); + core::Status::NGX_OK.into() + } +} + +#[derive(Debug, Default)] +struct ModuleConfig { + enable: bool, + access_key: String, + secret_key: String, + s3_bucket: String, + s3_endpoint: String, +} + +#[no_mangle] +static mut ngx_http_awssigv4_commands: [ngx_command_t; 6] = [ + ngx_command_t { + name: ngx_string!("awssigv4"), + type_: (NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_awssigv4_commands_set_enable), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_command_t { + name: ngx_string!("awssigv4_access_key"), + type_: (NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_awssigv4_commands_set_access_key), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_command_t { + name: ngx_string!("awssigv4_secret_key"), + type_: (NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_awssigv4_commands_set_secret_key), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_command_t { + name: ngx_string!("awssigv4_s3_bucket"), + type_: (NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_awssigv4_commands_set_s3_bucket), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_command_t { + name: ngx_string!("awssigv4_s3_endpoint"), + type_: (NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_awssigv4_commands_set_s3_endpoint), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_null_command!(), +]; + +#[no_mangle] +static ngx_http_awssigv4_module_ctx: ngx_http_module_t = ngx_http_module_t { + preconfiguration: Some(Module::preconfiguration), + postconfiguration: Some(Module::postconfiguration), + create_main_conf: Some(Module::create_main_conf), + init_main_conf: Some(Module::init_main_conf), + create_srv_conf: Some(Module::create_srv_conf), + merge_srv_conf: Some(Module::merge_srv_conf), + create_loc_conf: Some(Module::create_loc_conf), + merge_loc_conf: Some(Module::merge_loc_conf), +}; + +ngx_modules!(ngx_http_awssigv4_module); + +#[no_mangle] +pub static mut ngx_http_awssigv4_module: ngx_module_t = ngx_module_t { + ctx_index: ngx_uint_t::max_value(), + index: ngx_uint_t::max_value(), + name: std::ptr::null_mut(), + spare0: 0, + spare1: 0, + version: nginx_version as ngx_uint_t, + signature: NGX_RS_MODULE_SIGNATURE.as_ptr() as *const c_char, + + ctx: &ngx_http_awssigv4_module_ctx as *const _ as *mut _, + commands: unsafe { &ngx_http_awssigv4_commands[0] as *const _ as *mut _ }, + type_: NGX_HTTP_MODULE as ngx_uint_t, + + init_master: None, + init_module: None, + init_process: None, + init_thread: None, + exit_thread: None, + exit_process: None, + exit_master: None, + + spare_hook0: 0, + spare_hook1: 0, + spare_hook2: 0, + spare_hook3: 0, + spare_hook4: 0, + spare_hook5: 0, + spare_hook6: 0, + spare_hook7: 0, +}; + +impl Merge for ModuleConfig { + fn merge(&mut self, prev: &ModuleConfig) -> Result<(), MergeConfigError> { + if prev.enable { + self.enable = true; + }; + + if self.access_key.is_empty() { + self.access_key = String::from(if !prev.access_key.is_empty() { + &prev.access_key + } else { + "" + }); + } + if self.enable && self.access_key.is_empty() { + return Err(MergeConfigError::NoValue); + } + + if self.secret_key.is_empty() { + self.secret_key = String::from(if !prev.secret_key.is_empty() { + &prev.secret_key + } else { + "" + }); + } + if self.enable && self.secret_key.is_empty() { + return Err(MergeConfigError::NoValue); + } + + if self.s3_bucket.is_empty() { + self.s3_bucket = String::from(if !prev.s3_bucket.is_empty() { + &prev.s3_bucket + } else { + "" + }); + } + if self.enable && self.s3_bucket.is_empty() { + return Err(MergeConfigError::NoValue); + } + + if self.s3_endpoint.is_empty() { + self.s3_endpoint = String::from(if !prev.s3_endpoint.is_empty() { + &prev.s3_endpoint + } else { + "s3.amazonaws.com" + }); + } + Ok(()) + } +} + +#[no_mangle] +extern "C" fn ngx_http_awssigv4_commands_set_enable( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + let val = (*args.add(1)).to_str(); + + // set default value optionally + conf.enable = false; + + if val.len() == 2 && val.eq_ignore_ascii_case("on") { + conf.enable = true; + } else if val.len() == 3 && val.eq_ignore_ascii_case("off") { + conf.enable = false; + } + }; + + std::ptr::null_mut() +} + +#[no_mangle] +extern "C" fn ngx_http_awssigv4_commands_set_access_key( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + conf.access_key = (*args.add(1)).to_string(); + }; + + std::ptr::null_mut() +} + +#[no_mangle] +extern "C" fn ngx_http_awssigv4_commands_set_secret_key( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + conf.secret_key = (*args.add(1)).to_string(); + }; + + std::ptr::null_mut() +} + +#[no_mangle] +extern "C" fn ngx_http_awssigv4_commands_set_s3_bucket( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + conf.s3_bucket = (*args.add(1)).to_string(); + if conf.s3_bucket.len() == 1 { + println!("Validation failed"); + return ngx::core::NGX_CONF_ERROR as _; + } + }; + std::ptr::null_mut() +} + +#[no_mangle] +extern "C" fn ngx_http_awssigv4_commands_set_s3_endpoint( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + conf.s3_endpoint = (*args.add(1)).to_string(); + }; + + std::ptr::null_mut() +} + +http_request_handler!(awssigv4_header_handler, |request: &mut Request| { + // get Module Config from request + let conf = unsafe { request.get_module_loc_conf::(&ngx_http_awssigv4_module) }; + let conf = conf.unwrap(); + ngx_log_debug_http!(request, "AWS signature V4 module {}", { + if conf.enable { + "enabled" + } else { + "disabled" + } + }); + if !conf.enable { + return core::Status::NGX_DECLINED; + } + + // TODO: build url properly from the original URL from client + let method = request.method(); + if !matches!(method, ngx::http::Method::HEAD | ngx::http::Method::GET) { + return HTTPStatus::FORBIDDEN.into(); + } + + let datetime = chrono::Utc::now(); + let uri = match request.unparsed_uri().to_str() { + Ok(v) => format!("https://{}.{}{}", conf.s3_bucket, conf.s3_endpoint, v), + Err(_) => return core::Status::NGX_DECLINED, + }; + + let datetime_now = datetime.format("%Y%m%dT%H%M%SZ"); + let datetime_now = datetime_now.to_string(); + + let signature = { + // NOTE: aws_sign_v4::AwsSign::new() implementation requires a HeaderMap. + // Iterate over requests headers_in and copy into HeaderMap + // Copy only headers that will be used to sign the request + let mut headers = HeaderMap::new(); + for (name, value) in request.headers_in_iterator() { + match name.to_lowercase().as_str() { + "host" => { + headers.insert(http::header::HOST, value.parse().unwrap()); + } + &_ => {} + }; + } + headers.insert("X-Amz-Date", datetime_now.parse().unwrap()); + ngx_log_debug_http!(request, "headers {:?}", headers); + ngx_log_debug_http!(request, "method {:?}", method); + ngx_log_debug_http!(request, "uri {:?}", uri); + ngx_log_debug_http!(request, "datetime_now {:?}", datetime_now); + + let s = aws_sign_v4::AwsSign::new( + method.as_str(), + &uri, + &datetime, + &headers, + "us-east-1", + conf.access_key.as_str(), + conf.secret_key.as_str(), + "s3", + "", + ); + s.sign() + }; + + request.add_header_in("authorization", signature.as_str()); + request.add_header_in("X-Amz-Date", datetime_now.as_str()); + + // done signing, let's print values we have in request.headers_out, request.headers_in + for (name, value) in request.headers_out_iterator() { + ngx_log_debug_http!(request, "headers_out {}: {}", name, value); + } + for (name, value) in request.headers_in_iterator() { + ngx_log_debug_http!(request, "headers_in {}: {}", name, value); + } + + core::Status::NGX_OK +}); diff --git a/examples/awssig_curl_combined.conf b/examples/awssig_curl_combined.conf new file mode 100644 index 0000000..7956e0e --- /dev/null +++ b/examples/awssig_curl_combined.conf @@ -0,0 +1,53 @@ +daemon off; +master_process off; +# worker_processes 1; + +# on linix load a module: +load_module modules/libawssig.so; +load_module modules/libcurl.so; + +# error_log /dev/stdout debug; +error_log error.log debug; + +# working_directory /tmp/cores/; +# worker_rlimit_core 500M; + +events { } + +http { + server { + listen *:8000; + server_name localhost; + + awssigv4_access_key my-access-key; + awssigv4_secret_key my-secret-key; + awssigv4_s3_bucket my-bucket; + + location / { + awssigv4 on; + curl on; + + proxy_pass http://localhost:8777; + ## (on | off ) to enable aws sig v4 + location /some { + awssigv4 off; + } + ## awssigv4_s3_endpoint if not set then 's3.amazonaws.com' + # awssigv4_s3_endpoint s3.amazonaws.com; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } + server { + listen 8777; + server_name localhost; + location / { + add_header x-authorization $http_authorization; + add_header x-Amz-Date $http_x_amz_date; + return 204; + } + } +} diff --git a/examples/curl.conf b/examples/curl.conf new file mode 100644 index 0000000..603fc15 --- /dev/null +++ b/examples/curl.conf @@ -0,0 +1,31 @@ +daemon off; +master_process off; +# worker_processes 1; + +# on linux load a module: +load_module modules/libcurl.so; + +# on mac os it would be dylib +# load_module modules/libcurl.dylib; + +# error_log /dev/stdout debug; +error_log error.log debug; + +events { } + +http { + server { + listen *:8000; + server_name localhost; + location / { + root html; + index index.html index.htm; + # libcurl module directive: + curl on; + } + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } +} diff --git a/examples/curl.rs b/examples/curl.rs new file mode 100644 index 0000000..d0bbb43 --- /dev/null +++ b/examples/curl.rs @@ -0,0 +1,147 @@ +use ngx::ffi::{ + nginx_version, ngx_array_push, ngx_command_t, ngx_conf_t, ngx_http_core_module, ngx_http_handler_pt, + ngx_http_module_t, ngx_http_phases_NGX_HTTP_ACCESS_PHASE, ngx_http_request_t, ngx_int_t, ngx_module_t, ngx_str_t, + ngx_uint_t, NGX_CONF_TAKE1, NGX_HTTP_LOC_CONF, NGX_HTTP_MODULE, NGX_RS_HTTP_LOC_CONF_OFFSET, + NGX_RS_MODULE_SIGNATURE, +}; +use ngx::http::MergeConfigError; +use ngx::{core, core::Status, http, http::HTTPModule}; +use ngx::{http_request_handler, ngx_log_debug_http, ngx_modules, ngx_null_command, ngx_string}; +use std::os::raw::{c_char, c_void}; + +struct Module; + +impl http::HTTPModule for Module { + type MainConf = (); + type SrvConf = (); + type LocConf = ModuleConfig; + + unsafe extern "C" fn postconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t { + let cmcf = http::ngx_http_conf_get_module_main_conf(cf, &ngx_http_core_module); + + let h = ngx_array_push(&mut (*cmcf).phases[ngx_http_phases_NGX_HTTP_ACCESS_PHASE as usize].handlers) + as *mut ngx_http_handler_pt; + if h.is_null() { + return core::Status::NGX_ERROR.into(); + } + // set an Access phase handler + *h = Some(curl_access_handler); + core::Status::NGX_OK.into() + } +} + +#[derive(Debug, Default)] +struct ModuleConfig { + enable: bool, +} + +#[no_mangle] +static mut ngx_http_curl_commands: [ngx_command_t; 2] = [ + ngx_command_t { + name: ngx_string!("curl"), + type_: (NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1) as ngx_uint_t, + set: Some(ngx_http_curl_commands_set_enable), + conf: NGX_RS_HTTP_LOC_CONF_OFFSET, + offset: 0, + post: std::ptr::null_mut(), + }, + ngx_null_command!(), +]; + +#[no_mangle] +static ngx_http_curl_module_ctx: ngx_http_module_t = ngx_http_module_t { + preconfiguration: Some(Module::preconfiguration), + postconfiguration: Some(Module::postconfiguration), + create_main_conf: Some(Module::create_main_conf), + init_main_conf: Some(Module::init_main_conf), + create_srv_conf: Some(Module::create_srv_conf), + merge_srv_conf: Some(Module::merge_srv_conf), + create_loc_conf: Some(Module::create_loc_conf), + merge_loc_conf: Some(Module::merge_loc_conf), +}; + +ngx_modules!(ngx_http_curl_module); + +#[no_mangle] +pub static mut ngx_http_curl_module: ngx_module_t = ngx_module_t { + ctx_index: ngx_uint_t::max_value(), + index: ngx_uint_t::max_value(), + name: std::ptr::null_mut(), + spare0: 0, + spare1: 0, + version: nginx_version as ngx_uint_t, + signature: NGX_RS_MODULE_SIGNATURE.as_ptr() as *const c_char, + + ctx: &ngx_http_curl_module_ctx as *const _ as *mut _, + commands: unsafe { &ngx_http_curl_commands[0] as *const _ as *mut _ }, + type_: NGX_HTTP_MODULE as ngx_uint_t, + + init_master: None, + init_module: None, + init_process: None, + init_thread: None, + exit_thread: None, + exit_process: None, + exit_master: None, + + spare_hook0: 0, + spare_hook1: 0, + spare_hook2: 0, + spare_hook3: 0, + spare_hook4: 0, + spare_hook5: 0, + spare_hook6: 0, + spare_hook7: 0, +}; + +impl http::Merge for ModuleConfig { + fn merge(&mut self, prev: &ModuleConfig) -> Result<(), MergeConfigError> { + if prev.enable { + self.enable = true; + }; + Ok(()) + } +} + +http_request_handler!(curl_access_handler, |request: &mut http::Request| { + let co = unsafe { request.get_module_loc_conf::(&ngx_http_curl_module) }; + let co = co.expect("module config is none"); + + ngx_log_debug_http!(request, "curl module enabled: {}", co.enable); + + match co.enable { + true => { + if request.user_agent().as_bytes().starts_with(b"curl") { + http::HTTPStatus::FORBIDDEN.into() + } else { + core::Status::NGX_DECLINED + } + } + false => core::Status::NGX_DECLINED, + } +}); + +#[no_mangle] +extern "C" fn ngx_http_curl_commands_set_enable( + cf: *mut ngx_conf_t, + _cmd: *mut ngx_command_t, + conf: *mut c_void, +) -> *mut c_char { + unsafe { + let conf = &mut *(conf as *mut ModuleConfig); + let args = (*(*cf).args).elts as *mut ngx_str_t; + + let val = (*args.add(1)).to_str(); + + // set default value optionally + conf.enable = false; + + if val.len() == 2 && val.eq_ignore_ascii_case("on") { + conf.enable = true; + } else if val.len() == 3 && val.eq_ignore_ascii_case("off") { + conf.enable = false; + } + }; + + std::ptr::null_mut() +} diff --git a/examples/httporigdst.conf b/examples/httporigdst.conf new file mode 100644 index 0000000..6fff8c2 --- /dev/null +++ b/examples/httporigdst.conf @@ -0,0 +1,16 @@ +http { + server { + # use iptables to capture all outgoing traffic and REDIRECT + # to listening port 15501 + listen 15501; + + # binding variables provided by module will lazily activate it + # and store a context + # variables can be used in config + location / { + # Return if no backend is available or proxy_pass + # return 200 "recv'd: $server_addr:$server_port\n\nproxy_pass http://$server_orig_addr:$server_orig_port\n"; + proxy_pass http://$server_orig_addr:$server_orig_port; + } + } +} \ No newline at end of file diff --git a/examples/httporigdst.rs b/examples/httporigdst.rs new file mode 100644 index 0000000..1e62e27 --- /dev/null +++ b/examples/httporigdst.rs @@ -0,0 +1,308 @@ +use ngx::ffi::{ + in_port_t, nginx_version, ngx_conf_t, ngx_connection_local_sockaddr, ngx_http_add_variable, ngx_http_module_t, + ngx_http_request_t, ngx_http_variable_t, ngx_inet_get_port, ngx_int_t, ngx_module_t, ngx_sock_ntop, ngx_str_t, + ngx_uint_t, ngx_variable_value_t, sockaddr, sockaddr_storage, INET_ADDRSTRLEN, NGX_HTTP_MODULE, + NGX_RS_MODULE_SIGNATURE, +}; +use ngx::{core, core::Status, http, http::HTTPModule}; +use ngx::{http_variable_get, ngx_http_null_variable, ngx_log_debug_http, ngx_modules, ngx_null_string, ngx_string}; +use std::os::raw::{c_char, c_int, c_void}; + +const IPV4_STRLEN: usize = INET_ADDRSTRLEN as usize; + +#[derive(Debug)] +struct NgxHttpOrigDstCtx { + orig_dst_addr: ngx_str_t, + orig_dst_port: ngx_str_t, +} + +impl Default for NgxHttpOrigDstCtx { + fn default() -> NgxHttpOrigDstCtx { + NgxHttpOrigDstCtx { + orig_dst_addr: ngx_null_string!(), + orig_dst_port: ngx_null_string!(), + } + } +} + +impl NgxHttpOrigDstCtx { + pub fn save(&mut self, addr: &str, port: in_port_t, pool: &mut core::Pool) -> core::Status { + let addr_data = pool.alloc(IPV4_STRLEN); + if addr_data.is_null() { + return core::Status::NGX_ERROR; + } + unsafe { libc::memcpy(addr_data, addr.as_ptr() as *const c_void, IPV4_STRLEN) }; + self.orig_dst_addr.len = IPV4_STRLEN; + self.orig_dst_addr.data = addr_data as *mut u8; + + let port_str = port.to_string(); + let port_data = pool.alloc(port_str.len()); + if port_data.is_null() { + return core::Status::NGX_ERROR; + } + unsafe { libc::memcpy(port_data, port_str.as_bytes().as_ptr() as *const c_void, port_str.len()) }; + self.orig_dst_port.len = port_str.len(); + self.orig_dst_port.data = port_data as *mut u8; + + core::Status::NGX_OK + } + + pub unsafe fn bind_addr(&self, v: *mut ngx_variable_value_t) { + if self.orig_dst_addr.len == 0 { + (*v).set_not_found(1); + return; + } + + (*v).set_valid(1); + (*v).set_no_cacheable(0); + (*v).set_not_found(0); + (*v).set_len(self.orig_dst_addr.len as u32); + (*v).data = self.orig_dst_addr.data; + } + + pub unsafe fn bind_port(&self, v: *mut ngx_variable_value_t) { + if self.orig_dst_port.len == 0 { + (*v).set_not_found(1); + return; + } + + (*v).set_valid(1); + (*v).set_no_cacheable(0); + (*v).set_not_found(0); + (*v).set_len(self.orig_dst_port.len as u32); + (*v).data = self.orig_dst_port.data; + } +} + +#[no_mangle] +static ngx_http_orig_dst_module_ctx: ngx_http_module_t = ngx_http_module_t { + preconfiguration: Some(Module::preconfiguration), + postconfiguration: Some(Module::postconfiguration), + create_main_conf: Some(Module::create_main_conf), + init_main_conf: Some(Module::init_main_conf), + create_srv_conf: Some(Module::create_srv_conf), + merge_srv_conf: Some(Module::merge_srv_conf), + create_loc_conf: Some(Module::create_loc_conf), + merge_loc_conf: Some(Module::merge_loc_conf), +}; + +ngx_modules!(ngx_http_orig_dst_module); + +#[no_mangle] +pub static mut ngx_http_orig_dst_module: ngx_module_t = ngx_module_t { + ctx_index: ngx_uint_t::max_value(), + index: ngx_uint_t::max_value(), + name: std::ptr::null_mut(), + spare0: 0, + spare1: 0, + version: nginx_version as ngx_uint_t, + signature: NGX_RS_MODULE_SIGNATURE.as_ptr() as *const c_char, + ctx: &ngx_http_orig_dst_module_ctx as *const _ as *mut _, + commands: std::ptr::null_mut(), + type_: NGX_HTTP_MODULE as ngx_uint_t, + + init_master: None, + init_module: None, + init_process: None, + init_thread: None, + exit_thread: None, + exit_process: None, + exit_master: None, + + spare_hook0: 0, + spare_hook1: 0, + spare_hook2: 0, + spare_hook3: 0, + spare_hook4: 0, + spare_hook5: 0, + spare_hook6: 0, + spare_hook7: 0, +}; + +#[no_mangle] +static mut ngx_http_orig_dst_vars: [ngx_http_variable_t; 3] = [ + // ngx_str_t name + // ngx_http_set_variable_pt set_handler + // ngx_http_get_variable_pt get_handler + // uintptr_t data + // ngx_uint_t flags + // ngx_uint_t index + ngx_http_variable_t { + name: ngx_string!("server_orig_addr"), + set_handler: None, + get_handler: Some(ngx_http_orig_dst_addr_variable), + data: 0, + flags: 0, + index: 0, + }, + ngx_http_variable_t { + name: ngx_string!("server_orig_port"), + set_handler: None, + get_handler: Some(ngx_http_orig_dst_port_variable), + data: 0, + flags: 0, + index: 0, + }, + ngx_http_null_variable!(), +]; + +unsafe fn ngx_get_origdst(request: &mut http::Request) -> Result<(String, in_port_t), core::Status> { + let c = request.connection(); + + if (*c).type_ != libc::SOCK_STREAM { + ngx_log_debug_http!(request, "httporigdst: connection is not type SOCK_STREAM"); + return Err(core::Status::NGX_DECLINED); + } + + if ngx_connection_local_sockaddr(c, std::ptr::null_mut(), 0) != core::Status::NGX_OK.into() { + ngx_log_debug_http!(request, "httporigdst: no local sockaddr from connection"); + return Err(core::Status::NGX_ERROR); + } + + let level: c_int; + let optname: c_int; + match (*(*c).local_sockaddr).sa_family as i32 { + libc::AF_INET => { + level = libc::SOL_IP; + optname = libc::SO_ORIGINAL_DST; + } + _ => { + ngx_log_debug_http!(request, "httporigdst: only support IPv4"); + return Err(core::Status::NGX_DECLINED); + } + } + + let mut addr: sockaddr_storage = { std::mem::zeroed() }; + let mut addrlen: libc::socklen_t = std::mem::size_of_val(&addr) as libc::socklen_t; + let rc = libc::getsockopt( + (*c).fd, + level, + optname, + &mut addr as *mut _ as *mut _, + &mut addrlen as *mut u32, + ); + if rc == -1 { + ngx_log_debug_http!(request, "httporigdst: getsockopt failed"); + return Err(core::Status::NGX_DECLINED); + } + let mut ip: Vec = vec![0; IPV4_STRLEN]; + let e = unsafe { + ngx_sock_ntop( + std::ptr::addr_of_mut!(addr) as *mut sockaddr, + std::mem::size_of::() as u32, + ip.as_mut_ptr(), + IPV4_STRLEN, + 0, + ) + }; + if e == 0 { + ngx_log_debug_http!(request, "httporigdst: ngx_sock_ntop failed to convert sockaddr"); + return Err(core::Status::NGX_ERROR); + } + + let port = unsafe { ngx_inet_get_port(std::ptr::addr_of_mut!(addr) as *mut sockaddr) }; + + Ok((String::from_utf8(ip).unwrap(), port)) +} + +http_variable_get!( + ngx_http_orig_dst_addr_variable, + |request: &mut http::Request, v: *mut ngx_variable_value_t, _: usize| { + let ctx = request.get_module_ctx::(&ngx_http_orig_dst_module); + if let Some(obj) = ctx { + ngx_log_debug_http!(request, "httporigdst: found context and binding variable",); + obj.bind_addr(v); + return core::Status::NGX_OK; + } + // lazy initialization: + // get original dest information + // create context + // set context + // bind address + ngx_log_debug_http!(request, "httporigdst: context not found, getting address"); + let r = ngx_get_origdst(request); + match r { + Err(e) => { + return e; + } + Ok((ip, port)) => { + // create context, + // set context + let new_ctx = request.pool().allocate::(Default::default()); + + if new_ctx.is_null() { + return core::Status::NGX_ERROR; + } + + ngx_log_debug_http!(request, "httporigdst: saving ip - {:?}, port - {}", ip, port,); + (*new_ctx).save(&ip, port, &mut request.pool()); + (*new_ctx).bind_addr(v); + request.set_module_ctx(new_ctx as *mut c_void, &ngx_http_orig_dst_module); + } + } + core::Status::NGX_OK + } +); + +http_variable_get!( + ngx_http_orig_dst_port_variable, + |request: &mut http::Request, v: *mut ngx_variable_value_t, _: usize| { + let ctx = request.get_module_ctx::(&ngx_http_orig_dst_module); + if let Some(obj) = ctx { + ngx_log_debug_http!(request, "httporigdst: found context and binding variable",); + obj.bind_port(v); + return core::Status::NGX_OK; + } + // lazy initialization: + // get original dest information + // create context + // set context + // bind port + ngx_log_debug_http!(request, "httporigdst: context not found, getting address"); + let r = ngx_get_origdst(request); + match r { + Err(e) => { + return e; + } + Ok((ip, port)) => { + // create context, + // set context + let new_ctx = request.pool().allocate::(Default::default()); + + if new_ctx.is_null() { + return core::Status::NGX_ERROR; + } + + ngx_log_debug_http!(request, "httporigdst: saving ip - {:?}, port - {}", ip, port,); + (*new_ctx).save(&ip, port, &mut request.pool()); + (*new_ctx).bind_port(v); + request.set_module_ctx(new_ctx as *mut c_void, &ngx_http_orig_dst_module); + } + } + core::Status::NGX_OK + } +); + +struct Module; + +impl HTTPModule for Module { + type MainConf = (); + type SrvConf = (); + type LocConf = (); + + // static ngx_int_t ngx_http_orig_dst_add_variables(ngx_conf_t *cf) + unsafe extern "C" fn preconfiguration(cf: *mut ngx_conf_t) -> ngx_int_t { + for mut v in ngx_http_orig_dst_vars { + if v.name.len == 0 { + break; + } + let var = ngx_http_add_variable(cf, &mut v.name, v.flags); + if var.is_null() { + return core::Status::NGX_ERROR.into(); + } + (*var).get_handler = v.get_handler; + (*var).data = v.data; + } + core::Status::NGX_OK.into() + } +} diff --git a/nginx-sys/Cargo.toml b/nginx-sys/Cargo.toml new file mode 100644 index 0000000..d17233d --- /dev/null +++ b/nginx-sys/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nginx-sys" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["staticlib","rlib"] + +[dependencies] + +[build-dependencies] +bindgen = "0.64.0" +which = "4.4.0" +duct = "0.13.6" +ureq = { version = "2.6.2", features = ["tls"] } +flate2 = "1.0.25" +tar = "0.4.38" diff --git a/nginx-sys/build.rs b/nginx-sys/build.rs new file mode 100644 index 0000000..22d51b2 --- /dev/null +++ b/nginx-sys/build.rs @@ -0,0 +1,645 @@ +extern crate bindgen; +extern crate duct; + +use duct::cmd; +use flate2::read::GzDecoder; +use std::error::Error as StdError; +use std::ffi::OsString; +use std::fs::{read_to_string, File}; +use std::io::ErrorKind::NotFound; +use std::io::{Error as IoError, Write}; +use std::path::{Path, PathBuf}; +use std::process::Output; +use std::{env, thread}; +use tar::Archive; +use which::which; + +/// The default version of zlib to use if the `ZLIB_VERSION` environment variable is not present +const ZLIB_DEFAULT_VERSION: &str = "1.2.13"; +const ZLIB_GPG_SERVER_AND_KEY_ID: (&str, &str) = ("keyserver.ubuntu.com", "783FCD8E58BCAFBA"); +const ZLIB_DOWNLOAD_URL_PREFIX: &str = "https://www.zlib.net"; +/// The default version of pcre2 to use if the `PCRE2_VERSION` environment variable is not present +const PCRE2_DEFAULT_VERSION: &str = "10.42"; +const PCRE2_GPG_SERVER_AND_KEY_ID: (&str, &str) = ("keyserver.ubuntu.com", "9766E084FB0F43D8"); +const PCRE2_DOWNLOAD_URL_PREFIX: &str = "https://github.com/PCRE2Project/pcre2/releases/download"; +/// The default version of openssl to use if the `OPENSSL_VERSION` environment variable is not present +const OPENSSL_DEFAULT_VERSION: &str = "3.0.7"; +const OPENSSL_GPG_SERVER_AND_KEY_IDS: (&str, &str) = ( + "keys.openpgp.org", + "\ +A21FAB74B0088AA361152586B8EF1A6BA9DA2D5C \ +8657ABB260F056B1E5190839D9C4D26D0E604491 \ +B7C1C14360F353A36862E4D5231C84CDDCC69C45 \ +95A9908DDFA16830BE9FB9003D30A3A9FF1360DC \ +7953AC1FBC3DC8B3B292393ED5E9E43F7DF9EE8C", +); +const OPENSSL_DOWNLOAD_URL_PREFIX: &str = "https://www.openssl.org/source/"; +/// The default version of NGINX to use if the `NGX_VERSION` environment variable is not present +const NGX_DEFAULT_VERSION: &str = "1.23.3"; +const NGX_GPG_SERVER_AND_KEY_ID: (&str, &str) = ("keyserver.ubuntu.com", "A0EA981B66B0D967"); +const NGX_DOWNLOAD_URL_PREFIX: &str = "https://nginx.org/download"; +/// If you are adding another dependency, you will need to add the server/public key tuple below. +const ALL_SERVERS_AND_PUBLIC_KEY_IDS: [(&str, &str); 4] = [ + ZLIB_GPG_SERVER_AND_KEY_ID, + PCRE2_GPG_SERVER_AND_KEY_ID, + OPENSSL_GPG_SERVER_AND_KEY_IDS, + NGX_GPG_SERVER_AND_KEY_ID, +]; +/// List of configure switches specifying the modules to build nginx with +const NGX_BASE_MODULES: [&str; 15] = [ + "--with-compat", + "--with-threads", + "--with-http_addition_module", + "--with-http_auth_request_module", + "--with-http_gunzip_module", + "--with-http_gzip_static_module", + "--with-http_random_index_module", + "--with-http_realip_module", + "--with-http_secure_link_module", + "--with-http_slice_module", + "--with-http_stub_status_module", + "--with-http_sub_module", + "--with-stream", + "--with-stream_realip_module", + "--with-stream_ssl_preread_module", +]; +/// Additional configuration flags to use when building on Linux. +const NGX_LINUX_ADDITIONAL_OPTS: [&str; 5] = [ + "--with-file-aio", + "--with-http_ssl_module", + "--with-stream_ssl_module", + "--with-cc-opt=-g -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC", + "--with-ld-opt=-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie", +]; +const ENV_VARS_TRIGGERING_RECOMPILE: [&str; 9] = [ + "DEBUG", + "OUT_DIR", + "ZLIB_VERSION", + "PCRE2_VERSION", + "OPENSSL_VERSION", + "NGX_VERSION", + "CARGO_CFG_TARGET_OS", + "CARGO_MANIFEST_DIR", + "CARGO_TARGET_TMPDIR", +]; + +/// Function invoked when `cargo build` is executed. +/// This function will download NGINX and all supporting dependencies, verify their integrity, +/// extract them, execute autoconf `configure` for NGINX, compile NGINX and finally install +/// NGINX in a subdirectory with the project. +fn main() -> Result<(), Box> { + // Create .cache directory + let cache_dir = make_cache_dir()?; + // Import GPG keys used to verify dependency tarballs + import_gpg_keys(&cache_dir)?; + // Configure and Compile NGINX + let (_nginx_install_dir, nginx_src_dir) = compile_nginx()?; + // Hint cargo to rebuild if any of the these environment variables values change + // because they will trigger a recompilation of NGINX with different parameters + for var in ENV_VARS_TRIGGERING_RECOMPILE { + println!("cargo:rerun-if-env-changed={var}"); + } + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=wrapper.h"); + // Read autoconf generated makefile for NGINX and generate Rust bindings based on its includes + generate_binding(nginx_src_dir); + Ok(()) +} + +/// Generates Rust bindings for NGINX +fn generate_binding(nginx_source_dir: PathBuf) { + let autoconf_makefile_path = nginx_source_dir.join("objs").join("Makefile"); + let clang_args: Vec = parse_includes_from_makefile(&autoconf_makefile_path) + .into_iter() + .map(|path| format!("-I{}", path.to_string_lossy())) + .collect(); + + let bindings = bindgen::Builder::default() + // Bindings will not compile on Linux without block listing this item + // It is worth investigating why this is + .blocklist_item("IPPORT_RESERVED") + // The input header we would like to generate bindings for. + .header("wrapper.h") + .clang_args(clang_args) + .layout_tests(false) + .generate() + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_dir_env = env::var("OUT_DIR").expect("The required environment variable OUT_DIR was not set"); + let out_path = PathBuf::from(out_dir_env); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +/* +########################################################################### +# NGINX Build Functions - Everything below here is for building NGINX # +########################################################################### + +In order to build Rust bindings for NGINX using the bindgen crate, we need +to do the following: + + 1. Download NGINX source code and all dependencies (zlib, pcre2, openssl) + 2. Verify the integrity of the downloaded files using GPG signatures + 3. Extract the downloaded files + 4. Run autoconf `configure` for NGINX + 5. Compile NGINX + 6. Install NGINX in a subdirectory of the project + 7. Read the autoconf generated makefile for NGINX and configure bindgen + to generate Rust bindings based on the includes in the makefile. + +Additionally, we want to provide the following features as part of the +build process: + * Allow the user to specify the version of NGINX to build + * Allow the user to specify the version of each dependency to build + * Only reconfigure and recompile NGINX if any of the above versions + change or the configuration flags change (like enabling or disabling + the debug mode) + * Not rely on the user having NGINX dependencies installed on their + system (zlib, pcre2, openssl) + * Keep source code and binaries confined to a subdirectory of the + project to avoid having to track files outside of the project + * If GPG is not installed, the build will still continue. However, the + integrity of the downloaded files will not be verified. +*/ + +fn zlib_archive_url() -> String { + let version = env::var("ZLIB_VERSION").unwrap_or_else(|_| ZLIB_DEFAULT_VERSION.to_string()); + format!("{ZLIB_DOWNLOAD_URL_PREFIX}/zlib-{version}.tar.gz") +} + +fn pcre2_archive_url() -> String { + let version = env::var("PCRE2_VERSION").unwrap_or_else(|_| PCRE2_DEFAULT_VERSION.to_string()); + format!("{PCRE2_DOWNLOAD_URL_PREFIX}/pcre2-{version}/pcre2-{version}.tar.gz") +} + +fn openssl_archive_url() -> String { + let version = env::var("OPENSSL_VERSION").unwrap_or_else(|_| OPENSSL_DEFAULT_VERSION.to_string()); + format!("{OPENSSL_DOWNLOAD_URL_PREFIX}/openssl-{version}.tar.gz") +} + +fn nginx_archive_url() -> String { + let version = env::var("NGX_VERSION").unwrap_or_else(|_| NGX_DEFAULT_VERSION.to_string()); + format!("{NGX_DOWNLOAD_URL_PREFIX}/nginx-{version}.tar.gz") +} + +/// Returns a list of tuples containing the URL to a tarball archive and the GPG signature used +/// to validate the integrity of the tarball. +fn all_archives() -> Vec<(String, String)> { + vec![ + (zlib_archive_url(), format!("{}.asc", zlib_archive_url())), + (pcre2_archive_url(), format!("{}.sig", pcre2_archive_url())), + (openssl_archive_url(), format!("{}.asc", openssl_archive_url())), + (nginx_archive_url(), format!("{}.asc", nginx_archive_url())), + ] +} + +fn gpg_path() -> Option { + which::which("gpg").ok() +} + +/// Returns the base path to extract tarball contents into +fn source_output_dir(cache_dir: &Path) -> PathBuf { + env::var("CARGO_TARGET_TMPDIR").map(PathBuf::from).unwrap_or_else(|_| { + cache_dir + .join("src") + .join(format!("{}-{}", env::consts::OS, env::consts::ARCH)) + }) +} + +#[allow(clippy::ptr_arg)] +/// Returns the path to install NGINX to +fn nginx_install_dir(base_dir: &PathBuf) -> PathBuf { + let nginx_version = env::var("NGX_VERSION").unwrap_or_else(|_| NGX_DEFAULT_VERSION.to_string()); + let platform = format!("{}-{}", env::consts::OS, env::consts::ARCH); + base_dir.join("nginx").join(nginx_version).join(platform) +} + +/// Imports all of the required GPG keys into the `.cache/.gnupu` directory in order to +/// validate the integrity of the downloaded tarballs. +fn import_gpg_keys(cache_dir: &Path) -> Result<(), Box> { + if let Some(gpg) = gpg_path() { + // We do not want to mess with the default gpg data for the running user, + // so we store all gpg data with our cache directory. + let gnupghome = cache_dir.join(".gnupg"); + if !gnupghome.exists() { + std::fs::create_dir_all(&gnupghome)?; + } + + let keys_to_import = ALL_SERVERS_AND_PUBLIC_KEY_IDS.iter().filter(|(_, key_id)| { + let key_id_record_file = gnupghome.join(format!("{key_id}.key")); + !key_id_record_file.exists() + }); + + for (server, key_ids) in keys_to_import { + for key_id in key_ids.split_whitespace() { + let output = cmd!( + &gpg, + "--homedir", + &gnupghome, + "--keyserver", + server, + "--recv-keys", + key_id + ) + .stderr_to_stdout() + .stderr_capture() + .run()?; + if !output.status.success() { + return Err(format!( + "Failed to import GPG key {} from server {}: {}", + key_id, + server, + String::from_utf8_lossy(&output.stdout) + ) + .into()); + } + println!("Imported GPG key: {key_id}"); + let key_id_record_file = gnupghome.join(format!("{key_ids}.key")); + File::create(key_id_record_file).expect("Unable to create key id record file"); + } + } + } + Ok(()) +} + +fn make_cache_dir() -> Result> { + let base_dir = env::var("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| env::current_dir().expect("Failed to get current directory")); + // Choose the parent directory of the manifest directory (nginx-sys) as the cache directory + // Fail if we do not have a parent directory + let cache_dir = base_dir + .parent() + .expect("Failed to find parent directory of manifest directory") + .join(".cache"); + if !cache_dir.exists() { + std::fs::create_dir_all(&cache_dir)?; + } + Ok(cache_dir) +} + +/// Downloads a tarball from the specified URL into the `.cache` directory. +fn download(cache_dir: &Path, url: &str) -> Result> { + fn proceed_with_download(file_path: &Path) -> bool { + // File does not exist or is zero bytes + !file_path.exists() || file_path.metadata().map_or(false, |m| m.len() < 1) + } + let filename = url.split('/').last().unwrap(); + let file_path = cache_dir.join(filename); + if proceed_with_download(&file_path) { + let mut reader = ureq::get(url).call()?.into_reader(); + let mut file = std::fs::File::create(&file_path)?; + std::io::copy(&mut reader, &mut file)?; + } + Ok(file_path) +} + +/// Validates that a file is a valid GPG signature file. +fn verify_signature_file(cache_dir: &Path, signature_path: &Path) -> Result<(), Box> { + if let Some(gpg) = gpg_path() { + let gnupghome = cache_dir.join(".gnupg"); + let output = cmd!(gpg, "--homedir", &gnupghome, "--list-packets", signature_path) + .stderr_to_stdout() + .stdout_capture() + .run()?; + if !output.status.success() { + eprintln!("{}", String::from_utf8_lossy(&output.stdout)); + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "GPG signature file verification failed for signature: {}", + signature_path.display() + ), + ))); + } + } else { + println!("GPG not found, skipping signature file verification"); + } + Ok(()) +} + +/// Validates the integrity of a tarball file against the cryptographic signature associated with +/// the file. +fn verify_archive_signature( + cache_dir: &Path, + archive_path: &Path, + signature_path: &Path, +) -> Result<(), Box> { + if let Some(gpg) = gpg_path() { + let gnupghome = cache_dir.join(".gnupg"); + let output = cmd!(gpg, "--homedir", &gnupghome, "--verify", signature_path, archive_path) + .stderr_to_stdout() + .stdout_capture() + .run()?; + if !output.status.success() { + eprintln!("{}", String::from_utf8_lossy(&output.stdout)); + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "GPG signature verification failed of archive failed [{}]", + archive_path.display() + ), + ))); + } + } else { + println!("GPG not found, skipping signature verification"); + } + Ok(()) +} + +/// Get a given tarball and signature file from a remote URL and copy it to the `.cache` directory. +fn get_archive(cache_dir: &Path, archive_url: &str, signature_url: &str) -> Result> { + let signature_path = download(cache_dir, signature_url)?; + if let Err(e) = verify_signature_file(cache_dir, &signature_path) { + std::fs::remove_file(&signature_path)?; + return Err(e); + } + let archive_path = download(cache_dir, archive_url)?; + match verify_archive_signature(cache_dir, &archive_path, &signature_path) { + Ok(_) => Ok(archive_path), + Err(e) => { + std::fs::remove_file(&archive_path)?; + Err(e) + } + } +} + +/// Extract a tarball into a subdirectory based on the tarball's name under the source base +/// directory. +fn extract_archive( + archive_path: &Path, + extract_output_base_dir: &Path, +) -> Result<(String, PathBuf), Box> { + if !extract_output_base_dir.exists() { + std::fs::create_dir_all(extract_output_base_dir)?; + } + let archive_file = + File::open(archive_path).unwrap_or_else(|_| panic!("Unable to open archive file: {}", archive_path.display())); + let stem = archive_path + .file_name() + .and_then(|s| s.to_str()) + .and_then(|s| s.rsplitn(3, '.').last()) + .expect("Unable to determine archive file name stem"); + let dependency_name = stem + .split_once('-') + .map(|(s, _)| s.to_owned()) + .unwrap_or_else(|| panic!("Unable to determine dependency name based on stem: {stem}")); + + let extract_output_dir = extract_output_base_dir.to_owned(); + let archive_output_dir = extract_output_dir.join(stem); + if !archive_output_dir.exists() { + Archive::new(GzDecoder::new(archive_file)) + .entries()? + .filter_map(|e| e.ok()) + .for_each(|mut entry| { + let path = entry.path().unwrap(); + let stripped_path = path.components().skip(1).collect::(); + entry.unpack(&archive_output_dir.join(stripped_path)).unwrap(); + }); + } else { + println!( + "Archive [{}] already extracted to directory: {}", + stem, + archive_output_dir.display() + ); + } + + Ok((dependency_name, archive_output_dir)) +} + +/// Extract all of the tarballs into subdirectories within the source base directory. +fn extract_all_archives(cache_dir: &Path) -> Result, Box> { + let archives = all_archives(); + let mut sources = Vec::new(); + let extract_output_base_dir = source_output_dir(cache_dir); + if !extract_output_base_dir.exists() { + std::fs::create_dir_all(&extract_output_base_dir)?; + } + for (archive_url, signature_url) in archives { + let archive_path = get_archive(cache_dir, &archive_url, &signature_url)?; + let (name, output_dir) = extract_archive(&archive_path, &extract_output_base_dir)?; + sources.push((name, output_dir)); + } + + Ok(sources) +} + +/// Invoke external processes to run autoconf `configure` to generate a makefile for NGINX and +/// then run `make install`. +fn compile_nginx() -> Result<(PathBuf, PathBuf), Box> { + fn find_dependency_path<'a>(sources: &'a [(String, PathBuf)], name: &str) -> &'a PathBuf { + sources + .iter() + .find(|(n, _)| n == name) + .map(|(_, p)| p) + .unwrap_or_else(|| panic!("Unable to find dependency [{name}] path")) + } + let cache_dir = make_cache_dir()?; + let nginx_install_dir = nginx_install_dir(&cache_dir); + let sources = extract_all_archives(&cache_dir)?; + let zlib_src_dir = find_dependency_path(&sources, "zlib"); + let openssl_src_dir = find_dependency_path(&sources, "openssl"); + let pcre2_src_dir = find_dependency_path(&sources, "pcre2"); + let nginx_src_dir = find_dependency_path(&sources, "nginx"); + let nginx_configure_flags = nginx_configure_flags(&nginx_install_dir, zlib_src_dir, openssl_src_dir, pcre2_src_dir); + let nginx_binary_exists = nginx_install_dir.join("sbin").join("nginx").exists(); + let autoconf_makefile_exists = nginx_src_dir.join("Makefile").exists(); + // We find out how NGINX was configured last time, so that we can compare it to what + // we are going to configure it to this time. If there are no changes, then we can assume + // that we do not need to reconfigure and rebuild NGINX. + let build_info_path = nginx_src_dir.join("last-build-info"); + let current_build_info = build_info(&nginx_configure_flags); + let build_info_no_change = if build_info_path.exists() { + read_to_string(&build_info_path).map_or(false, |s| s == current_build_info) + } else { + false + }; + + println!("NGINX already installed: {nginx_binary_exists}"); + println!("NGINX autoconf makefile already created: {autoconf_makefile_exists}"); + println!("NGINX build info changed: {}", !build_info_no_change); + + if !nginx_binary_exists || !autoconf_makefile_exists || !build_info_no_change { + std::fs::create_dir_all(&nginx_install_dir)?; + configure(nginx_configure_flags, nginx_src_dir)?; + make(nginx_src_dir, "install")?; + let mut output = File::create(build_info_path)?; + // Store the configure flags of the last successful build + output.write_all(current_build_info.as_bytes())?; + } + Ok((nginx_install_dir, nginx_src_dir.to_owned())) +} + +/// Returns the options in which NGINX was built with +fn build_info(nginx_configure_flags: &[String]) -> String { + // Flags should contain strings pointing to OS/platform as well as dependency versions, + // so if any of that changes, it can trigger a rebuild + nginx_configure_flags.join(" ") +} + +/// Generate the flags to use with autoconf `configure` for NGINX based on the downloaded +/// dependencies' paths. Note: the paths differ based on cargo targets because they may be +/// configured differently for different os/platform targets. +fn nginx_configure_flags( + nginx_install_dir: &Path, + zlib_src_dir: &Path, + openssl_src_dir: &Path, + pcre2_src_dir: &Path, +) -> Vec { + fn format_source_path(flag: &str, path: &Path) -> String { + format!( + "{}={}", + flag, + path.as_os_str().to_str().expect("Unable to read source path as string") + ) + } + let modules = || -> Vec { + let mut modules = vec![ + format_source_path("--with-zlib", zlib_src_dir), + format_source_path("--with-pcre", pcre2_src_dir), + format_source_path("--with-openssl", openssl_src_dir), + ]; + for module in NGX_BASE_MODULES { + modules.push(module.to_string()); + } + modules + }; + let mut nginx_opts = vec![format_source_path("--prefix", nginx_install_dir)]; + if env::var("NGX_DEBUG").map_or(false, |s| s == "true") { + println!("Enabling --with-debug"); + nginx_opts.push("--with-debug".to_string()); + } + if env::var("CARGO_CFG_TARGET_OS").map_or(env::consts::OS == "linux", |s| s == "linux") { + for flag in NGX_LINUX_ADDITIONAL_OPTS { + nginx_opts.push(flag.to_string()); + } + } + for flag in modules() { + nginx_opts.push(flag); + } + + nginx_opts +} + +/// Run external process invoking autoconf `configure` for NGINX. +fn configure(nginx_configure_flags: Vec, nginx_src_dir: &Path) -> std::io::Result { + let flags = nginx_configure_flags + .iter() + .map(OsString::from) + .collect::>(); + let configure_executable = nginx_src_dir.join("configure"); + if !configure_executable.exists() { + panic!( + "Unable to find NGINX configure script at: {}", + configure_executable.to_string_lossy() + ); + } + println!( + "Running NGINX configure script with flags: {:?}", + nginx_configure_flags.join(" ") + ); + duct::cmd(configure_executable, flags) + .dir(nginx_src_dir) + .stderr_to_stdout() + .run() +} + +/// Run `make` within the NGINX source directory as an external process. +fn make(nginx_src_dir: &Path, arg: &str) -> std::io::Result { + // Give preference to the binary with the name of gmake if it exists because this is typically + // the GNU 4+ on MacOS (if it is installed via homebrew). + let make_bin_path = match (which("gmake"), which("make")) { + (Ok(path), _) => Ok(path), + (_, Ok(path)) => Ok(path), + _ => Err(IoError::new(NotFound, "Unable to find make in path (gmake or make)")), + }?; + + // Level of concurrency to use when building nginx - cargo nicely provides this information + let num_jobs = match env::var("NUM_JOBS") { + Ok(s) => s.parse::().ok(), + Err(_) => thread::available_parallelism().ok().map(|n| n.get()), + } + .unwrap_or(1); + + /* Use the duct dependency here to merge the output of STDOUT and STDERR into a single stream, + and to provide the combined output as a reader which can be iterated over line-by-line. We + use duct to do this because it is a lot of work to implement this from scratch. */ + cmd!(make_bin_path, "-j", num_jobs.to_string(), arg) + .dir(nginx_src_dir) + .stderr_to_stdout() + .run() +} + +/// Reads through the makefile generated by autoconf and finds all of the includes +/// used to compile nginx. This is used to generate the correct bindings for the +/// nginx source code. +fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec { + fn extract_include_part(line: &str) -> &str { + line.strip_suffix('\\').map_or(line, |s| s.trim()) + } + /// Extracts the include path from a line of the autoconf generated makefile. + fn extract_after_i_flag(line: &str) -> Option<&str> { + let mut parts = line.split("-I "); + match parts.next() { + Some(_) => parts.next().map(extract_include_part), + None => None, + } + } + + let mut includes = vec![]; + let makefile_contents = match std::fs::read_to_string(nginx_autoconf_makefile_path) { + Ok(path) => path, + Err(e) => { + panic!( + "Unable to read makefile from path [{}]. Error: {}", + nginx_autoconf_makefile_path.to_string_lossy(), + e + ); + } + }; + + let mut includes_lines = false; + for line in makefile_contents.lines() { + if !includes_lines { + if let Some(stripped) = line.strip_prefix("ALL_INCS") { + includes_lines = true; + if let Some(part) = extract_after_i_flag(stripped) { + includes.push(part); + } + continue; + } + } + + if includes_lines { + if let Some(part) = extract_after_i_flag(line) { + includes.push(part); + } else { + break; + } + } + } + + let makefile_dir = nginx_autoconf_makefile_path + .parent() + .expect("makefile path has no parent") + .parent() + .expect("objs dir has no parent") + .to_path_buf() + .canonicalize() + .expect("Unable to canonicalize makefile path"); + + includes + .into_iter() + .map(PathBuf::from) + .map(|path| { + if path.is_absolute() { + path + } else { + makefile_dir.join(path) + } + }) + .collect() +} diff --git a/nginx-sys/src/lib.rs b/nginx-sys/src/lib.rs new file mode 100644 index 0000000..cf11f1a --- /dev/null +++ b/nginx-sys/src/lib.rs @@ -0,0 +1,97 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +#![allow(clippy::all)] +#![allow(improper_ctypes)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +use std::fmt; +use std::ptr::copy_nonoverlapping; +use std::slice; + +pub fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char { + let ptr: *mut u_char = unsafe { ngx_palloc(pool, data.len() as _) as _ }; + unsafe { + copy_nonoverlapping(data.as_ptr(), ptr, data.len()); + } + ptr +} + +impl ngx_str_t { + // convert nginx string to str slice + pub fn to_str(&self) -> &str { + unsafe { + let slice = slice::from_raw_parts(self.data, self.len as usize); + return std::str::from_utf8(slice).unwrap(); + } + } + + // get string + pub fn to_string(&self) -> String { + return String::from(self.to_str()); + } + + /// create from string + pub fn from_string(pool: *mut ngx_pool_t, data: String) -> Self { + ngx_str_t { + data: str_to_uchar(pool, data.as_str()), + len: data.len() as _, + } + } + + /// create from string + pub fn from_str(pool: *mut ngx_pool_t, data: &str) -> Self { + ngx_str_t { + data: str_to_uchar(pool, data), + len: data.len() as _, + } + } +} + +impl From for &[u8] { + fn from(s: ngx_str_t) -> Self { + if s.len == 0 || s.data.is_null() { + return Default::default(); + } + unsafe { slice::from_raw_parts(s.data, s.len as usize) } + } +} + +impl TryFrom for String { + type Error = std::string::FromUtf8Error; + + fn try_from(s: ngx_str_t) -> Result { + let bytes: &[u8] = s.into(); + String::from_utf8(bytes.into()) + } +} + +impl fmt::Display for ngx_str_t { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", String::from_utf8_lossy((*self).into())) + } +} + +impl TryFrom for &str { + type Error = std::str::Utf8Error; + + fn try_from(s: ngx_str_t) -> Result { + std::str::from_utf8(s.into()) + } +} + +pub fn add_to_ngx_table(table: *mut ngx_table_elt_t, pool: *mut ngx_pool_t, key: &str, value: &str) -> Option<()> { + if table.is_null() { + return None; + } + unsafe { table.as_mut() }.map(|table| { + table.hash = 1; + table.key.len = key.len() as _; + table.key.data = str_to_uchar(pool, key); + table.value.len = value.len() as _; + table.value.data = str_to_uchar(pool, value); + table.lowcase_key = str_to_uchar(pool, String::from(key).to_ascii_lowercase().as_str()); + }) +} diff --git a/nginx-sys/wrapper.h b/nginx-sys/wrapper.h new file mode 100644 index 0000000..c53f3c9 --- /dev/null +++ b/nginx-sys/wrapper.h @@ -0,0 +1,11 @@ +#include +#include +#include +#include + +// Define as constants since bindgen can't parse these values +const size_t NGX_RS_HTTP_MAIN_CONF_OFFSET = NGX_HTTP_MAIN_CONF_OFFSET; +const size_t NGX_RS_HTTP_SRV_CONF_OFFSET = NGX_HTTP_SRV_CONF_OFFSET; +const size_t NGX_RS_HTTP_LOC_CONF_OFFSET = NGX_HTTP_LOC_CONF_OFFSET; + +const char *NGX_RS_MODULE_SIGNATURE = NGX_MODULE_SIGNATURE; diff --git a/nginx.mk b/nginx.mk deleted file mode 100644 index f8f5d32..0000000 --- a/nginx.mk +++ /dev/null @@ -1,70 +0,0 @@ -NGINX_VER = 1.13.7 -UNAME_S := $(shell uname -s) -NGX_MODULES = --with-compat --with-threads --with-http_addition_module \ - --with-http_auth_request_module --with-http_gunzip_module --with-http_gzip_static_module \ - --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module \ - --with-http_slice_module --with-http_stub_status_module --with-http_sub_module \ - --with-stream --with-stream_realip_module --with-stream_ssl_preread_module - -ifeq ($(UNAME_S),Linux) - NGINX_SRC = nginx-linux - NGX_OPT= $(NGX_MODULES) \ - --with-file-aio --with-http_ssl_module --with-stream_ssl_module \ - --with-cc-opt='-g -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' \ - --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' -endif -ifeq ($(UNAME_S),Darwin) - NGINX_SRC = nginx-darwin - NGX_OPT= $(NGX_MODULES) -endif -NGX_DEBUG="--with-debug" -RUST_COMPILER_TAG = 1.21.0 -RUST_TOOL = nginmesh/ngx-rust-tool:${RUST_COMPILER_TAG} -export ROOT_DIR=$(shell dirname $$PWD) -DOCKER_TOOL=docker run -it -v ${ROOT_DIR}:/src -w /src/${MODULE_PROJ_NAME} ${RUST_TOOL} - - -nginx-build: - cd nginx/${NGINX_SRC}; \ - ./configure --prefix=${PWD}/nginx/install $(NGX_OPT); \ - make; \ - make install - - -setup-nginx: - mkdir -p nginx - -nginx-source: setup-nginx - rm -rf nginx/${NGINX_SRC} - wget http://nginx.org/download/nginx-${NGINX_VER}.tar.gz - tar zxf nginx-${NGINX_VER}.tar.gz - mv nginx-${NGINX_VER} ${NGINX_SRC} - mv ${NGINX_SRC} nginx - rm nginx-${NGINX_VER}.tar.gz* - -nginx-configure: - cd nginx/${NGINX_SRC}; \ - ./configure $(NGX_OPT) - - -nginx-setup: nginx-source nginx-configure - -nginx-test: nginx-source nginx-build - - -nginx-module: - cd nginx/${NGINX_SRC}; \ - make modules; - -# need to run inside container -linux-shell: - ${DOCKER_TOOL} /bin/bash - - - -linux-setup: - ${DOCKER_TOOL} make nginx-setup - -linux-module: - ${DOCKER_TOOL} make nginx-module - diff --git a/ngx-binding/.gitignore b/ngx-binding/.gitignore deleted file mode 100644 index da70e20..0000000 --- a/ngx-binding/.gitignore +++ /dev/null @@ -1 +0,0 @@ -nginx \ No newline at end of file diff --git a/ngx-binding/Cargo.lock b/ngx-binding/Cargo.lock deleted file mode 100644 index 2c5358a..0000000 --- a/ngx-binding/Cargo.lock +++ /dev/null @@ -1,756 +0,0 @@ -[root] -name = "ngx-rust" -version = "0.1.2" -dependencies = [ - "bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aho-corasick" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ansi_term" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "aster" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "base64" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bindgen" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitflags" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytes" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cexpr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nom 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cfg-if" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "clang-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clap" -version = "2.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "conv" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "custom_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "env_logger" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "glob" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "httparse" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hyper" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "iovec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazycell" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.30" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libloading" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "magenta" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "magenta-sys" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memchr" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mime" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miow" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "net2" -version = "0.2.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nom" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "percent-encoding" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "quasi" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quasi_codegen" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "redox_syscall" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "regex" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rustc_version" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "safemem" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scoped-tls" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "semver" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "slab" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smallvec" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "strsim" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syntex" -version = "0.58.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syntex_errors" -version = "0.58.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syntex_pos" -version = "0.58.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syntex_syntax" -version = "0.58.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "take" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "term" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "term_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "textwrap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "time" -version = "0.1.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-io" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-proto" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-service" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicase" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-segmentation" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-width" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vec_map" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "which" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" -"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" -"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0" -"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" -"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" -"checksum bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33024f55a754d920637461adf87fb485702a69bdf7ac1d307b7e18da93bae505" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" -"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6" -"checksum cexpr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cdbb21df6ff3497a61df5059994297f746267020ba38ce237aad9c875f7b4313" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a" -"checksum clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2267a8fdd4dce6956ba6649e130f62fb279026e5e84b92aa939ac8f85ce3f9f0" -"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" -"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" -"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" -"checksum futures 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a82bdc62350ca9d7974c760e9665102fc9d740992a528c2254aa930e53b783c4" -"checksum futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a283c84501e92cade5ea673a2a7ca44f71f209ccdd302a3e0896f50083d2c5ff" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" -"checksum hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "641abc3e3fcf0de41165595f801376e01106bca1fd876dda937730e477ca004c" -"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" -"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" -"checksum libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)" = "2370ca07ec338939e356443dac2296f581453c35fe1e3a3ed06023c49435f915" -"checksum libloading 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b699b2cb9154b7a5c7b8c40fd200bd077791b292556a44ddae07482192123c6" -"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" -"checksum magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf0336886480e671965f794bc9b6fce88503563013d1bfb7a502c81fe3ac527" -"checksum magenta-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40d014c7011ac470ae28e2f76a02bfea4a8480f73e701353b49ad7a8d75f4699" -"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" -"checksum mime 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "153f98dde2b135dece079e5478ee400ae1bab13afa52d66590eacfc40e912435" -"checksum mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dbd91d3bfbceb13897065e97b2ef177a09a438cb33612b2d371bf568819a9313" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" -"checksum nom 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06989cbd367e06f787a451f3bc67d8c3e0eaa10b461cc01152ffab24261a31b1" -"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" -"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" -"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" -"checksum rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "eb250fd207a4729c976794d03db689c9be1d634ab5a1c9da9492a13d8fecbcdf" -"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" -"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" -"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" -"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" -"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" -"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" -"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" -"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" -"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e" -"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c" -"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047" -"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791" -"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" -"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" -"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" -"checksum textwrap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f728584ea33b0ad19318e20557cb0a39097751dbb07171419673502f848c7af6" -"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" -"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" -"checksum tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e85d419699ec4b71bfe35bbc25bb8771e52eff0471a7f75c853ad06e200b4f86" -"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af" -"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" -"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -"checksum unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e01da42520092d0cd2d6ac3ae69eb21a22ad43ff195676b86f8c37f487d6b80" -"checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" -"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" -"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d238435618c0f298d2d75596c2d4fa7d4ea469c0c1c3ff824737ed50ad5ab61c" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/ngx-binding/Cargo.toml b/ngx-binding/Cargo.toml deleted file mode 100644 index a038c85..0000000 --- a/ngx-binding/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "ngx_rust" -version = "0.1.2" -authors = ["Sehyo Chang sehyo@nginx.com"] - -[lib] -doctest = false -test = true -crate-type = ["staticlib","rlib"] - - -[dev-dependencies] -futures = "0.1.14" -hyper = "0.11.2" -tokio-core = "0.1.9" - -[build-dependencies] -bindgen = "0.30.0" - - diff --git a/ngx-binding/Makefile b/ngx-binding/Makefile deleted file mode 100644 index b16fe55..0000000 --- a/ngx-binding/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -NGINX_VER = 1.13.7 -UNAME_S := $(shell uname -s) -NGX_MODULES = --with-compat --with-threads --with-http_addition_module \ - --with-http_auth_request_module --with-http_gunzip_module --with-http_gzip_static_module \ - --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module \ - --with-http_slice_module --with-http_stub_status_module --with-http_sub_module \ - --with-stream --with-stream_realip_module --with-stream_ssl_preread_module - -ifeq ($(UNAME_S),Linux) - NGINX_SRC = nginx-linux - NGX_OPT= $(NGX_MODULES) \ - --with-file-aio --with-http_ssl_module --with-stream_ssl_module \ - --with-cc-opt='-g -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' \ - --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' -endif -ifeq ($(UNAME_S),Darwin) - NGINX_SRC = nginx-darwin - NGX_OPT= $(NGX_MODULES) -endif - - -setup-nginx: - mkdir -p nginx - -nginx-source: setup-nginx - rm -rf nginx/${NGINX_SRC} - wget http://nginx.org/download/nginx-${NGINX_VER}.tar.gz - tar zxf nginx-${NGINX_VER}.tar.gz - mv nginx-${NGINX_VER} ${NGINX_SRC} - mv ${NGINX_SRC} nginx - rm nginx-${NGINX_VER}.tar.gz* - -nginx-configure: - cd nginx/${NGINX_SRC}; \ - ./configure $(NGX_OPT) - - -nginx-setup: nginx-source nginx-configure - - -nginx-module: - cd nginx/${NGINX_SRC}; \ - make modules; - - -clean: - rm -rf nginx - rm -f src/bindings.rs diff --git a/ngx-binding/build.rs b/ngx-binding/build.rs deleted file mode 100644 index db19f7f..0000000 --- a/ngx-binding/build.rs +++ /dev/null @@ -1,74 +0,0 @@ -extern crate bindgen; - -use std::process::Command; -use std::process::Output; -use std::env; -use std::io::Result; - -#[cfg(target_os = "macos")] -const NGIX_DIR: &str = "./nginx/nginx-darwin"; - -#[cfg(target_os = "linux")] -const NGIX_DIR: &str = "./nginx/nginx-linux"; - -// perform make with argument -fn make(arg: &str) -> Result { - let current_path = env::current_dir().unwrap(); - let path_name = format!("{}",current_path.display()); - println!("executing make command at {}",path_name); - let result = Command::new("/usr/bin/make") - .args(&[arg]) - .current_dir(path_name) - .output(); - - match result { - Err(e) => { - return Err(e); - }, - - Ok(output) => { - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - return Ok(output); - } - } -} - - -fn configure() -> Result { - make("nginx-setup") -} - - - - -fn generate_binding() { - let bindings = bindgen::Builder::default() - // The input header we would like to generate - // bindings for. - .header("wrapper.h") - .layout_tests(false) - .clang_arg(format!("-I{}/src/core",NGIX_DIR)) - .clang_arg(format!("-I{}/src/event",NGIX_DIR)) - .clang_arg(format!("-I{}/src/event/modules",NGIX_DIR)) - .clang_arg(format!("-I{}/src/os/unix",NGIX_DIR)) - .clang_arg(format!("-I{}/objs",NGIX_DIR)) - .clang_arg(format!("-I{}/src/http",NGIX_DIR)) - .clang_arg(format!("-I{}/src/http/modules",NGIX_DIR)) - // Finish the builder and generate the bindings. - .generate() - // Unwrap the Result and panic on failure. - .expect("Unable to generate bindings"); - - bindings - .write_to_file("src/bindings.rs") - .expect("Couldn't write bindings!"); -} - -fn main() { - - configure(); - generate_binding(); - -} diff --git a/ngx-binding/src/.gitignore b/ngx-binding/src/.gitignore deleted file mode 100644 index b1dd71c..0000000 --- a/ngx-binding/src/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -bindings.rs -helloworld.rs -helloworld_grpc.rs -route_guide.rs -route_guide_grpc.rs -check.rs -attributes.rs -status.rs -service_grpc.rs -quota.rs -report.rs diff --git a/ngx-binding/src/lib.rs b/ngx-binding/src/lib.rs deleted file mode 100644 index b9e76f5..0000000 --- a/ngx-binding/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ - - -pub mod bindings; -pub mod nginx_http; -pub mod nginx; - - -pub mod log; \ No newline at end of file diff --git a/ngx-binding/src/log.rs b/ngx-binding/src/log.rs deleted file mode 100644 index 9949ef7..0000000 --- a/ngx-binding/src/log.rs +++ /dev/null @@ -1,37 +0,0 @@ - -#[macro_export] -macro_rules! ngx_debug { - - ($level:expr,$log:expr,$($arg:tt)*) => { - if (*$log).log_level & $level as usize > 0{ - let c_message = ::std::ffi::CString::new(format!($($arg)*)).unwrap(); - $crate::bindings::ngx_log_error_core($crate::bindings::NGX_LOG_DEBUG as usize, $log, 0, c_message.as_ptr()); - } - } -} - -#[macro_export] -macro_rules! ngx_http_debug { - - ($request:expr,$($arg:tt)*) => { - unsafe { - ngx_debug!($crate::bindings::NGX_LOG_DEBUG_HTTP,(*($request).connection).log,$($arg)*); - } - } -} - - -#[macro_export] -macro_rules! ngx_event_debug { - - ($($arg:tt)*) => { - unsafe { - ngx_debug!($crate::bindings::NGX_LOG_DEBUG_EVENT,(*$crate::bindings::ngx_cycle).log,$($arg)*); - } - } -} - - - - - diff --git a/ngx-binding/src/nginx.rs b/ngx-binding/src/nginx.rs deleted file mode 100644 index c5015c5..0000000 --- a/ngx-binding/src/nginx.rs +++ /dev/null @@ -1,98 +0,0 @@ -/** - * harness to test nginx - */ - -use std::process::Command; -use std::process::Output; -use std::io::Result; -use std::env; -use std::fs; - -const NGINX_INSTALL_PATH: &str = "nginx/install"; -const NGINX_BIN: &str = "sbin/nginx"; -const NGINX_CONFIG: &str = "conf/nginx.conf"; - -pub struct Nginx { - - pub install_path: String // install path -} - - -impl Nginx { - - pub fn new(path: String) -> Nginx { - Nginx { install_path: path } - } - - // create nginx with default - pub fn default() -> Nginx { - let path = env::current_dir().unwrap(); - let install_path = format!("{}/{}",path.display(),NGINX_INSTALL_PATH); - Nginx { install_path: install_path } - } - - - // get bin path - pub fn bin_path(&mut self) -> String { - format!("{}/{}",self.install_path,NGINX_BIN) - } - - - pub fn cmd(&mut self, args: &[&str] ) -> Result { - let bin_path = self.bin_path(); - let result = Command::new(&bin_path) - .args(args) - .output(); - - match result { - Err(e) => { - return Err(e); - }, - - Ok(output) => { - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - return Ok(output); - } - } - - } - - // complete stop the nginx binary - pub fn stop(&mut self) -> Result { - self.cmd(&["-s","stop"]) - } - - // start the nginx binary - pub fn start(&mut self) -> Result { - self.cmd(&[]) - } - - - // make sure we stop existing nginx and start new master process - // intentinally ignore failure in stop - pub fn restart(&mut self) -> Result { - - self.stop(); - self.start() - } - - - // replace config with another config - pub fn replace_config(&mut self, from: &str) -> Result { - let config_path = format!("{}/{}",self.install_path,NGINX_CONFIG); - println!("copying config from: {} to: {}",from,config_path); // replace with logging - fs::copy(from , config_path) - } - - -} - - - - - - - - diff --git a/ngx-binding/src/nginx_http.rs b/ngx-binding/src/nginx_http.rs deleted file mode 100644 index 2910648..0000000 --- a/ngx-binding/src/nginx_http.rs +++ /dev/null @@ -1,159 +0,0 @@ - -use std::str; -use std::slice; - -use bindings::ngx_http_request_s; -use bindings::ngx_http_headers_in_t; -use bindings::ngx_http_headers_out_t; -use bindings::ngx_list_part_t; -use bindings::ngx_table_elt_t; -use bindings::ngx_list_t; -use bindings::ngx_uint_t; -use bindings::ngx_str_t; -use bindings::ngx_log_t; -use bindings::ngx_log_error_core; -use bindings::NGX_LOG_ERR; -use bindings::ngx_cycle; - - -impl ngx_str_t { - // convert nginx string to str slice - pub fn to_str(&self) -> &str { - - unsafe { - let slice = slice::from_raw_parts(self.data,self.len) ; - return str::from_utf8(slice).unwrap(); - } - - } - - // get string - pub fn to_string(&self) -> String { - return String::from(self.to_str()); - } -} - -impl ngx_http_request_s { - - /* - pub fn scheme(&self) -> char { - unsafe { (*self.schema_start)}; - } - */ - -} - -impl ngx_http_headers_in_t { - - // host - pub fn host_str(&self) -> &str { - unsafe { (*self.host).value.to_str() } - } - - pub fn user_agent_str(&self) -> &str { - unsafe { (*self.user_agent).value.to_str() } - } - - // referrer - pub fn referer_str(&self) -> Option<&str> { - - let referer = self.referer; - - if referer.is_null() { - return None; - } - - return Some(unsafe { (*referer).value.to_str() }); - } - - pub fn headers_iterator(&self) -> NgxListIterator { - list_iterator( &self.headers ) - } - -} - -impl ngx_http_headers_out_t { - - pub fn content_length_str(&self) -> &str { - unsafe { (*self.content_length).value.to_str() } - } - - pub fn server_str(&self) -> &str { - unsafe { (*self.server).value.to_str() } - } - - pub fn headers_iterator(&self) -> NgxListIterator { - list_iterator( &self.headers ) - } - -} - - -pub struct NgxListIterator { - - done: bool , - part: *const ngx_list_part_t, - h: *const ngx_table_elt_t, - i: ngx_uint_t -} - - -// create new http request iterator -pub fn list_iterator(list: *const ngx_list_t) -> NgxListIterator { - - unsafe { - let part: *const ngx_list_part_t = &(*list).part ; - - NgxListIterator { - done: false, - part: part, - h: (*part).elts as *const ngx_table_elt_t, - i: 0 - } - } - -} - -// iterator for ngx_list_t - -impl Iterator for NgxListIterator { - - // type Item = (&str,&str); - // TODO: try to use str instead of string - - type Item = (String,String); - - fn next(&mut self) -> Option { - - unsafe { - if self.done { - return None; - } else { - if self.i >= (*self.part).nelts { - if (*self.part).next.is_null() { - self.done = true; - return None - } - - // loop back - self.part = (*self.part).next; - self.h = (*self.part).elts as *mut ngx_table_elt_t; - self.i = 0; - } - - let header: *const ngx_table_elt_t = self.h.offset(self.i as isize); - - let header_name: ngx_str_t = (*header).key; - - let header_value: ngx_str_t = (*header).value; - - self.i = self.i + 1; - - return Some( (header_name.to_string(),header_value.to_string()) ) ; - - } - } - - } - -} diff --git a/ngx-binding/wrapper.h b/ngx-binding/wrapper.h deleted file mode 100644 index 3cfb3dd..0000000 --- a/ngx-binding/wrapper.h +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..866c756 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +max_width = 120 \ No newline at end of file diff --git a/src/core/buffer.rs b/src/core/buffer.rs new file mode 100644 index 0000000..4ea72cb --- /dev/null +++ b/src/core/buffer.rs @@ -0,0 +1,93 @@ +use crate::ffi::*; + +use std::slice; + +pub trait Buffer { + fn as_ngx_buf(&self) -> *const ngx_buf_t; + + fn as_ngx_buf_mut(&mut self) -> *mut ngx_buf_t; + + fn as_bytes(&self) -> &[u8] { + let buf = self.as_ngx_buf(); + unsafe { slice::from_raw_parts((*buf).pos, self.len()) } + } + + fn len(&self) -> usize { + let buf = self.as_ngx_buf(); + unsafe { + let pos = (*buf).pos; + let last = (*buf).last; + assert!(last >= pos); + usize::wrapping_sub(last as _, pos as _) + } + } + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn set_last_buf(&mut self, last: bool) { + let buf = self.as_ngx_buf_mut(); + unsafe { + (*buf).set_last_buf(if last { 1 } else { 0 }); + } + } + + fn set_last_in_chain(&mut self, last: bool) { + let buf = self.as_ngx_buf_mut(); + unsafe { + (*buf).set_last_in_chain(if last { 1 } else { 0 }); + } + } +} + +pub trait MutableBuffer: Buffer { + fn as_bytes_mut(&mut self) -> &mut [u8] { + let buf = self.as_ngx_buf_mut(); + unsafe { slice::from_raw_parts_mut((*buf).pos, self.len()) } + } +} + +pub struct TemporaryBuffer(*mut ngx_buf_t); + +impl TemporaryBuffer { + pub fn from_ngx_buf(buf: *mut ngx_buf_t) -> TemporaryBuffer { + assert!(!buf.is_null()); + TemporaryBuffer(buf) + } +} + +impl Buffer for TemporaryBuffer { + fn as_ngx_buf(&self) -> *const ngx_buf_t { + self.0 + } + + fn as_ngx_buf_mut(&mut self) -> *mut ngx_buf_t { + self.0 + } +} + +impl MutableBuffer for TemporaryBuffer { + fn as_bytes_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut((*self.0).pos, self.len()) } + } +} + +pub struct MemoryBuffer(*mut ngx_buf_t); + +impl MemoryBuffer { + pub fn from_ngx_buf(buf: *mut ngx_buf_t) -> MemoryBuffer { + assert!(!buf.is_null()); + MemoryBuffer(buf) + } +} + +impl Buffer for MemoryBuffer { + fn as_ngx_buf(&self) -> *const ngx_buf_t { + self.0 + } + + fn as_ngx_buf_mut(&mut self) -> *mut ngx_buf_t { + self.0 + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..271e23d --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,66 @@ +mod buffer; +mod pool; +mod status; +mod string; + +pub use buffer::*; +pub use pool::*; +pub use status::*; +pub use string::*; + +/// Static empty configuration directive initializer for [`ngx_command_t`]. +/// +/// This is typically used to terminate an array of configuration directives. +/// +/// [`ngx_command_t`]: https://nginx.org/en/docs/dev/development_guide.html#config_directives +#[macro_export] +macro_rules! ngx_null_command { + () => { + ngx_command_t { + name: $crate::ngx_null_string!(), + type_: 0, + set: None, + conf: 0, + offset: 0, + post: ::std::ptr::null_mut(), + } + }; +} + +/// Static empty configuration variable initializer for [`ngx_http_variable_t`]. +/// +/// This is typically used to terminate an array of HTTP variable types. +/// +/// [`ngx_http_variable_t`]: https://nginx.org/en/docs/dev/development_guide.html#http_variables +#[macro_export] +macro_rules! ngx_http_null_variable { + () => { + ngx_http_variable_t { + name: $crate::ngx_null_string!(), + set_handler: None, + get_handler: None, + data: 0, + flags: 0, + index: 0, + } + }; +} + +/// Static empty configuration variable initializer for [`ngx_stream_variable_t`]. +/// +/// This is typically used to terminate an array of Stream variable types. +/// +/// [`ngx_stream_variable_t`]: TODO: find appropriate link +#[macro_export] +macro_rules! ngx_stream_null_variable { + () => { + ngx_stream_variable_t { + name: $crate::ngx_null_string!(), + set_handler: None, + get_handler: None, + data: 0, + flags: 0, + index: 0, + } + }; +} diff --git a/src/core/pool.rs b/src/core/pool.rs new file mode 100644 index 0000000..21f9aa9 --- /dev/null +++ b/src/core/pool.rs @@ -0,0 +1,101 @@ +use crate::core::buffer::{Buffer, MemoryBuffer, TemporaryBuffer}; +use crate::ffi::*; + +use std::os::raw::c_void; +use std::{mem, ptr}; + +pub struct Pool(*mut ngx_pool_t); + +impl Pool { + /// # Safety + /// + /// The caller has provided a valid `ngx_pool_t` that points to valid memory and is non-null. + /// A null argument will assert and panic. + pub unsafe fn from_ngx_pool(pool: *mut ngx_pool_t) -> Pool { + assert!(!pool.is_null()); + Pool(pool) + } + + pub fn create_buffer(&mut self, size: usize) -> Option { + let buf = unsafe { ngx_create_temp_buf(self.0, size) }; + if buf.is_null() { + return None; + } + + Some(TemporaryBuffer::from_ngx_buf(buf)) + } + + pub fn create_buffer_from_str(&mut self, str: &str) -> Option { + let mut buffer = self.create_buffer(str.len())?; + unsafe { + let mut buf = buffer.as_ngx_buf_mut(); + ptr::copy_nonoverlapping(str.as_ptr(), (*buf).pos, str.len()); + (*buf).last = (*buf).pos.add(str.len()); + } + Some(buffer) + } + + pub fn create_buffer_from_static_str(&mut self, str: &'static str) -> Option { + let buf = self.calloc_type::(); + if buf.is_null() { + return None; + } + + // We cast away const, but buffers with the memory flag are read-only + let start = str.as_ptr() as *mut u8; + let end = unsafe { start.add(str.len()) }; + + unsafe { + (*buf).start = start; + (*buf).pos = start; + (*buf).last = end; + (*buf).end = end; + (*buf).set_memory(1); + } + + Some(MemoryBuffer::from_ngx_buf(buf)) + } + + unsafe fn add_cleanup_for_value(&mut self, value: *mut T) -> Result<(), ()> { + let cln = ngx_pool_cleanup_add(self.0, 0); + if cln.is_null() { + return Err(()); + } + (*cln).handler = Some(cleanup_type::); + (*cln).data = value as *mut c_void; + + Ok(()) + } + + pub fn alloc(&mut self, size: usize) -> *mut c_void { + unsafe { ngx_palloc(self.0, size) } + } + + pub fn alloc_type(&mut self) -> *mut T { + self.alloc(mem::size_of::()) as *mut T + } + + pub fn calloc(&mut self, size: usize) -> *mut c_void { + unsafe { ngx_pcalloc(self.0, size) } + } + + pub fn calloc_type(&mut self) -> *mut T { + self.calloc(mem::size_of::()) as *mut T + } + + pub fn allocate(&mut self, value: T) -> *mut T { + unsafe { + let p = self.alloc(mem::size_of::()) as *mut T; + ptr::write(p, value); + if self.add_cleanup_for_value(p).is_err() { + ptr::drop_in_place(p); + return ptr::null_mut(); + }; + p + } + } +} + +unsafe extern "C" fn cleanup_type(data: *mut c_void) { + ptr::drop_in_place(data as *mut T); +} diff --git a/src/core/status.rs b/src/core/status.rs new file mode 100644 index 0000000..3b614d2 --- /dev/null +++ b/src/core/status.rs @@ -0,0 +1,52 @@ +use crate::ffi::*; +use std::fmt; + +#[derive(Ord, PartialOrd, Eq, PartialEq)] +pub struct Status(pub ngx_int_t); + +impl Status { + pub fn is_ok(&self) -> bool { + self == &Status::NGX_OK + } +} + +impl fmt::Debug for Status { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl From for ngx_int_t { + fn from(val: Status) -> Self { + val.0 + } +} + +macro_rules! ngx_codes { + ( + $( + $(#[$docs:meta])* + ($konst:ident); + )+ + ) => { + impl Status { + $( + $(#[$docs])* + pub const $konst: Status = Status($konst as ngx_int_t); + )+ + + } + } +} + +ngx_codes! { + (NGX_OK); + (NGX_ERROR); + (NGX_AGAIN); + (NGX_BUSY); + (NGX_DONE); + (NGX_DECLINED); + (NGX_ABORT); +} +pub const NGX_CONF_ERROR: *const () = -1isize as *const (); +// pub const CONF_OK: Status = Status(NGX_CONF_OK as ngx_int_t); diff --git a/src/core/string.rs b/src/core/string.rs new file mode 100644 index 0000000..55ee5a7 --- /dev/null +++ b/src/core/string.rs @@ -0,0 +1,101 @@ +use crate::ffi::*; + +use std::borrow::Cow; +use std::slice; +use std::str::{self, Utf8Error}; + +/// Static string initializer for [`ngx_str_t`]. +/// +/// The resulting byte string is always nul-terminated (just like a C string). +/// +/// [`ngx_str_t`]: https://nginx.org/en/docs/dev/development_guide.html#string_overview +#[macro_export] +macro_rules! ngx_string { + ($s:expr) => {{ + $crate::ffi::ngx_str_t { + len: $s.len() as _, + data: concat!($s, "\0").as_ptr() as *mut u8, + } + }}; +} + +/// Static empty string initializer for [`ngx_str_t`]. +/// +/// [`ngx_str_t`]: https://nginx.org/en/docs/dev/development_guide.html#string_overview +#[macro_export] +macro_rules! ngx_null_string { + () => { + $crate::ffi::ngx_str_t { + len: 0, + data: ::std::ptr::null_mut(), + } + }; +} + +/// Representation of a borrowed [Nginx string]. +/// +/// [Nginx string]: https://nginx.org/en/docs/dev/development_guide.html#string_overview +pub struct NgxStr([u_char]); + +impl NgxStr { + /// Create an [`NgxStr`] from an [`ngx_str_t`]. + /// + /// [`ngx_str_t`]: https://nginx.org/en/docs/dev/development_guide.html#string_overview + /// + /// # Safety + /// + /// The caller has provided a valid `ngx_str_t` with a `data` pointer that points + /// to range of bytes of at least `len` bytes, whose content remains valid and doesn't + /// change for the lifetime of the returned `NgxStr`. + pub unsafe fn from_ngx_str<'a>(str: ngx_str_t) -> &'a NgxStr { + slice::from_raw_parts(str.data, str.len).into() + } + + /// Access the [`NgxStr`] as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + /// Yields a `&str` slice if the [`NgxStr`] contains valid UTF-8. + pub fn to_str(&self) -> Result<&str, Utf8Error> { + str::from_utf8(self.as_bytes()) + } + + /// Converts an [`NgxStr`] into a [`Cow`], replacing invalid UTF-8 sequences. + /// + /// See [`String::from_utf8_lossy`]. + pub fn to_string_lossy(&self) -> Cow { + String::from_utf8_lossy(self.as_bytes()) + } + + /// Returns `true` if the [`NgxStr`] is empty, otherwise `false`. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl From<&[u8]> for &NgxStr { + fn from(bytes: &[u8]) -> Self { + // SAFETY: An `NgxStr` is identical to a `[u8]` slice, given `u_char` is an alias for `u8`. + unsafe { &*(bytes as *const [u8] as *const NgxStr) } + } +} + +impl From<&str> for &NgxStr { + fn from(s: &str) -> Self { + s.as_bytes().into() + } +} + +impl AsRef<[u8]> for NgxStr { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl Default for &NgxStr { + fn default() -> Self { + // SAFETY: The null `ngx_str_t` is always a valid Nginx string. + unsafe { NgxStr::from_ngx_str(ngx_null_string!()) } + } +} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs new file mode 100644 index 0000000..887a73e --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1 @@ +pub use nginx_sys::*; diff --git a/src/http/conf.rs b/src/http/conf.rs new file mode 100644 index 0000000..4761bb0 --- /dev/null +++ b/src/http/conf.rs @@ -0,0 +1,33 @@ +use crate::ffi::*; + +use std::os::raw::c_void; + +/// # Safety +/// +/// The caller has provided a valid `ngx_conf_t` that points to valid memory and is non-null. +pub unsafe fn ngx_http_conf_get_module_main_conf( + cf: *mut ngx_conf_t, + module: &ngx_module_t, +) -> *mut ngx_http_core_main_conf_t { + let http_conf_ctx = (*cf).ctx as *mut ngx_http_conf_ctx_t; + *(*http_conf_ctx).main_conf.add(module.ctx_index) as *mut ngx_http_core_main_conf_t +} + +/// # Safety +/// +/// The caller has provided a valid `ngx_conf_t` that points to valid memory and is non-null. +pub unsafe fn ngx_http_conf_get_module_srv_conf(cf: *mut ngx_conf_t, module: &ngx_module_t) -> *mut c_void { + let http_conf_ctx = (*cf).ctx as *mut ngx_http_conf_ctx_t; + *(*http_conf_ctx).srv_conf.add(module.ctx_index) +} + +/// # Safety +/// +/// The caller has provided a valid `ngx_conf_t` that points to valid memory and is non-null. +pub unsafe fn ngx_http_conf_get_module_loc_conf( + cf: *mut ngx_conf_t, + module: &ngx_module_t, +) -> *mut ngx_http_core_loc_conf_t { + let http_conf_ctx = (*cf).ctx as *mut ngx_http_conf_ctx_t; + *(*http_conf_ctx).loc_conf.add(module.ctx_index) as *mut ngx_http_core_loc_conf_t +} diff --git a/src/http/mod.rs b/src/http/mod.rs new file mode 100644 index 0000000..230ce0b --- /dev/null +++ b/src/http/mod.rs @@ -0,0 +1,9 @@ +mod conf; +mod module; +mod request; +mod status; + +pub use conf::*; +pub use module::*; +pub use request::*; +pub use status::*; diff --git a/src/http/module.rs b/src/http/module.rs new file mode 100644 index 0000000..62167dd --- /dev/null +++ b/src/http/module.rs @@ -0,0 +1,115 @@ +use crate::core::NGX_CONF_ERROR; +use crate::core::*; +use crate::ffi::*; + +use core::ptr; +use std::os::raw::{c_char, c_void}; + +#[derive(Debug)] +pub enum MergeConfigError { + /// No value provided for configuration argument + NoValue, +} + +impl std::error::Error for MergeConfigError {} + +impl std::fmt::Display for MergeConfigError { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + MergeConfigError::NoValue => "no value".fmt(fmt), + } + } +} + +pub trait Merge { + fn merge(&mut self, prev: &Self) -> Result<(), MergeConfigError>; +} + +impl Merge for () { + fn merge(&mut self, _prev: &Self) -> Result<(), MergeConfigError> { + Ok(()) + } +} + +pub trait HTTPModule { + type MainConf: Merge + Default; + type SrvConf: Merge + Default; + type LocConf: Merge + Default; + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn preconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t { + Status::NGX_OK.into() + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn postconfiguration(_cf: *mut ngx_conf_t) -> ngx_int_t { + Status::NGX_OK.into() + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn create_main_conf(cf: *mut ngx_conf_t) -> *mut c_void { + let mut pool = Pool::from_ngx_pool((*cf).pool); + pool.allocate::(Default::default()) as *mut c_void + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn init_main_conf(_cf: *mut ngx_conf_t, _conf: *mut c_void) -> *mut c_char { + ptr::null_mut() + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn create_srv_conf(cf: *mut ngx_conf_t) -> *mut c_void { + let mut pool = Pool::from_ngx_pool((*cf).pool); + pool.allocate::(Default::default()) as *mut c_void + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn merge_srv_conf(_cf: *mut ngx_conf_t, prev: *mut c_void, conf: *mut c_void) -> *mut c_char { + let prev = &mut *(prev as *mut Self::SrvConf); + let conf = &mut *(conf as *mut Self::SrvConf); + match conf.merge(prev) { + Ok(_) => ptr::null_mut(), + Err(_) => NGX_CONF_ERROR as _, + } + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn create_loc_conf(cf: *mut ngx_conf_t) -> *mut c_void { + let mut pool = Pool::from_ngx_pool((*cf).pool); + pool.allocate::(Default::default()) as *mut c_void + } + + /// # Safety + /// + /// Callers should provide valid non-null `ngx_conf_t` arguments. Implementers must + /// guard against null inputs or risk runtime errors. + unsafe extern "C" fn merge_loc_conf(_cf: *mut ngx_conf_t, prev: *mut c_void, conf: *mut c_void) -> *mut c_char { + let prev = &mut *(prev as *mut Self::LocConf); + let conf = &mut *(conf as *mut Self::LocConf); + match conf.merge(prev) { + Ok(_) => ptr::null_mut(), + Err(_) => NGX_CONF_ERROR as _, + } + } +} diff --git a/src/http/request.rs b/src/http/request.rs new file mode 100644 index 0000000..b07bba2 --- /dev/null +++ b/src/http/request.rs @@ -0,0 +1,646 @@ +use crate::core::*; +use crate::ffi::*; +use crate::http::status::*; +use crate::ngx_null_string; +use std::fmt; +use std::os::raw::c_void; + +use std::error::Error; +use std::str::FromStr; + +/// Define a static request handler. +/// +/// Handlers are expected to take a single [`Request`] argument and return a [`Status`]. +#[macro_export] +macro_rules! http_request_handler { + ( $name: ident, $handler: expr ) => { + #[no_mangle] + extern "C" fn $name(r: *mut ngx_http_request_t) -> ngx_int_t { + let status: Status = $handler(unsafe { &mut $crate::http::Request::from_ngx_http_request(r) }); + status.0 + } + }; +} + +/// Define a static post subrequest handler. +/// +/// Handlers are expected to take a single [`Request`] argument and return a [`Status`]. +#[macro_export] +macro_rules! http_subrequest_handler { + ( $name: ident, $handler: expr ) => { + #[no_mangle] + unsafe extern "C" fn $name(r: *mut ngx_http_request_t, data: *mut c_void, rc: ngx_int_t) -> ngx_int_t { + $handler(r, data, rc) + } + }; +} + +/// Define a static variable setter. +/// +/// The set handler allows setting the property referenced by the variable. +/// The set handler expects a [`Request`], [`mut ngx_variable_valut_t`], and a [`usize`]. +/// Variables: https://nginx.org/en/docs/dev/development_guide.html#http_variables +#[macro_export] +macro_rules! http_variable_set { + ( $name: ident, $handler: expr ) => { + #[no_mangle] + unsafe extern "C" fn $name(r: *mut ngx_http_request_t, v: *mut ngx_variable_value_t, data: usize) { + $handler( + unsafe { &mut $crate::http::Request::from_ngx_http_request(r) }, + v, + data, + ); + } + }; +} + +/// Define a static variable evaluator. +/// +/// The get handler is responsible for evaluating a variable in the context of a specific request. +/// Variable evaluators accept a [`Request`] input argument and two output +/// arguments: [`ngx_http_variable_valut_t`] and [`usize`]. +/// Variables: https://nginx.org/en/docs/dev/development_guide.html#http_variables +#[macro_export] +macro_rules! http_variable_get { + ( $name: ident, $handler: expr ) => { + #[no_mangle] + unsafe extern "C" fn $name(r: *mut ngx_http_request_t, v: *mut ngx_variable_value_t, data: usize) -> ngx_int_t { + let status: Status = $handler( + unsafe { &mut $crate::http::Request::from_ngx_http_request(r) }, + v, + data, + ); + status.0 + } + }; +} + +#[repr(transparent)] +pub struct Request(ngx_http_request_t); + +impl Request { + /// Create a [`Request`] from an [`ngx_http_request_t`]. + /// + /// [`ngx_http_request_t`]: https://nginx.org/en/docs/dev/development_guide.html#http_request + /// + /// # Safety + /// + /// The caller has provided a valid non-null pointer to a valid `ngx_http_request_t` + /// which shares the same representation as `Request`. + pub unsafe fn from_ngx_http_request<'a>(r: *mut ngx_http_request_t) -> &'a mut Request { + &mut *r.cast::() + } + + /// Is this the main request (as opposed to a subrequest)? + pub fn is_main(&self) -> bool { + let main = self.0.main.cast(); + std::ptr::eq(self, main) + } + + /// Request pool. + pub fn pool(&self) -> Pool { + // SAFETY: This request is allocated from `pool`, thus must be a valid pool. + unsafe { Pool::from_ngx_pool(self.0.pool) } + } + + /// Pointer to a [`ngx_connection_t`] client connection object. + /// + /// [`ngx_connection_t`]: https://nginx.org/en/docs/dev/development_guide.html#connection + pub fn connection(&self) -> *mut ngx_connection_t { + self.0.connection + } + + pub fn log(&self) -> *mut ngx_log_t { + unsafe { (*self.connection()).log } + } + + /// Module location configuration. + fn get_module_loc_conf_ptr(&self, module: &ngx_module_t) -> *mut c_void { + unsafe { *self.0.loc_conf.add(module.ctx_index) } + } + + /// Module location configuration. + pub fn get_module_loc_conf(&self, module: &ngx_module_t) -> Option<&T> { + let lc_prt = self.get_module_loc_conf_ptr(module) as *mut T; + if lc_prt.is_null() { + return None; + } + let lc = unsafe { &*lc_prt }; + Some(lc) + } + + /// Get Module context pointer + fn get_module_ctx_ptr(&self, module: &ngx_module_t) -> *mut c_void { + unsafe { *self.0.ctx.add(module.ctx_index) } + } + + /// Get Module context + pub fn get_module_ctx(&self, module: &ngx_module_t) -> Option<&T> { + let cf = self.get_module_ctx_ptr(module) as *mut T; + + if cf.is_null() { + return None; + } + let co = unsafe { &*cf }; + Some(co) + } + + pub fn set_module_ctx(&self, value: *mut c_void, module: &ngx_module_t) { + unsafe { + *self.0.ctx.add(module.ctx_index) = value; + }; + } + + /// Get the value of a [complex value]. + /// + /// [complex value]: https://nginx.org/en/docs/dev/development_guide.html#http_complex_values + pub fn get_complex_value(&self, cv: &ngx_http_complex_value_t) -> Option<&NgxStr> { + let r = (self as *const Request as *mut Request).cast(); + let val = cv as *const ngx_http_complex_value_t as *mut ngx_http_complex_value_t; + // SAFETY: `ngx_http_complex_value` does not mutate `r` or `val` and guarentees that + // a valid Nginx string is stored in `value` if it successfully returns. + unsafe { + let mut value = ngx_null_string!(); + if ngx_http_complex_value(r, val, &mut value) != NGX_OK as ngx_int_t { + return None; + } + Some(NgxStr::from_ngx_str(value)) + } + } + + /// Discard (read and ignore) the [request body]. + /// + /// [request body]: https://nginx.org/en/docs/dev/development_guide.html#http_request_body + pub fn discard_request_body(&mut self) -> Status { + unsafe { Status(ngx_http_discard_request_body(&mut self.0)) } + } + + /// Client HTTP [User-Agent]. + /// + /// [User-Agent]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent + pub fn user_agent(&self) -> &NgxStr { + unsafe { NgxStr::from_ngx_str((*self.0.headers_in.user_agent).value) } + } + + /// Set HTTP status of response. + pub fn set_status(&mut self, status: HTTPStatus) { + self.0.headers_out.status = status.into(); + } + + pub fn add_header_in(&mut self, key: &str, value: &str) -> Option<()> { + let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.0.headers_in.headers) as _ }; + add_to_ngx_table(table, self.0.pool, key, value) + } + + pub fn add_header_out(&mut self, key: &str, value: &str) -> Option<()> { + let table: *mut ngx_table_elt_t = unsafe { ngx_list_push(&mut self.0.headers_out.headers) as _ }; + add_to_ngx_table(table, self.0.pool, key, value) + } + + /// Set response body [Content-Length]. + /// + /// [Content-Length]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length + pub fn set_content_length_n(&mut self, n: usize) { + self.0.headers_out.content_length_n = n as off_t; + } + + /// Send the output header. + /// + /// Do not call this function until all output headers are set. + pub fn send_header(&mut self) -> Status { + unsafe { Status(ngx_http_send_header(&mut self.0)) } + } + + /// Flag indicating that the output does not require a body. + /// + /// For example, this flag is used by `HTTP HEAD` requests. + pub fn header_only(&self) -> bool { + self.0.header_only() != 0 + } + + /// request method + pub fn method(&self) -> Method { + Method::from_ngx(self.0.method) + } + + /// path part of request only + pub fn path(&self) -> &NgxStr { + unsafe { NgxStr::from_ngx_str(self.0.uri) } + } + + /// full uri - containing path and args + pub fn unparsed_uri(&self) -> &NgxStr { + unsafe { NgxStr::from_ngx_str(self.0.unparsed_uri) } + } + + /// Send the [response body]. + /// + /// This function can be called multiple times. + /// Set the `last_buf` flag in the last body buffer. + /// + /// [response body]: https://nginx.org/en/docs/dev/development_guide.html#http_request_body + pub fn output_filter(&mut self, body: &mut ngx_chain_t) -> Status { + unsafe { Status(ngx_http_output_filter(&mut self.0, body)) } + } + + /// Perform internal redirect to a location + pub fn internal_redirect(&self, location: &str) -> Status { + assert!(!location.is_empty(), "uri location is empty"); + let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, location) as *mut _; + + // FIXME: check status of ngx_http_named_location or ngx_http_internal_redirect + if location.starts_with('@') { + unsafe { + ngx_http_named_location((self as *const Request as *mut Request).cast(), uri_ptr); + } + } else { + unsafe { + ngx_http_internal_redirect( + (self as *const Request as *mut Request).cast(), + uri_ptr, + std::ptr::null_mut(), + ); + } + } + Status::NGX_DONE + } + + /// Send a subrequest + pub fn subrequest( + &self, + uri: &str, + module: &ngx_module_t, + post_callback: unsafe extern "C" fn(*mut ngx_http_request_t, *mut c_void, ngx_int_t) -> ngx_int_t, + ) -> Status { + let uri_ptr = &mut ngx_str_t::from_str(self.0.pool, uri) as *mut _; + // ------------- + // allocate memory and set values for ngx_http_post_subrequest_t + let sub_ptr = self.pool().alloc(std::mem::size_of::()); + + // assert!(sub_ptr.is_null()); + let post_subreq = sub_ptr as *const ngx_http_post_subrequest_t as *mut ngx_http_post_subrequest_t; + unsafe { + (*post_subreq).handler = Some(post_callback); + (*post_subreq).data = self.get_module_ctx_ptr(module); // WARN: safety! ensure that ctx is already set + } + // ------------- + + let mut psr: *mut ngx_http_request_t = std::ptr::null_mut(); + let r = unsafe { + ngx_http_subrequest( + (self as *const Request as *mut Request).cast(), + uri_ptr, + std::ptr::null_mut(), + &mut psr as *mut _, + sub_ptr as *mut _, + NGX_HTTP_SUBREQUEST_WAITED as _, + ) + }; + + // previously call of ngx_http_subrequest() would ensure that the pointer is not null anymore + let mut sr = unsafe { &mut *psr }; + + /* + * allocate fake request body to avoid attempts to read it and to make + * sure real body file (if already read) won't be closed by upstream + */ + sr.request_body = self.pool().alloc(std::mem::size_of::()) as *mut _; + + if sr.request_body.is_null() { + return Status::NGX_ERROR; + } + sr.set_header_only(1 as _); + Status(r) + } + + /// Iterate over headers_in + /// each header item is (String, String) (copied) + pub fn headers_in_iterator(&self) -> NgxListIterator { + unsafe { list_iterator(&self.0.headers_in.headers) } + } + + /// Iterate over headers_out + /// each header item is (String, String) (copied) + pub fn headers_out_iterator(&self) -> NgxListIterator { + unsafe { list_iterator(&self.0.headers_out.headers) } + } +} + +// trait OnSubRequestDone { + +// } + +impl fmt::Debug for Request { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Request").field("request_", &self.0).finish() + } +} + +pub struct NgxListIterator { + done: bool, + part: *const ngx_list_part_t, + h: *const ngx_table_elt_t, + i: ngx_uint_t, +} + +// create new http request iterator +/// # Safety +/// +/// The caller has provided a valid `ngx_str_t` which can be dereferenced validly. +pub unsafe fn list_iterator(list: *const ngx_list_t) -> NgxListIterator { + let part: *const ngx_list_part_t = &(*list).part; + + NgxListIterator { + done: false, + part, + h: (*part).elts as *const ngx_table_elt_t, + i: 0, + } +} + +// iterator for ngx_list_t +impl Iterator for NgxListIterator { + // type Item = (&str,&str); + // TODO: try to use str instead of string + // something like pub struct Header(ngx_table_elt_t); + // then header would have key and value + + type Item = (String, String); + + fn next(&mut self) -> Option { + unsafe { + if self.done { + None + } else { + if self.i >= (*self.part).nelts { + if (*self.part).next.is_null() { + self.done = true; + return None; + } + + // loop back + self.part = (*self.part).next; + self.h = (*self.part).elts as *mut ngx_table_elt_t; + self.i = 0; + } + + let header: *const ngx_table_elt_t = self.h.add(self.i); + let header_name: ngx_str_t = (*header).key; + let header_value: ngx_str_t = (*header).value; + self.i += 1; + Some((header_name.to_string(), header_value.to_string())) + } + } + } +} + +/// A possible error value when converting `Method` +pub struct InvalidMethod { + _priv: (), +} + +/// Request method verb +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Method(MethodInner); + +impl Method { + /// UNKNOWN + pub const UNKNOWN: Method = Method(MethodInner::Unknown); + + /// GET + pub const GET: Method = Method(MethodInner::Get); + + /// HEAD + pub const HEAD: Method = Method(MethodInner::Head); + + /// POST + pub const POST: Method = Method(MethodInner::Post); + + /// PUT + pub const PUT: Method = Method(MethodInner::Put); + + /// DELETE + pub const DELETE: Method = Method(MethodInner::Delete); + + /// MKCOL + pub const MKCOL: Method = Method(MethodInner::Mkcol); + + /// COPY + pub const COPY: Method = Method(MethodInner::Copy); + + /// MOVE + pub const MOVE: Method = Method(MethodInner::Move); + + /// OPTIONS + pub const OPTIONS: Method = Method(MethodInner::Options); + + /// PROPFIND + pub const PROPFIND: Method = Method(MethodInner::Propfind); + + /// PROPPATCH + pub const PROPPATCH: Method = Method(MethodInner::Proppatch); + + /// LOCK + pub const LOCK: Method = Method(MethodInner::Lock); + + /// UNLOCK + pub const UNLOCK: Method = Method(MethodInner::Unlock); + + /// PATCH + pub const PATCH: Method = Method(MethodInner::Patch); + + /// TRACE + pub const TRACE: Method = Method(MethodInner::Trace); + + /// CONNECT + pub const CONNECT: Method = Method(MethodInner::Connect); + + #[inline] + pub fn as_str(&self) -> &str { + match self.0 { + MethodInner::Unknown => "UNKNOWN", + MethodInner::Get => "GET", + MethodInner::Head => "HEAD", + MethodInner::Post => "POST", + MethodInner::Put => "PUT", + MethodInner::Delete => "DELETE", + MethodInner::Mkcol => "MKCOL", + MethodInner::Copy => "COPY", + MethodInner::Move => "MOVE", + MethodInner::Options => "OPTIONS", + MethodInner::Propfind => "PROPFIND", + MethodInner::Proppatch => "PROPPATCH", + MethodInner::Lock => "LOCK", + MethodInner::Unlock => "UNLOCK", + MethodInner::Patch => "PATCH", + MethodInner::Trace => "TRACE", + MethodInner::Connect => "CONNECT", + } + } + + fn from_bytes(_t: &[u8]) -> Result { + todo!() + } + + fn from_ngx(t: ngx_uint_t) -> Method { + let t = t as _; + match t { + NGX_HTTP_GET => Method(MethodInner::Get), + NGX_HTTP_HEAD => Method(MethodInner::Head), + NGX_HTTP_POST => Method(MethodInner::Post), + NGX_HTTP_PUT => Method(MethodInner::Put), + NGX_HTTP_DELETE => Method(MethodInner::Delete), + NGX_HTTP_MKCOL => Method(MethodInner::Mkcol), + NGX_HTTP_COPY => Method(MethodInner::Copy), + NGX_HTTP_MOVE => Method(MethodInner::Move), + NGX_HTTP_OPTIONS => Method(MethodInner::Options), + NGX_HTTP_PROPFIND => Method(MethodInner::Propfind), + NGX_HTTP_PROPPATCH => Method(MethodInner::Proppatch), + NGX_HTTP_LOCK => Method(MethodInner::Lock), + NGX_HTTP_UNLOCK => Method(MethodInner::Unlock), + NGX_HTTP_PATCH => Method(MethodInner::Patch), + NGX_HTTP_TRACE => Method(MethodInner::Trace), + NGX_HTTP_CONNECT => Method(MethodInner::Connect), + _ => Method(MethodInner::Unknown), + } + } +} + +impl AsRef for Method { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl<'a> PartialEq<&'a Method> for Method { + #[inline] + fn eq(&self, other: &&'a Method) -> bool { + self == *other + } +} + +impl<'a> PartialEq for &'a Method { + #[inline] + fn eq(&self, other: &Method) -> bool { + *self == other + } +} + +impl PartialEq for Method { + #[inline] + fn eq(&self, other: &str) -> bool { + self.as_ref() == other + } +} + +impl PartialEq for str { + #[inline] + fn eq(&self, other: &Method) -> bool { + self == other.as_ref() + } +} + +impl<'a> PartialEq<&'a str> for Method { + #[inline] + fn eq(&self, other: &&'a str) -> bool { + self.as_ref() == *other + } +} + +impl<'a> PartialEq for &'a str { + #[inline] + fn eq(&self, other: &Method) -> bool { + *self == other.as_ref() + } +} + +impl fmt::Debug for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +impl fmt::Display for Method { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.as_ref()) + } +} + +impl<'a> From<&'a Method> for Method { + #[inline] + fn from(t: &'a Method) -> Self { + t.clone() + } +} + +impl<'a> TryFrom<&'a [u8]> for Method { + type Error = InvalidMethod; + + #[inline] + fn try_from(t: &'a [u8]) -> Result { + Method::from_bytes(t) + } +} + +impl<'a> TryFrom<&'a str> for Method { + type Error = InvalidMethod; + + #[inline] + fn try_from(t: &'a str) -> Result { + TryFrom::try_from(t.as_bytes()) + } +} + +impl FromStr for Method { + type Err = InvalidMethod; + + #[inline] + fn from_str(t: &str) -> Result { + TryFrom::try_from(t) + } +} + +impl InvalidMethod { + #[allow(dead_code)] + fn new() -> InvalidMethod { + InvalidMethod { _priv: () } + } +} + +impl fmt::Debug for InvalidMethod { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("InvalidMethod") + // skip _priv noise + .finish() + } +} + +impl fmt::Display for InvalidMethod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid HTTP method") + } +} + +impl Error for InvalidMethod {} + +#[derive(Clone, PartialEq, Eq, Hash)] +enum MethodInner { + Unknown, + Get, + Head, + Post, + Put, + Delete, + Mkcol, + Copy, + Move, + Options, + Propfind, + Proppatch, + Lock, + Unlock, + Patch, + Trace, + Connect, +} diff --git a/src/http/status.rs b/src/http/status.rs new file mode 100644 index 0000000..5aa3c11 --- /dev/null +++ b/src/http/status.rs @@ -0,0 +1,200 @@ +use crate::core::Status; +use crate::ffi::*; +use std::error::Error; +use std::fmt; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct HTTPStatus(pub ngx_uint_t); + +/// A possible error value when converting a `HTTPStatus` from a `u16` or `&str` +/// +/// This error indicates that the supplied input was not a valid number, was less +/// than 100, or was greater than 599. +#[derive(Debug)] +pub struct InvalidHTTPStatusCode { + _priv: (), +} + +impl InvalidHTTPStatusCode { + fn new() -> InvalidHTTPStatusCode { + InvalidHTTPStatusCode { _priv: () } + } +} + +impl fmt::Display for InvalidHTTPStatusCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("invalid status code".to_string().as_str()) + } +} + +impl Error for InvalidHTTPStatusCode {} + +impl From for Status { + fn from(val: HTTPStatus) -> Self { + Status(val.0 as ngx_int_t) + } +} + +impl From for ngx_uint_t { + fn from(val: HTTPStatus) -> Self { + val.0 + } +} + +impl fmt::Debug for HTTPStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl HTTPStatus { + #[inline] + pub fn from_u16(src: u16) -> Result { + if !(100..600).contains(&src) { + return Err(InvalidHTTPStatusCode::new()); + } + + Ok(HTTPStatus(src.into())) + } + + /// Converts a &[u8] to a status code + pub fn from_bytes(src: &[u8]) -> Result { + if src.len() != 3 { + return Err(InvalidHTTPStatusCode::new()); + } + + let a = src[0].wrapping_sub(b'0') as u16; + let b = src[1].wrapping_sub(b'0') as u16; + let c = src[2].wrapping_sub(b'0') as u16; + + if a == 0 || a > 5 || b > 9 || c > 9 { + return Err(InvalidHTTPStatusCode::new()); + } + + let status = (a * 100) + (b * 10) + c; + Ok(HTTPStatus(status.into())) + } +} + +macro_rules! http_status_codes { + ( + $( + $(#[$docs:meta])* + ($num:expr, $konst:ident, $phrase:expr); + )+ + ) => { + impl HTTPStatus { + $( + $(#[$docs])* + pub const $konst: HTTPStatus = HTTPStatus($num); + )+ + + } + } +} + +http_status_codes! { + /// 100 CONTINUE + (100, CONTINUE, "Continue"); + /// 101 SWITCHING_PROTOCOLS + (101, SWITCHING_PROTOCOLS, "Switching Protocols"); + /// 102 PROCESSING + (102, PROCESSING, "Processing"); + /// 200 OK + (200, OK, "OK"); + /// 201 Created + (201, CREATED, "Created"); + /// 202 Accepted + (202, ACCEPTED, "Accepted"); + /// 204 No Content + (204, NO_CONTENT, "No Content"); + /// 206 Partial Content + (206, PARTIAL_CONTENT, "Partial Content"); + + /// 300 SPECIAL_RESPONSE + (300, SPECIAL_RESPONSE, "SPECIAL_RESPONSE"); + /// 301 Moved Permanently + (301, MOVED_PERMANENTLY, "Moved Permanently"); + /// 302 Moved Temporarily + (302, MOVED_TEMPORARILY, "Moved Temporarily"); + /// 303 See Other + (303, SEE_OTHER, "See Other"); + /// 304 Not Modified + (304, NOT_MODIFIED, "Not Modified"); + /// 307 Temporary Redirect + (307, TEMPORARY_REDIRECT, "Temporary Redirect"); + /// 308 Permanent Redirect + (308, PERMANENT_REDIRECT, "Permanent Redirect"); + + /// 400 Bad Request + (400, BAD_REQUEST, "Bad Request"); + /// 401 Unauthorized + (401, UNAUTHORIZED, "Unauthorized"); + /// 403 Forbidden + (403, FORBIDDEN, "Forbidden"); + /// 404 Not Found + (404, NOT_FOUND, "Not Found"); + /// 405 Method Not Allowed + (405, NOT_ALLOWED, "Method Not Allowed"); + /// 408 Request Time Out + (408, REQUEST_TIME_OUT, "Request Time Out"); + /// 409 Conflict + (409, CONFLICT, "Conflict"); + /// 411 Length Required + (411, LENGTH_REQUIRED, "Length Required"); + /// 412 Precondition Failed + (412, PRECONDITION_FAILED, "Precondition Failed"); + /// 413 Payload Too Large + (413, REQUEST_ENTITY_TOO_LARGE, "Payload Too Large"); + /// 414 Request Uri Too Large + (414, REQUEST_URI_TOO_LARGE, "Request Uri Too Large"); + /// 415 Unsupported Media Type + (415, UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"); + /// 416 Range Not Satisfiable + (416, RANGE_NOT_SATISFIABLE, "Range Not Satisfiable"); + /// 421 Misdirected Request + (421, MISDIRECTED_REQUEST, "Misdirected Request"); + /// 429 Too Many Requests + (429, TOO_MANY_REQUESTS, "Too Many Requests"); + + // /* Our own HTTP codes */ + // /* The special code to close connection without any response */ + /// 444 CLOSE + (444, CLOSE, "CLOSE"); + + /// 494 NGINX_CODES + (494, NGINX_CODES, "NGINX_CODES"); + + /// 494 REQUEST_HEADER_TOO_LARGE + (494, REQUEST_HEADER_TOO_LARGE, "REQUEST_HEADER_TOO_LARGE"); + + /// 495 NGX_HTTPS_CERT_ERROR + (495, HTTPS_CERT_ERROR, "NGX_HTTPS_CERT_ERROR"); + /// 496 NGX_HTTPS_NO_CERT + (496, HTTPS_NO_CERT, "NGX_HTTPS_NO_CERT"); + + // /* + // * We use the special code for the plain HTTP requests that are sent to + // * HTTPS port to distinguish it from 4XX in an error page redirection + // */ + /// 497 TO_HTTPS + (497, TO_HTTPS, "TO_HTTPS"); + + /// 499 CLIENT_CLOSED_REQUEST + (499, CLIENT_CLOSED_REQUEST, "CLIENT_CLOSED_REQUEST"); + + /// 500 INTERNAL_SERVER_ERROR + (500, INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR"); + /// 501 NOT_IMPLEMENTED + (501, NOT_IMPLEMENTED, "NOT_IMPLEMENTED"); + /// 502 BAD_GATEWAY + (502, BAD_GATEWAY, "BAD_GATEWAY"); + /// 503 SERVICE_UNAVAILABLE + (503, SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE"); + /// 504 GATEWAY_TIME_OUT + (504, GATEWAY_TIME_OUT, "GATEWAY_TIME_OUT"); + /// 505 VERSION_NOT_SUPPORTED + (505, VERSION_NOT_SUPPORTED, "VERSION_NOT_SUPPORTED"); + /// 507 INSUFFICIENT_STORAGE + (507, INSUFFICIENT_STORAGE, "INSUFFICIENT_STORAGE"); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..821c19d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,37 @@ +pub mod core; +pub mod ffi; +pub mod http; +pub mod log; + +/// Define modules exported by this library. +/// +/// These are normally generated by the Nginx module system, but need to be +/// defined when building modules outside of it. +#[macro_export] +macro_rules! ngx_modules { + ($( $mod:ident ),+) => { + #[no_mangle] + pub static mut ngx_modules: [*const ngx_module_t; $crate::count!($( $mod, )+) + 1] = [ + $( unsafe { &$mod } as *const ngx_module_t, )+ + std::ptr::null() + ]; + + #[no_mangle] + pub static mut ngx_module_names: [*const c_char; $crate::count!($( $mod, )+) + 1] = [ + $( concat!(stringify!($mod), "\0").as_ptr() as *const i8, )+ + std::ptr::null() + ]; + + #[no_mangle] + pub static mut ngx_module_order: [*const c_char; 1] = [ + std::ptr::null() + ]; + }; +} + +/// Count number of arguments +#[macro_export] +macro_rules! count { + () => { 0usize }; + ($x:tt, $( $xs:tt ),*) => { 1usize + $crate::count!($( $xs, )*) }; +} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..37d4ac4 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,29 @@ +/// Write to logger at a specified level. +/// +/// See [Logging](https://nginx.org/en/docs/dev/development_guide.html#logging) +/// for available log levels. +#[macro_export] +macro_rules! ngx_log_debug { + ( $log:expr, $($arg:tt)* ) => { + let log_level = unsafe { (*$log).log_level }; + if log_level != 0 { + let level = $crate::ffi::NGX_LOG_DEBUG as $crate::ffi::ngx_uint_t; + let fmt = ::std::ffi::CString::new("%s").unwrap(); + let c_message = ::std::ffi::CString::new(format!($($arg)*)).unwrap(); + unsafe { + $crate::ffi::ngx_log_error_core(level, $log, 0, fmt.as_ptr(), c_message.as_ptr()); + } + } + } +} + +/// Log to request connection log at level [`NGX_LOG_DEBUG_HTTP`]. +/// +/// [`NGX_LOG_DEBUG_HTTP`]: https://nginx.org/en/docs/dev/development_guide.html#logging +#[macro_export] +macro_rules! ngx_log_debug_http { + ( $request:expr, $($arg:tt)* ) => { + let log = unsafe { (*$request.connection()).log }; + $crate::ngx_log_debug!(log, $($arg)*); + } +} diff --git a/tests/log_test.rs b/tests/log_test.rs index 78785a3..b5a1858 100644 --- a/tests/log_test.rs +++ b/tests/log_test.rs @@ -1,49 +1,101 @@ -extern crate ngx_rust; -extern crate futures; -extern crate hyper; -extern crate tokio_core; +use std::env; +use std::fs; +use std::io::Result; +use std::process::Command; +use std::process::Output; + +const NGX_DEFAULT_VERSION: &str = "1.23.3"; +const NGINX_BIN: &str = "sbin/nginx"; +const NGINX_CONFIG: &str = "conf/nginx.conf"; + +/// harness to test nginx +pub struct Nginx { + pub install_path: String, +} + +impl Default for Nginx { + /// create nginx with default + fn default() -> Nginx { + let path = env::current_dir().unwrap(); + let version = env::var("NGX_VERSION").unwrap_or_else(|_| NGX_DEFAULT_VERSION.to_string()); + let platform = format!("{}-{}", env::consts::OS, env::consts::ARCH); + let ngx_path = format!(".cache/nginx/{}/{}", version, platform); + let install_path = format!("{}/{}", path.display(), ngx_path); + Nginx { install_path } + } +} + +impl Nginx { + pub fn new(path: String) -> Nginx { + Nginx { install_path: path } + } + /// get bin path to nginx instance + pub fn bin_path(&mut self) -> String { + format!("{}/{}", self.install_path, NGINX_BIN) + } + + /// start nginx process with arguments + pub fn cmd(&mut self, args: &[&str]) -> Result { + let bin_path = self.bin_path(); + let result = Command::new(bin_path).args(args).output(); + + match result { + Err(e) => Err(e), + + Ok(output) => { + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + Ok(output) + } + } + } + + /// complete stop the nginx binary + pub fn stop(&mut self) -> Result { + self.cmd(&["-s", "stop"]) + } + + /// start the nginx binary + pub fn start(&mut self) -> Result { + self.cmd(&[]) + } + + // make sure we stop existing nginx and start new master process + // intentinally ignore failure in stop + pub fn restart(&mut self) -> Result { + self.stop(); + self.start() + } + + // replace config with another config + pub fn replace_config(&mut self, from: &str) -> Result { + let config_path = format!("{}/{}", self.install_path, NGINX_CONFIG); + println!("copying config from: {} to: {}", from, config_path); // replace with logging + fs::copy(from, config_path) + } +} #[cfg(test)] mod tests { + use super::*; use std::env; - use ngx_rust::nginx::Nginx; - use futures::Future; - use hyper::Client; - use hyper::StatusCode; - use tokio_core::reactor::Core; const TEST_NGINX_CONFIG: &str = "tests/nginx.conf"; - #[test] fn test() { - let mut nginx = Nginx::default(); let path = env::current_dir().unwrap(); - let test_config_path = format!("{}/{}",path.display(),TEST_NGINX_CONFIG); + let test_config_path = format!("{}/{}", path.display(), TEST_NGINX_CONFIG); (nginx.replace_config(&test_config_path)).expect("copy done"); - let output = nginx.restart().expect("fail to start"); + let output = nginx.restart().expect("fail to start"); assert!(output.status.success()); - // make request to 30000 - - let mut core = Core::new().unwrap(); - - let client = Client::new(&core.handle()); - - let uri = "http://localhost:30000".parse().unwrap(); - let work = client.get(uri).map(|res| { - let status = res.status(); - println!("Response: {}", status); - assert_eq!(status,StatusCode::Ok); - - }); - - core.run(work).unwrap(); + let output = nginx.stop().expect("fail to stop"); + assert!(output.status.success()); } - - -} \ No newline at end of file +} diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index 09c18ce..0000000 --- a/tools/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Tools to compile and run rust based modules - - diff --git a/tools/rust/Dockerfile b/tools/rust/Dockerfile deleted file mode 100644 index 31d365d..0000000 --- a/tools/rust/Dockerfile +++ /dev/null @@ -1,50 +0,0 @@ -FROM ubuntu:16.04 -ARG RUST_VERSION=1.26.0 - -MAINTAINER Sehyo Chang "sehyo@nginx.com" - -# install nginx required libraries - -RUN apt-get update - -# install dev en -RUN apt-get install vim autoconf automake libtool curl make wget unzip gnupg binutils pkg-config -y - -# essential c compiler toolchain includes automake -#n RUN apt-get install build-essential -y -# install clang -RUN wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - -RUN echo 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial main' >> /etc/apt/sources.list -RUN wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - -RUN apt-get update -RUN apt-get install llvm-3.9-dev libclang-3.9-dev clang-3.9 -y - -# This is special hack to comment out IPPORT_RESERVED because it conflicts with in.h -# which prevents from compilation of rust binding.rs -RUN sed -i 's:# define IPPORT_RESERVED:// #define IPPORT_RESERVED:' /usr/include/netdb.h - -# install PCRE library -RUN apt-get install libpcre3 libpcre3-dev zlib1g-dev libssl-dev -y - - -# install rust -RUN curl -sO https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init && \ - chmod +x rustup-init && \ - ./rustup-init -y --default-toolchain $RUST_VERSION --no-modify-path && \ - rm -rf \ - rustup-init \ - /var/lib/apt/lists/* \ - /tmp/* \ - /var/tmp/* - - -ENV PATH $PATH:/root/.cargo/bin - - - -# install protobuf -RUN curl -OL https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip -RUN unzip protoc-3.3.0-linux-x86_64.zip -d protoc3 -RUN mv protoc3/bin/* /usr/local/bin/ -RUN rm protoc-3.3.0-linux-x86_64.zip - diff --git a/tools/rust/Makefile b/tools/rust/Makefile deleted file mode 100644 index 44a49ed..0000000 --- a/tools/rust/Makefile +++ /dev/null @@ -1,13 +0,0 @@ - -TAG = 1.26.0 -NAME = nginxinc/ngx-rust-tool - - -all: build push - -build: - docker build -t $(NAME):$(TAG) --build-arg RUST_VERSION=$(TAG) . - -push: - docker push $(NAME):$(TAG) - diff --git a/tools/rust/README.md b/tools/rust/README.md deleted file mode 100644 index 76d049f..0000000 --- a/tools/rust/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Purpose - -Base image used for building rust based NGINX module. - -It contains rust tools chain and protobuf compiler - -'make build;push'