Giter Site home page Giter Site logo

dunglas / vulcain Goto Github PK

View Code? Open in Web Editor NEW
3.5K 55.0 104.0 3.46 MB

Fast and idiomatic client-driven REST APIs.

Home Page: https://vulcain.rocks

License: GNU Affero General Public License v3.0

Dockerfile 0.21% Go 83.02% PHP 14.09% HTML 0.12% JavaScript 1.22% Shell 1.33%
api http2 server-push rest-api rest graphql reverse-proxy vulcain vulcain-server hacktoberfest

vulcain's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vulcain's Issues

Regression since 0.4.1

Hello,

I upgraded Caddy on my staging env yesterday (using xcaddy --with github.com/dunglas/vulcain/caddy) and observed that responses from unchanged requests are now broken.

Request (basically, fetch + preload all IRIs of an Hydra Collection):

GET /api/books
fields: "@type", "/hydra:member/*/@id", "/hydra:totalItems"
preload: "/hydra:member/*/@id"

Produced (using v0.0.0-20220906084821-705c1113298e built on Sept 27, 2022):

{
  "hydra:member": [
    {
      "@id": "\/api\/books\/01FT5VZY5HPGXGTJFMERMZ8E01"
    }
  ],
  "@type": "hydra:Collection",
  "hydra:totalItems": 1
}

Now(v0.4.1):

{
  "hydra:member": [
    {}
  ],
  "hydra:totalItems": 1
}

Did something changed I'm not aware of? I couldn't find any info in the changelog nor in the documentation.
At first glance it just looks like Vulcain doesn't like @ properties anymore.

Thanks,
Ben

Stateless authorization on preload requests

Hello,

I could not find any documentation about stateless authorization, for example when using the lexik-jwt-authentication-bundle in combination with API-platform. We are getting a 401 Unauthorized on the preload responses, is this expected behaviour or not?

My code:

const bookResp = await fetch("/api/books", {
        "headers": {
            "accept": "application/ld+json",
            "accept-language": "en,nl;q=0.9",
            "authorization": "Bearer " + token,
            "preload": "\"/hydra:member/*/author\"",
            "fields": "\"/hydra:member/*/author/name\"",
        },
        "referrer": "https://localhost/api",
        "referrerPolicy": "strict-origin-when-cross-origin",
        "method": "GET",
        "credentials": "include"
    });
    const bookJSON = await bookResp.json();
    console.log(bookJSON)

    for (const book of bookJSON['hydra:member']) {
        const author = await fetch(book.author, {
            "headers": {
                "accept": "application/ld+json",
                "authorization": "Bearer " + token,
            },
            "method": "GET",
            "referrer": "https://localhost/api",
            "referrerPolicy": "strict-origin-when-cross-origin",
            "method": "GET",
            "credentials": "include"
        });
        console.log(await author.json());
    }

Improve memory usage by using a streaming JSON parser and encoder

Currently, we have to wait for the response from the upstream server to be entirely sent, the to load it entirely in memory to analyze and modify the received JSON document.
Analyzing and modifying the JSON stream on the go could improve the memory usage (but probably not the overall performance because we need to compute and to send the Content-Length HTTP header before the JSON body anyway).

This isn't an easy pick because gjson doesn't support JSON streams for now (tidwall/gjson#25, tidwall/gjson#78). We'll have to use json.Decoder directly or to create a new library for this.

Mistagged caddy module?

Hello,

I just built and pulled Caddy with Mercure & Vulcain, with your new, ecofriendly technic :

ADD --chmod=500 https://caddyserver.com/api/download?os=linux&arch=${TARGETARCH}&p=github.com/dunglas/mercure/caddy&p=github.com/dunglas/vulcain/caddy /usr/bin/caddy

Then I checked in my container which version of everything I have, but here's my result:

/app # caddy version
v2.7.5 h1:HoysvZkLcN2xJExEepaFHK92Qgs7xAiCFydN5x5Hs6Q=
/app # caddy list-modules
[... List of standard modules ...]
  Standard modules: 106

http.handlers.mercure v0.15.3
http.handlers.vulcain v0.5.0

  Non-standard modules: 2

So I'm wondering, is the module's version mistagged? Do do I don't download the good version of it? Or this link (p=github.com/dunglas/vulcain/caddy) is not good?

Cannot tag Vulcain in Stackoverflow

Just to mention, as stated in /doc/hepl.md, requiring help should be done through Stackoverflow with the use of vulcain tag. Sadly, this tag has never been created there and only users with 15k+ reputation can.
I don't have enough reputation to tag my questions with it. Could you please create it?

Flaky test

Enabling the race detector in tests sometimes breaks the test suite:

Run go test -race -covermode atomic -coverprofile=cover.out github.com/dunglas/vulcain
2023-08-03T13:24:26.605Z	INFO	vulcain/server.go:155	vulcain started	{"protocol": "https", "addr": "127.0.0.1:4343"}
127.0.0.1 - - [03/Aug/2023:13:24:26 +0000] "GET /forwarded HTTP/2.0" 200 84 "" "Go-http-client/2.0"
2023-08-03T13:24:27.725Z	INFO	vulcain/server.go:155	vulcain started	{"protocol": "https", "addr": "127.0.0.1:4343"}
2023-08-03T13:24:27.759Z	DEBUG	vulcain/vulcain.go:244	link preload header added	{"relation": "/books/1.jsonld?preload=%22%2Fauthor%22"}
2023-08-03T13:24:27.759Z	DEBUG	vulcain/vulcain.go:288	failed to push	{"node": "/hydra:member/*", "relation": "/books/1.jsonld?preload=%22%2Fauthor%22", "error": "feature not supported"}
2023-08-03T13:24:27.759Z	DEBUG	vulcain/vulcain.go:244	link preload header added	{"relation": "/books/2.jsonld?preload=%22%2Fauthor%22"}
2023-08-03T13:24:27.759Z	DEBUG	vulcain/vulcain.go:288	failed to push	{"node": "/hydra:member/*", "relation": "/books/2.jsonld?preload=%22%2Fauthor%22", "error": "feature not supported"}
127.0.0.1 - - [03/Aug/2023:13:24:27 +0000] "GET /books.jsonld?fields=\"/hydra:member/*\"&preload=\"/hydra:member/*/author\" HTTP/2.0" 200 102 "" "Go-http-client/2.0"
2023-08-03T13:24:28.811Z	INFO	vulcain/server.go:155	vulcain started	{"protocol": "https", "addr": "127.0.0.1:4343"}
2023-08-03T13:24:28.839Z	DEBUG	vulcain/vulcain.go:244	link preload header added	{"relation": "/authors/1.jsonld"}
2023-08-03T13:24:28.839Z	DEBUG	vulcain/vulcain.go:288	failed to push	{"node": "/author", "relation": "/authors/1.jsonld", "error": "feature not supported"}
2023-08-03T13:24:28.839Z	DEBUG	vulcain/vulcain.go:244	link preload header added	{"relation": "/books/99.jsonld"}
2023-08-03T13:24:28.840Z	DEBUG	vulcain/vulcain.go:288	failed to push	{"node": "/related", "relation": "/books/99.jsonld", "error": "feature not supported"}
127.0.0.1 - - [03/Aug/2023:13:24:28 +0000] "GET /books/1.jsonld HTTP/2.0" 200 59 "" "Go-http-client/2.0"
2023-08-03T13:24:29.906Z	INFO	vulcain/server.go:155	vulcain started	{"protocol": "https", "addr": "127.0.0.1:4343"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books.jsonld?fields=%22/hydra:member/*/author%22 HTTP/2.0" 200 100 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/1.jsonld?fields=%22%2Fauthor%22 HTTP/2.0" 200 30 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.274Z	DEBUG	vulcain/pusher.go:134	pusher not found	{"url": "/books.jsonld", "explicitRequestID": "attack"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books.jsonld HTTP/2.0" 200 54 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/1.jsonld HTTP/2.0" 200 30 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.473Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/1.jsonld?preload=%22%2Fauthor%22"}
2023-08-03T13:24:30.474Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/2.jsonld?preload=%22%2Fauthor%22"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/2.jsonld?preload=%22%2Fauthor%22 HTTP/2.0" 200 145 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.476Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/authors/1.jsonld"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/1.jsonld?preload=%22%2Fauthor%22 HTTP/2.0" 200 145 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books.jsonld?preload=%22/hydra:member/*/author%22 HTTP/2.0" 200 256 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /authors/1.jsonld HTTP/2.0" 200 51 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.672Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/1.jsonld"}
2023-08-03T13:24:30.673Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/2.jsonld"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/2.jsonld HTTP/2.0" 200 145 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.674Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/authors/1.jsonld"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/1.jsonld HTTP/2.0" 200 145 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /authors/1.jsonld HTTP/2.0" 200 51 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books.jsonld HTTP/2.0" 200 208 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.861Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/1.jsonld?fields=%22%2Fauthor%22&preload=%22%2Fauthor%22"}
2023-08-03T13:24:30.861Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/2.jsonld?fields=%22%2Fauthor%22&preload=%22%2Fauthor%22"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/2.jsonld?fields=%22%2Fauthor%22&preload=%22%2Fauthor%22 HTTP/2.0" 200 30 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:30.862Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/authors/1.jsonld"}
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/1.jsonld?fields=%22%2Fauthor%22&preload=%22%2Fauthor%22 HTTP/2.0" 200 30 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /authors/1.jsonld HTTP/2.0" 200 51 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books.jsonld?fields=%22/hydra:member/*/author%22&preload=%22/hydra:member/*/author%22 HTTP/2.0" 200 158 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:30 +0000] "GET /books/1.jsonld?fields=%22%2Fauthor%22 HTTP/2.0" 200 30 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:31.055Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/1.jsonld"}
2023-08-03T13:24:31.055Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/books/2.jsonld"}
  net/http.(*http2serverConn).startPush.func1.1()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:6889 +0x64

Goroutine 310 (running) created at:
  net/http.(*http2serverConn).processHeaders()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:5762 +0xdf5
  net/http.(*http2serverConn).processFrame()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:5262 +0x45e
  net/http.(*http2serverConn).processFrameFromReader()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:5205 +0x2ed
  net/http.(*http2serverConn).serve()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4694 +0x15d5
  net/http.(*http2Server).ServeConn()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4270 +0x1804
  net/http.http2ConfigureServer.func1()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4060 +0x124
  net/http.(*conn).serve()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/server.go:1903 +0x1c41
  net/http.(*Server).Serve.func3()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/server.go:3089 +0x58

Goroutine 317 (running) created at:
  net/http.(*http2serverConn).startPush.func1()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:6889 +0x8d5
  net/http.(*http2serverConn).startFrameWrite()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4984 +0x219
  net/http.(*http2serverConn).scheduleFrameWrite()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:5102 +0x3c8
  net/http.(*http2serverConn).writeFrame()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4955 +0x3c7
  net/http.(*http2serverConn).startPush()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:6893 +0x491
  net/http.(*http2serverConn).serve()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4725 +0x12a9
  net/http.(*http2Server).ServeConn()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4270 +0x1804
  net/http.http2ConfigureServer.func1()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/h2_bundle.go:4060 +0x124
  net/http.(*conn).serve()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/server.go:1903 +0x1c41
  net/http.(*Server).Serve.func3()
      /opt/hostedtoolcache/go/1.20.6/x64/src/net/http/server.go:3089 +0x58
==================
2023-08-03T13:24:31.252Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/authors/1.jsonld"}
2023-08-03T13:24:31.254Z	DEBUG	vulcain/vulcain.go:244	link preload header added	{"relation": "/books/2.jsonld"}
127.0.0.1 - - [03/Aug/2023:13:24:31 +0000] "GET /books/1.jsonld HTTP/2.0" 200 145 "" "Symfony HttpClient/Amp"
2023-08-03T13:24:31.254Z	DEBUG	vulcain/vulcain.go:288	failed to push	{"node": "/hydra:member/*", "relation": "/books/2.jsonld", "error": "Maximum allowed pushes (2) reached"}
127.0.0.1 - - [03/Aug/2023:13:24:31 +0000] "GET /authors/1.jsonld HTTP/2.0" 200 51 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:31 +0000] "GET /books.jsonld HTTP/2.0" 200 208 "" "Symfony HttpClient/Amp"
--- FAIL: TestH2PushLimit (0.20s)
    testing.go:1446: race detected during execution of test
2023-08-03T13:24:31.276Z	INFO	vulcain/server.go:155	vulcain started	{"protocol": "https", "addr": "127.0.0.1:4343"}
2023-08-03T13:24:31.458Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/oa/books/1"}
2023-08-03T13:24:31.459Z	DEBUG	vulcain/vulcain.go:293	relation pushed	{"relation": "/oa/books/2"}
127.0.0.1 - - [03/Aug/2023:13:24:31 +0000] "GET /oa/books/1 HTTP/2.0" 200 77 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:31 +0000] "GET /oa/books/2 HTTP/2.0" 200 77 "" "Symfony HttpClient/Amp"
127.0.0.1 - - [03/Aug/2023:13:24:31 +0000] "GET /oa/books.json HTTP/2.0" 200 28 "" "Symfony HttpClient/Amp"
{"level":"error","ts":1691069071.5171943,"caller":"vulcain/server.go:89","msg":"http: proxy error","error":"dial tcp: lookup test.invalid on 127.0.0.53:53: no such host","stacktrace":"github.com/dunglas/vulcain.(*server).ServeHTTP.func3\n\t/home/runner/work/vulcain/vulcain/server.go:89\nnet/http/httputil.(*ReverseProxy).ServeHTTP\n\t/opt/hostedtoolcache/go/1.20.6/x64/src/net/http/httputil/reverseproxy.go:475\ngithub.com/dunglas/vulcain.(*server).ServeHTTP\n\t/home/runner/work/vulcain/vulcain/server.go:102\nnet/http.serverHandler.ServeHTTP\n\t/opt/hostedtoolcache/go/1.20.6/x64/src/net/http/server.go:2936\nnet/http.(*conn).serve\n\t/opt/hostedtoolcache/go/1.20.6/x64/src/net/http/server.go:1995"}
FAIL
	github.com/dunglas/vulcain	coverage: 93.5% of statements
FAIL	github.com/dunglas/vulcain	4.980s

Passing query params to sub-resources

There is a mention about using GraphQL as a query language for Vulcain here : https://github.com/dunglas/vulcain/blob/master/docs/graphql.md#using-graphql-as-query-language-for-vulcain

I've been playing on a toyproject about Vulcain and I'm wondering if all GraphQL queries can be converted to a HTTP Request using Vulcain, and especially, deep arguments like the following :

Given this query

{
  persons(orderBy: name_ASC) {
    id
    name,
    starships(type: destroyer) {
      name,
      weapons(category: nuclear) {
        name
      }
    }
  }
}

I believe it may be translated to the following requests :

GET /persons?orderBy=name
Preload: /*/starships/*/weapons

[
  {
    "id": "/persons/1",
    "name": "Person 1",
    "starships": "/starships?person=/persons/1&type=destroyer"
  } 
]
GET /starships?person=/persons/1&type=destroyer

[
  {
    "id": "/starships/1",
    "name": "Starship 1",
    "weapons": "/weapons?starship=/startships/1&category=nuclear"
  }
]
GET /weapons?starship=/startships/1&category=nuclear

[
  {
    "id": "/weapons/1",
    "name": "Weapon 1",
  }
]

Now It's easy to change weapons(category: nuclear) to weapons(category: explosives) in GraphQL, but I'm not sure how to do it with Vulcain. Do you have any insight about this problem?

Clear up inconsistency in documentation of production-readiness of the Gateway Server

While reading the documentation, I stumbled on this inconsistency in the documentation.

On the readme.md it says:

A reference, production-grade, implementation gateway server is also available in this repository.

While on the linked page (Install The Gateway Server), it says about the Gateway server:

The Gateway Server is still experimental

So what is it? Either it is "experimental" or it is "production-grade". So one of these wordings should be changed.

Or it is somehow both, but some clarification on "why" would be in order in that case.

Preload only working on localhost

When making API calls from http://localhost:19006 push seems to be working fine and it loads 400 requests in a few seconds however when I try via 192.168.1.181:19006 or on my iOS app making fetch requests it seems to be failing and loading them normally which takes over 30s.
success
failed

I'm using the API Platform Docker for the backend. Any help would be appreciated

CORS

How to configure CORS headers?

Gateway Server Setup with Docker-Compose

Hi,

Just a quick question. I am struggling to get my nginx+ssl configuration working with vulcain. With the current setup i have, see below, i receive the following vulcain error: (site_url represents my real url)

time="2019-12-04T12:09:47Z" level=error msg="http: proxy error: x509: certificate is valid for site_url, not api"

I have the following Docker-compose.yml:

version: '3.7'

services:
  php:
    container_name: mcapi_php
    build:
      context: ./api
      target: api_platform_php
      dockerfile: prod.Dockerfile
      args:
        PHP_VERSION: 7.3
        APCU_VERSION: 5.1.18
        VARNISH_VERSION: 6.3
    healthcheck:
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s
    volumes:
      - "./api:/var/www/html"
    restart: unless-stopped

  api:
    container_name: mcapi_nginx
    image: nginx:1.17-alpine
    depends_on:
      - php
#    ports:
#      - target: 80
#        published: 80
#        protocol: tcp
#      - target: 443
#        published: 443
#        protocol: tcp
    volumes:
      - "./api:/var/www/html"
      - "./api/docker/nginx/conf.d/default.prod.conf:/etc/nginx/conf.d/default.conf"
      - "./api/docker/nginx/nginx.conf:/etc/nginx/nginx.conf"
      - "./api/docker/nginx/nginxconfig.io:/etc/nginx/nginxconfig.io"
      - "./api/docker/data/certbot/conf:/etc/letsencrypt"
      - "./api/docker/data/certbot/www:/var/www/certbot"
    restart: unless-stopped
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

  certbot:
    container_name: mcapi_cerbot
    image: certbot/certbot
    volumes:
      - "./api/docker/data/certbot/conf:/etc/letsencrypt"
      - "./api/docker/data/certbot/www:/var/www/certbot"
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

  vulcain:
    container_name: mcapi_vulcain
    image: dunglas/vulcain
    environment:
#      - UPSTREAM=http://cache-proxy
      - UPSTREAM=https://api
      - CERT_FILE=/etc/letsencrypt/live/rsite_url/fullchain.pem
      - KEY_FILE=/etc/letsencrypt/live/site_url/privkey.pem
    depends_on:
#      - cache-proxy
      - api
    volumes:
      - "./api/docker/data/certbot/conf:/etc/letsencrypt"
    ports:
      - target: 443
        published: 443
        protocol: tcp
    restart: unless-stopped

#  cache-proxy:
#    container_name: mcapi_varnish
#    build:
#      context: ./api
#      target: api_platform_varnish
#      dockerfile: prod.Dockerfile
#      args:
#        PHP_VERSION: 7.3
#        APCU_VERSION: 5.1.18
#        VARNISH_VERSION: 6.3
#    depends_on:
#      - api
#    tmpfs:
#      - /usr/local/var/varnish:exec
#    restart: unless-stopped

My guess here, Vulcain requires that nginx be using http only? However, i would prefer to use my nginx ssl setup. Any information would be greatly appreciated.

Support setting accepted media-type on Preload & Fields pushes

I choose to view Vulcain as a possible REST maturity models level 4. So I'm wondering if it should be possible to specify accepted media-type on pushes / preloads via Preload and Fields in order to fit well with already mature REST API's that uses media type for content negotiation.

In all transparency we use that in eZ Platform, and it's a rather powerful concept as it allows integrators to extend certain api endpoints. But unlike graphQL where caller can ask for whatever, in this case someone would need to code alternative representations.

While it would have to be in a format that can work both in the header and query format, something like the following might be a way to do it:

GET /books/ HTTP/2
Preload: /member:application%2Fvnd.acme.externalmember+json/*/author
Fields: /author/familyName

Open questions:

  • Handling of /, here shown encoded using %2F as possible measure to avoid conflicts with path
  • If type should have to be repeated on Fields: or not if already specified. Here suggested to be optional however no strong opinion on that.
  • Separator to use to show start of media type, here shows as :, however that is legal in URI's so maybe it will have to be something else, did not find any MIME's with this tough
  • Should it also support MIME type parameter? Like ;charset=UTF-8

Local error: tls: bad record MAC

Hi,

I am new to Vulcain, will try to explain the issue as good as possible.
At the moment I am facing an issue about TLS, this is what I execute to produce it:

curl 'https://localhost:8443' -X OPTIONS -H 'Sec-Fetch-Mode: no-cors' -H 'Access-Control-Request-Method: POST'     
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Which results in the following log entry:

vulcain_1   | time="2020-04-06T18:51:56Z" level=info msg="Vulcain started" addr= protocol=https
vulcain_1   | 2020/04/06 18:52:04 http: TLS handshake error from 192.160.1.1:33854: local error: tls: bad record MAC

First I only used the image directly but this is impossible to debug so I created my own as follows:

Dockerfile

FROM alpine

COPY --from=dunglas/vulcain /vulcain /
EXPOSE 80 443

COPY entry-point.sh /entry-point
RUN chmod +x /entry-point
RUN ls -la /

WORKDIR /
ENV DEBUG=1

CMD /entry-point

entry-point.sh

#!/bin/sh

# Make sure 'debug' env is set, which prints "1", weirdly enough vulcain reports that the erorr log level is INFO, issue for another day I guess..
echo "CUSTOM ENTRY POINT, env[DEBUG]: $DEBUG"

# Make sure the certs are correctly mounted from dev-tls image (comes with api-platform (some php framework))
ls -la /certs

# Start vulcain with some additional attempts (no GoLang experience..) to enable debugging to get any clue of what is going on...
/vulcain --debug -vvv

This is the startup log:

Attaching to inventory_vulcain_1
vulcain_1   | CUSTOM ENTRY POINT, env[DEBUG]: 1
vulcain_1   | total 20
vulcain_1   | drwxr-xr-x    2 root     root          4096 Mar 28 14:28 .
vulcain_1   | drwxr-xr-x    1 root     root          4096 Apr  6 18:51 ..
vulcain_1   | -rw-r--r--    1 root     root          1484 Mar 28 14:25 localhost.crt
vulcain_1   | -rw-------    1 root     root          1708 Mar 28 14:25 localhost.key
vulcain_1   | -rw-r--r--    1 root     root          3192 Mar 28 14:25 server.pem
vulcain_1   | time="2020-04-06T18:51:56Z" level=info msg="Vulcain started" addr= protocol=https
vulcain_1   | 2020/04/06 18:52:04 http: TLS handshake error from 192.160.1.1:33854: local error: tls: bad record MAC

Some additional info which might be interesting:

$ docker-compose ps
...
inventory_vulcain_1    /bin/sh -c /entry-point          Up             0.0.0.0:8443->443/tcp, 80/tcp              

$ ip addr list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether e0:d5:5e:ad:4d:93 brd ff:ff:ff:ff:ff:ff
    inet 192.168.178.16/24 brd 192.168.178.255 scope global dynamic noprefixroute enp2s0
       valid_lft 2803sec preferred_lft 2803sec
    inet6 2001:1c02:3608:5a00:e2d5:5eff:fead:4d93/128 scope global dynamic noprefixroute 
       valid_lft 43551sec preferred_lft 14751sec
    inet6 2001:1c02:3608:5a00:64f1:c95:8445:18ab/64 scope global temporary dynamic 
       valid_lft 57598sec preferred_lft 28798sec
    inet6 2001:1c02:3608:5a00:a254:3c48:e1f5:eece/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 57598sec preferred_lft 28798sec
    inet6 fe80::f072:b7e:12be:9908/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: wlp3s0: <BROADCAST,MULTICAST> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 30:24:32:dd:2c:aa brd ff:ff:ff:ff:ff:ff
611: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:49:dc:1f:b9 brd ff:ff:ff:ff:ff:ff
    inet 192.160.0.1/24 brd 192.160.0.255 scope global docker0
       valid_lft forever preferred_lft forever
612: br-83afbf5f0e7b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:45:25:5b:0c brd ff:ff:ff:ff:ff:ff
    inet 192.160.1.1/24 brd 192.160.1.255 scope global br-83afbf5f0e7b
       valid_lft forever preferred_lft forever
630: vethe2a0b2f@if629: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-83afbf5f0e7b state UP group default 
    link/ether 3a:b5:1e:ba:0f:94 brd ff:ff:ff:ff:ff:ff link-netnsid 1
632: veth9e8e091@if631: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-83afbf5f0e7b state UP group default 
    link/ether 06:0a:e5:7d:18:af brd ff:ff:ff:ff:ff:ff link-netnsid 2
634: veth387d66d@if633: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-83afbf5f0e7b state UP group default 
    link/ether 3e:ec:72:73:af:1d brd ff:ff:ff:ff:ff:ff link-netnsid 0
636: veth3475f84@if635: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-83afbf5f0e7b state UP group default 
    link/ether 2a:a2:17:d0:8d:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 3
638: veth0e9e1c7@if637: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-83afbf5f0e7b state UP group default 
    link/ether 76:6c:7c:28:65:1f brd ff:ff:ff:ff:ff:ff link-netnsid 4
640: vetha177b13@if639: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-83afbf5f0e7b state UP group default 
    link/ether 82:e9:bc:d1:28:41 brd ff:ff:ff:ff:ff:ff link-netnsid 5

# I have assigned a custom subnet for docker so it doesn't conflict with VPN connections
$ cat /etc/docker/daemon.json                                                                                                                                                                             

{
 "default-address-pools":
 [
 {"base":"192.160.1.0/16","size":24}
 ]
}

Not a networking expert neither vulcain expert so I am not sure what to look for here.
If needed I could create a dockerfile for Golang and install from source and run the code directly and make some modifications to enable debug level logging, will work on that as soon as I have time in case no ideas have been brought up here yet about what could cause this issue.

Thanks in advance.

edit;

This is what is used to generate the certs you see in the directory listing:

# use this self-generated certificate only in dev, IT IS NOT SECURE!


# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG NGINX_VERSION=1.17


FROM nginx:${NGINX_VERSION}-alpine

# persistent / runtime deps
RUN apk add --no-cache \
		nss-tools \
	;

WORKDIR /certs

ARG MKCERT_VERSION=1.4.1
RUN set -eux; \
	wget -O /usr/local/bin/mkcert https://github.com/FiloSottile/mkcert/releases/download/v$MKCERT_VERSION/mkcert-v$MKCERT_VERSION-linux-amd64; \
	chmod +x /usr/local/bin/mkcert; \
	mkcert --cert-file localhost.crt --key-file localhost.key localhost; \
	# the file must be named server.pem - the default certificate path in webpack-dev-server
	cat localhost.key; \
	cat localhost.crt; \
	ls -la /certs; \
	cat localhost.key localhost.crt > server.pem

VOLUME /certs

# add redirect from http://localhost to https://localhost
RUN set -eux; \
	{ \
		echo 'server {'; \
		echo '    return 301 https://$host$request_uri;'; \
		echo '}'; \
	} | tee /etc/nginx/conf.d/default.conf

And these are the relevant parts of the docker-compose file:

api:
    build:
      context: ./api
      target: api_platform_nginx
      <<: *api-cache-from
    image: ${NGINX_IMAGE:-quay.io/api-platform/nginx}
    depends_on:
      - php
    volumes:
      - ./api/public:/srv/api/public:ro

  vulcain:
    #image: dunglas/vulcain
    build: ./docker/vulcain
    environment:
      - CERT_FILE=/certs/localhost.crt
      - KEY_FILE=/certs/localhost.key
      - UPSTREAM=http://api
      - DEBUG=1
    depends_on:
      - api
      - dev-tls
    volumes:
      - dev-certs:/certs:ro
    ports:
      - target: 443
        published: 8443
        protocol: tcp

dev-tls:
    build:
      context: ./docker/dev-tls
    volumes:
      - dev-certs:/certs:rw
    ports:
      - target: 80
        published: 80
        protocol: tcp

volumes:
  dev-certs: {}

edit2;

Also regenerated the ssl certs using the readme from this repo to make sure the problem is not within the dev-tls image that comes with api-platform:

openssl req -x509 -newkey rsa:4096 -keyout localhost.key -out localhost.crt -days 365

# Remove the passphrase, otherwise you get the error from #13 
openssl rsa -in localhost.key -out localhost.key

$ openssl x509 -in localhost.crt -text -noout                                                       
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            70:9c:eb:9f:b8:06:c6:83:66:ba:8a:12:8e:0a:d8:75:72:05:17:37
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = NL, ST = NH, L = Amsterdam, O = Resourcefull, CN = localhost
        Validity
            Not Before: Apr  7 07:59:29 2020 GMT
            Not After : Apr  7 07:59:29 2021 GMT
        Subject: C = NL, ST = NH, L = Amsterdam, O = Resourcefull, CN = localhost

Unfortunately I still see exactly the same error after regenerating the ssl certs, updating docker-composer file to point to custom volume with new certs and rebuild: TLS handshake error from 192.160.1.1:57424: local error: tls: bad record MAC

Aucun preload ne fonctionne

Bonjour,

Je viens de découvrir le protocole et je le trouve super intéressant !
Maintenant j'aimerais le mettre en application, et c'est là que ça se corse.

J'ai une API construite via API Platform, et un front fait avec ReactJS qui vient interroger mon API via la gateway vulcain.
J'ai beau envoyer mon header Preload, je n'arrive pas à faire fonctionner le multiplexing http2. Je dois passer à côté de quelque chose de tout bête... La gateway est accessible sur https://localhost:8443 et qui tape sur mon api qui se trouve sur http://localhost/api
Mon premier appel à GET https://localhost:8443/api/produits renvoie ceci :

[
  {
    "id": 1,
    "cle": 121206,
    "type_cle": 23,
    "label_home_cle": 814,
    "gamme_cle": "737",
    "nb_max": 5,
    "pieces": 2,
    "chambres": 1,
    "sdb": 1,
    "surface": 35,
    "photos": [...],
    "criteres": [...],
    "translations": [...],
    "residence": "/api/residences/1",
    "availabilities": [
      "/api/availabilities/1",
      "/api/availabilities/2",
      "/api/availabilities/3",
      ...
    ],
    "typeCle": 23,
    "labelHomeCle": 814,
    "gammeCle": "737",
    "nbMax": 5
  }
]

J'ai passé en en-tête {Preload: "/api/produits/*/availabilities/*"}
Dans Chrome je vois bien qu'il a envoyé une "Requête préliminaire". Mais quand je boucle sur les "availabilities" pour en récupérer les infos, j'ai autant d'ajax qui son relancés.
Voici le bout de JS en question :

return await axios.get('https://localhost:8443/api/produits', {
            params: apiParams,
            headers: {Preload: "/api/produits/*/availabilities/*"}
        }).then(async results => {
            let i = 0;
            for (const product of results.data) {
                for (const availability of product.availabilities) {
                    await axios.get('https://localhost:8443' + availability);
                    console.log(availability);
                    i++;

                    if (i > 10) return results.data;
                }
            }
            return results.data;
        });

Et voici le contenu de ma timeline chrome :
image

Cela fait plusieurs heures que je patauge sur ce problème, sans comprendre ce qui cloche.
Toute aide serait la bienvenue.
Merci d'avance.

Will 'object' remain a permanent key for targeting nested content ?

Hi Dunglas,

I see examples and I have to say that the object attribute is a little weird for me..

This key name is coming for your conception or from another architecture?

IMHO, I'll prefer to use a key name named like content or value or more specific to Vulca(i)n outbreak or rash.

Thanks for reading !

[Question] Can/should i use Nginx and/or Traefik with Vulcain? Where should be Varnish?

Hello, everybody! Cool idea, cool project!

But i'm not understand how to modify my current scheme to use Vulcain.

Now:

(https, http/2)-> Traefik -(http)-> Nginx -(static or fastcgi)-> php-fpm (ApiPlatform)

  • Traefik is my ingress controller with auto routing to some microservices via docker labels. +http/2 endpoint.
  • Nginx handles static files and routes dynamic to php-fpm.
  1. How it will looks with Vulcain?
  2. Where should be Varnish?

Filtering resources does not work with multiple wildcards

Hi,
I'm experimenting with Vulcain and I'm stuck on the following situation.
I have three calls, simplified:

GET /customers
Response:

[
  {
    "id": 1,
    "name": "Foo",
    "settings": "/customer/1/settings"
  }
]

GET /customer/{id}/settings
Response:

{
  "allowed": false,
  "addresses": "/customer/{id}/settings/addresses"
}

GET /customer/{id}/settings/addresses
Response:

[
    {
      "address": "Street 1",
      "city": "Example city"
    }
  ]

When I try to filter the address resource because I want it to only return the city property, the following is not working:
Fields: /*/settings/addresses/*/city.

When I specify the exact address key (i.e. 0) it does work (Fields: /*/settings/addresses/0/city).

Am I missing something?

[Question] Relations preloaded within first 'main' request, making the gateway server requests very slow

Hi,

First of all, very interesting project!

I'm implementing the Vulcain Gateway server in my application and it's performing slow. To be sure it's not the fault of my application I downloaded and ran the demo and this is also slow at preloading the relations.
It looks like Vulcain is preloading every relation in the main request and slowing down the request when it's calling the 'conferences' endpoint:
Schermafbeelding 2020-03-10 om 15 27 05
This is in contrast to the slide in the Symfonycon slides, where the preloading 'conferences' request takes about the same time (bit more than 4sec) it needs to request the 'conferences' endpoint without preloading:
image

I'm not sure if this is how Vulcain is supposed to work, but if it is I don't really see how Vulcain can be faster than GraphQL for example?

tls: failed to parse private key

After to clone and go build I ran:

mkdir tls
openssl req -x509 -newkey rsa:4096 -keyout tls/key.pem -out tls/cert.pem -days 365
In this command I added the PEM pass phrase

$ docker run -it -v (pwd)/:/app golang:1.13 bash
root@9e73b18f1a60:/app# go build
root@9e73b18f1a60:/app# UPSTREAM='http://172.18.0.2' ADDR=':3000' KEY_FILE='tls/key.pem' CERT_FILE='tls/cert.pem' ./vulcain
INFO[0000] Vulcain started                               addr=":3000" protocol=https
FATA[0000] tls: failed to parse private key

I think is because vulcain does not know the phrase key

ACME_HOSTS on prod env "x509: certificate signed by unknown authority"

Hi,

i'm trying to use Vulcain in my prod stack (no issue in dev with self signed certificate)
So, i'm Docker user and this is my service config in my docker-compose.yml file :

vulcain:
        image: dunglas/vulcain
        environment:
            - ACME_HOSTS=api.example.com
            - UPSTREAM=http://api
        depends_on:
            - api
        deploy:
            labels:
                - traefik.enable=true
                - traefik.tcp.routers.isc-api.rule=HostSNI(`api.example.com`)
                - traefik.tcp.routers.isc-api.entrypoints=websecure
                - traefik.tcp.routers.isc-api.tls=true
                - traefik.tcp.routers.isc-api.tls.passthrough=true
                - traefik.tcp.routers.isc-api.service=service-api-isc
                - traefik.tcp.services.service-api-isc.loadbalancer.server.port=443
        <<: *network

And below, content docker log for Vulcain service:

{"level":"info","ts":1606489658.6425629,"caller":"vulcain/server.go:147","msg":"vulcain started","protocol":"https","addr":""}
2020/11/27 15:09:10 http: TLS handshake error from 10.0.7.11:39992: Get "https://acme-v02.api.letsencrypt.org/directory": x509: certificate signed by unknown authority
2020/11/27 15:09:10 http: TLS handshake error from 10.0.7.11:39990: acme/autocert: missing certificate
2020/11/27 15:09:10 http: TLS handshake error from 10.0.7.11:39996: acme/autocert: missing certificate
2020/11/27 15:09:10 http: TLS handshake error from 10.0.7.11:39998: acme/autocert: missing certificate

For additional informations, i'm using Traefik and Docker Swarm but i don't think that is an impact to this issue

What's wrong with acme config?

Thanks
BenWa

Preload link should include the "crossorigin" attribute, if initial request uses CORS

I've been trying to get Vulcain working in a situation where CORS is necessary. At the moment it seems to me that browsers are blocking preload requests, because the generated link headers do not include the crossorigin attribute (docs).

2021-04-25_17-03

I'd propose adding crossorigin=anonymous, if the Access-Control-Allow-Origin header is present. And crossorigin=use-credentials, if the Access-Control-Allow-Credentials header is true (docs).

However, I'm not entirely sure what should happen, if the related resources are owned by different API-s (e.g. bookapi.com/book/1 is referencing authorapi.com/author/2). In that case the abovementioned could not be implied from the response headers.

Since the Link headers are added by Vulcain, there's no way for the upstream to handle this or is there?

HTTP and HTTPS Vulcain server question

Good day!

The quote from Gateway Server Configuration

If ACME_HOSTS or both CERT_FILE and KEY_FILE are provided, an HTTPS server supporting HTTP/2 connection will be started. If not, an HTTP server will be started (not compatible with HTTP/2 Server Push, and not secure).

From quote above I made assumption that HTTPS and HTTP servers can not work simultaneously.

And here in an example of how to start vulcain using docker Install The Gateway Server/Docker Image

    -e UPSTREAM='http://your-api' -e ACME_HOSTS='example.com' \
    -p 80:80 -p 443:443 \
    dunglas/vulcain

In example above, both - http (80) and https (443) ports are forwarded. Does that mean that both versions of server will run same time?

Unable to decode with compressed response from origin

I was working in a Api Platform app with Caddy behind a IIS server. The IIS server manages HTTP/2 and stream the headers in lowercase to the Caddy server running HTTP/1.1. According to the RFC, the HTTP/2 headers should be case-senstive and with all headers in lowercase, and the HTTP/1.1 should be case-insensitive. The code that extracts the headers only verifies the "Field" header, in a case-sensitive manner, causing that the Fields header can not be readed correctly.
I would try to test a patch, but I don't know Go, so I can't guarranty nothing.

Response's body is percent encoded when parametrized queries

Hello,

When I fetch a resource with a "fields" query parameter, the received fields are percent encoded. But when I vanilla fetch or header-based-fields fetch, the resource's fields are "normal".

Front
scenario A
What I code:

const eventResp = await fetch("https://localhost:8000/events/2?fields=\"\/subtitle\",\"name\"")
console.log(await eventResp.json())

What it outputs:

Object { subtitle: "Internationaux%20de%20France%20de%20tennis%202019", name: "Roland%20Garros%202019" }

ScenarioB
What I code:

const eventResp = await fetch("https://localhost:8000/events/2")
console.log(await eventResp.json())

What it outputs:

Object { "@context": "/contexts/Event", "@id": "/events/2", "@type": "https://schema.org/Event", id: 2, name: "Roland Garros 2019", subtitle: "Internationaux de France de tennis 2019", heading: "120ème édition des Internationaux de France de tennis sont le plus grand tournoi de la saison de tennis sur terre battue.", startDate: "2019-05-26T00:00:00+00:00", endDate: "2019-06-09T00:00:00+00:00", visibility: "public", … }

Back
PHP 6.1 / APIP 3.0.3, served by Caddy 2.7.4 / http.handlers.vulcain v0.5.0

localhost:8000

log

route {
    root * /app/public
    vulcain
    push
    php_fastcgi backend:9000
    encode zstd gzip
    file_server
}

Another proof/test with Postman (see screenshot here)

  1. Is it intended?
  2. What can I do to avoid this behavior?
  3. Do you need more information from my context?

Thank you!

Add an option to limit depth

  • The server SHOULD limit the maximum number of resources to push.
  • The depth of the selector SHOULD also be limited by the server.

Vulcain does not work "out of the box" with API Platform

Hi,
I've been trying for a long time to set up Vulcain with Api Pltform without success. I have the feeling that it is more a Vulain issue as I never found how to make it work through Caddy module.

Here are my steps to reproduce the issue (largely inspired form your demo):

Download Api Platform v3.0.9 release

curl -L https://github.com/api-platform/api-platform/archive/refs/tags/v3.0.9.tar.gz
tar xzvf v3.0.9.tar.gz
cd api-platform-3.0.9

Fix PNPM issue as stated here: api-platform/api-platform#2417

Configure the API to be Fully Normalized

# api/config/packages/api_platform.yaml
api_platform:
    defaults:
        normalization_context:
            iri_only: true
    # ...

Start the app

docker compose up -d

Create entites like here https://github.com/dunglas/demo-vulcain-api-platform

Update schema + load fixtures :

docker compose exec php bin/console doctrine:schema:update --force
docker compose exec php composer req --dev alice
# api/fixtures/data.yaml
App\Entity\Conference:
    conference_{1..10}:
        name: '<catchPhrase()>'

App\Entity\Session:
    session_{1..100}:
        conference: '@conference_*'
        title: '<catchPhrase()>'
        summary: '<sentences(3, true)>'
        author: '<name()>'

App\Entity\Feedback:
    feedback_{1..100}:
        session: '@session_*'
        comment: '<sentences(3, true)>'
        rating: '<numberBetween(0, 5)>'
docker compose exec php bin/console hautelook:fixtures:load

Try with this code: (inspired from your demo)

<!DOCTYPE html>
<title>Maximum waterfall: API Platform X Vulcain</title>
<script>
  async function fetchJson(url, opts = {}) {
    const resp = await fetch(url, opts);
    return resp.json();
  }

  (async function() {
// Load ALL data from the API
    console.time('download');
    const conferences = await fetchJson('/conferences', { headers: { Preload: '/hydra:member/*/sessions/*/feedback/*' }});
    // const conferences = await fetchJson('/conferences');

    // Fetch conferences
    conferences['hydra:member'].forEach(async conferenceRel => {
      const conference = await fetchJson(conferenceRel);

      // Fetch sessions
      conference.sessions.forEach(async sessionURL => {
        const session = await fetchJson(sessionURL);

        // Fetch feedback
        session.feedback.forEach(async feedbackURL => {
          const feedback = await fetchJson(feedbackURL);
        })
      })
    })
    console.timeEnd('download');
  })();
</script>

With Slow 3G throttling + Disable cache in Chrom DevTools I get the the cascade profile like here on the left:
image

Is there anything more to do to enable Vulcain?

Request made by the browser does not contain the "authorization" header

I'm trying to use Vulcain on my API developed with api-platform created from the distribution made available in the api-platform doc.

My API is secured by two headers: "authorization" which contains a Bearer and "workspace" which contains an ID allowing the API to know on which workspace to retrieve the information.

I have a user object which contains a "defaultRoles" attribute which lists all the roles of the user.
So I applied the #[ApiProperty(push:true)] annotation on this attribute.
When the browser makes sub-requests to retrieve the "defaultRoles/:id" resources, the API returns a 401 because the requests generated by the browser do not contain the "authorization" and "workspace" headers.

I couldn't find any resources explaining how Vulcain works with a secure API.

How can I add the header in that request ?

Official Docker Container

Please provide an official docker container for the gateway server.
It'd be much easier to deploy this way.

Curren state

Vulcain's approach is quite intriguing. However major browsers per my understanding have deprecated the h2 server push feature.

Does this mean that vulcain in current state is unusable?

I understand that it might make use of Early Hints feature in the future.

[Idea] Add Link header of pushed ressources

As suggested in the title, an idea would be for vulcain to add the Link header for every pushed ressources. It allows Vulcain to be behind a reverse proxy that will handle the pushed itself if necessary.

At least it would be great to have it if Server Push is not available (eg: Vulcain in http mode)

Fields issue on preload entity

Hi!
I'm playing with Vulcain but i'm facing to an issue...
I've 2 entities in an Api Platform project (greeting and author, author have many greetings, for relationship)

If i fetch greetings endpoint:

window.fetch('https://localhost/greetings', {
        headers: {
            preload: '"/hydra:member/*/author"',
            fields: '"/hydra:member/*/@id", "/hydra:member/*/name", "/hydra:member/*/author/@id"'
        }
    }).then(() => window.fetch('https://localhost/authors/1'))

Of course authors/1 exists and it linked to all my greetings for more simplicity.

So... In this case, with fields contains "/hydra:member/*/authors/@id" (it the same with firstname or anyother property) push doesn't work
image

But if instead "/hydra:member/*/authors/@id", i've "/hydra:member/*/authors" (without /@id) push works (but i get full object after, of course)
image

What am i missing....? Documentation seems clear about, i think this should work...

Why is it AGPL licensed?

While AGPL is a valid license, it might be blocking the usage of Vulcain in enterprise environments (legal considerations). Is there any reason to not publish it under an even more less restrictive license as MIT?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.