├── examples ├── full │ ├── ballerina │ │ ├── .gitignore │ │ ├── .dockerignore │ │ ├── Ballerina.toml │ │ ├── Ballerina.lock │ │ ├── Dockerfile │ │ └── src │ │ │ └── server │ │ │ └── main.bal │ ├── php-mark │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── composer.json │ │ ├── Dockerfile │ │ ├── server.php │ │ └── composer.lock │ ├── rust-actix │ │ ├── .gitignore │ │ ├── .dockerignore │ │ ├── dummy.rs │ │ ├── Cargo.toml │ │ ├── Dockerfile │ │ ├── src │ │ │ └── main.rs │ │ └── Cargo.lock │ ├── index.js │ ├── swift-perfect │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ ├── Package.swift │ │ ├── .gitignore │ │ └── Sources │ │ │ └── server │ │ │ └── main.swift │ ├── python-flask │ │ ├── .gitignore │ │ ├── requirements.txt │ │ ├── Dockerfile │ │ └── server.py │ ├── crystal-kemal │ │ ├── .gitignore │ │ ├── shard.yml │ │ ├── Dockerfile │ │ ├── shard.lock │ │ └── server.cr │ ├── deno-oak │ │ ├── deps.ts │ │ ├── Dockerfile │ │ └── server.ts │ ├── go-fiber │ │ ├── go.mod │ │ ├── Dockerfile │ │ ├── go.sum │ │ └── server.go │ ├── wrk-post.lua │ ├── v │ │ ├── .gitignore │ │ ├── server.mod │ │ ├── Dockerfile │ │ ├── demo-vex.v │ │ ├── vweb_example.v │ │ ├── server.v │ │ └── demo-valval.v │ ├── ruby-sinatra │ │ ├── Gemfile │ │ ├── Dockerfile │ │ ├── Gemfile.lock │ │ └── server.rb │ ├── docker-compose.yml │ └── README.md └── index.js ├── .vscode ├── settings.json └── launch.json ├── .dockerignore ├── test └── integration │ ├── moleculer.config.sidecar.js │ ├── services │ ├── greeter.service.js │ ├── ex-beta.js │ └── ex-alpha.js │ ├── utils.js │ └── index.spec.js ├── .npmignore ├── prettier.config.js ├── Dockerfile ├── Dockerfile-alpine ├── .editorconfig ├── .eslintrc.js ├── index.js ├── LICENSE ├── .github └── workflows │ ├── integration.yml │ └── release.yml ├── .gitignore ├── package.json ├── src └── index.js └── README.md /examples/full/ballerina/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /examples/full/php-mark/.dockerignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /examples/full/php-mark/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /examples/full/rust-actix/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /examples/full/ballerina/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /examples/full/index.js: -------------------------------------------------------------------------------- 1 | require("../../index"); 2 | -------------------------------------------------------------------------------- /examples/full/rust-actix/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /examples/full/rust-actix/dummy.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /examples/full/swift-perfect/.dockerignore: -------------------------------------------------------------------------------- 1 | .build/ 2 | -------------------------------------------------------------------------------- /examples/full/python-flask/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /examples/full/crystal-kemal/.gitignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | /.crystal/ 3 | /.shards/ 4 | -------------------------------------------------------------------------------- /examples/full/deno-oak/deps.ts: -------------------------------------------------------------------------------- 1 | import "https://deno.land/x/oak/mod.ts"; 2 | -------------------------------------------------------------------------------- /examples/full/python-flask/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.2 2 | requests==2.25.1 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true 5 | } -------------------------------------------------------------------------------- /examples/full/go-fiber/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.15 4 | 5 | require github.com/gofiber/fiber/v2 v2.3.0 6 | -------------------------------------------------------------------------------- /examples/full/php-mark/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "mark-php/mark": "^1.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/full/wrk-post.lua: -------------------------------------------------------------------------------- 1 | wrk.method = "POST" 2 | wrk.body = "{}" 3 | wrk.headers["Content-Type"] = "application/json" 4 | -------------------------------------------------------------------------------- /examples/full/v/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | server 3 | *.exe 4 | *.exe~ 5 | *.so 6 | *.dylib 7 | *.dll 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | * 3 | 4 | # Allow only bundle 5 | !/dist/moleculer-sidecar-linux 6 | !/dist/moleculer-sidecar-alpine 7 | -------------------------------------------------------------------------------- /examples/full/ballerina/Ballerina.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | org-name= "moleculer" 3 | version= "0.1.0" 4 | 5 | [platform] 6 | target = "java8" 7 | -------------------------------------------------------------------------------- /examples/full/ballerina/Ballerina.lock: -------------------------------------------------------------------------------- 1 | org_name = "moleculer" 2 | version = "0.1.0" 3 | lockfile_version = "1.0.0" 4 | ballerina_version = "1.2.12" 5 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const moduleName = process.argv[2] || "full"; 4 | process.argv.splice(2, 1); 5 | 6 | require("./" + moduleName); 7 | -------------------------------------------------------------------------------- /examples/full/v/server.mod: -------------------------------------------------------------------------------- 1 | Module { 2 | name: 'v' 3 | description: 'Example V project for Moleculer Sidecar' 4 | version: '0.0.0' 5 | dependencies: [] 6 | } -------------------------------------------------------------------------------- /examples/full/ruby-sinatra/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem 'sinatra', '~> 2.1.0' 6 | gem 'json', '~> 2.5.1' 7 | -------------------------------------------------------------------------------- /test/integration/moleculer.config.sidecar.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | nodeID: "sidecar", 3 | //logger: false, 4 | logLevel: "error", 5 | transporter: "Fake" 6 | }; 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .github/ 3 | benchmark/ 4 | coverage/ 5 | dev/ 6 | docs/ 7 | example/ 8 | examples/ 9 | typings/ 10 | test/ 11 | *.db 12 | .editorconfig 13 | .eslintrc.js 14 | prettier.config.js 15 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | useTabs: true, 3 | printWidth: 100, 4 | trailingComma: "none", 5 | tabWidth: 4, 6 | singleQuote: false, 7 | semi: true, 8 | bracketSpacing: true, 9 | arrowParens: "avoid" 10 | }; 11 | -------------------------------------------------------------------------------- /examples/full/ruby-sinatra/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.7-alpine 2 | 3 | RUN apk add build-base 4 | 5 | WORKDIR /app 6 | 7 | COPY Gemfile Gemfile.lock ./ 8 | 9 | RUN bundle install 10 | 11 | COPY . . 12 | 13 | EXPOSE 5006 14 | 15 | CMD ruby server.rb 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | WORKDIR /sidecar 4 | 5 | ENV NODE_ENV=production 6 | 7 | COPY dist/moleculer-sidecar-linux ./moleculer-sidecar 8 | 9 | RUN apt add libbsd.-dev 10 | 11 | EXPOSE 5103 12 | 13 | ENTRYPOINT ["/sidecar/moleculer-sidecar"] 14 | -------------------------------------------------------------------------------- /examples/full/go-fiber/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.15.6-alpine AS build 2 | WORKDIR /src 3 | ENV CGO_ENABLED=0 4 | COPY . . 5 | RUN go build -o /out/go-fiber . 6 | 7 | FROM scratch AS bin 8 | COPY --from=build /out/go-fiber / 9 | EXPOSE 5002 10 | 11 | CMD ["./go-fiber"] 12 | -------------------------------------------------------------------------------- /Dockerfile-alpine: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | WORKDIR /sidecar 4 | 5 | ENV NODE_ENV=production 6 | 7 | COPY dist/moleculer-sidecar-alpine ./moleculer-sidecar 8 | 9 | RUN apk add --no-cache libstdc++ 10 | 11 | EXPOSE 5103 12 | 13 | ENTRYPOINT ["/sidecar/moleculer-sidecar"] 14 | -------------------------------------------------------------------------------- /examples/full/v/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM thevlang/vlang-alpine AS build 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | 7 | RUN v server.v 8 | 9 | 10 | FROM alpine 11 | WORKDIR /app 12 | COPY --from=build /app/server /app/server 13 | 14 | EXPOSE 5005 15 | 16 | CMD ["/app/server"] 17 | -------------------------------------------------------------------------------- /examples/full/python-flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | #RUN apk --update add bash nano 4 | 5 | WORKDIR /app 6 | COPY ./requirements.txt . 7 | 8 | RUN pip install -r requirements.txt 9 | 10 | COPY . . 11 | 12 | ENV FLASK_APP=server.py 13 | EXPOSE 5000 14 | 15 | CMD ["python", "server.py"] 16 | -------------------------------------------------------------------------------- /examples/full/swift-perfect/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:5.3 as builder 2 | WORKDIR /app 3 | COPY . . 4 | RUN apt-get update && apt-get install -y openssl libssl-dev uuid-dev 5 | RUN swift build 6 | 7 | FROM swift:5.3-slim 8 | WORKDIR /app 9 | COPY --from=builder /app/.build/x86_64-unknown-linux-gnu/debug/ ./ 10 | EXPOSE 5008 11 | CMD ["./server"] 12 | -------------------------------------------------------------------------------- /examples/full/crystal-kemal/shard.yml: -------------------------------------------------------------------------------- 1 | name: 2 | version: 0.1.0 3 | 4 | # authors: 5 | # - name 6 | 7 | # description: | 8 | # Short description of 9 | 10 | dependencies: 11 | kemal: 12 | github: kemalcr/kemal 13 | 14 | # development_dependencies: 15 | # webmock: 16 | # github: manastech/webmock.cr 17 | 18 | # license: MIT 19 | -------------------------------------------------------------------------------- /examples/full/v/demo-vex.v: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | import nedpals.vex.server 4 | import nedpals.vex.ctx 5 | 6 | fn main() { 7 | mut s := server.new() 8 | 9 | s.get('/greet/:name', fn (req ctx.Req, mut res ctx.Resp) { 10 | name := req.params['name'] 11 | res.send('Hello, $name!', 200) 12 | }) 13 | println('Starting server listening on port 5005...') 14 | s.serve(5005) 15 | } 16 | -------------------------------------------------------------------------------- /examples/full/rust-actix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-actix" 3 | version = "0.1.0" 4 | authors = ["Icebob "] 5 | edition = "2018" 6 | 7 | [[bin]] 8 | name = "rust-actix" 9 | path = "src/main.rs" 10 | 11 | [dependencies] 12 | actix-web = "3" 13 | serde = "1.0.118" 14 | serde_json = "1.0.59" 15 | reqwest = { version = "0.10", features = ["json"] } 16 | url = "2.2.0" 17 | -------------------------------------------------------------------------------- /examples/full/crystal-kemal/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM crystallang/crystal:1.0.0 2 | WORKDIR /data 3 | 4 | RUN apt-get update && \ 5 | apt-get install -y libgconf-2-4 curl libreadline-dev && \ 6 | # Cleanup leftovers 7 | apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 8 | 9 | COPY server.cr shard.yml shard.lock ./ 10 | RUN shards install --ignore-crystal-version 11 | RUN crystal build server.cr 12 | 13 | CMD ["./server"] 14 | -------------------------------------------------------------------------------- /examples/full/crystal-kemal/shard.lock: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | shards: 3 | exception_page: 4 | git: https://github.com/crystal-loot/exception_page.git 5 | version: 0.1.4 6 | 7 | kemal: 8 | git: https://github.com/kemalcr/kemal.git 9 | version: 1.0.0 10 | 11 | kilt: 12 | git: https://github.com/jeromegn/kilt.git 13 | version: 0.4.1 14 | 15 | radix: 16 | git: https://github.com/luislavena/radix.git 17 | version: 0.4.1 18 | 19 | -------------------------------------------------------------------------------- /examples/full/swift-perfect/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "server", 7 | products: [ 8 | .executable(name: "server", targets: ["server"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", from: "3.0.0"), 12 | ], 13 | targets: [ 14 | .target(name: "server", dependencies: ["PerfectHTTPServer"]) 15 | ] 16 | ) 17 | -------------------------------------------------------------------------------- /examples/full/php-mark/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | WORKDIR /app 4 | 5 | ENV DEBIAN_FRONTEND noninteractive 6 | 7 | RUN apt-get update && apt-get install -y ca-certificates git php-cli php-curl --no-install-recommends && \ 8 | rm -r /var/lib/apt/lists/* 9 | 10 | COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer 11 | COPY ./composer.* ./ 12 | RUN composer install 13 | 14 | COPY . . 15 | 16 | EXPOSE 5001 17 | 18 | CMD [ "php", "server.php", "start" ] 19 | -------------------------------------------------------------------------------- /examples/full/rust-actix/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.48 AS build 2 | 3 | WORKDIR /app 4 | 5 | COPY dummy.rs . 6 | COPY ./Cargo.* ./ 7 | RUN sed -i 's#src/main.rs#dummy.rs#' Cargo.toml && \ 8 | cat ./Cargo.toml && \ 9 | cargo build --release && \ 10 | sed -i 's#dummy.rs#src/main.rs#' Cargo.toml 11 | 12 | COPY . . 13 | 14 | RUN cargo build --release 15 | 16 | 17 | FROM rust:1.48 18 | WORKDIR /app 19 | COPY --from=build /app/target/release/rust-actix /app/rust-actix 20 | 21 | EXPOSE 5004 22 | 23 | CMD ["/app/rust-actix"] 24 | -------------------------------------------------------------------------------- /examples/full/ruby-sinatra/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | json (2.5.1) 5 | mustermann (1.1.1) 6 | ruby2_keywords (~> 0.0.1) 7 | rack (2.2.3) 8 | rack-protection (2.1.0) 9 | rack 10 | ruby2_keywords (0.0.2) 11 | sinatra (2.1.0) 12 | mustermann (~> 1.0) 13 | rack (~> 2.2) 14 | rack-protection (= 2.1.0) 15 | tilt (~> 2.0) 16 | tilt (2.0.10) 17 | 18 | PLATFORMS 19 | ruby 20 | 21 | DEPENDENCIES 22 | json (~> 2.5.1) 23 | sinatra (~> 2.1.0) 24 | 25 | BUNDLED WITH 26 | 2.1.2 27 | -------------------------------------------------------------------------------- /examples/full/deno-oak/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hayd/alpine-deno:1.6.2 2 | 3 | WORKDIR /app 4 | 5 | # Cache the dependencies as a layer (the following two steps are re-run only when deps.ts is modified). 6 | # Ideally cache deps.ts will download and compile _all_ external files used in main.ts. 7 | COPY deps.ts . 8 | RUN deno cache deps.ts 9 | 10 | # These steps will be re-run upon each file change in your working directory: 11 | COPY . . 12 | 13 | # Compile the main app so that it doesn't need to be compiled each startup/entry. 14 | RUN deno cache main.ts 15 | 16 | EXPOSE 5007 17 | 18 | CMD ["run", "--allow-net", "--allow-env", "server.ts"] 19 | -------------------------------------------------------------------------------- /examples/full/ballerina/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ballerina/ballerina as builder 2 | WORKDIR /home/ballerina 3 | USER root 4 | RUN mkdir -p src && chmod 777 src 5 | COPY . . 6 | #RUN ballerina build server 7 | #RUN ls -al 8 | CMD ["ballerina", "run", "server"] 9 | 10 | # FROM ballerina/jre8:v1 11 | # RUN addgroup troupe \ 12 | # && adduser -S -s /bin/bash -g 'ballerina' -G troupe -D ballerina \ 13 | # && apk add --update --no-cache bash \ 14 | # && chown -R ballerina:troupe /usr/bin/java \ 15 | # && rm -rf /var/cache/apk/* 16 | 17 | # WORKDIR /home/ballerina 18 | # COPY --from=builder /home/ballerina/target/bin/server.jar ./ 19 | # USER ballerina 20 | 21 | # EXPOSE 5009 22 | 23 | # CMD ["java", "-jar", "server.jar"] 24 | -------------------------------------------------------------------------------- /test/integration/services/greeter.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | name: "greeter", 5 | actions: { 6 | hello: { 7 | rest: { 8 | method: "GET", 9 | path: "/hello" 10 | }, 11 | handler: jest.fn(async () => "Hello Moleculer") 12 | }, 13 | 14 | welcome: { 15 | rest: "/welcome", 16 | params: { 17 | name: "string" 18 | }, 19 | handler: jest.fn(async ctx => `Welcome, ${ctx.params.name}`) 20 | }, 21 | 22 | echo: { 23 | rest: "/echo", 24 | handler(ctx) { 25 | return { 26 | params: ctx.params, 27 | meta: ctx.meta 28 | }; 29 | } 30 | } 31 | }, 32 | 33 | events: { 34 | "post.updated": { 35 | handler: jest.fn() 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = tab 11 | indent_size = 4 12 | space_after_anon_function = true 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | indent_style = space 23 | indent_size = 4 24 | 25 | [{package,bower}.json] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | [*.{yml,yaml}] 30 | indent_style = space 31 | indent_size = 2 32 | 33 | [*.js] 34 | quote_type = "double" 35 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | commonjs: true, 5 | es6: true, 6 | jquery: false, 7 | jest: true, 8 | jasmine: true 9 | }, 10 | extends: ["eslint:recommended", "plugin:security/recommended", "plugin:prettier/recommended"], 11 | parserOptions: { 12 | sourceType: "module", 13 | ecmaVersion: 2018 14 | }, 15 | plugins: ["node", "promise", "security"], 16 | rules: { 17 | "no-var": ["error"], 18 | "no-console": ["warn"], 19 | "no-unused-vars": ["warn"], 20 | "no-trailing-spaces": ["error"], 21 | "security/detect-object-injection": ["off"], 22 | "security/detect-non-literal-require": ["off"], 23 | "security/detect-non-literal-fs-filename": ["off"], 24 | "no-process-exit": ["off"], 25 | "node/no-unpublished-require": 0, 26 | "require-atomic-updates": 0, 27 | "object-curly-spacing": ["warn", "always"] 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /examples/full/v/vweb_example.v: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | import vweb 4 | 5 | const ( 6 | port = 8082 7 | ) 8 | 9 | struct App { 10 | vweb.Context 11 | mut: 12 | cnt int 13 | } 14 | 15 | fn main() { 16 | println('vweb example') 17 | vweb.run(port) 18 | } 19 | 20 | pub fn (mut app App) init_once() { 21 | app.handle_static('.') 22 | } 23 | 24 | pub fn (mut app App) json_endpoint() vweb.Result { 25 | return app.json('{"a": 3}') 26 | } 27 | 28 | pub fn (mut app App) index() vweb.Result { 29 | app.cnt++ 30 | //show := true 31 | // app.text('Hello world from vweb') 32 | //hello := 'Hello world from vweb' 33 | //numbers := [1, 2, 3] 34 | //return $vweb.html() 35 | return app.text('Hello world from vweb') 36 | } 37 | 38 | pub fn (mut app App) show_text() vweb.Result { 39 | return app.text('Hello world from vweb') 40 | } 41 | 42 | pub fn (mut app App) cookie() vweb.Result { 43 | app.set_cookie(name: 'cookie', value: 'test') 44 | return app.text('Headers: $app.headers') 45 | } 46 | -------------------------------------------------------------------------------- /examples/full/v/server.v: -------------------------------------------------------------------------------- 1 | import net.http 2 | import os 3 | import vweb 4 | //import json 5 | 6 | struct App { 7 | vweb.Context 8 | mut: 9 | cnt int 10 | } 11 | 12 | fn register_service_schema()? { 13 | mut sidecar_address := os.getenv('SIDECAR_ADDRESS') 14 | if sidecar_address == "" { 15 | sidecar_address = 'http://localhost:5103' 16 | } 17 | 18 | println('Registering service schema ($sidecar_address)') 19 | url := "http://localhost:5103/v1/registry/services" 20 | data := '{ 21 | "name": "v-demo", 22 | "settings": { 23 | "baseUrl": "http://v-demo:5005" 24 | }, 25 | "actions": { 26 | "hello": "/actions/hello", 27 | "welcome": { 28 | "params": { 29 | "name": "string|no-empty|trim" 30 | }, 31 | "handler": "/actions/welcome" 32 | } 33 | }, 34 | "events": { 35 | "sample.event": "/events/sample.event" 36 | } 37 | }' 38 | resp := http.post_json(url, data)? 39 | println('Response' + resp.text) 40 | } 41 | 42 | 43 | register_service_schema() 44 | 45 | println("Listening on port 5005...") 46 | vweb.run(5005) 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @moleculer/sidecar 3 | * Copyright (c) 2020 MoleculerJS (https://github.com/moleculerjs/sidecar) 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | // Need references in order to pkg put them into the bundle. 10 | require("amqplib"); 11 | require("avsc"); 12 | require("dotenv"); 13 | require("etcd3"); 14 | require("ioredis"); 15 | require("jaeger-client"); 16 | require("kafka-node"); 17 | require("mqtt"); 18 | require("msgpack5"); 19 | require("nats"); 20 | require("node-nats-streaming"); 21 | require("notepack.io"); 22 | require("protobufjs"); 23 | require("redlock"); 24 | require("rhea-promise"); 25 | require("thrift"); 26 | 27 | const { Runner } = require("moleculer"); 28 | const path = require("path"); 29 | 30 | //console.log("argv:", process.argv); 31 | 32 | const runner = new Runner(); 33 | module.exports = runner 34 | .start(process.argv) 35 | .then(async broker => { 36 | await broker.loadService(path.join(__dirname, "src", "index.js")); 37 | return broker; 38 | }) 39 | .catch(err => { 40 | // eslint-disable-next-line no-console 41 | console.error(err); 42 | process.exit(1); 43 | }); 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 MoleculerJS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | node-version: [10.x, 12.x, 14.x] 14 | fail-fast: false 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | 23 | - name: Install dependencies 24 | run: npm ci --silent 25 | 26 | - name: Run tests 27 | run: npm run test 28 | 29 | #- name: Start containers 30 | # run: docker-compose up -d 31 | # working-directory: ./test 32 | 33 | #- name: Sleeping 30 secs 34 | # run: sleep 30 35 | 36 | #- name: Check containers 37 | # run: docker-compose ps 38 | # working-directory: ./test 39 | 40 | #- name: Check logs 41 | # run: docker-compose logs 42 | # working-directory: ./test 43 | 44 | #- name: Run integration tests 45 | # run: npm test 46 | 47 | #- name: Stop containers 48 | # run: docker-compose down -v 49 | # working-directory: ./test 50 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "type": "node", 10 | "request": "launch", 11 | "name": "Launch demo", 12 | "program": "${workspaceRoot}/examples/index.js", 13 | "cwd": "${workspaceRoot}", 14 | "args": [ 15 | "simple" 16 | ] 17 | }, 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Jest", 22 | "program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js", 23 | "args": ["--runInBand"], 24 | "cwd": "${workspaceRoot}", 25 | "runtimeArgs": [ 26 | "--nolazy" 27 | ] 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "Jest single", 33 | "program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js", 34 | "args": ["--runInBand", "${fileBasenameNoExtension}"], 35 | "console": "internalConsole", 36 | "cwd": "${workspaceRoot}", 37 | "runtimeArgs": [ 38 | "--nolazy" 39 | ] 40 | }, 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /test/integration/utils.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | 3 | module.exports = { 4 | POST: async function (url, payload, noRes) { 5 | const res = await fetch(url, { 6 | method: "POST", 7 | cache: "no-cache", 8 | headers: { 9 | "Content-Type": "application/json", 10 | Accept: "application/json" 11 | }, 12 | body: payload ? JSON.stringify(payload) : undefined 13 | }); 14 | 15 | return { 16 | status: res.status, 17 | json: noRes ? null : await res.json() 18 | }; 19 | }, 20 | 21 | DELETE: async function (url) { 22 | const res = await fetch(url, { 23 | method: "DELETE", 24 | cache: "no-cache" 25 | }); 26 | 27 | return { 28 | status: res.status, 29 | json: await res.json() 30 | }; 31 | }, 32 | 33 | parseBody: function (req) { 34 | let body = []; 35 | 36 | return new Promise((resolve, reject) => { 37 | req.on("error", err => reject(err)) 38 | .on("data", chunk => body.push(chunk)) 39 | .on("end", () => resolve(Buffer.concat(body).toString())); 40 | }); 41 | }, 42 | 43 | sendResponse: function (res, code, body) { 44 | const headers = {}; 45 | if (typeof body == "object") { 46 | headers["Content-Type"] = "application/json"; 47 | body = JSON.stringify(body); 48 | } 49 | 50 | res.writeHead(code, headers); 51 | res.end(body); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /examples/full/ruby-sinatra/server.rb: -------------------------------------------------------------------------------- 1 | # myapp.rb 2 | require 'sinatra' 3 | require 'json' 4 | require 'net/http' 5 | 6 | SIDECAR_ADDRESS = ENV['SIDECAR_ADDRESS'] || 'http://localhost:5103' 7 | 8 | configure do 9 | set :bind, '0.0.0.0' 10 | set :port, 5006 11 | set :default_content_type, 'application/json' 12 | end 13 | 14 | post '/actions/hello' do 15 | logger.info "Hello action called." 16 | [200, {}, JSON[{ 17 | 'response' => "Hello from Ruby!" 18 | }]] 19 | end 20 | 21 | post '/actions/welcome' do 22 | logger.info "Welcome action called." 23 | 24 | data = JSON.parse request.body.read 25 | params = data['params'] 26 | meta = data['meta'] 27 | 28 | [200, {}, JSON[{ 29 | 'response' => "Hello #{params['name']} from Ruby!" 30 | }]] 31 | end 32 | 33 | post '/events/sample.event' do 34 | logger.info "Sample event happened." 35 | [200, {}, "{}"] 36 | end 37 | 38 | def register_service_schema 39 | puts "Registering service schema (#{SIDECAR_ADDRESS})..." 40 | 41 | uri = URI("#{SIDECAR_ADDRESS}/v1/registry/services") 42 | req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') 43 | req.body = { 44 | name: "ruby-demo", 45 | settings: { 46 | baseUrl: "http://ruby-demo:5006" 47 | }, 48 | actions: { 49 | hello: "/actions/hello", 50 | welcome: { 51 | params: { 52 | name: "string|no-empty|trim" 53 | }, 54 | handler: "/actions/welcome" 55 | } 56 | }, 57 | events: { 58 | "sample.event": "/events/sample.event" 59 | } 60 | }.to_json 61 | res = Net::HTTP.start(uri.hostname, uri.port) do |http| 62 | http.request(req) 63 | end 64 | puts res 65 | end 66 | 67 | register_service_schema 68 | -------------------------------------------------------------------------------- /examples/full/crystal-kemal/server.cr: -------------------------------------------------------------------------------- 1 | require "http/client" 2 | require "kemal" 3 | 4 | ENV["SIDECAR_ADDRESS"] ||= "http://localhost:5103" 5 | SIDECAR_ADDRESS = ENV["SIDECAR_ADDRESS"] 6 | 7 | def register_service_schema 8 | puts "Registering service schema (#{SIDECAR_ADDRESS})..." 9 | 10 | serviceSchema = "{ 11 | \"name\": \"crystal-demo\", 12 | \"settings\": { 13 | \"baseUrl\": \"http://crystal-demo:5010\" 14 | }, 15 | \"actions\": { 16 | \"hello\": \"/actions/hello\", 17 | \"welcome\": { 18 | \"params\": { 19 | \"name\": \"string|no-empty|trim\" 20 | }, 21 | \"handler\": \"/actions/welcome\" 22 | } 23 | }, 24 | \"events\": { 25 | \"sample.event\": \"/events/sample.event\" 26 | } 27 | }" 28 | 29 | uri = "#{SIDECAR_ADDRESS}/v1/registry/services" 30 | response = HTTP::Client.post(uri, headers: HTTP::Headers{"Content-Type" => "application/json"}, body: serviceSchema) 31 | puts response.body 32 | end 33 | 34 | register_service_schema 35 | 36 | post "/actions/hello" do |env| 37 | puts "Hello action called." 38 | 39 | env.response.content_type = "application/json" 40 | { "response": "Hello from Crystal!" }.to_json 41 | end 42 | 43 | post "/actions/welcome" do |env| 44 | puts "Welcome action called." 45 | 46 | params = env.params.json["params"].as(Hash(String, JSON::Any)) 47 | name = params["name"] 48 | 49 | env.response.content_type = "application/json" 50 | { "response": "Hello #{name} from Crystal!" }.to_json 51 | end 52 | 53 | post "/events/sample.event" do |env| 54 | puts "Sample event happened." 55 | end 56 | 57 | Kemal.run do |config| 58 | server = config.server.not_nil! 59 | server.bind_tcp "0.0.0.0", 5010, reuse_port: true 60 | end 61 | -------------------------------------------------------------------------------- /examples/full/swift-perfect/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/screenshots 64 | 65 | Packages/ 66 | PerfectTemplate.xcodeproj 67 | Tests/ 68 | webroot/ 69 | Package.pins 70 | Package.resolved 71 | .DS_Store 72 | -------------------------------------------------------------------------------- /examples/full/deno-oak/server.ts: -------------------------------------------------------------------------------- 1 | import { Application, Router } from "https://deno.land/x/oak/mod.ts"; 2 | 3 | const SIDECAR_ADDRESS = Deno.env.get('SIDECAR_ADDRESS') || "http://localhost:5103"; 4 | 5 | const router = new Router(); 6 | router 7 | .post("/actions/hello", ctx => { 8 | console.log("Hello action called."); 9 | ctx.response.body = { response: "Hello from Deno!" }; 10 | }) 11 | .post("/actions/welcome", async ctx => { 12 | console.log("Welcome action called."); 13 | const body = ctx.request.body(); 14 | const params = (await body.value).params; 15 | ctx.response.body = { response: `Hello ${params.name} from Deno!` }; 16 | }) 17 | .post("/events/sample.event", ctx => { 18 | console.log("Sample event happened."); 19 | ctx.response.status = 200; 20 | }); 21 | 22 | const app = new Application(); 23 | app.use(router.routes()); 24 | app.use(router.allowedMethods()); 25 | 26 | async function registerServiceSchema() { 27 | console.log(`Registering service schema (${SIDECAR_ADDRESS})...`); 28 | 29 | const headers = new Headers(); 30 | headers.set('Content-Type', 'application/json'); 31 | 32 | const rs = new ReadableStream(); 33 | 34 | const res = await fetch(`${SIDECAR_ADDRESS}/v1/registry/services`, { 35 | method: "POST", 36 | headers, 37 | body: JSON.stringify({ 38 | name: "deno-demo", 39 | settings: { 40 | baseUrl: "http://deno-demo:5007" 41 | }, 42 | actions: { 43 | hello: "/actions/hello", 44 | welcome: { 45 | params: { 46 | name: "string|no-empty|trim" 47 | }, 48 | handler: "/actions/welcome" 49 | } 50 | }, 51 | events: { 52 | "sample.event": "/events/sample.event" 53 | } 54 | }) 55 | }); 56 | console.log("Response:", await res.text()); 57 | } 58 | 59 | await registerServiceSchema(); 60 | console.log("Listening on port 5007"); 61 | await app.listen({ port: 5007 }); 62 | -------------------------------------------------------------------------------- /examples/full/v/demo-valval.v: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | //import net.http 4 | //import json 5 | //import sync 6 | 7 | import watchmen123456.valval 8 | 9 | struct ActionRequestParam { 10 | name string 11 | } 12 | 13 | struct ActionRequest { 14 | params ActionRequestParam 15 | } 16 | /* 17 | fn worker_fetch(p &sync.PoolProcessor, cursor int, worker_id int) voidptr { 18 | id := p.get_int_item(cursor) 19 | resp := http.get('https://hacker-news.firebaseio.com/v0/item/${id}.json') or { 20 | println('failed to fetch data from /v0/item/${id}.json') 21 | return sync.no_result 22 | } 23 | story := json.decode(Story,resp.text) or { 24 | println('failed to decode a story') 25 | return sync.no_result 26 | } 27 | println('# $cursor) $story.title | $story.url') 28 | return sync.no_result 29 | }*/ 30 | 31 | fn hello(req valval.Request) valval.Response { 32 | return valval.response_ok('hello world') 33 | } 34 | 35 | // Fetches top HN stories in parallel, depending on how many cores you have 36 | fn main() { 37 | /*resp := http.post('https://hacker-news.firebaseio.com/v0/topstories.json') or { 38 | println('failed to fetch data from /v0/topstories.json') 39 | return 40 | } 41 | mut ids := json.decode([]int,resp.text) or { 42 | println('failed to decode topstories.json') 43 | return 44 | } 45 | if ids.len > 10 { 46 | ids = ids[0..10] 47 | } 48 | mut fetcher_pool := sync.new_pool_processor({ 49 | callback: worker_fetch 50 | }) 51 | // NB: if you do not call set_max_jobs, the pool will try to use an optimal 52 | // number of threads, one per each core in your system, which in most 53 | // cases is what you want anyway... You can override the automatic choice 54 | // by setting the VJOBS environment variable too. 55 | // fetcher_pool.set_max_jobs( 4 ) 56 | fetcher_pool.work_on_items_i(ids) 57 | */ 58 | 59 | mut app := valval.new_app(true) 60 | app.route('/', hello) 61 | valval.runserver(app, 8012) 62 | } 63 | -------------------------------------------------------------------------------- /examples/full/ballerina/src/server/main.bal: -------------------------------------------------------------------------------- 1 | import ballerina/http; 2 | import ballerina/io; 3 | import ballerina/system; 4 | //import ballerina/docker; 5 | 6 | //@docker:Config { 7 | // name: "ballerina-demo" 8 | //} 9 | service common on new http:Listener(5009) { 10 | 11 | resource function hello(http:Caller caller, 12 | http:Request req) returns error? { 13 | io:println("Hello action called"); 14 | 15 | check caller->respond({ 16 | response: "Hello from Ballerina!" 17 | }); 18 | } 19 | 20 | resource function welcome(http:Caller caller, 21 | http:Request req) returns @tainted error? { 22 | io:println("Welcome action called"); 23 | json payload = check req.getJsonPayload(); 24 | map body = >payload; 25 | json params = body["params"]; 26 | json|error name = params.name; 27 | string strName = <@untainted> name.toString(); 28 | check caller->respond({ 29 | response: string `Hello ${strName} from Ballerina!` 30 | }); 31 | } 32 | 33 | resource function sample_event(http:Caller caller, 34 | http:Request req) returns error? { 35 | io:println("Sample event happened"); 36 | 37 | check caller->respond(); 38 | } 39 | } 40 | 41 | public function main() returns @tainted error? { 42 | string SIDECAR_ADDRESS = system:getEnv("SIDECAR_ADDRESS") != "" ? system:getEnv("SIDECAR_ADDRESS") : "http://localhost:5103"; 43 | io:println(string `Registering service schema (${SIDECAR_ADDRESS})...`); 44 | 45 | http:Client clientEP = new (SIDECAR_ADDRESS); 46 | 47 | json content = { 48 | "name": "ballerina-demo", 49 | "settings": { 50 | "baseUrl": "http://ballerina-demo:5009" 51 | }, 52 | "actions": { 53 | "hello": "/common/hello", 54 | "welcome": { 55 | "params": { 56 | "name": "string|no-empty|trim" 57 | }, 58 | "handler": "/common/welcome" 59 | } 60 | }, 61 | "events": { 62 | "sample.event": "/common/sample_event" 63 | } 64 | }; 65 | 66 | http:Response resp = check clientEP->post("/v1/registry/services", content); 67 | 68 | json response = check resp.getJsonPayload(); 69 | io:println("Response: ", response); 70 | } 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /test/integration/services/ex-beta.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const { POST, parseBody, sendResponse } = require("../utils"); 3 | 4 | let server, port; 5 | let callStack = []; 6 | 7 | module.exports = { 8 | start, 9 | stop, 10 | server, 11 | port, 12 | schema: () => ({ 13 | name: "ex-beta", 14 | 15 | settings: { 16 | baseUrl: `http://localhost:${port}` 17 | }, 18 | 19 | actions: { 20 | echo: "/my-actions/echo", 21 | emitUpdate: "/emit.post.update", 22 | broadcastUpdate: "/broadcast.post.update" 23 | } 24 | }), 25 | 26 | callStack 27 | }; 28 | 29 | async function httpHandler(req, res) { 30 | const { headers, method, url } = req; 31 | const path = url.substring(url.indexOf("/")); 32 | 33 | let body = await parseBody(req); 34 | if (body) { 35 | body = JSON.parse(body); 36 | } 37 | 38 | callStack.push({ 39 | path, 40 | method, 41 | headers, 42 | body 43 | }); 44 | 45 | switch (path) { 46 | case "/my-actions/echo": { 47 | sendResponse(res, 200, { 48 | response: { 49 | ...body, 50 | meta: { 51 | ...body.meta, 52 | beta: "ok" 53 | } 54 | } 55 | }); 56 | break; 57 | } 58 | 59 | case "/emit.post.update": { 60 | POST( 61 | global.sidecarBaseURL + "/v1/emit/post.updated", 62 | { 63 | params: { 64 | id: 1, 65 | title: "First post" 66 | } 67 | }, 68 | true 69 | ); 70 | sendResponse(res, 200); 71 | break; 72 | } 73 | 74 | case "/broadcast.post.update": { 75 | POST( 76 | global.sidecarBaseURL + "/v1/broadcast/post.updated", 77 | { 78 | params: { 79 | id: 1, 80 | title: "First post" 81 | } 82 | }, 83 | true 84 | ); 85 | sendResponse(res, 200); 86 | break; 87 | } 88 | default: { 89 | sendResponse(res, 404, "Not found"); 90 | } 91 | } 92 | } 93 | 94 | function start() { 95 | server = http.createServer(httpHandler).listen(0); 96 | port = server.address().port; 97 | // console.log(`Ex-Aplha started on port ${port}`); 98 | } 99 | 100 | function stop() { 101 | server.close(); 102 | // console.log(`Ex-Aplha stopped.`); 103 | } 104 | -------------------------------------------------------------------------------- /examples/full/go-fiber/go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= 2 | github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 3 | github.com/gofiber/fiber/v2 v2.3.0 h1:82ufvLne0cxzdkDOeLkUmteA+z1uve9JQ/ZFsMOnkzc= 4 | github.com/gofiber/fiber/v2 v2.3.0/go.mod h1:f8BRRIMjMdRyt2qmJ/0Sea3j3rwwfufPrh9WNBRiVZ0= 5 | github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= 6 | github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 7 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 8 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 9 | github.com/valyala/fasthttp v1.18.0 h1:IV0DdMlatq9QO1Cr6wGJPVW1sV1Q8HvZXAIcjorylyM= 10 | github.com/valyala/fasthttp v1.18.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A= 11 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= 12 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 13 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 14 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 15 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 16 | golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 17 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 18 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 19 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 20 | golang.org/x/sys v0.0.0-20201210223839-7e3030f88018 h1:XKi8B/gRBuTZN1vU9gFsLMm6zVz5FSCDzm8JYACnjy8= 21 | golang.org/x/sys v0.0.0-20201210223839-7e3030f88018/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 23 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 24 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 25 | -------------------------------------------------------------------------------- /examples/full/go-fiber/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "os" 10 | 11 | "github.com/gofiber/fiber/v2" 12 | //"github.com/gofiber/fiber/v2/middleware/cors" 13 | ) 14 | 15 | var sidecarAddress string = os.Getenv("SIDECAR_ADDRESS") 16 | 17 | func sendResponse(c *fiber.Ctx, content string) error { 18 | return c.JSON(&fiber.Map{ 19 | "response": content, 20 | }) 21 | } 22 | 23 | type requestParam struct { 24 | Name string `json:"name"` 25 | } 26 | 27 | type requestBody struct { 28 | Params requestParam `json:"params"` 29 | } 30 | 31 | func registerServiceSchema() { 32 | // var localAddress string = "http://localhost:5002" 33 | 34 | fmt.Printf("Registering service schema (%s)...\n", sidecarAddress) 35 | postBody := `{ 36 | "name": "go-demo", 37 | "settings": { 38 | "baseUrl": "http://go-demo:5002" 39 | }, 40 | "actions": { 41 | "hello": "/actions/hello", 42 | "welcome": { 43 | "params": { 44 | "name": "string|no-empty|trim" 45 | }, 46 | "handler": "/actions/welcome" 47 | } 48 | }, 49 | "events": { 50 | "sample.event": "/events/sample.event" 51 | } 52 | }` 53 | 54 | resp, err := http.Post(sidecarAddress+"/v1/registry/services", "application/json", bytes.NewBufferString(postBody)) 55 | if err != nil { 56 | log.Fatalln(err) 57 | } 58 | 59 | defer resp.Body.Close() 60 | 61 | body, err := ioutil.ReadAll(resp.Body) 62 | if err != nil { 63 | log.Fatalln(err) 64 | } 65 | 66 | log.Println("Response: " + string(body)) 67 | 68 | } 69 | 70 | func main() { 71 | if sidecarAddress == "" { 72 | sidecarAddress = "http://localhost:5103" 73 | } 74 | 75 | app := fiber.New() 76 | 77 | //app.Use(cors.New()) 78 | 79 | app.Use(func(c *fiber.Ctx) error { 80 | c.Accepts("application/json") 81 | return c.Next() 82 | }) 83 | 84 | app.Post("/actions/hello", func(c *fiber.Ctx) error { 85 | return sendResponse(c, "Hello from Go!") 86 | }) 87 | 88 | app.Post("/actions/welcome", func(c *fiber.Ctx) error { 89 | body := new(requestBody) 90 | c.BodyParser(body) 91 | return sendResponse(c, "Hello "+body.Params.Name+" from Go!") 92 | }) 93 | 94 | app.Post("events/sample.event", func(c *fiber.Ctx) error { 95 | log.Println("Sample event happened.") 96 | return c.SendStatus(200) 97 | }) 98 | 99 | registerServiceSchema() 100 | 101 | log.Fatal(app.Listen("0.0.0.0:5002")) 102 | } 103 | -------------------------------------------------------------------------------- /examples/full/php-mark/server.php: -------------------------------------------------------------------------------- 1 | count = 4; // process count 12 | 13 | // Action handler response sender 14 | function sendResponse($content) { 15 | return new Response(200, ['Content-Type' => 'application/json'], json_encode([ 16 | 'response' => $content 17 | ])); 18 | } 19 | 20 | // Action handler 21 | $api->post( 22 | '/actions/hello', 23 | function ($request) { 24 | echo("Hello action called.\n"); 25 | return sendResponse('Hello from PHP!'); 26 | } 27 | ); 28 | 29 | // Action handler 30 | $api->post( 31 | '/actions/welcome', 32 | function ($request) { 33 | echo("Welcome action called. Body:\n" . $request->rawBody()); 34 | $body = json_decode($request->rawBody(), true); 35 | $params = $body['params']; 36 | $meta = $body['meta']; 37 | return sendResponse('Hello ' . $params['name'] . ' from PHP!'); 38 | } 39 | ); 40 | 41 | // Event handler 42 | $api->post( 43 | '/events/sample.event', 44 | function ($request) { 45 | echo("Sample event happened.\n"); 46 | return new Response(200, [], ""); 47 | } 48 | ); 49 | 50 | // Make a HTTP request 51 | function httpPost($url, $data) 52 | { 53 | $curl = curl_init($url); 54 | curl_setopt($curl, CURLOPT_POST, true); 55 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 56 | curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); 57 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 58 | $response = curl_exec($curl); 59 | curl_close($curl); 60 | if ($response == false) { 61 | echo "Sidecar is not available. Exiting...\n"; 62 | exit(1); 63 | } 64 | return $response; 65 | } 66 | 67 | function registerServiceSchema() { 68 | global $SIDECAR_ADDRESS; 69 | 70 | echo("Registering services to Sidecar (" . $SIDECAR_ADDRESS . ")...\n"); 71 | $regRes = httpPost($SIDECAR_ADDRESS . '/v1/registry/services', '{ 72 | "name": "php-demo", 73 | "settings": { 74 | "baseUrl": "http://php-demo:5001" 75 | }, 76 | "actions": { 77 | "hello": "/actions/hello", 78 | "welcome": { 79 | "params": { 80 | "name": "string|no-empty|trim" 81 | }, 82 | "handler": "/actions/welcome" 83 | } 84 | }, 85 | "events": { 86 | "sample.event": "/events/sample.event" 87 | } 88 | }'); 89 | echo("Response: " . $regRes . "\n"); 90 | } 91 | 92 | // Register schema 93 | registerServiceSchema(); 94 | 95 | // Start nano 96 | $api->start(); 97 | -------------------------------------------------------------------------------- /examples/full/rust-actix/src/main.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{post, web, App, HttpResponse, HttpServer, Responder}; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_json; 4 | use std::env; 5 | use url::Url; 6 | 7 | #[derive(Deserialize)] 8 | struct ActionParams { 9 | name: String, 10 | } 11 | 12 | #[derive(Deserialize)] 13 | struct ActionRequestBody { 14 | params: ActionParams 15 | } 16 | 17 | #[derive(Serialize, Deserialize)] 18 | struct ActionResponse { 19 | response: String, 20 | } 21 | 22 | #[post("/actions/hello")] 23 | async fn hello() -> impl Responder { 24 | println!("Hello action called."); 25 | HttpResponse::Ok() 26 | .json(ActionResponse { 27 | response: "Hello world from Rust!".to_string() 28 | }) 29 | } 30 | 31 | #[post("/actions/welcome")] 32 | async fn welcome(body: web::Json) -> impl Responder { 33 | println!("Welcome action called."); 34 | let msg = ["Hello ", &body.params.name, " from Rust!"].concat(); 35 | HttpResponse::Ok() 36 | .json(ActionResponse { 37 | response: msg 38 | }) 39 | } 40 | 41 | #[post("/events/sample.event")] 42 | async fn sample_event() -> impl Responder { 43 | println!("Sample event happened."); 44 | HttpResponse::Ok() 45 | } 46 | 47 | #[actix_web::main] 48 | async fn main() -> Result<(), std::io::Error> { 49 | 50 | let mut sidecar_address = "http://localhost:5103".to_string(); 51 | if env::var("SIDECAR_ADDRESS").is_ok() { 52 | sidecar_address = env::var("SIDECAR_ADDRESS").unwrap(); 53 | } 54 | 55 | println!("Registering service schema {}...\n", sidecar_address); 56 | 57 | let url = Url::parse(format!("{}/v1/registry/services", sidecar_address).as_str()).expect("Invalid Sidecar URL"); 58 | 59 | let echo_json: serde_json::Value = reqwest::Client::new() 60 | .post(url) 61 | .json(&serde_json::json!({ 62 | "name": "rust-demo", 63 | "settings": { 64 | "baseUrl": "http://rust-demo:5004" 65 | }, 66 | "actions": { 67 | "hello": "/actions/hello", 68 | "welcome": { 69 | "params": { 70 | "name": "string|no-empty|trim" 71 | }, 72 | "handler": "/actions/welcome" 73 | } 74 | }, 75 | "events": { 76 | "sample.event": "/events/sample.event" 77 | } 78 | })) 79 | .send() 80 | .await.expect("Some error happened") 81 | .json() 82 | .await.expect("Some error happened"); 83 | 84 | println!("Response: {:#?}", echo_json); 85 | 86 | println!("Starting server on '0.0.0.0:5004'..."); 87 | HttpServer::new(|| { 88 | App::new() 89 | .service(hello) 90 | .service(welcome) 91 | .service(sample_event) 92 | }) 93 | .bind("0.0.0.0:5004")? 94 | .workers(4) 95 | .run() 96 | .await 97 | } 98 | -------------------------------------------------------------------------------- /test/integration/services/ex-alpha.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const { POST, parseBody, sendResponse } = require("../utils"); 3 | 4 | let server, port; 5 | let callStack = []; 6 | 7 | module.exports = { 8 | start, 9 | stop, 10 | server, 11 | port, 12 | schema: () => ({ 13 | name: "ex-alpha", 14 | 15 | settings: { 16 | baseUrl: `http://localhost:${port}` 17 | }, 18 | 19 | actions: { 20 | list: "/list", 21 | danger: "/danger", 22 | silentHazard: "/silentHazard", 23 | proxy: "/proxy" 24 | }, 25 | 26 | events: { 27 | "user.**": "/userEvents" 28 | } 29 | }), 30 | 31 | async callGreeterWelcome() { 32 | const res = await callAction("greeter.welcome", { name: "Sidecar" }); 33 | return res.json; 34 | }, 35 | 36 | callStack 37 | }; 38 | 39 | async function httpHandler(req, res) { 40 | const { headers, method, url } = req; 41 | const path = url.substring(url.indexOf("/")); 42 | 43 | let body = await parseBody(req); 44 | if (body) { 45 | body = JSON.parse(body); 46 | } 47 | 48 | callStack.push({ 49 | path, 50 | method, 51 | //headers, 52 | body 53 | }); 54 | 55 | switch (path) { 56 | case "/list": { 57 | sendResponse(res, 200, { 58 | response: ["alpha", "beta", "gamma"], 59 | meta: { a: "5" } 60 | }); 61 | break; 62 | } 63 | case "/danger": { 64 | sendResponse(res, 400, { 65 | error: { 66 | name: "MoleculerClientError", 67 | code: 400, 68 | type: "INVALID_INPUT", 69 | message: "Some user input is not valid", 70 | data: { 71 | // Any useful data 72 | action: "posts.list", 73 | params: { 74 | limit: "asd" 75 | } 76 | } 77 | } 78 | }); 79 | 80 | break; 81 | } 82 | 83 | case "/silentHazard": { 84 | sendResponse(res, 501); 85 | break; 86 | } 87 | 88 | case "/proxy": { 89 | const response = await callAction("ex-beta.echo", body.params, { 90 | ...body.meta, 91 | alpha: "ok" 92 | }); 93 | sendResponse(res, response.status, response.json); 94 | break; 95 | } 96 | 97 | case "/userEvents": { 98 | sendResponse(res, 200); 99 | //console.log("User event received.", body); 100 | break; 101 | } 102 | 103 | default: { 104 | sendResponse(res, 404, "Not found"); 105 | } 106 | } 107 | } 108 | 109 | function callAction(name, params, meta) { 110 | return POST(global.sidecarBaseURL + "/v1/call/" + name, { 111 | params, 112 | meta 113 | }); 114 | } 115 | 116 | function start() { 117 | server = http.createServer(httpHandler).listen(0); 118 | port = server.address().port; 119 | // console.log(`Ex-Aplha started on port ${port}`); 120 | } 121 | 122 | function stop() { 123 | server.close(); 124 | // console.log(`Ex-Aplha stopped.`); 125 | } 126 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moleculer-sidecar", 3 | "version": "0.0.0", 4 | "description": "Sidecar module for Moleculer microservices framework with HTTP support.", 5 | "main": "src/index.js", 6 | "bin": "index.js", 7 | "scripts": { 8 | "dev": "node examples/index.js full --repl", 9 | "ci": "jest --watch", 10 | "test": "jest --runInBand --coverage", 11 | "deps": "npm-check -u", 12 | "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 13 | "build": "pkg . -t node14-win,node14-macos,node14-linux,node14-alpine --out-path dist/" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/moleculerjs/sidecar.git" 18 | }, 19 | "keywords": [ 20 | "moleculer", 21 | "microservices", 22 | "sidecar" 23 | ], 24 | "author": "MoleculerJS", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/moleculerjs/sidecar/issues" 28 | }, 29 | "homepage": "https://github.com/moleculerjs/sidecar#readme", 30 | "devDependencies": { 31 | "benchmarkify": "^2.1.3", 32 | "coveralls": "^3.1.0", 33 | "eslint": "^7.13.0", 34 | "eslint-config-prettier": "^7.1.0", 35 | "eslint-plugin-node": "^11.1.0", 36 | "eslint-plugin-prettier": "^3.1.4", 37 | "eslint-plugin-promise": "^4.2.1", 38 | "eslint-plugin-security": "^1.4.0", 39 | "jest": "^26.6.3", 40 | "jest-cli": "^26.6.3", 41 | "nodemon": "^2.0.6", 42 | "npm-check": "^5.9.2", 43 | "pkg": "^4.4.9", 44 | "prettier": "^2.2.1" 45 | }, 46 | "jest": { 47 | "testEnvironment": "node", 48 | "rootDir": "./src", 49 | "roots": [ 50 | "../test" 51 | ], 52 | "coverageDirectory": "../coverage", 53 | "coveragePathIgnorePatterns": [ 54 | "/node_modules/" 55 | ] 56 | }, 57 | "engines": { 58 | "node": ">= 10.x.x" 59 | }, 60 | "dependencies": { 61 | "amqplib": "^0.6.0", 62 | "avsc": "^5.5.3", 63 | "dotenv": "^8.2.0", 64 | "etcd3": "^1.1.0", 65 | "fastest-validator": "^1.9.0", 66 | "ioredis": "^4.19.4", 67 | "jaeger-client": "^3.18.1", 68 | "kafka-node": "^5.0.0", 69 | "kleur": "^4.1.3", 70 | "lodash": "^4.17.20", 71 | "moleculer": "^0.14.0", 72 | "moleculer-repl": "^0.6.4", 73 | "moleculer-web": "^0.10.0-beta1", 74 | "mqtt": "^4.2.6", 75 | "msgpack5": "^4.4.0", 76 | "nats": "^1.4.12", 77 | "node-fetch": "^2.6.1", 78 | "node-nats-streaming": "^0.3.2", 79 | "notepack.io": "^2.3.0", 80 | "protobufjs": "^6.10.2", 81 | "redlock": "^4.2.0", 82 | "rhea-promise": "^1.0.0", 83 | "thrift": "^0.12.0" 84 | }, 85 | "pkg": { 86 | "assets": [ 87 | "node_modules/etcd3/proto/**/*.*" 88 | ], 89 | "scripts": [ 90 | "src/**/*.js", 91 | "node_modules/moleculer-repl/src/commands/*.js", 92 | "node_modules/moleculer-repl/src/utils.js", 93 | "!node_modules/moleculer-repl/src/commands/index.js" 94 | ] 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/full/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | sidecar: 5 | image: sidecar-demo-main 6 | build: 7 | context: ../../ 8 | dockerfile: Dockerfile-alpine 9 | environment: 10 | SIDECAR_PORT: 5103 11 | TRANSPORTER: nats://nats:4222 12 | depends_on: 13 | - nats 14 | networks: 15 | - internal 16 | - external 17 | restart: always 18 | 19 | nats: 20 | image: nats 21 | ports: 22 | - "4222:4222" 23 | networks: 24 | - external 25 | restart: always 26 | 27 | ballerina-demo: 28 | image: sidecar-demo-ballerina 29 | build: 30 | context: ./ballerina 31 | environment: 32 | SIDECAR_ADDRESS: http://sidecar:5103 33 | depends_on: 34 | - sidecar 35 | networks: 36 | - internal 37 | restart: always 38 | 39 | deno-demo: 40 | image: sidecar-demo-deno 41 | build: 42 | context: ./deno-oak 43 | environment: 44 | SIDECAR_ADDRESS: http://sidecar:5103 45 | depends_on: 46 | - sidecar 47 | networks: 48 | - internal 49 | restart: always 50 | 51 | go-demo: 52 | image: sidecar-demo-go 53 | build: 54 | context: ./go-fiber 55 | environment: 56 | SIDECAR_ADDRESS: http://sidecar:5103 57 | depends_on: 58 | - sidecar 59 | networks: 60 | - internal 61 | restart: always 62 | 63 | php-demo: 64 | image: sidecar-demo-php 65 | build: 66 | context: ./php-mark 67 | environment: 68 | SIDECAR_ADDRESS: http://sidecar:5103 69 | depends_on: 70 | - sidecar 71 | networks: 72 | - internal 73 | restart: always 74 | 75 | python-demo: 76 | image: sidecar-demo-python 77 | build: 78 | context: ./python-flask 79 | environment: 80 | SIDECAR_ADDRESS: http://sidecar:5103 81 | depends_on: 82 | - sidecar 83 | networks: 84 | - internal 85 | restart: always 86 | 87 | ruby-demo: 88 | image: sidecar-demo-ruby 89 | build: 90 | context: ./ruby-sinatra 91 | environment: 92 | SIDECAR_ADDRESS: http://sidecar:5103 93 | depends_on: 94 | - sidecar 95 | networks: 96 | - internal 97 | restart: always 98 | 99 | rust-demo: 100 | image: sidecar-demo-rust 101 | build: 102 | context: ./rust-actix 103 | environment: 104 | SIDECAR_ADDRESS: http://sidecar:5103 105 | depends_on: 106 | - sidecar 107 | networks: 108 | - internal 109 | restart: always 110 | 111 | swift-demo: 112 | image: sidecar-demo-swift 113 | build: 114 | context: ./swift-perfect 115 | environment: 116 | SIDECAR_ADDRESS: http://sidecar:5103 117 | depends_on: 118 | - sidecar 119 | networks: 120 | - internal 121 | restart: always 122 | 123 | crystal-demo: 124 | image: sidecar-demo-crystal 125 | build: 126 | context: ./crystal-kemal 127 | environment: 128 | SIDECAR_ADDRESS: http://sidecar:5103 129 | depends_on: 130 | - sidecar 131 | networks: 132 | - internal 133 | restart: always 134 | 135 | networks: 136 | internal: {} 137 | external: {} 138 | -------------------------------------------------------------------------------- /examples/full/README.md: -------------------------------------------------------------------------------- 1 | # Moleculer Sidecar Example (full) 2 | 3 | This example demonstrates the Moleculer Sidecar polyglot microservices functionality. It starts a Sidecar container and external service containers: 4 | 5 | - **Ballerina**: v1.2.12 6 | - **Crystal**: v1.0.0 7 | - **Deno**: Oak v6.4.1 8 | - **Go**: Fiber v2.3.0 9 | - **PHP**: Mark v1.1 10 | - **Python**: Flask v1.1.2 11 | - **Ruby**: Sinatra v2.1.0 12 | - **Rust**: Actix v3 13 | - **Swift**: Perfect v3.0.0 14 | 15 | >I'm not familiar with these languages & frameworks, I've just copy-pasted the examples from the Internet :) If you have better knowledge and you can improve them, please send a PR. 16 | 17 | ## Start 18 | 19 | ```bash 20 | docker-compose up -d --build 21 | ``` 22 | 23 | ### Registered actions 24 | ``` 25 | mol $ actions -i 26 | ╔════════════════════════╤═══════╤══════════╤════════╤════════╗ 27 | ║ Action │ Nodes │ State │ Cached │ Params ║ 28 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 29 | ║ ballerina-demo.hello │ 1 │ OK │ No │ ║ 30 | ║ ballerina-demo.welcome │ 1 │ OK │ No │ name ║ 31 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 32 | ║ crystal-demo.hello │ 1 │ OK │ No │ ║ 33 | ║ crystal-demo.welcome │ 1 │ OK │ No │ name ║ 34 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 35 | ║ deno-demo.hello │ 1 │ OK │ No │ ║ 36 | ║ deno-demo.welcome │ 1 │ OK │ No │ name ║ 37 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 38 | ║ go-demo.hello │ 1 │ OK │ No │ ║ 39 | ║ go-demo.welcome │ 1 │ OK │ No │ name ║ 40 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 41 | ║ php-demo.hello │ 1 │ OK │ No │ ║ 42 | ║ php-demo.welcome │ 1 │ OK │ No │ name ║ 43 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 44 | ║ python-demo.hello │ 1 │ OK │ No │ ║ 45 | ║ python-demo.welcome │ 1 │ OK │ No │ name ║ 46 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 47 | ║ ruby-demo.hello │ 1 │ OK │ No │ ║ 48 | ║ ruby-demo.welcome │ 1 │ OK │ No │ name ║ 49 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 50 | ║ rust-demo.hello │ 1 │ OK │ No │ ║ 51 | ║ rust-demo.welcome │ 1 │ OK │ No │ name ║ 52 | ╟────────────────────────┼───────┼──────────┼────────┼────────╢ 53 | ║ swift-demo.hello │ 1 │ OK │ No │ ║ 54 | ║ swift-demo.welcome │ 1 │ OK │ No │ name ║ 55 | ╚════════════════════════╧═══════╧══════════╧════════╧════════╝ 56 | ``` 57 | 58 | ### Test 59 | **Start a Moleculer CLI instance** 60 | ```bash 61 | moleculer connect nats://localhost:4222 62 | ``` 63 | 64 | **Call `python-demo` service** 65 | ```bash 66 | call python-demo.hello 67 | call python-demo.welcome --name Moleculer 68 | ``` 69 | 70 | **Call `go-demo` service** 71 | ```bash 72 | call go-demo.hello 73 | call go-demo.welcome --name Moleculer 74 | ``` 75 | 76 | **Call `php-demo` service** 77 | ```bash 78 | call php-demo.hello 79 | call php-demo.welcome --name Moleculer 80 | ``` 81 | 82 | **Emitting an events** (all services are listening it) 83 | ```bash 84 | broadcast sample.event 85 | ``` 86 | 87 | ## Stop 88 | ```bash 89 | docker-compose down -v 90 | ``` 91 | -------------------------------------------------------------------------------- /examples/full/swift-perfect/Sources/server/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import FoundationNetworking 3 | //import os 4 | import PerfectHTTP 5 | import PerfectHTTPServer 6 | 7 | func helloHandler(request: HTTPRequest, response: HTTPResponse) { 8 | print("Hello action called."); 9 | response.setHeader(.contentType, value: "application/json") 10 | 11 | do { 12 | let data: [String:Any] = [ "response": "Hello from Swift!" ] 13 | try response.setBody(json: data) 14 | } catch { 15 | } 16 | response.completed() 17 | } 18 | 19 | func welcomeHandler(request: HTTPRequest, response: HTTPResponse) { 20 | print("Welcome action called."); 21 | do { 22 | let decoded = try request.postBodyString!.jsonDecode() as! [String:Any] 23 | let params = decoded["params"] as! [String:Any] 24 | let name = params["name"] as! String 25 | 26 | response.setHeader(.contentType, value: "application/json") 27 | let data: [String:Any] = [ "response": "Hello " + name + " from Swift!" ] 28 | try response.setBody(json: data) 29 | 30 | } catch {} 31 | response.completed() 32 | } 33 | 34 | func eventHandler(request: HTTPRequest, response: HTTPResponse) { 35 | print("Sample event happened."); 36 | response.setHeader(.contentType, value: "application/json") 37 | response.appendBody(string: "{}") 38 | response.completed() 39 | } 40 | 41 | func registerServiceSchema() { 42 | var SIDECAR_ADDRESS = "http://localhost:5103"; 43 | if let v = ProcessInfo.processInfo.environment["SIDECAR_ADDRESS"] { 44 | SIDECAR_ADDRESS = v 45 | } 46 | print("Registering service schema (" + SIDECAR_ADDRESS + ")..."); 47 | 48 | let url = URL(string: SIDECAR_ADDRESS + "/v1/registry/services")! 49 | var request = URLRequest(url: url) 50 | request.httpMethod = "POST" //set http method as POST 51 | request.addValue("application/json", forHTTPHeaderField: "Content-Type") 52 | request.httpBody = """ 53 | { 54 | "name": "swift-demo", 55 | "settings": { 56 | "baseUrl": "http://swift-demo:5008" 57 | }, 58 | "actions": { 59 | "hello": "/actions/hello", 60 | "welcome": { 61 | "params": { 62 | "name": "string|no-empty|trim" 63 | }, 64 | "handler": "/actions/welcome" 65 | } 66 | }, 67 | "events": { 68 | "sample.event": "/events/sample.event" 69 | } 70 | } 71 | """.data(using: .utf8); 72 | 73 | let task = URLSession.shared.dataTask(with: request) { data, response, error in 74 | guard let data = data, 75 | let response = response as? HTTPURLResponse, 76 | error == nil else { // check for fundamental networking error 77 | print("error", error ?? "Unknown error") 78 | fatalError("Terminating...") 79 | } 80 | 81 | guard (200 ... 299) ~= response.statusCode else { // check for http errors 82 | print("statusCode should be 2xx, but is \(response.statusCode)") 83 | print("response = \(response)") 84 | fatalError("Terminating...") 85 | } 86 | 87 | let responseString = String(data: data, encoding: .utf8) 88 | print("responseString = " + responseString!) 89 | } 90 | 91 | task.resume() 92 | 93 | } 94 | 95 | registerServiceSchema() 96 | 97 | var routes = Routes() 98 | routes.add(method: .post, uri: "/actions/hello", handler: helloHandler) 99 | routes.add(method: .post, uri: "/actions/welcome", handler: welcomeHandler) 100 | routes.add(method: .post, uri: "/events/sample.event", handler: eventHandler) 101 | 102 | try HTTPServer.launch(name: "localhost", port: 5008, routes: routes) 103 | 104 | 105 | -------------------------------------------------------------------------------- /examples/full/python-flask/server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import request 3 | import json 4 | import os 5 | import requests 6 | 7 | app = Flask(__name__) 8 | 9 | SIDECAR_ADDRESS = os.getenv('SIDECAR_ADDRESS') 10 | if SIDECAR_ADDRESS is None: 11 | SIDECAR_ADDRESS = 'http://localhost:5103' 12 | 13 | ''' 14 | Generate JSON responsefor actions 15 | ''' 16 | def generate_json_response(content): 17 | return json.dumps({'response': content }) 18 | 19 | ''' 20 | Simple action handler 21 | ''' 22 | @app.route("/actions/hello", methods=["POST"]) 23 | def hello(): 24 | app.logger.info("Hello action called.") 25 | return generate_json_response("Hello from Python!"), 200, {'Content-Type':'application/json'} 26 | 27 | ''' 28 | Action handler with params 29 | ''' 30 | @app.route("/actions/welcome", methods=["POST"]) 31 | def welcome(): 32 | app.logger.info("Welcome action called.") 33 | body = request.get_json() 34 | params = body.get('params') 35 | # meta = body.get('meta') 36 | return generate_json_response('Hello {} from Python!'.format(params['name'])), 200, {'Content-Type':'application/json'} 37 | 38 | ''' 39 | Event handler 40 | ''' 41 | @app.route("/events/sample.event", methods=["POST"]) 42 | def sampleEvent(): 43 | # body = request.get_json() 44 | # params = body['params'] 45 | # meta = body['meta'] 46 | app.logger.info("Sample event happened.") 47 | 48 | return "OK" 49 | 50 | ''' 51 | Send a POST request to the Sidecar 52 | ''' 53 | def POST(url, content): 54 | return requests.post(url = SIDECAR_ADDRESS + url, json = content) 55 | 56 | ''' 57 | Register the service to the Sidecar 58 | ''' 59 | def register_service_schema(): 60 | schema = { 61 | 'name': "python-demo", 62 | 'settings': { 63 | 'baseUrl': 'http://python-demo:5000' 64 | }, 65 | 'actions': { 66 | 'hello': '/actions/hello', 67 | 'welcome': { 68 | 'params': { 69 | 'name': 'string|no-empty|trim' 70 | }, 71 | 'handler': '/actions/welcome' 72 | } 73 | }, 74 | 'events': { 75 | 'sample.event': '/events/sample.event' 76 | } 77 | } 78 | 79 | # Register schema 80 | rsp = POST('/v1/registry/services', schema) 81 | 82 | print("Response: " + rsp.text) 83 | 84 | ''' 85 | Call an action 86 | Example: 87 | callAction("posts.list", params = { 'limit: 5, 'offset: 0 }, meta = { 'from': 'python' }) 88 | ''' 89 | def callAction(action, **kwargs): 90 | content = { 91 | 'params': kwargs.get('params'), 92 | 'meta': kwargs.get('meta'), 93 | 'options': kwargs.get('options') 94 | } 95 | 96 | print("Calling '{}' action...".format(action)) 97 | rsp = POST('/v1/call/' + action, content) 98 | 99 | return rsp 100 | 101 | ''' 102 | Emit an event 103 | ''' 104 | def emitEvent(event, **kwargs): 105 | content = { 106 | 'params': kwargs.get('params'), 107 | 'meta': kwargs.get('meta'), 108 | 'options': kwargs.get('options') 109 | } 110 | 111 | print("Emitting '{}' event...".format(event)) 112 | POST('/v1/emit/' + event, content) 113 | 114 | ''' 115 | Get services list from Sidecar 116 | ''' 117 | def getServiceList(): 118 | rsp = callAction("$node.services") 119 | json = rsp.json() 120 | print("Services:") 121 | 122 | for item in json.get("response"): 123 | print(" {}".format(item.get("fullName"))) 124 | print("") 125 | 126 | ''' 127 | Start registration 128 | ''' 129 | def start(): 130 | print("Registering service to the Sidecar ({})...".format(SIDECAR_ADDRESS)) 131 | register_service_schema() 132 | 133 | getServiceList() 134 | 135 | emitEvent("python-service.started") 136 | 137 | 138 | 139 | start() 140 | 141 | if __name__ == "__main__": 142 | app.run(host='0.0.0.0', port= 5000, debug=True) 143 | -------------------------------------------------------------------------------- /examples/full/php-mark/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "edf5a55fa4704f2361d0eb2b9be0aa05", 8 | "packages": [ 9 | { 10 | "name": "mark-php/mark", 11 | "version": "v1.1.10", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/passwalls/mark.git", 15 | "reference": "1f0ed9327809976e8e012d51d5fa72b309910681" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/passwalls/mark/zipball/1f0ed9327809976e8e012d51d5fa72b309910681", 20 | "reference": "1f0ed9327809976e8e012d51d5fa72b309910681", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "nikic/fast-route": "^1.3", 25 | "php": ">=7.1", 26 | "workerman/workerman": "^4.0.4" 27 | }, 28 | "suggest": { 29 | "ext-event": "For better performance. " 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "psr-4": { 34 | "Mark\\": "./src" 35 | } 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "description": "a PHP micro framework based on workerman", 42 | "support": { 43 | "issues": "https://github.com/passwalls/mark/issues", 44 | "source": "https://github.com/passwalls/mark/tree/v1.1.10" 45 | }, 46 | "time": "2020-12-14T02:42:11+00:00" 47 | }, 48 | { 49 | "name": "nikic/fast-route", 50 | "version": "v1.3.0", 51 | "source": { 52 | "type": "git", 53 | "url": "https://github.com/nikic/FastRoute.git", 54 | "reference": "181d480e08d9476e61381e04a71b34dc0432e812" 55 | }, 56 | "dist": { 57 | "type": "zip", 58 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", 59 | "reference": "181d480e08d9476e61381e04a71b34dc0432e812", 60 | "shasum": "" 61 | }, 62 | "require": { 63 | "php": ">=5.4.0" 64 | }, 65 | "require-dev": { 66 | "phpunit/phpunit": "^4.8.35|~5.7" 67 | }, 68 | "type": "library", 69 | "autoload": { 70 | "psr-4": { 71 | "FastRoute\\": "src/" 72 | }, 73 | "files": [ 74 | "src/functions.php" 75 | ] 76 | }, 77 | "notification-url": "https://packagist.org/downloads/", 78 | "license": [ 79 | "BSD-3-Clause" 80 | ], 81 | "authors": [ 82 | { 83 | "name": "Nikita Popov", 84 | "email": "nikic@php.net" 85 | } 86 | ], 87 | "description": "Fast request router for PHP", 88 | "keywords": [ 89 | "router", 90 | "routing" 91 | ], 92 | "support": { 93 | "issues": "https://github.com/nikic/FastRoute/issues", 94 | "source": "https://github.com/nikic/FastRoute/tree/master" 95 | }, 96 | "time": "2018-02-13T20:26:39+00:00" 97 | }, 98 | { 99 | "name": "workerman/workerman", 100 | "version": "v4.0.15", 101 | "source": { 102 | "type": "git", 103 | "url": "https://github.com/walkor/Workerman.git", 104 | "reference": "580c2476d77a707879332125fa79fedb0ec74e9d" 105 | }, 106 | "dist": { 107 | "type": "zip", 108 | "url": "https://api.github.com/repos/walkor/Workerman/zipball/580c2476d77a707879332125fa79fedb0ec74e9d", 109 | "reference": "580c2476d77a707879332125fa79fedb0ec74e9d", 110 | "shasum": "" 111 | }, 112 | "require": { 113 | "php": ">=5.3" 114 | }, 115 | "suggest": { 116 | "ext-event": "For better performance. " 117 | }, 118 | "type": "library", 119 | "autoload": { 120 | "psr-4": { 121 | "Workerman\\": "./" 122 | } 123 | }, 124 | "notification-url": "https://packagist.org/downloads/", 125 | "license": [ 126 | "MIT" 127 | ], 128 | "authors": [ 129 | { 130 | "name": "walkor", 131 | "email": "walkor@workerman.net", 132 | "homepage": "http://www.workerman.net", 133 | "role": "Developer" 134 | } 135 | ], 136 | "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.", 137 | "homepage": "http://www.workerman.net", 138 | "keywords": [ 139 | "asynchronous", 140 | "event-loop" 141 | ], 142 | "support": { 143 | "email": "walkor@workerman.net", 144 | "forum": "http://wenda.workerman.net/", 145 | "issues": "https://github.com/walkor/workerman/issues", 146 | "source": "https://github.com/walkor/workerman", 147 | "wiki": "http://doc.workerman.net/" 148 | }, 149 | "time": "2020-10-28T03:37:31+00:00" 150 | } 151 | ], 152 | "packages-dev": [], 153 | "aliases": [], 154 | "minimum-stability": "stable", 155 | "stability-flags": [], 156 | "prefer-stable": false, 157 | "prefer-lowest": false, 158 | "platform": [], 159 | "platform-dev": [], 160 | "plugin-api-version": "2.0.0" 161 | } 162 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build artifacts 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v** 9 | 10 | jobs: 11 | build: 12 | name: Build binaries 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Use Node.js 14.x 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: 14.x 21 | 22 | - name: "Install NPM packages" 23 | run: npm ci 24 | 25 | - name: "Build bundle with pkg" 26 | run: npm run build 27 | 28 | - name: List bundles 29 | run: ls -al dist 30 | 31 | - name: Upload artifacts 32 | uses: actions/upload-artifact@v2 33 | with: 34 | name: bundles 35 | path: ./dist 36 | 37 | release: 38 | name: Create Github Release 39 | needs: [build] 40 | # if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v2 44 | 45 | - name: Fetch artifacts 46 | uses: actions/download-artifact@v2 47 | with: 48 | name: bundles 49 | path: ./dist 50 | # - name: Get short tag name 51 | # uses: jungwinter/split@v1 52 | # id: split 53 | # with: 54 | # msg: ${{ github.ref }} 55 | # seperator: / 56 | 57 | #- name: Create Release 58 | # id: create_release 59 | # uses: ncipollo/release-action@v1 60 | # with: 61 | # token: ${{ secrets.GITHUB_TOKEN }} 62 | # allowUpdates: true 63 | # tag: unreleased 64 | # # tag: ${{ steps.split.outputs._2 }} 65 | # name: Unreleased 66 | # commit: ${{ github.sha }} 67 | # omitBody: true 68 | # draft: false 69 | # prerelease: true 70 | # artifacts: "dist/*, dist/*.exe" 71 | 72 | - name: Upload Release Assets 73 | uses: actions/github-script@v2 74 | with: 75 | github-token: ${{secrets.GITHUB_TOKEN}} 76 | script: | 77 | // Example codes: https://github.com/ncipollo/release-action/blob/main/src/Releases.ts 78 | //console.log('environment', process.versions, process.env); 79 | 80 | const fs = require('fs').promises; 81 | 82 | //console.log("context:", context); 83 | const { repo: { owner, repo }, sha } = context; 84 | const release_id = 35727994; // "Unreleased" 85 | 86 | /* 87 | //console.log({ owner, repo, sha }); 88 | const release = await github.repos.createRelease({ 89 | owner, repo, 90 | tag_name: process.env.GITHUB_REF, 91 | draft: true, 92 | target_commitish: sha 93 | }); 94 | 95 | // if (process.env.GITHUB_REF == "refs/heads/master") Use "Unreleased", else create new release 96 | 97 | //console.log('created release', { release }); 98 | */ 99 | 100 | // https://api.github.com/repos/moleculerjs/sidecar/releases/35727994/assets 101 | const listExistingArtifacts = (await github.repos.listReleaseAssets({ 102 | owner, 103 | repo, 104 | release_id, 105 | })).data; 106 | 107 | for (let file of await fs.readdir('./dist')) { 108 | if ([".", ".."].includes(file)) continue; 109 | 110 | const found = listExistingArtifacts.find(item => item.name == file); 111 | if (found) { 112 | console.log('Deleting previous', file, "..."); 113 | await github.repos.deleteReleaseAsset({ 114 | asset_id: found.id, 115 | owner, 116 | repo 117 | }); 118 | } 119 | 120 | console.log(`Uploading '${file}'...`); 121 | let retry = 3; 122 | while (retry++ > 0) { 123 | try { 124 | await github.repos.uploadReleaseAsset({ 125 | owner, repo, 126 | release_id, 127 | name: file, 128 | data: await fs.readFile(`./dist/${file}`) 129 | }); 130 | console.log(`Uploaded '${file}'.`); 131 | break; 132 | }catch(err) { 133 | if (error.status >= 500 && retry > 0) { 134 | console.log(`Failed to upload artifact '${file}'. Retrying...`); 135 | } 136 | } 137 | } 138 | } 139 | 140 | console.log("Done."); 141 | 142 | docker: 143 | name: Build & Publish Docker image 144 | needs: [build] 145 | runs-on: ubuntu-latest 146 | steps: 147 | - uses: actions/checkout@v2 148 | 149 | - name: Fetch artifacts 150 | uses: actions/download-artifact@v2 151 | with: 152 | name: bundles 153 | path: ./dist 154 | 155 | - name: chmod 156 | run: chmod +x dist/* 157 | 158 | - name: Set up QEMU 159 | uses: docker/setup-qemu-action@v1 160 | 161 | - name: Set up Docker Buildx 162 | uses: docker/setup-buildx-action@v1 163 | 164 | - name: Login to DockerHub 165 | uses: docker/login-action@v1 166 | with: 167 | username: ${{ secrets.DOCKERHUB_USERNAME }} 168 | password: ${{ secrets.DOCKERHUB_TOKEN }} 169 | 170 | - name: Build and push 171 | id: docker_build 172 | uses: docker/build-push-action@v2 173 | with: 174 | context: . 175 | file: ./Dockerfile-alpine 176 | push: true 177 | tags: moleculer/sidecar:latest 178 | 179 | - name: Image digest 180 | run: "echo 'Pushed image digest: ${{ steps.docker_build.outputs.digest }}'" 181 | 182 | # publish: 183 | # needs: [release] 184 | # runs-on: ubuntu-latest 185 | # strategy: 186 | # matrix: 187 | # version: [linux, macos, windows] 188 | 189 | # steps: 190 | # - uses: actions/checkout@v1 191 | 192 | # - name: Fetch artifacts 193 | # uses: actions/download-artifact@v1 194 | # with: 195 | # name: ${{ matrix.version }} 196 | # path: ./${{ matrix.version }} 197 | 198 | # - name: Build Zip Archives 199 | # run: | 200 | # mkdir -p workdir/ 201 | # mv ${{ matrix.version }}/ workdir/v/ 202 | # cd workdir/v/ 203 | # chmod 755 v || true 204 | # chmod 755 v.exe || true 205 | # cd .. 206 | # zip -r9 --symlinks ../v_${{ matrix.version }}.zip v/* 207 | # cd .. 208 | # rm -rf workdir/ 209 | 210 | # - name: Get short tag name 211 | # uses: jungwinter/split@v1 212 | # id: split 213 | # with: 214 | # msg: ${{ github.ref }} 215 | # seperator: / 216 | 217 | # - name: Get release 218 | # id: get_release_info 219 | # uses: leahlundqvist/get-release@v1.3.1 220 | # env: 221 | # GITHUB_TOKEN: ${{ github.token }} 222 | # with: 223 | # tag_name: ${{ steps.split.outputs._2 }} 224 | 225 | # - name: Upload Release Asset 226 | # id: upload-release-asset 227 | # uses: actions/upload-release-asset@v1.0.1 228 | # env: 229 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 230 | # with: 231 | # upload_url: ${{ steps.get_release_info.outputs.upload_url }} 232 | # asset_path: ./v_${{ matrix.version }}.zip 233 | # asset_name: v_${{ matrix.version }}.zip 234 | # asset_content_type: application/zip 235 | -------------------------------------------------------------------------------- /test/integration/index.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { POST, DELETE } = require("./utils"); 4 | 5 | process.env.SIDECAR_PORT = 0; 6 | 7 | process.argv = [ 8 | process.argv[0], 9 | __filename, 10 | "--config", 11 | "test/integration/moleculer.config.sidecar.js" 12 | ]; 13 | 14 | const { ServiceBroker, Context } = require("moleculer"); 15 | const GreeterService = require("./services/greeter.service"); 16 | 17 | const SidecarRunner = require("../../index"); 18 | 19 | describe("Integration tests", () => { 20 | const broker = new ServiceBroker({ logger: false, transporter: "Fake" }); 21 | broker.createService(GreeterService); 22 | 23 | const ExAlpha = require("./services/ex-alpha"); 24 | const ExBeta = require("./services/ex-beta"); 25 | let sidecarBroker, sidecarBaseURL; 26 | beforeAll(async () => { 27 | await ExAlpha.start(); 28 | await ExBeta.start(); 29 | await broker.start(); 30 | sidecarBroker = await SidecarRunner; 31 | await broker.Promise.delay(500); // TODO 32 | }); 33 | afterAll(async () => { 34 | await broker.stop(); 35 | await sidecarBroker.stop(); 36 | await ExAlpha.stop(); 37 | await ExBeta.stop(); 38 | }); 39 | 40 | describe("Test service register/unregister", () => { 41 | it("test environment", async () => { 42 | const res = await sidecarBroker.call("greeter.hello"); 43 | expect(res).toBe("Hello Moleculer"); 44 | 45 | const sidecarService = sidecarBroker.getLocalService("$sidecar"); 46 | expect(sidecarService).toBeDefined(); 47 | sidecarBaseURL = `http://localhost:${sidecarService.listenAddress.port}`; 48 | global.sidecarBaseURL = sidecarBaseURL; 49 | 50 | expect(sidecarBroker.getLocalService("ex-alpha")).toBeUndefined(); 51 | expect(sidecarBroker.getLocalService("ex-beta")).toBeUndefined(); 52 | }); 53 | 54 | it("should not register empty schema", async () => { 55 | const res = await POST(`${sidecarBaseURL}/v1/registry/services`); 56 | expect(res.status).toBe(422); 57 | expect(res.json).toEqual({ 58 | name: "ValidationError", 59 | message: "Parameters validation error!", 60 | type: "VALIDATION_ERROR", 61 | code: 422, 62 | data: [ 63 | { 64 | action: "$sidecar.registerService", 65 | field: "name", 66 | message: "The 'name' field is required.", 67 | nodeID: "sidecar", 68 | type: "required" 69 | } 70 | ] 71 | }); 72 | }); 73 | 74 | it("should register schema with name", async () => { 75 | const res = await POST(`${sidecarBaseURL}/v1/registry/services`, { 76 | name: "ex-test" 77 | }); 78 | 79 | expect(res.status).toBe(200); 80 | expect(res.json).toEqual({ status: "OK" }); 81 | 82 | await broker.Promise.delay(50); 83 | 84 | expect(sidecarBroker.getLocalService("ex-test")).toBeDefined(); 85 | }); 86 | 87 | it("should unregister schema", async () => { 88 | const res = await DELETE(`${sidecarBaseURL}/v1/registry/services/ex-test`); 89 | 90 | expect(res.status).toBe(200); 91 | expect(res.json).toEqual({ status: "OK" }); 92 | 93 | await broker.Promise.delay(50); 94 | 95 | expect(sidecarBroker.getLocalService("ex-test")).toBeUndefined(); 96 | }); 97 | }); 98 | 99 | describe("Test external service actions", () => { 100 | it("should register ex-alpha schema", async () => { 101 | const res = await POST(`${sidecarBaseURL}/v1/registry/services`, ExAlpha.schema()); 102 | 103 | expect(res.status).toBe(200); 104 | expect(res.json).toEqual({ status: "OK" }); 105 | 106 | await broker.Promise.delay(500); 107 | }); 108 | 109 | it("should call ex-alpha service 'list' action", async () => { 110 | expect(await broker.call("ex-alpha.list")).toEqual(["alpha", "beta", "gamma"]); 111 | }); 112 | 113 | it("should call ex-alpha service 'danger' action", async () => { 114 | expect.assertions(7); 115 | try { 116 | await broker.call("ex-alpha.danger"); 117 | } catch (err) { 118 | expect(err).toBeInstanceOf(Error); 119 | expect(err.name).toBe("MoleculerClientError"); 120 | expect(err.message).toBe("Some user input is not valid"); 121 | expect(err.code).toBe(400); 122 | expect(err.type).toBe("INVALID_INPUT"); 123 | expect(err.stack).toBeDefined(); 124 | expect(err.data).toEqual({ 125 | action: "posts.list", 126 | params: { 127 | limit: "asd" 128 | } 129 | }); 130 | } 131 | }); 132 | 133 | it("should call ex-alpha service 'silentHazard' action", async () => { 134 | expect.assertions(7); 135 | try { 136 | await broker.call("ex-alpha.silentHazard"); 137 | } catch (err) { 138 | expect(err).toBeInstanceOf(Error); 139 | expect(err.name).toBe("MoleculerError"); 140 | expect(err.message).toBe("Something happened"); 141 | expect(err.code).toBe(501); 142 | expect(err.type).toBe("Not Implemented"); 143 | expect(err.stack).toBeDefined(); 144 | expect(err.data).toBeUndefined(); 145 | } 146 | }); 147 | 148 | it("should register ex-beta schema", async () => { 149 | const res = await POST(`${sidecarBaseURL}/v1/registry/services`, ExBeta.schema()); 150 | 151 | expect(res.status).toBe(200); 152 | expect(res.json).toEqual({ status: "OK" }); 153 | 154 | await broker.Promise.delay(500); 155 | }); 156 | 157 | it("should call ex-beta service 'echo' action", async () => { 158 | expect( 159 | await broker.call( 160 | "ex-beta.echo", 161 | { a: 5, b: "John", c: 123.45, d: true, e: [1, 2, 3] }, 162 | { 163 | meta: { 164 | user: { 165 | id: 1, 166 | name: "John Doe" 167 | } 168 | } 169 | } 170 | ) 171 | ).toEqual({ 172 | action: "ex-beta.echo", 173 | nodeID: broker.nodeID, 174 | params: { a: 5, b: "John", c: 123.45, d: true, e: [1, 2, 3] }, 175 | meta: { user: { id: 1, name: "John Doe" }, beta: "ok" }, 176 | options: {} 177 | }); 178 | }); 179 | 180 | it("should call ex-beta service 'echo' action via ex-alpha.proxy", async () => { 181 | expect( 182 | await broker.call( 183 | "ex-alpha.proxy", 184 | { a: 5, b: "John", c: 123.45, d: true, e: [1, 2, 3] }, 185 | { 186 | meta: { 187 | user: { 188 | id: 1, 189 | name: "John Doe" 190 | } 191 | } 192 | } 193 | ) 194 | ).toEqual({ 195 | action: "ex-beta.echo", 196 | nodeID: "sidecar", 197 | params: { a: 5, b: "John", c: 123.45, d: true, e: [1, 2, 3] }, 198 | meta: { user: { id: 1, name: "John Doe" }, alpha: "ok", beta: "ok" }, 199 | options: {} 200 | }); 201 | }); 202 | 203 | it("should call the 'greeter.welcome' with params", async () => { 204 | const res = await ExAlpha.callGreeterWelcome(); 205 | expect(res).toEqual({ meta: {}, response: "Welcome, Sidecar" }); 206 | expect(GreeterService.actions.welcome.handler).toBeCalledTimes(1); 207 | }); 208 | }); 209 | 210 | describe("Test external service events", () => { 211 | it("should call the event handler in ex-alpha", async () => { 212 | ExAlpha.callStack.length = 0; 213 | 214 | await broker.emit("user.created", { user: { id: 5, name: "John Doe" } }); 215 | await broker.Promise.delay(100); 216 | 217 | expect(ExAlpha.callStack[0]).toEqual({ 218 | method: "POST", 219 | path: "/userEvents", 220 | body: { 221 | event: "user.created", 222 | eventGroups: ["ex-alpha"], 223 | eventType: "emit", 224 | meta: {}, 225 | nodeID: broker.nodeID, 226 | params: { user: { id: 5, name: "John Doe" } } 227 | } 228 | }); 229 | }); 230 | 231 | it("should call the event handler in ex-alpha", async () => { 232 | await broker.broadcast("user.created", { user: { id: 5, name: "John Doe" } }); 233 | await broker.Promise.delay(100); 234 | 235 | expect(ExAlpha.callStack[1]).toEqual({ 236 | method: "POST", 237 | path: "/userEvents", 238 | body: { 239 | event: "user.created", 240 | //eventGroups: ["ex-alpha"], 241 | eventType: "broadcast", 242 | meta: {}, 243 | nodeID: broker.nodeID, 244 | params: { user: { id: 5, name: "John Doe" } } 245 | } 246 | }); 247 | }); 248 | 249 | it("should receive the greeter service the 'post.updated' event (emit)", async () => { 250 | await broker.call("ex-beta.emitUpdate"); 251 | await broker.Promise.delay(100); 252 | expect(GreeterService.events["post.updated"].handler).toBeCalledTimes(1); 253 | expect(GreeterService.events["post.updated"].handler).toBeCalledWith( 254 | { id: 1, title: "First post" }, 255 | "sidecar", 256 | "post.updated", 257 | expect.any(Context) 258 | ); 259 | }); 260 | 261 | it("should receive the greeter service the 'post.updated' event (broadcast)", async () => { 262 | GreeterService.events["post.updated"].handler.mockClear(); 263 | await broker.call("ex-beta.broadcastUpdate"); 264 | await broker.Promise.delay(100); 265 | expect(GreeterService.events["post.updated"].handler).toBeCalledTimes(1); 266 | expect(GreeterService.events["post.updated"].handler).toBeCalledWith( 267 | { id: 1, title: "First post" }, 268 | "sidecar", 269 | "post.updated", 270 | expect.any(Context) 271 | ); 272 | }); 273 | }); 274 | }); 275 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @moleculer/sidecar 3 | * Copyright (c) 2020 MoleculerJS (https://github.com/moleculerjs/sidecar) 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | const _ = require("lodash"); 10 | const kleur = require("kleur"); 11 | const fetch = require("node-fetch"); 12 | const { MoleculerError, MoleculerClientError } = require("moleculer").Errors; 13 | const ApiGateway = require("moleculer-web"); 14 | const pkg = require("../package.json"); 15 | 16 | module.exports = { 17 | name: "$sidecar", 18 | 19 | mixins: [ApiGateway], 20 | 21 | metadata: { 22 | $category: "gateway", 23 | $description: "Sidecar service", 24 | $official: true, 25 | $package: { 26 | name: pkg.name, 27 | version: pkg.version, 28 | repo: pkg.repository ? pkg.repository.url : null 29 | } 30 | }, 31 | 32 | settings: { 33 | port: process.env.SIDECAR_PORT != null ? process.env.SIDECAR_PORT : 5103, 34 | path: "/v1", 35 | 36 | routes: [ 37 | { 38 | path: "/", 39 | 40 | mappingPolicy: "restrict", 41 | 42 | bodyParsers: { 43 | json: true 44 | }, 45 | 46 | aliases: { 47 | // Registry-access endpoints 48 | "GET /registry/nodes": "$node.list", 49 | "GET /registry/services": "$node.services", 50 | "GET /registry/actions": "$node.actions", 51 | "GET /registry/events": "$node.events", 52 | 53 | "POST /registry/services": "$sidecar.registerService", 54 | "DELETE /registry/services/:serviceName": "$sidecar.unregisterService", 55 | 56 | // Calling action 57 | "POST /call/:action": "$sidecar.callAction", 58 | 59 | // Emitting event 60 | "POST /emit/:event": "$sidecar.emitEvent", 61 | "POST /broadcast/:event": "$sidecar.broadcastEvent" 62 | } 63 | } 64 | ] 65 | }, 66 | 67 | created() { 68 | this.services = new Map(); 69 | }, 70 | 71 | actions: { 72 | addRoute: false, 73 | removeRoute: false, 74 | 75 | /** 76 | * Register an external service 77 | */ 78 | registerService: { 79 | params: { 80 | name: "string|no-empty" 81 | }, 82 | async handler(ctx) { 83 | const schema = _.cloneDeep(ctx.params); 84 | 85 | // 1. Check the service is exist 86 | let svc = this.broker.getLocalService({ 87 | name: schema.name, 88 | version: schema.version 89 | }); 90 | 91 | // 2. If yes, destroy 92 | if (svc) { 93 | this.logger.info(`Destroy previous '${schema.name}' service...`); 94 | await this.broker.destroyService(svc); 95 | } 96 | 97 | // 3. Convert the schema, fulfill the action/event handlers 98 | this.logger.info(kleur.yellow().bold(`Create new '${schema.name}' service...`)); 99 | 100 | if (schema.actions) { 101 | Object.entries(schema.actions).forEach(([name, action]) => { 102 | // Handle shorthand action def 103 | if (typeof action == "string") { 104 | schema.actions[name] = { handler: action }; 105 | action = schema.actions[name]; 106 | } 107 | 108 | if (typeof action.handler != "string") { 109 | throw new MoleculerClientError( 110 | "Invalid action handler", 111 | 400, 112 | "INVALID_HANDLER", 113 | { action } 114 | ); 115 | } 116 | 117 | action.handler = this.generateActionHandler(action, schema); 118 | }); 119 | } 120 | 121 | if (schema.events) { 122 | Object.entries(schema.events).forEach(([name, event]) => { 123 | // Handle shorthand event def 124 | if (typeof event == "string") { 125 | schema.events[name] = { handler: event }; 126 | event = schema.events[name]; 127 | } 128 | if (typeof event.handler != "string") { 129 | throw new MoleculerClientError( 130 | "Invalid event handler", 131 | 400, 132 | "INVALID_HANDLER", 133 | { event } 134 | ); 135 | } 136 | 137 | event.handler = this.generateEventHandler(event, schema); 138 | }); 139 | } 140 | 141 | svc = this.broker.createService(schema); 142 | 143 | return { status: "OK" }; 144 | } 145 | }, 146 | 147 | /** 148 | * Unregister a loaded external service 149 | */ 150 | unregisterService: { 151 | params: { 152 | serviceName: "string|no-empty|trim" 153 | }, 154 | async handler(ctx) { 155 | // 1. Check the service is exist 156 | const svc = this.broker.getLocalService(ctx.params.serviceName); 157 | 158 | // 2. If yes, destroy 159 | if (!svc) { 160 | throw new MoleculerClientError( 161 | "Service not found", 162 | 404, 163 | "NOT_FOUND", 164 | ctx.params 165 | ); 166 | } 167 | 168 | this.logger.info(kleur.yellow().bold(`Destroy '${svc.fullName}' service...`)); 169 | await this.broker.destroyService(svc); 170 | 171 | return { status: "OK" }; 172 | } 173 | }, 174 | 175 | /** 176 | * Call an action 177 | */ 178 | callAction: { 179 | params: { 180 | action: "string|no-empty|trim", 181 | params: "any|optional", 182 | meta: "object|optional", 183 | options: "object|optional" 184 | }, 185 | async handler(ctx) { 186 | const payload = ctx.params; 187 | try { 188 | const response = await ctx.call( 189 | payload.action, 190 | payload.params != null ? payload.params : {}, 191 | { 192 | meta: payload.meta, 193 | timeout: payload.timeout, 194 | retries: payload.retries 195 | } 196 | ); 197 | 198 | return { 199 | response, 200 | meta: ctx.meta 201 | }; 202 | } catch (err) { 203 | ctx.meta.$statusCode = err.code || 500; 204 | 205 | return { 206 | error: _.pick(err, [ 207 | "name", 208 | "message", 209 | "code", 210 | "type", 211 | "stack", 212 | "data", 213 | "nodeID" 214 | ]), 215 | meta: ctx.meta 216 | }; 217 | } 218 | } 219 | }, 220 | 221 | /** 222 | * Emit an event 223 | */ 224 | emitEvent: { 225 | params: { 226 | event: "string|no-empty|trim", 227 | params: "any|optional", 228 | meta: "object|optional", 229 | options: "object|optional" 230 | }, 231 | async handler(ctx) { 232 | const payload = ctx.params; 233 | try { 234 | await ctx.emit(payload.event, payload.params != null ? payload.params : {}, { 235 | meta: payload.meta, 236 | groups: payload.groups 237 | }); 238 | } catch (err) { 239 | this.logger.error("Unable to emit event", err); 240 | } 241 | } 242 | }, 243 | 244 | /** 245 | * Broadcast an event 246 | */ 247 | broadcastEvent: { 248 | params: { 249 | event: "string|no-empty|trim", 250 | params: "any|optional", 251 | meta: "object|optional", 252 | options: "object|optional" 253 | }, 254 | async handler(ctx) { 255 | const payload = ctx.params; 256 | try { 257 | await ctx.broadcast(payload.event, payload.params, { 258 | meta: payload.meta, 259 | groups: payload.groups 260 | }); 261 | } catch (err) { 262 | this.logger.error("Unable to broadcast event", err); 263 | } 264 | } 265 | } 266 | }, 267 | 268 | methods: { 269 | /** 270 | * Generate REST handler for action 271 | * 272 | * @param {*} action 273 | * @param {*} schema 274 | */ 275 | generateActionHandler(action, schema) { 276 | //console.log("generateActionHandler", action); 277 | const fullUrl = 278 | (schema.settings && schema.settings.baseUrl ? schema.settings.baseUrl : "") + 279 | action.handler; 280 | 281 | return async ctx => { 282 | const res = await this.postRequest(fullUrl, { 283 | action: ctx.action.name, 284 | nodeID: ctx.nodeID, 285 | params: ctx.params, 286 | meta: ctx.meta, 287 | options: { 288 | timeout: ctx.timeout 289 | } 290 | }); 291 | 292 | if (res.meta) { 293 | // Merge the received meta into "ctx.meta" 294 | Object.assign(ctx.meta, res.meta); 295 | } 296 | 297 | return res.response; 298 | }; 299 | }, 300 | 301 | /** 302 | * Generate REST handler for event 303 | * @param {*} event 304 | * @param {*} schema 305 | */ 306 | generateEventHandler(event, schema) { 307 | //console.log("generateActionHandler", action); 308 | const fullUrl = 309 | (schema.settings && schema.settings.baseUrl ? schema.settings.baseUrl : "") + 310 | event.handler; 311 | 312 | return async ctx => { 313 | try { 314 | await this.postRequest(fullUrl, { 315 | event: ctx.eventName, 316 | eventType: ctx.eventType, 317 | eventGroups: ctx.eventGroups, 318 | nodeID: ctx.nodeID, 319 | params: ctx.params, 320 | meta: ctx.meta 321 | }); 322 | } catch (err) { 323 | this.logger.error("Unable to call sidecar event subscription handler", err); 324 | } 325 | }; 326 | }, 327 | 328 | /** 329 | * POST request for external services. 330 | * @param {String} url 331 | * @param {any?} payload 332 | */ 333 | postRequest(url, payload) { 334 | return fetch(url, { 335 | method: "POST", 336 | cache: "no-cache", 337 | headers: { 338 | "Content-Type": "application/json" 339 | }, 340 | body: payload ? JSON.stringify(payload) : undefined 341 | }).then(async res => { 342 | let payload; 343 | const contentType = res.headers.get("content-type"); 344 | if (contentType && contentType.startsWith("application/json")) 345 | payload = await res.json(); 346 | else payload = await res.text(); 347 | 348 | if (!res.ok) { 349 | let err; 350 | if (payload && payload.error) { 351 | err = new MoleculerError(res.statusText); 352 | Object.assign(err, payload.error); 353 | } else { 354 | err = new MoleculerError("Something happened", res.status, res.statusText); 355 | } 356 | 357 | throw err; 358 | } 359 | 360 | return payload; 361 | }); 362 | } 363 | }, 364 | 365 | started() { 366 | this.listenAddress = this.server.address(); 367 | }, 368 | 369 | stopped() { 370 | // 371 | } 372 | }; 373 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Moleculer logo](http://moleculer.services/images/banner.png) 2 | 3 | ![Integration Test](https://github.com/moleculerjs/sidecar/workflows/Integration%20Test/badge.svg) 4 | 5 | # [WIP] Moleculer Sidecar 6 | Moleculer Sidecar allows using external services (written in other programming languages which is not supported officially) in a Moleculer microservices project. The Sidecar is a HTTP server which gives a REST interface to communicate other Moleculer services instead of implementing the full Moleculer protocol és Service Registry & Discovery features. Sidecar is a full-fledged MoleculerJS node with all features (e.g. parameter validation, retries, fallback, timeout...etc), it means in the external services, you don't need to implement them because Sidecar manages them. 7 | 8 | ## Features 9 | - write services any programming languages (which have http server & client support). 10 | - bundled to executable, no need to install Node.js environment for running. 11 | - configure Sidecar via environment variables or `moleculer.config.js` file. 12 | - contains all official transporter, cacher, discoverer, serializer libraries. 13 | - easy to add to any Docker image. 14 | - official Docker image, use it as a Sidecar pod container in Kubernetes cluster. 15 | 16 | ## Install 17 | 18 | ### Using executables 19 | Download binaries from [releases](https://github.com/moleculerjs/sidecar/releases). 20 | 21 | ### Docker 22 | **Simple start** 23 | ```bash 24 | docker run -p 5103:5103 moleculer/sidecar 25 | ``` 26 | 27 | **Start with REPL mode** 28 | ```bash 29 | docker run --it -p 5103:5103 moleculer/sidecar --repl 30 | ``` 31 | 32 | **Start with config file** 33 | ```bash 34 | docker run -v /my-folder/moleculer.config.js:/sidecar/moleculer.config.js moleculer/sidecar --config 35 | ``` 36 | 37 | ### Kubernetes 38 | TODO 39 | 40 | ### Using from source 41 | TODO 42 | 43 | ## Usage (via HTTP interface) 44 | The ServiceBroker inside the Sidecar can be configured via `moleculer.config.js` file or environment variables. The Sidecar HTTP server is listening on port 5103 (by default). _(Why 5103? To make it easier to remember: SIDE = S->5 I->1 D->0 E->3 = 5103)._ If you don't like it, you can change it with `SIDECAR_PORT` environment variable. 45 | 46 | ### Register an external service 47 | The request body should contains one or more service schema where the action/event handlers should be an URL what points to the external service HTTP endpoint. In the Service schema you can use all MoleculerJS features (e.g. parameter validation, metrics, tracing, bulkhead, timeout, retries ...etc), they are handled by the Sidecar. 48 | ``` 49 | POST /v1/registry/services 50 | ``` 51 | 52 | **Request body** 53 | ```js 54 | { 55 | name: "posts", 56 | version: 1, 57 | 58 | settings: { 59 | // It means, your HTTP server running on port 5000 and sidecar can reach it on `localhost` 60 | // The URLs in action/event handlers contains relative URL. 61 | baseUrl: "http://localhost:5000" 62 | }, 63 | 64 | actions: { 65 | list: { 66 | params: { 67 | limit: "number", 68 | offset: "number" 69 | }, 70 | // Shorthand handler URL what will be called by sidecar 71 | handler: "/actions/posts/list" 72 | } 73 | }, 74 | 75 | events: { 76 | "user.created": { 77 | // Shorthand handler URL what will be called by sidecar 78 | handler: "/events/user.created" 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | ### Call a service 85 | 86 | Calling `comments.create` service action. 87 | ``` 88 | POST /v1/call/comments.create 89 | ``` 90 | 91 | **Request body** 92 | ```js 93 | { 94 | // Context params 95 | params: { 96 | title: "Lorem ipsum", 97 | content: "Lorem ipsum dolor sit amet..." 98 | }, 99 | // Context meta 100 | meta: { 101 | user: { 102 | id: 12345 103 | } 104 | }, 105 | // Calling options 106 | options: { 107 | timeout: 3000 108 | } 109 | } 110 | ``` 111 | 112 | **Response body** 113 | ```js 114 | { 115 | // Response data 116 | response: { 117 | id: 1, 118 | title: "Lorem ipsum", 119 | content: "Lorem ipsum dolor sit amet..." 120 | }, 121 | // Optional: Context meta if you changed the content. 122 | meta: { 123 | user: { 124 | id: 12345 125 | } 126 | } 127 | } 128 | ``` 129 | 130 | **Error response body** 131 | ```js 132 | { 133 | error: { 134 | name: "MoleculerClientError", 135 | code: 422, 136 | type: "VALIDATION_ERROR", 137 | message: "Title is required", 138 | data: { 139 | action: "comments.create", 140 | params: { 141 | title: null 142 | } 143 | } 144 | } 145 | } 146 | ``` 147 | 148 | ### Emit an event 149 | 150 | Emit a `post.created` event. 151 | ``` 152 | POST /v1/emit/post.created 153 | ``` 154 | 155 | In case of broadcast use the following URL: 156 | ``` 157 | POST /broadcast/post.created 158 | ``` 159 | 160 | **Request body** 161 | ```js 162 | { 163 | // Context params 164 | params: { 165 | id: 1, 166 | title: "First post", 167 | content: "Post content", 168 | author: 12345 169 | }, 170 | // Context meta 171 | meta: { 172 | user: { 173 | id: 12345 174 | } 175 | }, 176 | // Emit options 177 | options: { 178 | groups: "users" 179 | } 180 | } 181 | ``` 182 | 183 | ### Accepting action requests 184 | If your external service implements actions, you should start a HTTP server. Create an endpoint for the action handler and set the address in the service schema. If another services calls your action, the Sidecar sends a POST request to the defined endpoint with the following request body: 185 | 186 | **Request body** 187 | ```js 188 | { 189 | // Action name 190 | action: "posts.list", 191 | 192 | // Caller NodeID 193 | nodeID: "node-123", 194 | 195 | // Context params 196 | params: { 197 | limit: 10, 198 | offset: 50 199 | }, 200 | 201 | // Context meta 202 | meta: { 203 | user: { 204 | id: 12345 205 | } 206 | }, 207 | 208 | // Calling options 209 | options: { 210 | timeout: 3000 211 | } 212 | } 213 | ``` 214 | 215 | You should send a response with the following form: 216 | 217 | **Response body** 218 | ```js 219 | { 220 | // Response data 221 | response: [ 222 | { id: 1, title: "First post" }, 223 | { id: 2, title: "Second post" }, 224 | { id: 3, title: "Third post" } 225 | ], 226 | // Optional: Context meta if you changed the content. 227 | meta: { 228 | user: { 229 | id: 12345 230 | } 231 | } 232 | } 233 | ``` 234 | 235 | If an error occured during execution, use correct response status code (4xx, 5xx) and send the following as a response body: 236 | 237 | **Error response body** 238 | ```js 239 | { 240 | // Error data 241 | error: { 242 | name: "MoleculerClientError", 243 | code: 400, 244 | type: "INVALID_INPUT", 245 | message: "Some user input is not valid", 246 | data: { 247 | // Any useful data 248 | action: "posts.list", 249 | params: { 250 | limit: "asd" 251 | } 252 | } 253 | } 254 | } 255 | ``` 256 | 257 | ### Accepting events 258 | If you want to subscribe to Moleculer events, you should start a HTTP server. Create an endpoint for the event handler and set the address in the service schema. If the event emitted, the Sidecar sends a POST request to the defined endpoint with the following request body: 259 | 260 | **Request body** 261 | ```js 262 | { 263 | // Event name 264 | event: "user.created", 265 | 266 | // Type of event (emit, broadcast) 267 | eventType: "emit", 268 | 269 | eventGroups: "posts", 270 | 271 | // Caller NodeID 272 | nodeID: "node-123", 273 | 274 | // Context params 275 | params: { 276 | limit: 10, 277 | offset: 50 278 | }, 279 | 280 | // Context meta 281 | meta: { 282 | user: { 283 | id: 12345 284 | } 285 | } 286 | } 287 | ``` 288 | 289 | ### Access the Sidecar's broker registry 290 | 291 | List of all Moleculer nodes 292 | ``` 293 | GET /v1/registry/nodes 294 | ``` 295 | 296 | List of all Moleculer services 297 | ``` 298 | GET /v1/registry/services 299 | ``` 300 | 301 | List of all Moleculer actions 302 | ``` 303 | GET /v1/registry/actions 304 | ``` 305 | 306 | List of all Moleculer event subscriptions 307 | ``` 308 | GET /v1/registry/events 309 | ``` 310 | 311 | ### Unregister an external service 312 | You can unregister your service if you call the following endpoint: 313 | ``` 314 | DELETE /v1/registry/services/:service-full-name 315 | ``` 316 | 317 | **Example** 318 | ``` 319 | DELETE /v1/registry/services/v1.posts 320 | ``` 321 | 322 | ## Example external services 323 | 324 | ### Go 325 | Using Fiber: [examples/full/go-fiber/server.go](examples/full/go-fiber/server.go) 326 | 327 | ### Python 328 | Using Flask: [examples/full/python-flask/server.py](examples/full/python-flask/server.py) 329 | 330 | ### Ruby 331 | Using Sinatra: [examples/full/ruby-sinatra/server.rb](examples/full/ruby-sinatra/server.rb) 332 | 333 | ### Rust 334 | Using Actix: [examples/full/rust-actix/src/main.rs](examples/full/rust-actix/src/main.rs) 335 | 336 | ### PHP 337 | Using Mark: [examples/full/php-mark/server.php](examples/full/php-mark/server.php) 338 | 339 | ### Ballerina 340 | Using built-in tools [examples/full/ballerina/src/server/main.bal](examples/full/ballerina/src/server/main.bal) 341 | 342 | ### Deno 343 | Using Oak: [examples/full/deno-oak/server.ts](examples/full/deno-oak/server.ts) 344 | 345 | ### Swift 346 | Using Perfect: [examples/full/swift-perfect/Sources/server/main.swift](examples/full/swift-perfect/Sources/server/main.swift) 347 | 348 | ### Crystal 349 | Using kemal: [examples/full/crystal-kemal/server.cr](examples/full/crystal-kemal/server.cr) 350 | 351 | ### C# 352 | TODO 353 | 354 | ### C++ 355 | TODO 356 | 357 | ### Kotlin 358 | TODO 359 | 360 | ### Delphi 361 | TODO 362 | 363 | ## Contribution 364 | Please send pull requests improving the usage and fixing bugs, improving documentation and providing better examples, or providing some testing, because these things are important. 365 | 366 | ## License 367 | The project is available under the [MIT license](https://tldrlegal.com/license/mit-license). 368 | 369 | ## Contact 370 | Copyright (c) 2021 MoleculerJS 371 | 372 | [![@MoleculerJS](https://img.shields.io/badge/github-moleculerjs-green.svg)](https://github.com/moleculerjs) [![@MoleculerJS](https://img.shields.io/badge/twitter-MoleculerJS-blue.svg)](https://twitter.com/MoleculerJS) 373 | -------------------------------------------------------------------------------- /examples/full/rust-actix/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "actix-codec" 5 | version = "0.3.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" 8 | dependencies = [ 9 | "bitflags", 10 | "bytes", 11 | "futures-core", 12 | "futures-sink", 13 | "log", 14 | "pin-project 0.4.27", 15 | "tokio", 16 | "tokio-util", 17 | ] 18 | 19 | [[package]] 20 | name = "actix-connect" 21 | version = "2.0.0" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "177837a10863f15ba8d3ae3ec12fac1099099529ed20083a27fdfe247381d0dc" 24 | dependencies = [ 25 | "actix-codec", 26 | "actix-rt", 27 | "actix-service", 28 | "actix-utils", 29 | "derive_more", 30 | "either", 31 | "futures-util", 32 | "http", 33 | "log", 34 | "trust-dns-proto", 35 | "trust-dns-resolver", 36 | ] 37 | 38 | [[package]] 39 | name = "actix-http" 40 | version = "2.2.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "452299e87817ae5673910e53c243484ca38be3828db819b6011736fc6982e874" 43 | dependencies = [ 44 | "actix-codec", 45 | "actix-connect", 46 | "actix-rt", 47 | "actix-service", 48 | "actix-threadpool", 49 | "actix-utils", 50 | "base64", 51 | "bitflags", 52 | "brotli2", 53 | "bytes", 54 | "cookie", 55 | "copyless", 56 | "derive_more", 57 | "either", 58 | "encoding_rs", 59 | "flate2", 60 | "futures-channel", 61 | "futures-core", 62 | "futures-util", 63 | "fxhash", 64 | "h2", 65 | "http", 66 | "httparse", 67 | "indexmap", 68 | "itoa", 69 | "language-tags", 70 | "lazy_static", 71 | "log", 72 | "mime", 73 | "percent-encoding", 74 | "pin-project 1.0.2", 75 | "rand", 76 | "regex", 77 | "serde", 78 | "serde_json", 79 | "serde_urlencoded", 80 | "sha-1", 81 | "slab", 82 | "time", 83 | ] 84 | 85 | [[package]] 86 | name = "actix-macros" 87 | version = "0.1.3" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" 90 | dependencies = [ 91 | "quote", 92 | "syn", 93 | ] 94 | 95 | [[package]] 96 | name = "actix-router" 97 | version = "0.2.5" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "bbd1f7dbda1645bf7da33554db60891755f6c01c1b2169e2f4c492098d30c235" 100 | dependencies = [ 101 | "bytestring", 102 | "http", 103 | "log", 104 | "regex", 105 | "serde", 106 | ] 107 | 108 | [[package]] 109 | name = "actix-rt" 110 | version = "1.1.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" 113 | dependencies = [ 114 | "actix-macros", 115 | "actix-threadpool", 116 | "copyless", 117 | "futures-channel", 118 | "futures-util", 119 | "smallvec", 120 | "tokio", 121 | ] 122 | 123 | [[package]] 124 | name = "actix-server" 125 | version = "1.0.4" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e" 128 | dependencies = [ 129 | "actix-codec", 130 | "actix-rt", 131 | "actix-service", 132 | "actix-utils", 133 | "futures-channel", 134 | "futures-util", 135 | "log", 136 | "mio", 137 | "mio-uds", 138 | "num_cpus", 139 | "slab", 140 | "socket2", 141 | ] 142 | 143 | [[package]] 144 | name = "actix-service" 145 | version = "1.0.6" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" 148 | dependencies = [ 149 | "futures-util", 150 | "pin-project 0.4.27", 151 | ] 152 | 153 | [[package]] 154 | name = "actix-testing" 155 | version = "1.0.1" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" 158 | dependencies = [ 159 | "actix-macros", 160 | "actix-rt", 161 | "actix-server", 162 | "actix-service", 163 | "log", 164 | "socket2", 165 | ] 166 | 167 | [[package]] 168 | name = "actix-threadpool" 169 | version = "0.3.3" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30" 172 | dependencies = [ 173 | "derive_more", 174 | "futures-channel", 175 | "lazy_static", 176 | "log", 177 | "num_cpus", 178 | "parking_lot", 179 | "threadpool", 180 | ] 181 | 182 | [[package]] 183 | name = "actix-tls" 184 | version = "2.0.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "24789b7d7361cf5503a504ebe1c10806896f61e96eca9a7350e23001aca715fb" 187 | dependencies = [ 188 | "actix-codec", 189 | "actix-service", 190 | "actix-utils", 191 | "futures-util", 192 | ] 193 | 194 | [[package]] 195 | name = "actix-utils" 196 | version = "2.0.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a" 199 | dependencies = [ 200 | "actix-codec", 201 | "actix-rt", 202 | "actix-service", 203 | "bitflags", 204 | "bytes", 205 | "either", 206 | "futures-channel", 207 | "futures-sink", 208 | "futures-util", 209 | "log", 210 | "pin-project 0.4.27", 211 | "slab", 212 | ] 213 | 214 | [[package]] 215 | name = "actix-web" 216 | version = "3.3.2" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "e641d4a172e7faa0862241a20ff4f1f5ab0ab7c279f00c2d4587b77483477b86" 219 | dependencies = [ 220 | "actix-codec", 221 | "actix-http", 222 | "actix-macros", 223 | "actix-router", 224 | "actix-rt", 225 | "actix-server", 226 | "actix-service", 227 | "actix-testing", 228 | "actix-threadpool", 229 | "actix-tls", 230 | "actix-utils", 231 | "actix-web-codegen", 232 | "awc", 233 | "bytes", 234 | "derive_more", 235 | "encoding_rs", 236 | "futures-channel", 237 | "futures-core", 238 | "futures-util", 239 | "fxhash", 240 | "log", 241 | "mime", 242 | "pin-project 1.0.2", 243 | "regex", 244 | "serde", 245 | "serde_json", 246 | "serde_urlencoded", 247 | "socket2", 248 | "time", 249 | "tinyvec", 250 | "url", 251 | ] 252 | 253 | [[package]] 254 | name = "actix-web-codegen" 255 | version = "0.4.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" 258 | dependencies = [ 259 | "proc-macro2", 260 | "quote", 261 | "syn", 262 | ] 263 | 264 | [[package]] 265 | name = "addr2line" 266 | version = "0.14.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" 269 | dependencies = [ 270 | "gimli", 271 | ] 272 | 273 | [[package]] 274 | name = "adler" 275 | version = "0.2.3" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 278 | 279 | [[package]] 280 | name = "aho-corasick" 281 | version = "0.7.15" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 284 | dependencies = [ 285 | "memchr", 286 | ] 287 | 288 | [[package]] 289 | name = "async-trait" 290 | version = "0.1.42" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" 293 | dependencies = [ 294 | "proc-macro2", 295 | "quote", 296 | "syn", 297 | ] 298 | 299 | [[package]] 300 | name = "autocfg" 301 | version = "1.0.1" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 304 | 305 | [[package]] 306 | name = "awc" 307 | version = "2.0.3" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691" 310 | dependencies = [ 311 | "actix-codec", 312 | "actix-http", 313 | "actix-rt", 314 | "actix-service", 315 | "base64", 316 | "bytes", 317 | "cfg-if 1.0.0", 318 | "derive_more", 319 | "futures-core", 320 | "log", 321 | "mime", 322 | "percent-encoding", 323 | "rand", 324 | "serde", 325 | "serde_json", 326 | "serde_urlencoded", 327 | ] 328 | 329 | [[package]] 330 | name = "backtrace" 331 | version = "0.3.55" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" 334 | dependencies = [ 335 | "addr2line", 336 | "cfg-if 1.0.0", 337 | "libc", 338 | "miniz_oxide", 339 | "object", 340 | "rustc-demangle", 341 | ] 342 | 343 | [[package]] 344 | name = "base-x" 345 | version = "0.2.8" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" 348 | 349 | [[package]] 350 | name = "base64" 351 | version = "0.13.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 354 | 355 | [[package]] 356 | name = "bitflags" 357 | version = "1.2.1" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 360 | 361 | [[package]] 362 | name = "block-buffer" 363 | version = "0.9.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 366 | dependencies = [ 367 | "generic-array", 368 | ] 369 | 370 | [[package]] 371 | name = "brotli-sys" 372 | version = "0.3.2" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" 375 | dependencies = [ 376 | "cc", 377 | "libc", 378 | ] 379 | 380 | [[package]] 381 | name = "brotli2" 382 | version = "0.3.2" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" 385 | dependencies = [ 386 | "brotli-sys", 387 | "libc", 388 | ] 389 | 390 | [[package]] 391 | name = "bumpalo" 392 | version = "3.4.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 395 | 396 | [[package]] 397 | name = "byteorder" 398 | version = "1.3.4" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 401 | 402 | [[package]] 403 | name = "bytes" 404 | version = "0.5.6" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 407 | 408 | [[package]] 409 | name = "bytestring" 410 | version = "0.1.5" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363" 413 | dependencies = [ 414 | "bytes", 415 | ] 416 | 417 | [[package]] 418 | name = "cc" 419 | version = "1.0.66" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 422 | 423 | [[package]] 424 | name = "cfg-if" 425 | version = "0.1.10" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 428 | 429 | [[package]] 430 | name = "cfg-if" 431 | version = "1.0.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 434 | 435 | [[package]] 436 | name = "const_fn" 437 | version = "0.4.4" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" 440 | 441 | [[package]] 442 | name = "cookie" 443 | version = "0.14.3" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" 446 | dependencies = [ 447 | "percent-encoding", 448 | "time", 449 | "version_check", 450 | ] 451 | 452 | [[package]] 453 | name = "copyless" 454 | version = "0.1.5" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" 457 | 458 | [[package]] 459 | name = "core-foundation" 460 | version = "0.9.1" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 463 | dependencies = [ 464 | "core-foundation-sys", 465 | "libc", 466 | ] 467 | 468 | [[package]] 469 | name = "core-foundation-sys" 470 | version = "0.8.2" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 473 | 474 | [[package]] 475 | name = "cpuid-bool" 476 | version = "0.1.2" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 479 | 480 | [[package]] 481 | name = "crc32fast" 482 | version = "1.2.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 485 | dependencies = [ 486 | "cfg-if 1.0.0", 487 | ] 488 | 489 | [[package]] 490 | name = "derive_more" 491 | version = "0.99.11" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" 494 | dependencies = [ 495 | "proc-macro2", 496 | "quote", 497 | "syn", 498 | ] 499 | 500 | [[package]] 501 | name = "digest" 502 | version = "0.9.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 505 | dependencies = [ 506 | "generic-array", 507 | ] 508 | 509 | [[package]] 510 | name = "discard" 511 | version = "1.0.4" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" 514 | 515 | [[package]] 516 | name = "either" 517 | version = "1.6.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 520 | 521 | [[package]] 522 | name = "encoding_rs" 523 | version = "0.8.26" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" 526 | dependencies = [ 527 | "cfg-if 1.0.0", 528 | ] 529 | 530 | [[package]] 531 | name = "enum-as-inner" 532 | version = "0.3.3" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" 535 | dependencies = [ 536 | "heck", 537 | "proc-macro2", 538 | "quote", 539 | "syn", 540 | ] 541 | 542 | [[package]] 543 | name = "flate2" 544 | version = "1.0.19" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" 547 | dependencies = [ 548 | "cfg-if 1.0.0", 549 | "crc32fast", 550 | "libc", 551 | "miniz_oxide", 552 | ] 553 | 554 | [[package]] 555 | name = "fnv" 556 | version = "1.0.7" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 559 | 560 | [[package]] 561 | name = "foreign-types" 562 | version = "0.3.2" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 565 | dependencies = [ 566 | "foreign-types-shared", 567 | ] 568 | 569 | [[package]] 570 | name = "foreign-types-shared" 571 | version = "0.1.1" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 574 | 575 | [[package]] 576 | name = "form_urlencoded" 577 | version = "1.0.0" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" 580 | dependencies = [ 581 | "matches", 582 | "percent-encoding", 583 | ] 584 | 585 | [[package]] 586 | name = "fuchsia-zircon" 587 | version = "0.3.3" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 590 | dependencies = [ 591 | "bitflags", 592 | "fuchsia-zircon-sys", 593 | ] 594 | 595 | [[package]] 596 | name = "fuchsia-zircon-sys" 597 | version = "0.3.3" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 600 | 601 | [[package]] 602 | name = "futures" 603 | version = "0.3.8" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" 606 | dependencies = [ 607 | "futures-channel", 608 | "futures-core", 609 | "futures-io", 610 | "futures-sink", 611 | "futures-task", 612 | "futures-util", 613 | ] 614 | 615 | [[package]] 616 | name = "futures-channel" 617 | version = "0.3.8" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" 620 | dependencies = [ 621 | "futures-core", 622 | "futures-sink", 623 | ] 624 | 625 | [[package]] 626 | name = "futures-core" 627 | version = "0.3.8" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" 630 | 631 | [[package]] 632 | name = "futures-io" 633 | version = "0.3.8" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" 636 | 637 | [[package]] 638 | name = "futures-macro" 639 | version = "0.3.8" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" 642 | dependencies = [ 643 | "proc-macro-hack", 644 | "proc-macro2", 645 | "quote", 646 | "syn", 647 | ] 648 | 649 | [[package]] 650 | name = "futures-sink" 651 | version = "0.3.8" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" 654 | 655 | [[package]] 656 | name = "futures-task" 657 | version = "0.3.8" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" 660 | dependencies = [ 661 | "once_cell", 662 | ] 663 | 664 | [[package]] 665 | name = "futures-util" 666 | version = "0.3.8" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" 669 | dependencies = [ 670 | "futures-channel", 671 | "futures-core", 672 | "futures-io", 673 | "futures-macro", 674 | "futures-sink", 675 | "futures-task", 676 | "memchr", 677 | "pin-project 1.0.2", 678 | "pin-utils", 679 | "proc-macro-hack", 680 | "proc-macro-nested", 681 | "slab", 682 | ] 683 | 684 | [[package]] 685 | name = "fxhash" 686 | version = "0.2.1" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 689 | dependencies = [ 690 | "byteorder", 691 | ] 692 | 693 | [[package]] 694 | name = "generic-array" 695 | version = "0.14.4" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 698 | dependencies = [ 699 | "typenum", 700 | "version_check", 701 | ] 702 | 703 | [[package]] 704 | name = "getrandom" 705 | version = "0.1.15" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 708 | dependencies = [ 709 | "cfg-if 0.1.10", 710 | "libc", 711 | "wasi", 712 | ] 713 | 714 | [[package]] 715 | name = "gimli" 716 | version = "0.23.0" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" 719 | 720 | [[package]] 721 | name = "h2" 722 | version = "0.2.7" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" 725 | dependencies = [ 726 | "bytes", 727 | "fnv", 728 | "futures-core", 729 | "futures-sink", 730 | "futures-util", 731 | "http", 732 | "indexmap", 733 | "slab", 734 | "tokio", 735 | "tokio-util", 736 | "tracing", 737 | "tracing-futures", 738 | ] 739 | 740 | [[package]] 741 | name = "hashbrown" 742 | version = "0.9.1" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 745 | 746 | [[package]] 747 | name = "heck" 748 | version = "0.3.2" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 751 | dependencies = [ 752 | "unicode-segmentation", 753 | ] 754 | 755 | [[package]] 756 | name = "hermit-abi" 757 | version = "0.1.17" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 760 | dependencies = [ 761 | "libc", 762 | ] 763 | 764 | [[package]] 765 | name = "hostname" 766 | version = "0.3.1" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 769 | dependencies = [ 770 | "libc", 771 | "match_cfg", 772 | "winapi 0.3.9", 773 | ] 774 | 775 | [[package]] 776 | name = "http" 777 | version = "0.2.2" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" 780 | dependencies = [ 781 | "bytes", 782 | "fnv", 783 | "itoa", 784 | ] 785 | 786 | [[package]] 787 | name = "http-body" 788 | version = "0.3.1" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 791 | dependencies = [ 792 | "bytes", 793 | "http", 794 | ] 795 | 796 | [[package]] 797 | name = "httparse" 798 | version = "1.3.4" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 801 | 802 | [[package]] 803 | name = "httpdate" 804 | version = "0.3.2" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" 807 | 808 | [[package]] 809 | name = "hyper" 810 | version = "0.13.9" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" 813 | dependencies = [ 814 | "bytes", 815 | "futures-channel", 816 | "futures-core", 817 | "futures-util", 818 | "h2", 819 | "http", 820 | "http-body", 821 | "httparse", 822 | "httpdate", 823 | "itoa", 824 | "pin-project 1.0.2", 825 | "socket2", 826 | "tokio", 827 | "tower-service", 828 | "tracing", 829 | "want", 830 | ] 831 | 832 | [[package]] 833 | name = "hyper-tls" 834 | version = "0.4.3" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" 837 | dependencies = [ 838 | "bytes", 839 | "hyper", 840 | "native-tls", 841 | "tokio", 842 | "tokio-tls", 843 | ] 844 | 845 | [[package]] 846 | name = "idna" 847 | version = "0.2.0" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 850 | dependencies = [ 851 | "matches", 852 | "unicode-bidi", 853 | "unicode-normalization", 854 | ] 855 | 856 | [[package]] 857 | name = "indexmap" 858 | version = "1.6.1" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" 861 | dependencies = [ 862 | "autocfg", 863 | "hashbrown", 864 | ] 865 | 866 | [[package]] 867 | name = "instant" 868 | version = "0.1.9" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 871 | dependencies = [ 872 | "cfg-if 1.0.0", 873 | ] 874 | 875 | [[package]] 876 | name = "iovec" 877 | version = "0.1.4" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 880 | dependencies = [ 881 | "libc", 882 | ] 883 | 884 | [[package]] 885 | name = "ipconfig" 886 | version = "0.2.2" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" 889 | dependencies = [ 890 | "socket2", 891 | "widestring", 892 | "winapi 0.3.9", 893 | "winreg 0.6.2", 894 | ] 895 | 896 | [[package]] 897 | name = "ipnet" 898 | version = "2.3.0" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 901 | 902 | [[package]] 903 | name = "itoa" 904 | version = "0.4.6" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 907 | 908 | [[package]] 909 | name = "js-sys" 910 | version = "0.3.46" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" 913 | dependencies = [ 914 | "wasm-bindgen", 915 | ] 916 | 917 | [[package]] 918 | name = "kernel32-sys" 919 | version = "0.2.2" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 922 | dependencies = [ 923 | "winapi 0.2.8", 924 | "winapi-build", 925 | ] 926 | 927 | [[package]] 928 | name = "language-tags" 929 | version = "0.2.2" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" 932 | 933 | [[package]] 934 | name = "lazy_static" 935 | version = "1.4.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 938 | 939 | [[package]] 940 | name = "libc" 941 | version = "0.2.81" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 944 | 945 | [[package]] 946 | name = "linked-hash-map" 947 | version = "0.5.3" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" 950 | 951 | [[package]] 952 | name = "lock_api" 953 | version = "0.4.2" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" 956 | dependencies = [ 957 | "scopeguard", 958 | ] 959 | 960 | [[package]] 961 | name = "log" 962 | version = "0.4.11" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 965 | dependencies = [ 966 | "cfg-if 0.1.10", 967 | ] 968 | 969 | [[package]] 970 | name = "lru-cache" 971 | version = "0.1.2" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 974 | dependencies = [ 975 | "linked-hash-map", 976 | ] 977 | 978 | [[package]] 979 | name = "match_cfg" 980 | version = "0.1.0" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 983 | 984 | [[package]] 985 | name = "matches" 986 | version = "0.1.8" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 989 | 990 | [[package]] 991 | name = "memchr" 992 | version = "2.3.4" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 995 | 996 | [[package]] 997 | name = "mime" 998 | version = "0.3.16" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 1001 | 1002 | [[package]] 1003 | name = "mime_guess" 1004 | version = "2.0.3" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 1007 | dependencies = [ 1008 | "mime", 1009 | "unicase", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "miniz_oxide" 1014 | version = "0.4.3" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" 1017 | dependencies = [ 1018 | "adler", 1019 | "autocfg", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "mio" 1024 | version = "0.6.23" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 1027 | dependencies = [ 1028 | "cfg-if 0.1.10", 1029 | "fuchsia-zircon", 1030 | "fuchsia-zircon-sys", 1031 | "iovec", 1032 | "kernel32-sys", 1033 | "libc", 1034 | "log", 1035 | "miow", 1036 | "net2", 1037 | "slab", 1038 | "winapi 0.2.8", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "mio-uds" 1043 | version = "0.6.8" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 1046 | dependencies = [ 1047 | "iovec", 1048 | "libc", 1049 | "mio", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "miow" 1054 | version = "0.2.2" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 1057 | dependencies = [ 1058 | "kernel32-sys", 1059 | "net2", 1060 | "winapi 0.2.8", 1061 | "ws2_32-sys", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "native-tls" 1066 | version = "0.2.6" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" 1069 | dependencies = [ 1070 | "lazy_static", 1071 | "libc", 1072 | "log", 1073 | "openssl", 1074 | "openssl-probe", 1075 | "openssl-sys", 1076 | "schannel", 1077 | "security-framework", 1078 | "security-framework-sys", 1079 | "tempfile", 1080 | ] 1081 | 1082 | [[package]] 1083 | name = "net2" 1084 | version = "0.2.37" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 1087 | dependencies = [ 1088 | "cfg-if 0.1.10", 1089 | "libc", 1090 | "winapi 0.3.9", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "num_cpus" 1095 | version = "1.13.0" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 1098 | dependencies = [ 1099 | "hermit-abi", 1100 | "libc", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "object" 1105 | version = "0.22.0" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" 1108 | 1109 | [[package]] 1110 | name = "once_cell" 1111 | version = "1.5.2" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 1114 | 1115 | [[package]] 1116 | name = "opaque-debug" 1117 | version = "0.3.0" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1120 | 1121 | [[package]] 1122 | name = "openssl" 1123 | version = "0.10.32" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" 1126 | dependencies = [ 1127 | "bitflags", 1128 | "cfg-if 1.0.0", 1129 | "foreign-types", 1130 | "lazy_static", 1131 | "libc", 1132 | "openssl-sys", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "openssl-probe" 1137 | version = "0.1.2" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 1140 | 1141 | [[package]] 1142 | name = "openssl-sys" 1143 | version = "0.9.60" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" 1146 | dependencies = [ 1147 | "autocfg", 1148 | "cc", 1149 | "libc", 1150 | "pkg-config", 1151 | "vcpkg", 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "parking_lot" 1156 | version = "0.11.1" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 1159 | dependencies = [ 1160 | "instant", 1161 | "lock_api", 1162 | "parking_lot_core", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "parking_lot_core" 1167 | version = "0.8.2" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" 1170 | dependencies = [ 1171 | "cfg-if 1.0.0", 1172 | "instant", 1173 | "libc", 1174 | "redox_syscall", 1175 | "smallvec", 1176 | "winapi 0.3.9", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "percent-encoding" 1181 | version = "2.1.0" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1184 | 1185 | [[package]] 1186 | name = "pin-project" 1187 | version = "0.4.27" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" 1190 | dependencies = [ 1191 | "pin-project-internal 0.4.27", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "pin-project" 1196 | version = "1.0.2" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" 1199 | dependencies = [ 1200 | "pin-project-internal 1.0.2", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "pin-project-internal" 1205 | version = "0.4.27" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" 1208 | dependencies = [ 1209 | "proc-macro2", 1210 | "quote", 1211 | "syn", 1212 | ] 1213 | 1214 | [[package]] 1215 | name = "pin-project-internal" 1216 | version = "1.0.2" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" 1219 | dependencies = [ 1220 | "proc-macro2", 1221 | "quote", 1222 | "syn", 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "pin-project-lite" 1227 | version = "0.1.11" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 1230 | 1231 | [[package]] 1232 | name = "pin-project-lite" 1233 | version = "0.2.0" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" 1236 | 1237 | [[package]] 1238 | name = "pin-utils" 1239 | version = "0.1.0" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1242 | 1243 | [[package]] 1244 | name = "pkg-config" 1245 | version = "0.3.19" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 1248 | 1249 | [[package]] 1250 | name = "ppv-lite86" 1251 | version = "0.2.10" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 1254 | 1255 | [[package]] 1256 | name = "proc-macro-hack" 1257 | version = "0.5.19" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1260 | 1261 | [[package]] 1262 | name = "proc-macro-nested" 1263 | version = "0.1.6" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 1266 | 1267 | [[package]] 1268 | name = "proc-macro2" 1269 | version = "1.0.24" 1270 | source = "registry+https://github.com/rust-lang/crates.io-index" 1271 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 1272 | dependencies = [ 1273 | "unicode-xid", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "quick-error" 1278 | version = "1.2.3" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1281 | 1282 | [[package]] 1283 | name = "quote" 1284 | version = "1.0.8" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 1287 | dependencies = [ 1288 | "proc-macro2", 1289 | ] 1290 | 1291 | [[package]] 1292 | name = "rand" 1293 | version = "0.7.3" 1294 | source = "registry+https://github.com/rust-lang/crates.io-index" 1295 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1296 | dependencies = [ 1297 | "getrandom", 1298 | "libc", 1299 | "rand_chacha", 1300 | "rand_core", 1301 | "rand_hc", 1302 | ] 1303 | 1304 | [[package]] 1305 | name = "rand_chacha" 1306 | version = "0.2.2" 1307 | source = "registry+https://github.com/rust-lang/crates.io-index" 1308 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1309 | dependencies = [ 1310 | "ppv-lite86", 1311 | "rand_core", 1312 | ] 1313 | 1314 | [[package]] 1315 | name = "rand_core" 1316 | version = "0.5.1" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1319 | dependencies = [ 1320 | "getrandom", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "rand_hc" 1325 | version = "0.2.0" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1328 | dependencies = [ 1329 | "rand_core", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "redox_syscall" 1334 | version = "0.1.57" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1337 | 1338 | [[package]] 1339 | name = "regex" 1340 | version = "1.4.2" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 1343 | dependencies = [ 1344 | "aho-corasick", 1345 | "memchr", 1346 | "regex-syntax", 1347 | "thread_local", 1348 | ] 1349 | 1350 | [[package]] 1351 | name = "regex-syntax" 1352 | version = "0.6.21" 1353 | source = "registry+https://github.com/rust-lang/crates.io-index" 1354 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 1355 | 1356 | [[package]] 1357 | name = "remove_dir_all" 1358 | version = "0.5.3" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1361 | dependencies = [ 1362 | "winapi 0.3.9", 1363 | ] 1364 | 1365 | [[package]] 1366 | name = "reqwest" 1367 | version = "0.10.10" 1368 | source = "registry+https://github.com/rust-lang/crates.io-index" 1369 | checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" 1370 | dependencies = [ 1371 | "base64", 1372 | "bytes", 1373 | "encoding_rs", 1374 | "futures-core", 1375 | "futures-util", 1376 | "http", 1377 | "http-body", 1378 | "hyper", 1379 | "hyper-tls", 1380 | "ipnet", 1381 | "js-sys", 1382 | "lazy_static", 1383 | "log", 1384 | "mime", 1385 | "mime_guess", 1386 | "native-tls", 1387 | "percent-encoding", 1388 | "pin-project-lite 0.2.0", 1389 | "serde", 1390 | "serde_json", 1391 | "serde_urlencoded", 1392 | "tokio", 1393 | "tokio-tls", 1394 | "url", 1395 | "wasm-bindgen", 1396 | "wasm-bindgen-futures", 1397 | "web-sys", 1398 | "winreg 0.7.0", 1399 | ] 1400 | 1401 | [[package]] 1402 | name = "resolv-conf" 1403 | version = "0.7.0" 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" 1405 | checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" 1406 | dependencies = [ 1407 | "hostname", 1408 | "quick-error", 1409 | ] 1410 | 1411 | [[package]] 1412 | name = "rust-actix" 1413 | version = "0.1.0" 1414 | dependencies = [ 1415 | "actix-web", 1416 | "reqwest", 1417 | "serde", 1418 | "serde_json", 1419 | "url", 1420 | ] 1421 | 1422 | [[package]] 1423 | name = "rustc-demangle" 1424 | version = "0.1.18" 1425 | source = "registry+https://github.com/rust-lang/crates.io-index" 1426 | checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" 1427 | 1428 | [[package]] 1429 | name = "rustc_version" 1430 | version = "0.2.3" 1431 | source = "registry+https://github.com/rust-lang/crates.io-index" 1432 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 1433 | dependencies = [ 1434 | "semver", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "ryu" 1439 | version = "1.0.5" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1442 | 1443 | [[package]] 1444 | name = "schannel" 1445 | version = "0.1.19" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1448 | dependencies = [ 1449 | "lazy_static", 1450 | "winapi 0.3.9", 1451 | ] 1452 | 1453 | [[package]] 1454 | name = "scopeguard" 1455 | version = "1.1.0" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1458 | 1459 | [[package]] 1460 | name = "security-framework" 1461 | version = "2.0.0" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" 1464 | dependencies = [ 1465 | "bitflags", 1466 | "core-foundation", 1467 | "core-foundation-sys", 1468 | "libc", 1469 | "security-framework-sys", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "security-framework-sys" 1474 | version = "2.0.0" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" 1477 | dependencies = [ 1478 | "core-foundation-sys", 1479 | "libc", 1480 | ] 1481 | 1482 | [[package]] 1483 | name = "semver" 1484 | version = "0.9.0" 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" 1486 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1487 | dependencies = [ 1488 | "semver-parser", 1489 | ] 1490 | 1491 | [[package]] 1492 | name = "semver-parser" 1493 | version = "0.7.0" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1496 | 1497 | [[package]] 1498 | name = "serde" 1499 | version = "1.0.118" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 1502 | dependencies = [ 1503 | "serde_derive", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "serde_derive" 1508 | version = "1.0.118" 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" 1510 | checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 1511 | dependencies = [ 1512 | "proc-macro2", 1513 | "quote", 1514 | "syn", 1515 | ] 1516 | 1517 | [[package]] 1518 | name = "serde_json" 1519 | version = "1.0.60" 1520 | source = "registry+https://github.com/rust-lang/crates.io-index" 1521 | checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" 1522 | dependencies = [ 1523 | "itoa", 1524 | "ryu", 1525 | "serde", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "serde_urlencoded" 1530 | version = "0.7.0" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 1533 | dependencies = [ 1534 | "form_urlencoded", 1535 | "itoa", 1536 | "ryu", 1537 | "serde", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "sha-1" 1542 | version = "0.9.2" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" 1545 | dependencies = [ 1546 | "block-buffer", 1547 | "cfg-if 1.0.0", 1548 | "cpuid-bool", 1549 | "digest", 1550 | "opaque-debug", 1551 | ] 1552 | 1553 | [[package]] 1554 | name = "sha1" 1555 | version = "0.6.0" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" 1558 | 1559 | [[package]] 1560 | name = "signal-hook-registry" 1561 | version = "1.3.0" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 1564 | dependencies = [ 1565 | "libc", 1566 | ] 1567 | 1568 | [[package]] 1569 | name = "slab" 1570 | version = "0.4.2" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1573 | 1574 | [[package]] 1575 | name = "smallvec" 1576 | version = "1.5.1" 1577 | source = "registry+https://github.com/rust-lang/crates.io-index" 1578 | checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" 1579 | 1580 | [[package]] 1581 | name = "socket2" 1582 | version = "0.3.19" 1583 | source = "registry+https://github.com/rust-lang/crates.io-index" 1584 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 1585 | dependencies = [ 1586 | "cfg-if 1.0.0", 1587 | "libc", 1588 | "winapi 0.3.9", 1589 | ] 1590 | 1591 | [[package]] 1592 | name = "standback" 1593 | version = "0.2.13" 1594 | source = "registry+https://github.com/rust-lang/crates.io-index" 1595 | checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" 1596 | dependencies = [ 1597 | "version_check", 1598 | ] 1599 | 1600 | [[package]] 1601 | name = "stdweb" 1602 | version = "0.4.20" 1603 | source = "registry+https://github.com/rust-lang/crates.io-index" 1604 | checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" 1605 | dependencies = [ 1606 | "discard", 1607 | "rustc_version", 1608 | "stdweb-derive", 1609 | "stdweb-internal-macros", 1610 | "stdweb-internal-runtime", 1611 | "wasm-bindgen", 1612 | ] 1613 | 1614 | [[package]] 1615 | name = "stdweb-derive" 1616 | version = "0.5.3" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" 1619 | dependencies = [ 1620 | "proc-macro2", 1621 | "quote", 1622 | "serde", 1623 | "serde_derive", 1624 | "syn", 1625 | ] 1626 | 1627 | [[package]] 1628 | name = "stdweb-internal-macros" 1629 | version = "0.2.9" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" 1632 | dependencies = [ 1633 | "base-x", 1634 | "proc-macro2", 1635 | "quote", 1636 | "serde", 1637 | "serde_derive", 1638 | "serde_json", 1639 | "sha1", 1640 | "syn", 1641 | ] 1642 | 1643 | [[package]] 1644 | name = "stdweb-internal-runtime" 1645 | version = "0.1.5" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 1648 | 1649 | [[package]] 1650 | name = "syn" 1651 | version = "1.0.56" 1652 | source = "registry+https://github.com/rust-lang/crates.io-index" 1653 | checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" 1654 | dependencies = [ 1655 | "proc-macro2", 1656 | "quote", 1657 | "unicode-xid", 1658 | ] 1659 | 1660 | [[package]] 1661 | name = "tempfile" 1662 | version = "3.1.0" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1665 | dependencies = [ 1666 | "cfg-if 0.1.10", 1667 | "libc", 1668 | "rand", 1669 | "redox_syscall", 1670 | "remove_dir_all", 1671 | "winapi 0.3.9", 1672 | ] 1673 | 1674 | [[package]] 1675 | name = "thiserror" 1676 | version = "1.0.22" 1677 | source = "registry+https://github.com/rust-lang/crates.io-index" 1678 | checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" 1679 | dependencies = [ 1680 | "thiserror-impl", 1681 | ] 1682 | 1683 | [[package]] 1684 | name = "thiserror-impl" 1685 | version = "1.0.22" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" 1688 | dependencies = [ 1689 | "proc-macro2", 1690 | "quote", 1691 | "syn", 1692 | ] 1693 | 1694 | [[package]] 1695 | name = "thread_local" 1696 | version = "1.0.1" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1699 | dependencies = [ 1700 | "lazy_static", 1701 | ] 1702 | 1703 | [[package]] 1704 | name = "threadpool" 1705 | version = "1.8.1" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 1708 | dependencies = [ 1709 | "num_cpus", 1710 | ] 1711 | 1712 | [[package]] 1713 | name = "time" 1714 | version = "0.2.23" 1715 | source = "registry+https://github.com/rust-lang/crates.io-index" 1716 | checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" 1717 | dependencies = [ 1718 | "const_fn", 1719 | "libc", 1720 | "standback", 1721 | "stdweb", 1722 | "time-macros", 1723 | "version_check", 1724 | "winapi 0.3.9", 1725 | ] 1726 | 1727 | [[package]] 1728 | name = "time-macros" 1729 | version = "0.1.1" 1730 | source = "registry+https://github.com/rust-lang/crates.io-index" 1731 | checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" 1732 | dependencies = [ 1733 | "proc-macro-hack", 1734 | "time-macros-impl", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "time-macros-impl" 1739 | version = "0.1.1" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" 1742 | dependencies = [ 1743 | "proc-macro-hack", 1744 | "proc-macro2", 1745 | "quote", 1746 | "standback", 1747 | "syn", 1748 | ] 1749 | 1750 | [[package]] 1751 | name = "tinyvec" 1752 | version = "1.1.0" 1753 | source = "registry+https://github.com/rust-lang/crates.io-index" 1754 | checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" 1755 | dependencies = [ 1756 | "tinyvec_macros", 1757 | ] 1758 | 1759 | [[package]] 1760 | name = "tinyvec_macros" 1761 | version = "0.1.0" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1764 | 1765 | [[package]] 1766 | name = "tokio" 1767 | version = "0.2.24" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" 1770 | dependencies = [ 1771 | "bytes", 1772 | "fnv", 1773 | "futures-core", 1774 | "iovec", 1775 | "lazy_static", 1776 | "libc", 1777 | "memchr", 1778 | "mio", 1779 | "mio-uds", 1780 | "pin-project-lite 0.1.11", 1781 | "signal-hook-registry", 1782 | "slab", 1783 | "winapi 0.3.9", 1784 | ] 1785 | 1786 | [[package]] 1787 | name = "tokio-tls" 1788 | version = "0.3.1" 1789 | source = "registry+https://github.com/rust-lang/crates.io-index" 1790 | checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" 1791 | dependencies = [ 1792 | "native-tls", 1793 | "tokio", 1794 | ] 1795 | 1796 | [[package]] 1797 | name = "tokio-util" 1798 | version = "0.3.1" 1799 | source = "registry+https://github.com/rust-lang/crates.io-index" 1800 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1801 | dependencies = [ 1802 | "bytes", 1803 | "futures-core", 1804 | "futures-sink", 1805 | "log", 1806 | "pin-project-lite 0.1.11", 1807 | "tokio", 1808 | ] 1809 | 1810 | [[package]] 1811 | name = "tower-service" 1812 | version = "0.3.0" 1813 | source = "registry+https://github.com/rust-lang/crates.io-index" 1814 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 1815 | 1816 | [[package]] 1817 | name = "tracing" 1818 | version = "0.1.22" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" 1821 | dependencies = [ 1822 | "cfg-if 1.0.0", 1823 | "log", 1824 | "pin-project-lite 0.2.0", 1825 | "tracing-core", 1826 | ] 1827 | 1828 | [[package]] 1829 | name = "tracing-core" 1830 | version = "0.1.17" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" 1833 | dependencies = [ 1834 | "lazy_static", 1835 | ] 1836 | 1837 | [[package]] 1838 | name = "tracing-futures" 1839 | version = "0.2.4" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" 1842 | dependencies = [ 1843 | "pin-project 0.4.27", 1844 | "tracing", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "trust-dns-proto" 1849 | version = "0.19.6" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "53861fcb288a166aae4c508ae558ed18b53838db728d4d310aad08270a7d4c2b" 1852 | dependencies = [ 1853 | "async-trait", 1854 | "backtrace", 1855 | "enum-as-inner", 1856 | "futures", 1857 | "idna", 1858 | "lazy_static", 1859 | "log", 1860 | "rand", 1861 | "smallvec", 1862 | "thiserror", 1863 | "tokio", 1864 | "url", 1865 | ] 1866 | 1867 | [[package]] 1868 | name = "trust-dns-resolver" 1869 | version = "0.19.6" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "6759e8efc40465547b0dfce9500d733c65f969a4cbbfbe3ccf68daaa46ef179e" 1872 | dependencies = [ 1873 | "backtrace", 1874 | "cfg-if 0.1.10", 1875 | "futures", 1876 | "ipconfig", 1877 | "lazy_static", 1878 | "log", 1879 | "lru-cache", 1880 | "resolv-conf", 1881 | "smallvec", 1882 | "thiserror", 1883 | "tokio", 1884 | "trust-dns-proto", 1885 | ] 1886 | 1887 | [[package]] 1888 | name = "try-lock" 1889 | version = "0.2.3" 1890 | source = "registry+https://github.com/rust-lang/crates.io-index" 1891 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1892 | 1893 | [[package]] 1894 | name = "typenum" 1895 | version = "1.12.0" 1896 | source = "registry+https://github.com/rust-lang/crates.io-index" 1897 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 1898 | 1899 | [[package]] 1900 | name = "unicase" 1901 | version = "2.6.0" 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" 1903 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1904 | dependencies = [ 1905 | "version_check", 1906 | ] 1907 | 1908 | [[package]] 1909 | name = "unicode-bidi" 1910 | version = "0.3.4" 1911 | source = "registry+https://github.com/rust-lang/crates.io-index" 1912 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1913 | dependencies = [ 1914 | "matches", 1915 | ] 1916 | 1917 | [[package]] 1918 | name = "unicode-normalization" 1919 | version = "0.1.16" 1920 | source = "registry+https://github.com/rust-lang/crates.io-index" 1921 | checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" 1922 | dependencies = [ 1923 | "tinyvec", 1924 | ] 1925 | 1926 | [[package]] 1927 | name = "unicode-segmentation" 1928 | version = "1.7.1" 1929 | source = "registry+https://github.com/rust-lang/crates.io-index" 1930 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 1931 | 1932 | [[package]] 1933 | name = "unicode-xid" 1934 | version = "0.2.1" 1935 | source = "registry+https://github.com/rust-lang/crates.io-index" 1936 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1937 | 1938 | [[package]] 1939 | name = "url" 1940 | version = "2.2.0" 1941 | source = "registry+https://github.com/rust-lang/crates.io-index" 1942 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" 1943 | dependencies = [ 1944 | "form_urlencoded", 1945 | "idna", 1946 | "matches", 1947 | "percent-encoding", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "vcpkg" 1952 | version = "0.2.11" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" 1955 | 1956 | [[package]] 1957 | name = "version_check" 1958 | version = "0.9.2" 1959 | source = "registry+https://github.com/rust-lang/crates.io-index" 1960 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1961 | 1962 | [[package]] 1963 | name = "want" 1964 | version = "0.3.0" 1965 | source = "registry+https://github.com/rust-lang/crates.io-index" 1966 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1967 | dependencies = [ 1968 | "log", 1969 | "try-lock", 1970 | ] 1971 | 1972 | [[package]] 1973 | name = "wasi" 1974 | version = "0.9.0+wasi-snapshot-preview1" 1975 | source = "registry+https://github.com/rust-lang/crates.io-index" 1976 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1977 | 1978 | [[package]] 1979 | name = "wasm-bindgen" 1980 | version = "0.2.69" 1981 | source = "registry+https://github.com/rust-lang/crates.io-index" 1982 | checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" 1983 | dependencies = [ 1984 | "cfg-if 1.0.0", 1985 | "serde", 1986 | "serde_json", 1987 | "wasm-bindgen-macro", 1988 | ] 1989 | 1990 | [[package]] 1991 | name = "wasm-bindgen-backend" 1992 | version = "0.2.69" 1993 | source = "registry+https://github.com/rust-lang/crates.io-index" 1994 | checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" 1995 | dependencies = [ 1996 | "bumpalo", 1997 | "lazy_static", 1998 | "log", 1999 | "proc-macro2", 2000 | "quote", 2001 | "syn", 2002 | "wasm-bindgen-shared", 2003 | ] 2004 | 2005 | [[package]] 2006 | name = "wasm-bindgen-futures" 2007 | version = "0.4.19" 2008 | source = "registry+https://github.com/rust-lang/crates.io-index" 2009 | checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" 2010 | dependencies = [ 2011 | "cfg-if 1.0.0", 2012 | "js-sys", 2013 | "wasm-bindgen", 2014 | "web-sys", 2015 | ] 2016 | 2017 | [[package]] 2018 | name = "wasm-bindgen-macro" 2019 | version = "0.2.69" 2020 | source = "registry+https://github.com/rust-lang/crates.io-index" 2021 | checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" 2022 | dependencies = [ 2023 | "quote", 2024 | "wasm-bindgen-macro-support", 2025 | ] 2026 | 2027 | [[package]] 2028 | name = "wasm-bindgen-macro-support" 2029 | version = "0.2.69" 2030 | source = "registry+https://github.com/rust-lang/crates.io-index" 2031 | checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" 2032 | dependencies = [ 2033 | "proc-macro2", 2034 | "quote", 2035 | "syn", 2036 | "wasm-bindgen-backend", 2037 | "wasm-bindgen-shared", 2038 | ] 2039 | 2040 | [[package]] 2041 | name = "wasm-bindgen-shared" 2042 | version = "0.2.69" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" 2045 | 2046 | [[package]] 2047 | name = "web-sys" 2048 | version = "0.3.46" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" 2051 | dependencies = [ 2052 | "js-sys", 2053 | "wasm-bindgen", 2054 | ] 2055 | 2056 | [[package]] 2057 | name = "widestring" 2058 | version = "0.4.3" 2059 | source = "registry+https://github.com/rust-lang/crates.io-index" 2060 | checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" 2061 | 2062 | [[package]] 2063 | name = "winapi" 2064 | version = "0.2.8" 2065 | source = "registry+https://github.com/rust-lang/crates.io-index" 2066 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 2067 | 2068 | [[package]] 2069 | name = "winapi" 2070 | version = "0.3.9" 2071 | source = "registry+https://github.com/rust-lang/crates.io-index" 2072 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2073 | dependencies = [ 2074 | "winapi-i686-pc-windows-gnu", 2075 | "winapi-x86_64-pc-windows-gnu", 2076 | ] 2077 | 2078 | [[package]] 2079 | name = "winapi-build" 2080 | version = "0.1.1" 2081 | source = "registry+https://github.com/rust-lang/crates.io-index" 2082 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 2083 | 2084 | [[package]] 2085 | name = "winapi-i686-pc-windows-gnu" 2086 | version = "0.4.0" 2087 | source = "registry+https://github.com/rust-lang/crates.io-index" 2088 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2089 | 2090 | [[package]] 2091 | name = "winapi-x86_64-pc-windows-gnu" 2092 | version = "0.4.0" 2093 | source = "registry+https://github.com/rust-lang/crates.io-index" 2094 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2095 | 2096 | [[package]] 2097 | name = "winreg" 2098 | version = "0.6.2" 2099 | source = "registry+https://github.com/rust-lang/crates.io-index" 2100 | checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" 2101 | dependencies = [ 2102 | "winapi 0.3.9", 2103 | ] 2104 | 2105 | [[package]] 2106 | name = "winreg" 2107 | version = "0.7.0" 2108 | source = "registry+https://github.com/rust-lang/crates.io-index" 2109 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 2110 | dependencies = [ 2111 | "winapi 0.3.9", 2112 | ] 2113 | 2114 | [[package]] 2115 | name = "ws2_32-sys" 2116 | version = "0.2.1" 2117 | source = "registry+https://github.com/rust-lang/crates.io-index" 2118 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2119 | dependencies = [ 2120 | "winapi 0.2.8", 2121 | "winapi-build", 2122 | ] 2123 | --------------------------------------------------------------------------------