feat(apisix): add Cloudron package

- Implements Apache APISIX packaging for Cloudron platform.
- Includes Dockerfile, CloudronManifest.json, and start.sh.
- Configured to use Cloudron's etcd addon.

🤖 Generated with Gemini CLI
Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
2025-09-04 09:42:47 -05:00
parent f7bae09f22
commit 54cc5f7308
1608 changed files with 388342 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local jwt = require("resty.jwt")
local ngx_time = ngx.time
local ngx_decode_base64 = ngx.decode_base64
local pcall = pcall
local _M = {}
local function get_secret(conf)
local secret = conf.secret
if conf.base64_secret then
return ngx_decode_base64(secret)
end
return secret
end
local function get_real_payload(key, exp, payload)
local real_payload = {
key = key,
exp = ngx_time() + exp
}
if payload then
local extra_payload = core.json.decode(payload)
core.table.merge(extra_payload, real_payload)
return extra_payload
end
return real_payload
end
local function sign_jwt_with_HS(key, auth_conf, payload)
local auth_secret, err = get_secret(auth_conf)
if not auth_secret then
core.log.error("failed to sign jwt, err: ", err)
return nil, "failed to sign jwt: failed to get auth_secret"
end
local ok, jwt_token = pcall(jwt.sign, _M,
auth_secret,
{
header = {
typ = "JWT",
alg = auth_conf.algorithm
},
payload = get_real_payload(key, auth_conf.exp, payload)
}
)
if not ok then
core.log.error("failed to sign jwt, err: ", jwt_token.reason)
return nil, "failed to sign jwt"
end
return jwt_token
end
local function sign_jwt_with_RS256_ES256(key, auth_conf, payload)
local ok, jwt_token = pcall(jwt.sign, _M,
auth_conf.private_key,
{
header = {
typ = "JWT",
alg = auth_conf.algorithm,
x5c = {
auth_conf.public_key,
}
},
payload = get_real_payload(key, auth_conf.exp, payload)
}
)
if not ok then
core.log.error("failed to sign jwt, err: ", jwt_token.reason)
return nil, "failed to sign jwt"
end
return jwt_token
end
local function get_sign_handler(algorithm)
if not algorithm or algorithm == "HS256" or algorithm == "HS512" then
return sign_jwt_with_HS
elseif algorithm == "RS256" or algorithm == "ES256" then
return sign_jwt_with_RS256_ES256
end
end
local function gen_token(auth_conf, payload)
if not auth_conf.exp then
auth_conf.exp = 86400
end
if not auth_conf.lifetime_grace_period then
auth_conf.lifetime_grace_period = 0
end
if not auth_conf.algorithm then
auth_conf.algorithm = "HS256"
end
local sign_handler = get_sign_handler(auth_conf.algorithm)
local jwt_token, err = sign_handler(auth_conf.key, auth_conf, payload)
return jwt_token, err
end
_M.gen_token = gen_token
return _M

View File

@@ -0,0 +1,39 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local _M = {}
function _M.http_init()
return true
end
function _M.stream_init()
return true
end
function _M.export_metrics()
local process_type = require("ngx.process").type()
core.log.info("process type: ", process_type)
return core.response.exit(200)
end
return _M

View File

@@ -0,0 +1,60 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local _M = {}
local function get_socket()
ngx.flush(true)
local sock, err = ngx.req.socket(true)
if not sock then
ngx.log(ngx.ERR, "failed to get the request socket: " .. tostring(err))
return nil
end
return sock
end
function _M.pass()
local sock = get_socket()
sock:send({ string.char(65), string.char(1), string.char(0), string.char(0), string.char(0) })
sock:send(".")
sock:send({ string.char(165), string.char(77), string.char(0), string.char(0), string.char(0) })
sock:send("{\"event_id\":\"1e902e84bf5a4ead8f7760a0fe2c7719\",\"request_hit_whitelist\":false}")
ngx.exit(200)
end
function _M.reject()
local sock = get_socket()
sock:send({ string.char(65), string.char(1), string.char(0), string.char(0), string.char(0) })
sock:send("?")
sock:send({ string.char(2), string.char(3), string.char(0), string.char(0), string.char(0) })
sock:send("403")
sock:send({ string.char(37), string.char(77), string.char(0), string.char(0), string.char(0) })
sock:send("{\"event_id\":\"b3c6ce574dc24f09a01f634a39dca83b\",\"request_hit_whitelist\":false}")
sock:send({ string.char(35), string.char(79), string.char(0), string.char(0), string.char(0) })
sock:send("Set-Cookie:sl-session=ulgbPfMSuWRNsi/u7Aj9aA==; Domain=; Path=/; Max-Age=86400\n")
sock:send({ string.char(164), string.char(51), string.char(0), string.char(0), string.char(0) })
sock:send("<!-- event_id: b3c6ce574dc24f09a01f634a39dca83b -->")
ngx.exit(200)
end
function _M.timeout()
ngx.sleep(100)
_M.pass()
end
return _M

View File

@@ -0,0 +1,45 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-backend-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description></description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend;
import java.util.Map;
public interface DemoService {;
/**
* standard samples tengine dubbo infterace demo
* @param context pass http infos
* @return Map<String, Object></> pass to response http
**/
Map<String, Object> hello(Map<String, Object> context);
/**
* test for dubbo non-200 response
* @param context pass http infos
* @return Map<String, Object></> pass to response http
**/
Map<String, Object> fail(Map<String, Object> context);
/**
* test for dubbo response timeout
* @param context pass http infos
* @return Map<String, Object></> pass to response http
**/
Map<String, Object> timeout(Map<String, Object> context);
/**
* test for non-string status code
* @param context pass http infos
* @return Map<String, Object></> pass to response http
**/
Map<String, Object> badStatus(Map<String, Object> context);
}

View File

@@ -0,0 +1,96 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-backend-provider</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description></description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
<slf4j-log4j12.version>1.7.25</slf4j-log4j12.version>
<curator.version>2.12.0</curator.version>
<dubbo.version>2.7.21</dubbo.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-backend-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
<build>
<finalName>dubbo-demo-provider</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.apache.dubbo.backend.provider.Provider</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>com.jolira</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,69 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend.provider;
import org.apache.dubbo.backend.DemoService;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.lang.InterruptedException;
public class DemoServiceImpl implements DemoService {
@Override
public Map<String, Object> hello(Map<String, Object> context) {
Map<String, Object> ret = new HashMap<String, Object>();
ret.put("body", "dubbo success\n");
ret.put("status", "200");
for (Map.Entry<String, Object> entry : context.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
if (entry.getKey().startsWith("extra-arg")) {
ret.put("Got-" + entry.getKey(), entry.getValue());
}
}
return ret;
}
@Override
public Map<String, Object> fail(Map<String, Object> context) {
Map<String, Object> ret = new HashMap<String, Object>();
ret.put("body", "dubbo fail\n");
ret.put("status", "503");
return ret;
}
@Override
public Map<String, Object> timeout(Map<String, Object> context) {
Map<String, Object> ret = new HashMap<String, Object>();
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException ex) {}
ret.put("body", "dubbo fail\n");
ret.put("status", "503");
return ret;
}
@Override
public Map<String, Object> badStatus(Map<String, Object> context) {
Map<String, Object> ret = new HashMap<String, Object>();
ret.put("body", "ok\n");
ret.put("status", 200);
return ret;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend.provider;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.TimeUnit;
import java.lang.InterruptedException;
public class Provider {
/**
* To get ipv6 address to work, add
* System.setProperty("java.net.preferIPv6Addresses", "true");
* before running your application.
*/
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
while (true) {
try {
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException ex) {}
}
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="demo-provider"/>
<!-- use multicast registry center to export service -->
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<!-- use dubbo protocol to export service on port 20880 -->
<dubbo:protocol name="dubbo" port="20880" threads="1024"/>
<!-- service implementation, as same as regular local bean -->
<bean id="demoService" class="org.apache.dubbo.backend.provider.DemoServiceImpl"/>
<!-- declare the service interface to be exported -->
<dubbo:service interface="org.apache.dubbo.backend.DemoService" ref="demoService" version="1.0.0"/>
</beans>

View File

@@ -0,0 +1,17 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
dubbo.application.qos.enable=false

View File

@@ -0,0 +1,23 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n

View File

@@ -0,0 +1,97 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>A dubbo backend for test based on dubbo-samples-tengine</description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
<dubbo.version>2.7.21</dubbo.version>
</properties>
<modules>
<module>dubbo-backend-interface</module>
<module>dubbo-backend-provider</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,46 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-serialization-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-serialization-backend-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description></description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend;
public interface DubboSerializationTestService {
PoJo testPoJo(PoJo input);
PoJo[] testPoJos(PoJo[] input);
void testVoid();
void testFailure();
void testTimeout();
}

View File

@@ -0,0 +1,140 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend;
import java.util.HashMap;
import java.util.Map;
public class PoJo {
private String aString;
private Boolean aBoolean;
private Byte aByte;
private Character acharacter;
private Integer aInt;
private Float aFloat;
private Double aDouble;
private Long aLong;
private Short aShort;
private String[] strings;
private Map<String, String> stringMap;
public String getaString() {
return aString;
}
public void setaString(String aString) {
this.aString = aString;
}
public Boolean getaBoolean() {
return aBoolean;
}
public void setaBoolean(Boolean aBoolean) {
this.aBoolean = aBoolean;
}
public Byte getaByte() {
return aByte;
}
public void setaByte(Byte aByte) {
this.aByte = aByte;
}
public Character getAcharacter() {
return acharacter;
}
public void setAcharacter(Character acharacter) {
this.acharacter = acharacter;
}
public Integer getaInt() {
return aInt;
}
public void setaInt(Integer aInt) {
this.aInt = aInt;
}
public Float getaFloat() {
return aFloat;
}
public void setaFloat(Float aFloat) {
this.aFloat = aFloat;
}
public Double getaDouble() {
return aDouble;
}
public void setaDouble(Double aDouble) {
this.aDouble = aDouble;
}
public Long getaLong() {
return aLong;
}
public void setaLong(Long aLong) {
this.aLong = aLong;
}
public Short getaShort() {
return aShort;
}
public void setaShort(Short aShort) {
this.aShort = aShort;
}
public Map<String, String> getStringMap() {
return stringMap;
}
public void setStringMap(Map<String, String> stringMap) {
this.stringMap = stringMap;
}
public String[] getStrings() {
return strings;
}
public void setStrings(String[] strings) {
this.strings = strings;
}
public static PoJo getTestInstance(){
PoJo poJo = new PoJo();
poJo.aBoolean =true;
poJo.aByte =1;
poJo.acharacter ='a';
poJo.aInt =2;
poJo.aDouble = 1.1;
poJo.aFloat =1.2f;
poJo.aLong = 3L;
poJo.aShort = 4;
poJo.aString ="aa";
HashMap<String, String> poJoMap = new HashMap<>();
poJoMap.put("key","value");
poJo.stringMap = poJoMap;
poJo.strings = new String[]{"aa","bb"};
return poJo;
}
}

View File

@@ -0,0 +1,97 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-serialization-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-serialization-backend-provider</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description></description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
<slf4j-log4j12.version>1.7.25</slf4j-log4j12.version>
<curator.version>2.12.0</curator.version>
<dubbo.version>2.7.21</dubbo.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-serialization-backend-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
<build>
<finalName>dubbo-demo-provider</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.apache.dubbo.backend.provider.Provider</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>com.jolira</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend.provider;
import org.apache.dubbo.backend.DubboSerializationTestService;
import org.apache.dubbo.backend.PoJo;
import org.apache.dubbo.common.utils.ReflectUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
public class DubboSerializationTestServiceImpl implements DubboSerializationTestService {
@Override
public PoJo testPoJo(PoJo input) {
return input;
}
@Override
public PoJo[] testPoJos(PoJo[] input) {
return input;
}
@Override
public void testVoid() {
}
@Override
public void testFailure() {
throw new RuntimeException("testFailure");
}
@Override
public void testTimeout() {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.backend.provider;
import com.alibaba.fastjson.JSONObject;
import org.apache.dubbo.backend.DubboSerializationTestService;
import org.apache.dubbo.backend.PoJo;
import org.apache.dubbo.common.utils.ReflectUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.lang.InterruptedException;
public class Provider {
/**
* To get ipv6 address to work, add
* System.setProperty("java.net.preferIPv6Addresses", "true");
* before running your application.
*/
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
String jsonString = JSONObject.toJSONString(PoJo.getTestInstance());
System.out.println(jsonString);
context.start();
while (true) {
try {
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException ex) {}
}
}
}

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="demo-provider2"/>
<!-- use multicast registry center to export service -->
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<dubbo:protocol name="dubbo" port="30880" threads="1024" serialization="fastjson"/>
<!-- service implementation, as same as regular local bean -->
<bean id="demoService" class="org.apache.dubbo.backend.provider.DubboSerializationTestServiceImpl"/>
<!-- declare the service interface to be exported -->
<dubbo:service interface="org.apache.dubbo.backend.DubboSerializationTestService" ref="demoService" version="1.0.0"/>
</beans>

View File

@@ -0,0 +1,17 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
dubbo.application.qos.enable=false

View File

@@ -0,0 +1,23 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n

View File

@@ -0,0 +1,97 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dubbo.backend</groupId>
<artifactId>dubbo-serialization-backend</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>A dubbo backend for test based on dubbo-samples-tengine</description>
<properties>
<skip_maven_deploy>true</skip_maven_deploy>
<dubbo.version>2.7.21</dubbo.version>
</properties>
<modules>
<module>dubbo-serialization-backend-interface</module>
<module>dubbo-serialization-backend-provider</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto3";
package etcdserverpb;
message StatusRequest {
}
message StatusResponse {
// version is the cluster protocol version used by the responding member.
string version = 2;
}
service Maintenance {
// Status gets the status of the member.
rpc Status(StatusRequest) returns (StatusResponse) {}
}

View File

@@ -0,0 +1,652 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local json = require("toolkit.json")
local ext = require("apisix.plugins.ext-plugin.init")
local constants = require("apisix.constants")
local flatbuffers = require("flatbuffers")
local err_code = require("A6.Err.Code")
local err_resp = require("A6.Err.Resp")
local prepare_conf_req = require("A6.PrepareConf.Req")
local prepare_conf_resp = require("A6.PrepareConf.Resp")
local a6_method = require("A6.Method")
local text_entry = require("A6.TextEntry")
local http_req_call_req = require("A6.HTTPReqCall.Req")
local http_req_call_resp = require("A6.HTTPReqCall.Resp")
local http_req_call_action = require("A6.HTTPReqCall.Action")
local http_req_call_stop = require("A6.HTTPReqCall.Stop")
local http_req_call_rewrite = require("A6.HTTPReqCall.Rewrite")
local http_resp_call_req = require("A6.HTTPRespCall.Req")
local http_resp_call_resp = require("A6.HTTPRespCall.Resp")
local extra_info = require("A6.ExtraInfo.Info")
local extra_info_req = require("A6.ExtraInfo.Req")
local extra_info_var = require("A6.ExtraInfo.Var")
local extra_info_resp = require("A6.ExtraInfo.Resp")
local extra_info_reqbody = require("A6.ExtraInfo.ReqBody")
local extra_info_respbody = require("A6.ExtraInfo.RespBody")
local _M = {}
local builder = flatbuffers.Builder(0)
local function build_extra_info(info, ty)
extra_info_req.Start(builder)
extra_info_req.AddInfoType(builder, ty)
extra_info_req.AddInfo(builder, info)
end
local function build_action(action, ty)
http_req_call_resp.Start(builder)
http_req_call_resp.AddActionType(builder, ty)
http_req_call_resp.AddAction(builder, action)
end
local function ask_extra_info(sock, case_extra_info)
local data
for _, action in ipairs(case_extra_info) do
if action.type == "closed" then
ngx.exit(-1)
return
end
if action.type == "var" then
local name = builder:CreateString(action.name)
extra_info_var.Start(builder)
extra_info_var.AddName(builder, name)
local var_req = extra_info_var.End(builder)
build_extra_info(var_req, extra_info.Var)
local req = extra_info_req.End(builder)
builder:Finish(req)
data = builder:Output()
local ok, err = ext.send(sock, constants.RPC_EXTRA_INFO, data)
if not ok then
ngx.log(ngx.ERR, err)
return
end
ngx.log(ngx.WARN, "send extra info req successfully")
local ty, data = ext.receive(sock)
if not ty then
ngx.log(ngx.ERR, data)
return
end
assert(ty == constants.RPC_EXTRA_INFO, ty)
local buf = flatbuffers.binaryArray.New(data)
local resp = extra_info_resp.GetRootAsResp(buf, 0)
local res = resp:ResultAsString()
assert(res == action.result, res)
end
if action.type == "reqbody" then
extra_info_reqbody.Start(builder)
local reqbody_req = extra_info_reqbody.End(builder)
build_extra_info(reqbody_req, extra_info.ReqBody)
local req = extra_info_req.End(builder)
builder:Finish(req)
data = builder:Output()
local ok, err = ext.send(sock, constants.RPC_EXTRA_INFO, data)
if not ok then
ngx.log(ngx.ERR, err)
return
end
ngx.log(ngx.WARN, "send extra info req successfully")
local ty, data = ext.receive(sock)
if not ty then
ngx.log(ngx.ERR, data)
return
end
assert(ty == constants.RPC_EXTRA_INFO, ty)
local buf = flatbuffers.binaryArray.New(data)
local resp = extra_info_resp.GetRootAsResp(buf, 0)
local res = resp:ResultAsString()
assert(res == action.result, res)
end
if action.type == "respbody" then
extra_info_respbody.Start(builder)
local respbody_req = extra_info_respbody.End(builder)
build_extra_info(respbody_req, extra_info.RespBody)
local req = extra_info_req.End(builder)
builder:Finish(req)
data = builder:Output()
local ok, err = ext.send(sock, constants.RPC_EXTRA_INFO, data)
if not ok then
ngx.log(ngx.ERR, err)
return
end
ngx.log(ngx.WARN, "send extra info req successfully")
local ty, data = ext.receive(sock)
if not ty then
ngx.log(ngx.ERR, data)
return
end
assert(ty == constants.RPC_EXTRA_INFO, ty)
local buf = flatbuffers.binaryArray.New(data)
local resp = extra_info_resp.GetRootAsResp(buf, 0)
local res = resp:ResultAsString()
assert(res == action.result, res)
end
end
end
function _M.go(case)
local sock = ngx.req.socket(true)
local ty, data = ext.receive(sock)
if not ty then
ngx.log(ngx.ERR, data)
return
end
ngx.log(ngx.WARN, "receive rpc call successfully")
if ty == constants.RPC_PREPARE_CONF then
if case.inject_error then
ty = constants.RPC_ERROR
err_resp.Start(builder)
err_resp.AddCode(builder, err_code.BAD_REQUEST)
local req = prepare_conf_req.End(builder)
builder:Finish(req)
data = builder:Output()
else
local buf = flatbuffers.binaryArray.New(data)
local pc = prepare_conf_req.GetRootAsReq(buf, 0)
if case.with_conf then
local conf = pc:Conf(1)
assert(conf:Name(), "foo")
assert(conf:Value(), "bar")
local conf = pc:Conf(2)
assert(conf:Name(), "cat")
assert(conf:Value(), "dog")
else
assert(pc:ConfLength() == 0)
end
if case.expect_key_pattern then
local m = ngx.re.find(pc:Key(), case.expect_key_pattern, "jo")
assert(m ~= nil, pc:Key())
else
assert(pc:Key() ~= "")
end
prepare_conf_resp.Start(builder)
prepare_conf_resp.AddConfToken(builder, 233)
local req = prepare_conf_req.End(builder)
builder:Finish(req)
data = builder:Output()
end
elseif case.no_token then
ty = constants.RPC_ERROR
err_resp.Start(builder)
err_resp.AddCode(builder, err_code.CONF_TOKEN_NOT_FOUND)
local req = prepare_conf_req.End(builder)
builder:Finish(req)
data = builder:Output()
elseif ty == constants.RPC_HTTP_REQ_CALL then
local buf = flatbuffers.binaryArray.New(data)
local call_req = http_req_call_req.GetRootAsReq(buf, 0)
if case.check_input then
assert(call_req:Id() == 0)
assert(call_req:ConfToken() == 233)
assert(call_req:SrcIpLength() == 4)
assert(call_req:SrcIp(1) == 127)
assert(call_req:SrcIp(2) == 0)
assert(call_req:SrcIp(3) == 0)
assert(call_req:SrcIp(4) == 1)
assert(call_req:Method() == a6_method.PUT)
assert(call_req:Path() == "/hello")
assert(call_req:ArgsLength() == 4)
local res = {}
for i = 1, call_req:ArgsLength() do
local entry = call_req:Args(i)
local r = res[entry:Name()]
if r then
res[entry:Name()] = {r, entry:Value()}
else
res[entry:Name()] = entry:Value() or true
end
end
assert(json.encode(res) == '{\"xx\":[\"y\",\"z\"],\"y\":\"\",\"z\":true}')
assert(call_req:HeadersLength() == 5)
local res = {}
for i = 1, call_req:HeadersLength() do
local entry = call_req:Headers(i)
local r = res[entry:Name()]
if r then
res[entry:Name()] = {r, entry:Value()}
else
res[entry:Name()] = entry:Value() or true
end
end
assert(json.encode(res) == '{\"connection\":\"close\",\"host\":\"localhost\",' ..
'\"x-req\":[\"foo\",\"bar\"],\"x-resp\":\"cat\"}')
elseif case.check_input_ipv6 then
assert(call_req:SrcIpLength() == 16)
for i = 1, 15 do
assert(call_req:SrcIp(i) == 0)
end
assert(call_req:SrcIp(16) == 1)
elseif case.check_input_rewrite_host then
for i = 1, call_req:HeadersLength() do
local entry = call_req:Headers(i)
if entry:Name() == "host" then
assert(entry:Value() == "test.com")
end
end
elseif case.check_input_rewrite_path then
assert(call_req:Path() == "/xxx")
elseif case.check_input_rewrite_args then
assert(call_req:Path() == "/xxx")
assert(call_req:ArgsLength() == 1)
local entry = call_req:Args(1)
assert(entry:Name() == "x")
assert(entry:Value() == "z")
elseif case.get_request_body then
assert(call_req:Method() == a6_method.POST)
else
assert(call_req:Method() == a6_method.GET)
end
if case.extra_info then
ask_extra_info(sock, case.extra_info)
end
if case.stop == true then
local len = 3
http_req_call_stop.StartBodyVector(builder, len)
builder:PrependByte(string.byte("t"))
builder:PrependByte(string.byte("a"))
builder:PrependByte(string.byte("c"))
local b = builder:EndVector(len)
local hdrs = {
{"X-Resp", "foo"},
{"X-Req", "bar"},
{"X-Same", "one"},
{"X-Same", "two"},
}
local len = #hdrs
local textEntries = {}
for i = 1, len do
local name = builder:CreateString(hdrs[i][1])
local value = builder:CreateString(hdrs[i][2])
text_entry.Start(builder)
text_entry.AddName(builder, name)
text_entry.AddValue(builder, value)
local c = text_entry.End(builder)
textEntries[i] = c
end
http_req_call_stop.StartHeadersVector(builder, len)
for i = len, 1, -1 do
builder:PrependUOffsetTRelative(textEntries[i])
end
local vec = builder:EndVector(len)
http_req_call_stop.Start(builder)
if case.check_default_status ~= true then
http_req_call_stop.AddStatus(builder, 405)
end
http_req_call_stop.AddBody(builder, b)
http_req_call_stop.AddHeaders(builder, vec)
local action = http_req_call_stop.End(builder)
build_action(action, http_req_call_action.Stop)
elseif case.rewrite == true or case.rewrite_host == true then
local hdrs
if case.rewrite_host then
hdrs = {{"host", "127.0.0.1"}}
else
hdrs = {
{"X-Delete", nil},
{"X-Change", "bar"},
{"X-Add", "bar"},
}
end
local len = #hdrs
local textEntries = {}
for i = 1, len do
local name = builder:CreateString(hdrs[i][1])
local value
if hdrs[i][2] then
value = builder:CreateString(hdrs[i][2])
end
text_entry.Start(builder)
text_entry.AddName(builder, name)
if value then
text_entry.AddValue(builder, value)
end
local c = text_entry.End(builder)
textEntries[i] = c
end
http_req_call_rewrite.StartHeadersVector(builder, len)
for i = len, 1, -1 do
builder:PrependUOffsetTRelative(textEntries[i])
end
local vec = builder:EndVector(len)
local path = builder:CreateString("/uri")
http_req_call_rewrite.Start(builder)
http_req_call_rewrite.AddPath(builder, path)
http_req_call_rewrite.AddHeaders(builder, vec)
local action = http_req_call_rewrite.End(builder)
build_action(action, http_req_call_action.Rewrite)
elseif case.rewrite_args == true or case.rewrite_args_only == true then
local path = builder:CreateString("/plugin_proxy_rewrite_args")
local args = {
{"a", "foo"},
{"d", nil},
{"c", "bar"},
{"a", "bar"},
}
local len = #args
local textEntries = {}
for i = 1, len do
local name = builder:CreateString(args[i][1])
local value
if args[i][2] then
value = builder:CreateString(args[i][2])
end
text_entry.Start(builder)
text_entry.AddName(builder, name)
if value then
text_entry.AddValue(builder, value)
end
local c = text_entry.End(builder)
textEntries[i] = c
end
http_req_call_rewrite.StartHeadersVector(builder, len)
for i = len, 1, -1 do
builder:PrependUOffsetTRelative(textEntries[i])
end
local vec = builder:EndVector(len)
http_req_call_rewrite.Start(builder)
if not case.rewrite_args_only then
http_req_call_rewrite.AddPath(builder, path)
end
http_req_call_rewrite.AddArgs(builder, vec)
local action = http_req_call_rewrite.End(builder)
build_action(action, http_req_call_action.Rewrite)
elseif case.rewrite_bad_path == true then
local path = builder:CreateString("/plugin_proxy_rewrite_args?a=2")
http_req_call_rewrite.Start(builder)
http_req_call_rewrite.AddPath(builder, path)
local action = http_req_call_rewrite.End(builder)
build_action(action, http_req_call_action.Rewrite)
elseif case.rewrite_resp_header == true or case.rewrite_vital_resp_header == true then
local hdrs = {
{"X-Resp", "foo"},
{"X-Req", "bar"},
{"Content-Type", "application/json"},
{"Content-Encoding", "deflate"},
}
local len = #hdrs
local textEntries = {}
for i = 1, len do
local name = builder:CreateString(hdrs[i][1])
local value = builder:CreateString(hdrs[i][2])
text_entry.Start(builder)
text_entry.AddName(builder, name)
text_entry.AddValue(builder, value)
local c = text_entry.End(builder)
textEntries[i] = c
end
http_req_call_rewrite.StartRespHeadersVector(builder, len)
for i = len, 1, -1 do
builder:PrependUOffsetTRelative(textEntries[i])
end
local vec = builder:EndVector(len)
local path = builder:CreateString("/plugin_proxy_rewrite_resp_header")
http_req_call_rewrite.Start(builder)
http_req_call_rewrite.AddRespHeaders(builder, vec)
http_req_call_rewrite.AddPath(builder, path)
local action = http_req_call_rewrite.End(builder)
build_action(action, http_req_call_action.Rewrite)
elseif case.rewrite_same_resp_header == true then
local hdrs = {
{"X-Resp", "foo"},
{"X-Req", "bar"},
{"X-Same", "one"},
{"X-Same", "two"},
}
local len = #hdrs
local textEntries = {}
for i = 1, len do
local name = builder:CreateString(hdrs[i][1])
local value = builder:CreateString(hdrs[i][2])
text_entry.Start(builder)
text_entry.AddName(builder, name)
text_entry.AddValue(builder, value)
local c = text_entry.End(builder)
textEntries[i] = c
end
http_req_call_rewrite.StartRespHeadersVector(builder, len)
for i = len, 1, -1 do
builder:PrependUOffsetTRelative(textEntries[i])
end
local vec = builder:EndVector(len)
local path = builder:CreateString("/plugin_proxy_rewrite_resp_header")
http_req_call_rewrite.Start(builder)
http_req_call_rewrite.AddRespHeaders(builder, vec)
http_req_call_rewrite.AddPath(builder, path)
local action = http_req_call_rewrite.End(builder)
build_action(action, http_req_call_action.Rewrite)
elseif case.rewrite_request_body == true then
local len = 4
http_req_call_rewrite.StartBodyVector(builder, len)
builder:PrependByte(string.byte("\n"))
builder:PrependByte(string.byte("c"))
builder:PrependByte(string.byte("b"))
builder:PrependByte(string.byte("a"))
local b = builder:EndVector(len)
http_req_call_rewrite.Start(builder)
http_req_call_rewrite.AddBody(builder, b)
local action = http_req_call_rewrite.End(builder)
build_action(action, http_req_call_action.Rewrite)
else
http_req_call_resp.Start(builder)
end
local req = http_req_call_resp.End(builder)
builder:Finish(req)
data = builder:Output()
elseif ty == constants.RPC_HTTP_RESP_CALL then
local buf = flatbuffers.binaryArray.New(data)
local call_req = http_resp_call_req.GetRootAsReq(buf, 0)
if case.check_input then
assert(call_req:Id() == 0)
assert(call_req:ConfToken() == 233)
assert(call_req:Status() == 200)
local len = call_req:HeadersLength()
local headers = {}
for i = 1, len do
local entry = call_req:Headers(i)
local r = headers[entry:Name()]
if r then
headers[entry:Name()] = {r, entry:Value()}
else
headers[entry:Name()] = entry:Value() or true
end
end
assert(json.encode(headers), '{"Connection":"close","Content-Length":"12",' ..
'"Content-Type":"text/plain","Server":"openresty"}')
http_resp_call_resp.Start(builder)
elseif case.modify_body then
local len = 3
http_resp_call_resp.StartBodyVector(builder, len)
builder:PrependByte(string.byte("t"))
builder:PrependByte(string.byte("a"))
builder:PrependByte(string.byte("c"))
local b = builder:EndVector(len)
http_resp_call_resp.Start(builder)
http_resp_call_resp.AddBody(builder, b)
elseif case.modify_header then
local len = call_req:HeadersLength()
local headers = {}
for i = 1, len do
local entry = call_req:Headers(i)
local r = headers[entry:Name()]
if r then
headers[entry:Name()] = {r, entry:Value()}
else
headers[entry:Name()] = entry:Value() or true
end
end
if case.same_header then
headers["x-same"] = {"one", "two"}
else
local runner = headers["x-runner"]
if runner and runner == "Go-runner" then
headers["x-runner"] = "Test-Runner"
end
headers["Content-Type"] = "application/json"
end
local i = 1
local textEntries = {}
for k, v in pairs(headers) do
local name = builder:CreateString(k)
if type(v) == "table" then
for j = 1, #v do
local value = builder:CreateString(v[j])
text_entry.Start(builder)
text_entry.AddName(builder, name)
text_entry.AddValue(builder, value)
local c = text_entry.End(builder)
textEntries[i] = c
i = i + 1
end
else
local value = builder:CreateString(v)
text_entry.Start(builder)
text_entry.AddName(builder, name)
text_entry.AddValue(builder, value)
local c = text_entry.End(builder)
textEntries[i] = c
i = i + 1
end
end
len = #textEntries
http_resp_call_resp.StartHeadersVector(builder, len)
for i = len, 1, -1 do
builder:PrependUOffsetTRelative(textEntries[i])
end
local vec = builder:EndVector(len)
http_resp_call_resp.Start(builder)
http_resp_call_resp.AddHeaders(builder, vec)
elseif case.modify_status then
local status = call_req:Status()
if status == 200 then
status = 304
end
http_resp_call_resp.Start(builder)
http_resp_call_resp.AddStatus(builder, status)
elseif case.extra_info then
ask_extra_info(sock, case.extra_info)
http_resp_call_resp.Start(builder)
else
http_resp_call_resp.Start(builder)
end
local resp = http_resp_call_resp.End(builder)
builder:Finish(resp)
data = builder:Output()
end
local ok, err = ext.send(sock, ty, data)
if not ok then
ngx.log(ngx.ERR, err)
return
end
ngx.log(ngx.WARN, "send rpc call response successfully")
end
function _M.header_too_short()
local sock = ngx.req.socket()
local ty, data = ext.receive(sock)
if not ty then
ngx.log(ngx.ERR, data)
return
end
ngx.log(ngx.WARN, "receive rpc call successfully")
local ok, err = sock:send({string.char(2), string.char(1)})
if not ok then
ngx.log(ngx.ERR, err)
return
end
end
function _M.data_too_short()
local sock = ngx.req.socket()
local ty, data = ext.receive(sock)
if not ty then
ngx.log(ngx.ERR, data)
return
end
ngx.log(ngx.WARN, "receive rpc call successfully")
local ok, err = sock:send({string.char(2), string.char(1), string.rep(string.char(0), 3)})
if not ok then
ngx.log(ngx.ERR, err)
return
end
end
return _M

View File

@@ -0,0 +1,63 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local cjson = require("cjson")
local http = require("resty.http")
local _M = {}
function _M.fetch_logs_from_loki(from, to, options)
options = options or {}
local direction = options.direction or "backward"
local limit = options.limit or "10"
local query = options.query or [[{job="apisix"} | json]]
local url = options.url or "http://127.0.0.1:3100/loki/api/v1/query_range"
local headers = options.headers or {
["X-Scope-OrgID"] = "tenant_1"
}
local httpc = http.new()
local res, err = httpc:request_uri(url, {
query = {
start = from,
["end"] = to,
direction = direction,
limit = limit,
query = query,
},
headers = headers
})
if not res or err then
return nil, err
end
if res.status > 300 then
return nil, "HTTP status code: " .. res.status .. ", body: " .. res.body
end
local data = cjson.decode(res.body)
if not data then
return nil, "failed to decode response body: " .. res.body
end
return data, nil
end
return _M

View File

@@ -0,0 +1,136 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local http = require "resty.http"
local _M = {}
-- Request APISIX and redirect to keycloak,
-- Login keycloak and return the res of APISIX
function _M.login_keycloak(uri, username, password)
local httpc = http.new()
local res, err = httpc:request_uri(uri, {method = "GET"})
if not res then
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
-- Use 500 to indicate error.
return nil, "Initial request was not redirected to ID provider authorization endpoint."
else
-- Extract cookies. Important since OIDC module tracks state with a session cookie.
local cookies = res.headers['Set-Cookie']
-- Concatenate cookies into one string as expected when sent in request header.
local cookie_str = _M.concatenate_cookies(cookies)
-- Call authorization endpoint we were redirected to.
-- Note: This typically returns a login form which is the case here for Keycloak as well.
-- However, how we process the form to perform the login is specific to Keycloak and
-- possibly even the version used.
res, err = httpc:request_uri(res.headers['Location'], {method = "GET"})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 200 then
-- Unexpected response.
return nil, res.body
end
-- Check if response code was ok.
if res.status ~= 200 then
return nil, "unexpected status " .. res.status
end
-- From the returned form, extract the submit URI and parameters.
local uri, params = res.body:match('.*action="(.*)%?(.*)" method="post">')
-- Substitute escaped ampersand in parameters.
params = params:gsub("&amp;", "&")
-- Get all cookies returned. Probably not so important since not part of OIDC specification.
local auth_cookies = res.headers['Set-Cookie']
-- Concatenate cookies into one string as expected when sent in request header.
local auth_cookie_str = _M.concatenate_cookies(auth_cookies)
-- Invoke the submit URI with parameters and cookies, adding username
-- and password in the body.
-- Note: Username and password are specific to the Keycloak Docker image used.
res, err = httpc:request_uri(uri .. "?" .. params, {
method = "POST",
body = "username=" .. username .. "&password=" .. password,
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
["Cookie"] = auth_cookie_str
}
})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
return nil, "Login form submission did not return redirect to redirect URI."
end
-- Extract the redirect URI from the response header.
-- TODO: Consider validating this against the plugin configuration.
local redirect_uri = res.headers['Location']
-- Invoke the redirect URI (which contains the authorization code as an URL parameter).
res, err = httpc:request_uri(redirect_uri, {
method = "GET",
headers = {
["Cookie"] = cookie_str
}
})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
return nil, "Invoking redirect URI with authorization code" ..
"did not return redirect to original URI."
end
return res, nil
end
end
-- Concatenate cookies into one string as expected when sent in request header.
function _M.concatenate_cookies(cookies)
local cookie_str = ""
if type(cookies) == 'string' then
cookie_str = cookies:match('([^;]*); .*')
else
-- Must be a table.
local len = #cookies
if len > 0 then
cookie_str = cookies[1]:match('([^;]*); .*')
for i = 2, len do
cookie_str = cookie_str .. "; " .. cookies[i]:match('([^;]*); .*')
end
end
end
return cookie_str, nil
end
return _M

View File

@@ -0,0 +1,215 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local http = require "resty.http"
local _M = {}
local default_opts = {
idp_uri = "http://127.0.0.1:8080/realms/test/protocol/cas",
cas_callback_uri = "/cas_callback",
logout_uri = "/logout",
}
function _M.get_default_opts()
return default_opts
end
-- Login keycloak and return the login original uri
function _M.login_keycloak(uri, username, password)
local httpc = http.new()
local res, err = httpc:request_uri(uri, {method = "GET"})
if not res then
return nil, err
elseif res.status ~= 302 then
return nil, "login was not redirected to keycloak."
else
local cookies = res.headers['Set-Cookie']
local cookie_str = _M.concatenate_cookies(cookies)
res, err = httpc:request_uri(res.headers['Location'], {method = "GET"})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 200 then
-- Unexpected response.
return nil, res.body
end
-- From the returned form, extract the submit URI and parameters.
local uri, params = res.body:match('.*action="(.*)%?(.*)" method="post">')
-- Substitute escaped ampersand in parameters.
params = params:gsub("&amp;", "&")
local auth_cookies = res.headers['Set-Cookie']
-- Concatenate cookies into one string as expected when sent in request header.
local auth_cookie_str = _M.concatenate_cookies(auth_cookies)
-- Invoke the submit URI with parameters and cookies, adding username
-- and password in the body.
-- Note: Username and password are specific to the Keycloak Docker image used.
res, err = httpc:request_uri(uri .. "?" .. params, {
method = "POST",
body = "username=" .. username .. "&password=" .. password,
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
["Cookie"] = auth_cookie_str
}
})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
return nil, "Login form submission did not return redirect to redirect URI."
end
local keycloak_cookie_str = _M.concatenate_cookies(res.headers['Set-Cookie'])
-- login callback
local redirect_uri = res.headers['Location']
res, err = httpc:request_uri(redirect_uri, {
method = "GET",
headers = {
["Cookie"] = cookie_str
}
})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
return nil, "login callback: " ..
"did not return redirect to original URI."
end
cookies = res.headers['Set-Cookie']
cookie_str = _M.concatenate_cookies(cookies)
return res, nil, cookie_str, keycloak_cookie_str
end
end
-- Login keycloak and return the login original uri
function _M.login_keycloak_for_second_sp(uri, keycloak_cookie_str)
local httpc = http.new()
local res, err = httpc:request_uri(uri, {method = "GET"})
if not res then
return nil, err
elseif res.status ~= 302 then
return nil, "login was not redirected to keycloak."
end
local cookies = res.headers['Set-Cookie']
local cookie_str = _M.concatenate_cookies(cookies)
res, err = httpc:request_uri(res.headers['Location'], {
method = "GET",
headers = {
["Cookie"] = keycloak_cookie_str
}
})
ngx.log(ngx.INFO, keycloak_cookie_str)
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
return nil, res.body
end
-- login callback
res, err = httpc:request_uri(res.headers['Location'], {
method = "GET",
headers = {
["Cookie"] = cookie_str
}
})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 302 then
-- Not a redirect which we expect.
return nil, "login callback: " ..
"did not return redirect to original URI."
end
cookies = res.headers['Set-Cookie']
cookie_str = _M.concatenate_cookies(cookies)
return res, nil, cookie_str
end
function _M.logout_keycloak(uri, cookie_str, keycloak_cookie_str)
local httpc = http.new()
local res, err = httpc:request_uri(uri, {
method = "GET",
headers = {
["Cookie"] = cookie_str
}
})
if not res then
return nil, err
elseif res.status ~= 302 then
return nil, "logout was not redirected to keycloak."
else
-- keycloak logout
res, err = httpc:request_uri(res.headers['Location'], {
method = "GET",
headers = {
["Cookie"] = keycloak_cookie_str
}
})
if not res then
-- No response, must be an error.
return nil, err
elseif res.status ~= 200 then
return nil, "Logout did not return 200."
end
return res, nil
end
end
-- Concatenate cookies into one string as expected when sent in request header.
function _M.concatenate_cookies(cookies)
local cookie_str = ""
if type(cookies) == 'string' then
cookie_str = cookies:match('([^;]*); .*')
else
-- Must be a table.
local len = #cookies
if len > 0 then
cookie_str = cookies[1]:match('([^;]*); .*')
for i = 2, len do
cookie_str = cookie_str .. "; " .. cookies[i]:match('([^;]*); .*')
end
end
end
return cookie_str, nil
end
return _M

View File

@@ -0,0 +1,78 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local json_decode = require("toolkit.json").decode
local json_encode = require("toolkit.json").encode
local ngx = ngx
local socket = ngx.req.socket
local _M = {}
function _M.dogstatsd()
local sock, err = socket()
if not sock then
core.log.error("failed to get the request socket: ", err)
return
end
while true do
local data, err = sock:receive()
if not data then
if err and err ~= "no more data" then
core.log.error("socket error, returning: ", err)
end
return
end
core.log.warn("message received: ", data)
end
end
function _M.loggly()
local sock, err = socket()
if not sock then
core.log.error("failed to get the request socket: ", err)
return
end
while true do
local data, err = sock:receive()
if not data then
if err and err ~= "no more data" then
core.log.error("socket error, returning: ", err)
end
return
end
local m, err = ngx.re.match(data, "(^[ -~]*] )([ -~]*)")
if not m then
core.log.error("unknown data received, failed to extract: ", err)
return
end
if #m ~= 2 then
core.log.error("failed to match two (header, log body) subgroups", #m)
end
-- m[1] contains syslog header header <14>1 .... & m[2] contains actual log body
local logbody = json_decode(m[2])
-- order keys
logbody = json_encode(logbody)
core.log.warn("message received: ", m[1] .. logbody)
end
end
return _M

View File

@@ -0,0 +1,128 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local ws_client = require "resty.websocket.client"
local protoc = require("protoc")
local pb = require("pb")
local _M = {}
local mt = { __index = _M }
local pb_state
local function load_proto()
pb.state(nil)
protoc.reload()
pb.option("int64_as_string")
local pubsub_protoc = protoc.new()
pubsub_protoc:addpath("apisix/include/apisix/model")
local ok, err = pcall(pubsub_protoc.loadfile, pubsub_protoc, "pubsub.proto")
if not ok then
ngx.log(ngx.ERR, "failed to load protocol: "..err)
return err
end
pb_state = pb.state(nil)
end
local function init_websocket_client(endpoint)
local ws, err = ws_client:new()
if not ws then
ngx.log(ngx.ERR, "failed to create websocket client: "..err)
return nil, err
end
local ok, err = ws:connect(endpoint)
if not ok then
ngx.log(ngx.ERR, "failed to connect: "..err)
return nil, err
end
return ws
end
function _M.new_ws(server)
local err = load_proto()
if err then
return nil, err
end
local ws, err = init_websocket_client(server)
if not ws then
return nil, err
end
local obj = setmetatable({
type = "ws",
ws_client = ws,
}, mt)
return obj
end
function _M.send_recv_ws_binary(self, data, is_raw)
pb.state(pb_state)
local ws = self.ws_client
if not is_raw then
data = pb.encode("PubSubReq", data)
end
local _, err = ws:send_binary(data)
if err then
return nil, err
end
local raw_data, _, err = ws:recv_frame()
if not raw_data then
ngx.log(ngx.ERR, "failed to receive the frame: ", err)
return nil, err
end
local data, err = pb.decode("PubSubResp", raw_data)
if not data then
ngx.log(ngx.ERR, "failed to decode the frame: ", err)
return nil, err
end
return data
end
function _M.send_recv_ws_text(self, text)
pb.state(pb_state)
local ws = self.ws_client
local _, err = ws:send_text(text)
if err then
return nil, err
end
local raw_data, _, err = ws:recv_frame()
if not raw_data then
ngx.log(ngx.ERR, "failed to receive the frame: ", err)
return nil, err
end
local data, err = pb.decode("PubSubResp", raw_data)
if not data then
ngx.log(ngx.ERR, "failed to decode the frame: ", err)
return nil, err
end
return data
end
function _M.close_ws(self)
self.ws_client:send_close()
end
return _M

View File

@@ -0,0 +1,787 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local json_decode = require("toolkit.json").decode
local json_encode = require("toolkit.json").encode
local rsa_public_key = [[
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw86xcJwNxL2MkWnjIGiw
94QY78Sq89dLqMdV/Ku2GIX9lYkbS0VDGtmxDGJLBOYW4cKTX+pigJyzglLgE+nD
z3VJf2oCqSV74gTyEdi7sw9e1rCyR6dR8VA7LEpIHwmhnDhhjXy1IYSKRdiVHLS5
sYmaAGckpUo3MLqUrgydGj5tFzvK/R/ELuZBdlZM+XuWxYry05r860E3uL+VdVCO
oU4RJQknlJnTRd7ht8KKcZb6uM14C057i26zX/xnOJpaVflA4EyEo99hKQAdr8Sh
G70MOLYvGCZxl1o8S3q4X67MxcPlfJaXnbog2AOOGRaFar88XiLFWTbXMCLuz7xD
zQIDAQAB
-----END PUBLIC KEY-----]]
local rsa_private_key = [[
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR
aeMgaLD3hBjvxKrz10uox1X8q7YYhf2ViRtLRUMa2bEMYksE5hbhwpNf6mKAnLOC
UuAT6cPPdUl/agKpJXviBPIR2LuzD17WsLJHp1HxUDssSkgfCaGcOGGNfLUhhIpF
2JUctLmxiZoAZySlSjcwupSuDJ0aPm0XO8r9H8Qu5kF2Vkz5e5bFivLTmvzrQTe4
v5V1UI6hThElCSeUmdNF3uG3wopxlvq4zXgLTnuLbrNf/Gc4mlpV+UDgTISj32Ep
AB2vxKEbvQw4ti8YJnGXWjxLerhfrszFw+V8lpeduiDYA44ZFoVqvzxeIsVZNtcw
Iu7PvEPNAgMBAAECggEAVpyN9m7A1F631/aLheFpLgMbeKt4puV7zQtnaJ2XrZ9P
PR7pmNDpTu4uF3k/D8qrIm+L+uhVa+hkquf3wDct6w1JVnfQ93riImbnoKdK13ic
DcEZCwLjByfjFMNCxZ/gAZca55fbExlqhFy6EHmMjhB8s2LsXcTHRuGxNI/Vyi49
sxECibe0U53aqdJbVWrphIS67cpwl4TUkN6mrHsNuDYNJ9dgkpapoqp4FTFQsBqC
afOK5qgJ68dWZ47FBUng+AZjdCncqAIuJxxItGVQP6YPsFs+OXcivIVHJr363TpC
l85FfdvqWV5OGBbwSKhNwiTNUVvfSQVmtURGWG/HbQKBgQD4gZ1z9+Lx19kT9WTz
lw93lxso++uhAPDTKviyWSRoEe5aN3LCd4My+/Aj+sk4ON/s2BV3ska5Im93j+vC
rCv3uPn1n2jUhWuJ3bDqipeTW4n/CQA2m/8vd26TMk22yOkkqw2MIA8sjJ//SD7g
tdG7up6DgGMP4hgbO89uGU7DAwKBgQDJtkKd0grh3u52Foeh9YaiAgYRwc65IE16
UyD1OJxIuX/dYQDLlo5KyyngFa1ZhWIs7qC7r3xXH+10kfJY+Q+5YMjmZjlL8SR1
Ujqd02R9F2//6OeswyReachJZbZdtiEw3lPa4jVFYfhSe0M2ZPxMwvoXb25eyCNI
1lYjSKq87wKBgHnLTNghjeDp4UKe6rNYPgRm0rDrhziJtX5JeUov1mALKb6dnmkh
GfRK9g8sQqKDfXwfC6Z2gaMK9YaryujGaWYoCpoPXtmJ6oLPXH4XHuLh4mhUiP46
xn8FEfSimuQS4/FMxH8A128GHQSI7AhGFFzlwfrBWcvXC+mNDsTvMmLxAoGARc+4
upppfccETQZ7JsitMgD1TMwA2f2eEwoWTAitvlXFNT9PYSbYVHaAJbga6PLLCbYF
FzAjHpxEOKYSdEyu7n/ayDL0/Z2V+qzc8KarDsg/0RgwppBbU/nUgeKb/U79qcYo
y4ai3UKNCS70Ei1dTMvmdpnwXwlxfNIBufB6dy0CgYBMYq9Lc31GkC6PcGEEbx6W
vjImOadWZbuOVnvEQjb5XCdcOsWsMcg96PtoeuyyHmhnEF1GsMzcIdQv/PHrvYpK
Yp8D0aqsLEgwGrJQER26FPpKmyIwvcL+nm6q5W31PnU9AOC/WEkB6Zs58hsMzD2S
kEJQcmfVew5mFXyxuEn3zA==
-----END PRIVATE KEY-----]]
local _M = {}
local function inject_headers()
local hdrs = ngx.req.get_headers()
for k, v in pairs(hdrs) do
if k:sub(1, 5) == "resp-" then
ngx.header[k:sub(6)] = v
end
end
end
function _M.hello()
ngx.req.read_body()
local s = "hello world"
ngx.header['Content-Length'] = #s + 1
ngx.say(s)
end
function _M.hello_chunked()
ngx.print("hell")
ngx.flush(true)
ngx.print("o w")
ngx.flush(true)
ngx.say("orld")
end
function _M.hello1()
ngx.say("hello1 world")
end
-- Fake endpoint, needed for testing authz-keycloak plugin.
function _M.course_foo()
ngx.say("course foo")
end
function _M.server_port()
ngx.print(ngx.var.server_port)
end
_M.server_port_route2 = _M.server_port
_M.server_port_hello = _M.server_port
_M.server_port_aa = _M.server_port
function _M.limit_conn()
ngx.sleep(0.3)
ngx.say("hello world")
end
function _M.plugin_proxy_rewrite()
ngx.say("uri: ", ngx.var.uri)
ngx.say("host: ", ngx.var.host)
ngx.say("scheme: ", ngx.var.scheme)
ngx.log(ngx.WARN, "plugin_proxy_rewrite get method: ", ngx.req.get_method())
end
function _M.plugin_proxy_rewrite_args()
ngx.say("uri: ", ngx.var.uri)
local args = ngx.req.get_uri_args()
local keys = {}
for k, _ in pairs(args) do
table.insert(keys, k)
end
table.sort(keys)
for _, key in ipairs(keys) do
if type(args[key]) == "table" then
ngx.say(key, ": ", table.concat(args[key], ','))
else
ngx.say(key, ": ", args[key])
end
end
end
function _M.specific_status()
local status = ngx.var.http_x_test_upstream_status
if status ~= nil then
ngx.status = status
ngx.say("upstream status: ", status)
end
end
function _M.status()
ngx.log(ngx.WARN, "client request host: ", ngx.var.http_host)
ngx.say("ok")
end
function _M.ewma()
if ngx.var.server_port == "1981"
or ngx.var.server_port == "1982" then
ngx.sleep(0.2)
else
ngx.sleep(0.1)
end
ngx.print(ngx.var.server_port)
end
local builtin_hdr_ignore_list = {
["x-forwarded-for"] = true,
["x-forwarded-proto"] = true,
["x-forwarded-host"] = true,
["x-forwarded-port"] = true,
}
function _M.uri()
ngx.say("uri: ", ngx.var.uri)
local headers = ngx.req.get_headers()
local keys = {}
for k in pairs(headers) do
if not builtin_hdr_ignore_list[k] then
table.insert(keys, k)
end
end
table.sort(keys)
for _, key in ipairs(keys) do
ngx.say(key, ": ", headers[key])
end
end
_M.uri_plugin_proxy_rewrite = _M.uri
_M.uri_plugin_proxy_rewrite_args = _M.uri
function _M.old_uri()
ngx.say("uri: ", ngx.var.uri)
local headers = ngx.req.get_headers()
local keys = {}
for k in pairs(headers) do
table.insert(keys, k)
end
table.sort(keys)
for _, key in ipairs(keys) do
ngx.say(key, ": ", headers[key])
end
end
function _M.opentracing()
ngx.say("opentracing")
end
function _M.with_header()
--split into multiple chunk
ngx.say("hello")
ngx.say("world")
ngx.say("!")
end
function _M.mock_zipkin()
ngx.req.read_body()
local data = ngx.req.get_body_data()
ngx.log(ngx.NOTICE, data)
local spans = json_decode(data)
local ver = ngx.req.get_uri_args()['span_version']
if ver == "1" then
if #spans ~= 5 then
ngx.log(ngx.ERR, "wrong number of spans: ", #spans)
ngx.exit(400)
end
else
if #spans ~= 3 then
-- request/proxy/response
ngx.log(ngx.ERR, "wrong number of spans: ", #spans)
ngx.exit(400)
end
end
for _, span in pairs(spans) do
local prefix = string.sub(span.name, 1, 6)
if prefix ~= 'apisix' then
ngx.log(ngx.ERR, "wrong prefix of name", prefix)
ngx.exit(400)
end
if not span.traceId then
ngx.log(ngx.ERR, "missing trace id")
ngx.exit(400)
end
if not span.localEndpoint then
ngx.log(ngx.ERR, "missing local endpoint")
ngx.exit(400)
end
if span.localEndpoint.serviceName ~= 'APISIX'
and span.localEndpoint.serviceName ~= 'apisix' then
ngx.log(ngx.ERR, "wrong serviceName: ", span.localEndpoint.serviceName)
ngx.exit(400)
end
if span.localEndpoint.port ~= 1984 then
ngx.log(ngx.ERR, "wrong port: ", span.localEndpoint.port)
ngx.exit(400)
end
local server_addr = ngx.req.get_uri_args()['server_addr']
if server_addr then
if span.localEndpoint.ipv4 ~= server_addr then
ngx.log(ngx.ERR, "server_addr mismatched")
ngx.exit(400)
end
end
end
end
function _M.wolf_rbac_login_rest()
ngx.req.read_body()
local data = ngx.req.get_body_data()
local args = json_decode(data)
if not args.username then
ngx.say(json_encode({ok=false, reason="ERR_USERNAME_MISSING"}))
ngx.exit(0)
end
if not args.password then
ngx.say(json_encode({ok=false, reason="ERR_PASSWORD_MISSING"}))
ngx.exit(0)
end
if args.username ~= "admin" then
ngx.say(json_encode({ok=false, reason="ERR_USER_NOT_FOUND"}))
ngx.exit(0)
end
if args.password ~= "123456" then
ngx.say(json_encode({ok=false, reason="ERR_PASSWORD_ERROR"}))
ngx.exit(0)
end
ngx.say(json_encode({ok=true, data={token="wolf-rbac-token",
userInfo={nickname="administrator",username="admin", id="100"}}}))
end
function _M.wolf_rbac_access_check()
local headers = ngx.req.get_headers()
local token = headers['x-rbac-token']
if token ~= 'wolf-rbac-token' then
ngx.say(json_encode({ok=false, reason="ERR_TOKEN_INVALID"}))
ngx.exit(0)
end
local args = ngx.req.get_uri_args()
local resName = args.resName
if resName == '/hello' or resName == '/wolf/rbac/custom/headers' then
ngx.say(json_encode({ok=true,
data={ userInfo={nickname="administrator",
username="admin", id="100"} }}))
elseif resName == '/hello/500' then
ngx.status = 500
ngx.say(json_encode({ok=false, reason="ERR_SERVER_ERROR"}))
elseif resName == '/hello/401' then
ngx.status = 401
ngx.say(json_encode({ok=false, reason="ERR_TOKEN_INVALID"}))
else
ngx.status = 403
ngx.say(json_encode({ok=false, reason="ERR_ACCESS_DENIED"}))
end
end
function _M.wolf_rbac_user_info()
local headers = ngx.req.get_headers()
local token = headers['x-rbac-token']
if token ~= 'wolf-rbac-token' then
ngx.say(json_encode({ok=false, reason="ERR_TOKEN_INVALID"}))
ngx.exit(0)
end
ngx.say(json_encode({ok=true,
data={ userInfo={nickname="administrator", username="admin", id="100"} }}))
end
function _M.wolf_rbac_change_pwd()
ngx.req.read_body()
local data = ngx.req.get_body_data()
local args = json_decode(data)
if args.oldPassword ~= "123456" then
ngx.say(json_encode({ok=false, reason="ERR_OLD_PASSWORD_INCORRECT"}))
ngx.exit(0)
end
ngx.say(json_encode({ok=true, data={ }}))
end
function _M.wolf_rbac_custom_headers()
local headers = ngx.req.get_headers()
ngx.say('id:' .. headers['X-UserId'] .. ',username:' .. headers['X-Username']
.. ',nickname:' .. headers['X-Nickname'])
end
function _M.websocket_handshake()
local websocket = require "resty.websocket.server"
local wb, err = websocket:new()
if not wb then
ngx.log(ngx.ERR, "failed to new websocket: ", err)
return ngx.exit(400)
end
local bytes, err = wb:send_text("hello")
if not bytes then
ngx.log(ngx.ERR, "failed to send text: ", err)
return ngx.exit(444)
end
end
_M.websocket_handshake_route = _M.websocket_handshake
function _M.api_breaker()
ngx.exit(tonumber(ngx.var.arg_code))
end
function _M.mysleep()
ngx.sleep(tonumber(ngx.var.arg_seconds))
if ngx.var.arg_abort then
ngx.exit(ngx.ERROR)
else
ngx.say(ngx.var.arg_seconds)
end
end
local function print_uri()
ngx.say(ngx.var.uri)
end
for i = 1, 100 do
_M["print_uri_" .. i] = print_uri
end
function _M.print_uri_detailed()
ngx.say("ngx.var.uri: ", ngx.var.uri)
ngx.say("ngx.var.request_uri: ", ngx.var.request_uri)
end
function _M.headers()
local args = ngx.req.get_uri_args()
for name, val in pairs(args) do
ngx.header[name] = nil
ngx.header[name] = val
end
ngx.say("/headers")
end
function _M.echo()
ngx.req.read_body()
local hdrs = ngx.req.get_headers()
for k, v in pairs(hdrs) do
ngx.header[k] = v
end
ngx.print(ngx.req.get_body_data() or "")
end
function _M.log()
ngx.req.read_body()
local body = ngx.req.get_body_data()
local ct = ngx.var.content_type
if ct ~= "text/plain" then
body = json_decode(body)
body = json_encode(body)
end
ngx.log(ngx.WARN, "request log: ", body or "nil")
end
function _M.server_error()
error("500 Internal Server Error")
end
function _M.log_request()
ngx.log(ngx.WARN, "uri: ", ngx.var.uri)
local headers = ngx.req.get_headers()
local keys = {}
for k in pairs(headers) do
table.insert(keys, k)
end
table.sort(keys)
for _, key in ipairs(keys) do
ngx.log(ngx.WARN, key, ": ", headers[key])
end
end
function _M.v3_auth_authenticate()
ngx.log(ngx.WARN, "etcd auth failed!")
end
function _M._well_known_openid_configuration()
local t = require("lib.test_admin")
local openid_data = t.read_file("t/plugin/openid-connect/configuration.json")
ngx.say(openid_data)
end
function _M.google_logging_token()
local args = ngx.req.get_uri_args()
local args_token_type = args.token_type or "Bearer"
ngx.req.read_body()
local data = ngx.decode_args(ngx.req.get_body_data())
local jwt = require("resty.jwt")
local access_scopes = "https://apisix.apache.org/logs:admin"
local verify = jwt:verify(rsa_public_key, data["assertion"])
if not verify.verified then
ngx.status = 401
ngx.say(json_encode({ error = "identity authentication failed" }))
return
end
local scopes_valid = type(verify.payload.scope) == "string" and
verify.payload.scope:find(access_scopes)
if not scopes_valid then
ngx.status = 403
ngx.say(json_encode({ error = "no access to this scopes" }))
return
end
local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
if expire_time <= 0 then
expire_time = 0
end
local jwt_token = jwt:sign(rsa_private_key, {
header = { typ = "JWT", alg = "RS256" },
payload = { exp = verify.payload.exp, scope = access_scopes }
})
ngx.say(json_encode({
access_token = jwt_token,
expires_in = expire_time,
token_type = args_token_type
}))
end
function _M.google_logging_entries()
local args = ngx.req.get_uri_args()
local args_token_type = args.token_type or "Bearer"
ngx.req.read_body()
local data = ngx.req.get_body_data()
local jwt = require("resty.jwt")
local access_scopes = "https://apisix.apache.org/logs:admin"
local headers = ngx.req.get_headers()
local token = headers["Authorization"]
if not token then
ngx.status = 401
ngx.say(json_encode({ error = "authentication header not exists" }))
return
end
token = string.sub(token, #args_token_type + 2)
local verify = jwt:verify(rsa_public_key, token)
if not verify.verified then
ngx.status = 401
ngx.say(json_encode({ error = "identity authentication failed" }))
return
end
local scopes_valid = type(verify.payload.scope) == "string" and
verify.payload.scope:find(access_scopes)
if not scopes_valid then
ngx.status = 403
ngx.say(json_encode({ error = "no access to this scopes" }))
return
end
local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
if expire_time <= 0 then
ngx.status = 403
ngx.say(json_encode({ error = "token has expired" }))
return
end
ngx.say(data)
end
function _M.google_secret_token()
local args = ngx.req.get_uri_args()
local args_token_type = args.token_type or "Bearer"
ngx.req.read_body()
local data = ngx.decode_args(ngx.req.get_body_data())
local jwt = require("resty.jwt")
local access_scopes = "https://www.googleapis.com/auth/cloud"
local verify = jwt:verify(rsa_public_key, data["assertion"])
if not verify.verified then
ngx.status = 401
ngx.say(json_encode({ error = "identity authentication failed" }))
return
end
local scopes_valid = type(verify.payload.scope) == "string" and
verify.payload.scope:find(access_scopes)
if not scopes_valid then
ngx.status = 403
ngx.say(json_encode({ error = "no access to this scope" }))
return
end
local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
if expire_time <= 0 then
expire_time = 0
end
local jwt_token = jwt:sign(rsa_private_key, {
header = { typ = "JWT", alg = "RS256" },
payload = { exp = verify.payload.exp, scope = access_scopes }
})
ngx.say(json_encode({
access_token = jwt_token,
expires_in = expire_time,
token_type = args_token_type
}))
end
function _M.google_secret_apisix_jack()
local args = ngx.req.get_uri_args()
local args_token_type = args.token_type or "Bearer"
local jwt = require("resty.jwt")
local access_scopes = "https://www.googleapis.com/auth/cloud"
local headers = ngx.req.get_headers()
local token = headers["Authorization"]
if not token then
ngx.status = 401
ngx.say(json_encode({ error = "authentication header not exists" }))
return
end
token = string.sub(token, #args_token_type + 2)
local verify = jwt:verify(rsa_public_key, token)
if not verify.verified then
ngx.status = 401
ngx.say(json_encode({ error = "identity authentication failed" }))
return
end
local scopes_valid = type(verify.payload.scope) == "string" and
verify.payload.scope:find(access_scopes)
if not scopes_valid then
ngx.status = 403
ngx.say(json_encode({ error = "no access to this scope" }))
return
end
local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
if expire_time <= 0 then
ngx.status = 403
ngx.say(json_encode({ error = "token has expired" }))
return
end
local response = {
name = "projects/647037004838/secrets/apisix/versions/1",
payload = {
data = "eyJrZXkiOiJ2YWx1ZSJ9",
dataCrc32c = "2296192492"
}
}
ngx.status = 200
ngx.say(json_encode(response))
end
function _M.google_secret_apisix_error_jack()
local args = ngx.req.get_uri_args()
local args_token_type = args.token_type or "Bearer"
local jwt = require("resty.jwt")
local access_scopes = "https://www.googleapis.com/auth/root/cloud"
local headers = ngx.req.get_headers()
local token = headers["Authorization"]
if not token then
ngx.status = 401
ngx.say(json_encode({ error = "authentication header not exists" }))
return
end
token = string.sub(token, #args_token_type + 2)
local verify = jwt:verify(rsa_public_key, token)
if not verify.verified then
ngx.status = 401
ngx.say(json_encode({ error = "identity authentication failed" }))
return
end
local scopes_valid = type(verify.payload.scope) == "string" and
verify.payload.scope:find(access_scopes)
if not scopes_valid then
ngx.status = 403
ngx.say(json_encode({ error = "no access to this scope" }))
return
end
local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
if expire_time <= 0 then
ngx.status = 403
ngx.say(json_encode({ error = "token has expired" }))
return
end
local response = {
name = "projects/647037004838/secrets/apisix_error/versions/1",
payload = {
data = "eyJrZXkiOiJ2YWx1ZSJ9",
dataCrc32c = "2296192492"
}
}
ngx.status = 200
ngx.say(json_encode(response))
end
function _M.google_secret_apisix_mysql()
local args = ngx.req.get_uri_args()
local args_token_type = args.token_type or "Bearer"
local jwt = require("resty.jwt")
local access_scopes = "https://www.googleapis.com/auth/cloud"
local headers = ngx.req.get_headers()
local token = headers["Authorization"]
if not token then
ngx.status = 401
ngx.say(json_encode({ error = "authentication header not exists" }))
return
end
token = string.sub(token, #args_token_type + 2)
local verify = jwt:verify(rsa_public_key, token)
if not verify.verified then
ngx.status = 401
ngx.say(json_encode({ error = "identity authentication failed" }))
return
end
local scopes_valid = type(verify.payload.scope) == "string" and
verify.payload.scope:find(access_scopes)
if not scopes_valid then
ngx.status = 403
ngx.say(json_encode({ error = "no access to this scope" }))
return
end
local expire_time = (verify.payload.exp or ngx.time()) - ngx.time()
if expire_time <= 0 then
ngx.status = 403
ngx.say(json_encode({ error = "token has expired" }))
return
end
local response = {
name = "projects/647037004838/secrets/apisix/versions/1",
payload = {
data = "c2VjcmV0",
dataCrc32c = "0xB03C4D4D"
}
}
ngx.status = 200
ngx.say(json_encode(response))
end
function _M.plugin_proxy_rewrite_resp_header()
ngx.req.read_body()
local s = "plugin_proxy_rewrite_resp_header"
ngx.header['Content-Length'] = #s + 1
ngx.say(s)
end
-- Please add your fake upstream above
function _M.go()
local action = string.sub(ngx.var.uri, 2)
action = string.gsub(action, "[/\\.-]", "_")
if not action or not _M[action] then
ngx.log(ngx.WARN, "undefined path in test server, uri: ", ngx.var.request_uri)
return ngx.exit(404)
end
inject_headers()
return _M[action]()
end
function _M.clickhouse_logger_server()
ngx.req.read_body()
local data = ngx.req.get_body_data()
local headers = ngx.req.get_headers()
ngx.log(ngx.WARN, "clickhouse body: ", data)
for k, v in pairs(headers) do
ngx.log(ngx.WARN, "clickhouse headers: " .. k .. ":" .. v)
end
ngx.say("ok")
end
function _M.mock_compressed_upstream_response()
local s = "compressed_response"
ngx.header['Content-Encoding'] = 'gzip'
ngx.say(s)
end
return _M

View File

@@ -0,0 +1,272 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local http = require("resty.http")
local json = require("toolkit.json")
local core = require("apisix.core")
local aes = require "resty.aes"
local ngx_encode_base64 = ngx.encode_base64
local str_find = core.string.find
local dir_names = {}
local _M = {}
local function com_tab(pattern, data, deep)
deep = deep or 1
for k, v in pairs(pattern) do
dir_names[deep] = k
if v == ngx.null then
v = nil
end
if type(v) == "table" and data[k] then
local ok, err = com_tab(v, data[k], deep + 1)
if not ok then
return false, err
end
elseif v ~= data[k] then
return false, "path: " .. table.concat(dir_names, "->", 1, deep)
.. " expect: " .. tostring(v) .. " got: "
.. tostring(data[k])
end
end
return true
end
local methods = {
[ngx.HTTP_GET ] = "GET",
[ngx.HTTP_HEAD ] = "HEAD",
[ngx.HTTP_PUT ] = "PUT",
[ngx.HTTP_POST ] = "POST",
[ngx.HTTP_DELETE ] = "DELETE",
[ngx.HTTP_OPTIONS] = "OPTIONS",
[ngx.HTTP_PATCH] = "PATCH",
[ngx.HTTP_TRACE] = "TRACE",
}
function _M.test_ipv6(uri)
local sock = ngx.socket.tcp()
local ok, err = sock:connect("[::1]", 1984)
if not ok then
ngx.say("failed to connect: ", err)
return
end
ngx.say("connected: ", ok)
local req = "GET " .. uri .. " HTTP/1.0\r\nHost: localhost\r\n"
.. "Connection: close\r\n\r\n"
-- req = "OK"
-- ngx.log(ngx.WARN, "req: ", req)
local bytes, err = sock:send(req)
if not bytes then
ngx.say("failed to send request: ", err)
return
end
ngx.say("request sent: ", bytes)
while true do
local line, err, part = sock:receive()
if line then
if line ~= "" then
ngx.say("received: ", line)
else
ngx.say("received:")
end
else
ngx.say("failed to receive a line: ", err, " [", part, "]")
break
end
end
ok, err = sock:close()
ngx.say("close: ", ok, " ", err)
end
function _M.comp_tab(left_tab, right_tab)
local err
dir_names = {}
local _
if type(left_tab) == "string" then
left_tab, _, err = json.decode(left_tab)
if not left_tab then
return false, "failed to decode expected data: " .. err
end
end
if type(right_tab) == "string" then
right_tab, _, err = json.decode(right_tab)
if not right_tab then
return false, "failed to decode expected data: " .. err
end
end
local ok, err = com_tab(left_tab, right_tab)
if not ok then
return false, err
end
return true
end
local function set_yaml(fn, data)
local profile = os.getenv("APISIX_PROFILE")
if profile then
fn = fn .. "-" .. profile .. ".yaml"
else
fn = fn .. ".yaml"
end
local f = assert(io.open(os.getenv("TEST_NGINX_HTML_DIR") .. "/../conf/" .. fn, 'w'))
assert(f:write(data))
f:close()
end
function _M.set_config_yaml(data)
set_yaml("config", data)
end
function _M.set_apisix_yaml(data)
set_yaml("apisix", data)
end
function _M.test(uri, method, body, pattern, headers)
if not headers then
headers = {}
end
if not headers["Content-Type"] then
headers["Content-Type"] = "application/x-www-form-urlencoded"
end
if type(body) == "table" then
-- {} will be encoded as '[]' whether decode_array_with_array_mt or not
body = json.encode(body)
end
if type(pattern) == "table" then
pattern = json.encode(pattern)
end
if type(method) == "number" then
method = methods[method]
end
local httpc = http.new()
-- https://github.com/ledgetech/lua-resty-http
uri = ngx.var.scheme .. "://" .. ngx.var.server_addr
.. ":" .. ngx.var.server_port .. uri
local res, err = httpc:request_uri(uri,
{
method = method,
body = body,
keepalive = false,
headers = headers,
}
)
if not res then
ngx.log(ngx.ERR, "failed http: ", err)
return nil, err
end
if res.status >= 300 then
return res.status, res.body, res.headers
end
if pattern == nil then
return res.status, "passed", res.body, res.headers
end
local res_data = json.decode(res.body)
local ok, err = _M.comp_tab(pattern, res_data)
if not ok then
return 500, "failed, " .. err, res_data
end
return 200, "passed", res_data, res.headers
end
function _M.read_file(path)
local f = assert(io.open(path, "rb"))
local cert = f:read("*all")
f:close()
return cert
end
function _M.req_self_with_http(uri, method, body, headers)
if type(body) == "table" then
body = json.encode(body)
end
if type(method) == "number" then
method = methods[method]
end
headers = headers or {}
local httpc = http.new()
-- https://github.com/ledgetech/lua-resty-http
uri = ngx.var.scheme .. "://" .. ngx.var.server_addr
.. ":" .. ngx.var.server_port .. uri
headers["Content-Type"] = "application/x-www-form-urlencoded"
local res, err = httpc:request_uri(uri,
{
method = method,
body = body,
keepalive = false,
headers = headers,
}
)
return res, err
end
function _M.aes_encrypt(origin)
local iv = "1234567890123456"
local aes_128_cbc_with_iv = assert(aes:new(iv, nil, aes.cipher(128, "cbc"), {iv=iv}))
if aes_128_cbc_with_iv ~= nil and str_find(origin, "---") then
local encrypted = aes_128_cbc_with_iv:encrypt(origin)
if encrypted == nil then
core.log.error("failed to encrypt key[", origin, "] ")
return origin
end
return ngx_encode_base64(encrypted)
end
return origin
end
return _M

View File

@@ -0,0 +1,62 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
--
-- Don't edit existing code, because the hooks are identified by line number.
-- Instead, append new code to this file.
--
local _M = {}
function _M.run1()
local var1 = "hello"
local var2 = "world"
return var1 .. var2
end
local upvar1 = 2
local upvar2 = "yes"
function _M.run2()
return upvar1
end
function _M.run3()
return upvar1 .. upvar2
end
local str = string.rep("a", 8192) .. "llzz"
local sk = require("socket")
function _M.hot1()
local t1 = sk.gettime()
for i=1,100000 do
string.find(str, "ll", 1, true)
end
local t2 = sk.gettime()
return t2 - t1
end
function _M.hot2()
local t1 = sk.gettime()
for i=1,100000 do
string.find(str, "ll", 1, true)
end
local t2 = sk.gettime()
return t2 - t1
end
return _M