dunglas / vulcain Goto Github PK
View Code? Open in Web Editor NEWFast and idiomatic client-driven REST APIs.
Home Page: https://vulcain.rocks
License: GNU Affero General Public License v3.0
Fast and idiomatic client-driven REST APIs.
Home Page: https://vulcain.rocks
License: GNU Affero General Public License v3.0
https://htmx.org/examples/ seems like a nice pattern to use with this.
clients get the SSE and then have simple patterns for updating the gui
apparently its a hypermedia pattern --
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
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());
}
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.
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?
In documentation it says:
Note: a higher-level library dedicated to Vulcain is being written.
Is this available somewhere?
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?
Tests fail randomly because - by design - pushes can be received in any order: https://github.com/dunglas/vulcain/runs/258924155
Doing a diff
here doesn't work. What we must check is that all logs are created, regardless of the order.
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
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?
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.
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.
I'm using the API Platform Docker for the backend. Any help would be appreciated
How to configure CORS headers?
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.
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:
/
, here shown encoded using %2F
as possible measure to avoid conflicts with pathFields:
or not if already specified. Here suggested to be optional however no strong opinion on that.:
, 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;charset=UTF-8
Cache-Digests and Casper allows to cancels server-push if the client is known to be in possession of the content.
There is a Go library for CASPer (https://github.com/tcnksm/go-casper) but unfortunately it isn't compatible with how I implemented recursive pushes.
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
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 :
Cela fait plusieurs heures que je patauge sur ce problème, sans comprendre ce qui cloche.
Toute aide serait la bienvenue.
Merci d'avance.
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 !
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)
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?
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:
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:
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?
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
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
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).
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?
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?
Sadly, it seems that Push-Support is being removed from Chrome.
https://evertpot.com/http-2-push-is-dead/
Any ideas what this means for this project?
I am working on a (WIP) implementation of parts of the Vulcain protocol to be used in a server less (OpenWhisk) environment here: https://github.com/trieloff/helix-vulcain-filters
Is it ok to use the term "Vulcain" in the project name?
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.
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)
Thank you!
Hi,
I noticed that the link to IETF page (https://datatracker.ietf.org/doc/draft-dunglas-vulcain/) from README page is not working.
I tried to find correct link on IETF website but could not find it.
Is it because the request for this new standard is not registered yet by IETF?
Laurent
SHOULD
limit the maximum number of resources to push.SHOULD
also be limited by the server.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:
Is there anything more to do to enable Vulcain?
Because of a bug in PHP, Server Push tests have been disabled in #97. We need to re-enable this.
Our options are:
nghttp
instead of PHPI'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 ?
Please provide an official docker container for the gateway server.
It'd be much easier to deploy this way.
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.
See https://tools.ietf.org/html/rfc7234#section-5.2.2.4.
We should also send a Warning: 214
header when we rewrite the URLs or remove some fields.
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)
My Symfony Docker Vulcain Installation failed, because this package is disabled or deleted? Why?
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
But if instead "/hydra:member/*/authors/@id", i've "/hydra:member/*/authors" (without /@id) push works (but i get full object after, of course)
What am i missing....? Documentation seems clear about, i think this should work...
In the Cache-Documentaion there is a broken link to X-Key.
https://github.com/dunglas/vulcain/blob/main/docs/cache.md
Broken in Master-Branch
https://github.com/varnish/varnish-modules/blob/master/docs/vmod_xkey.rst
Branch 6.5 is available
https://github.com/varnish/varnish-modules/blob/6.5/docs/vmod_xkey.rst
Can you give a working example? Or maybe i should not use it in one process for some reason?
It would be nice to have all this features together in single process without using network.
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.