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:
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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>
|
@@ -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);
|
||||
}
|
@@ -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>
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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) {}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
@@ -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
|
@@ -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
|
@@ -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>
|
@@ -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>
|
@@ -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();
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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>
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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) {}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
@@ -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
|
@@ -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
|
@@ -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>
|
32
CloudronPackages/APISIX/apisix-source/t/lib/etcd.proto
Normal file
32
CloudronPackages/APISIX/apisix-source/t/lib/etcd.proto
Normal 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) {}
|
||||
}
|
652
CloudronPackages/APISIX/apisix-source/t/lib/ext-plugin.lua
Normal file
652
CloudronPackages/APISIX/apisix-source/t/lib/ext-plugin.lua
Normal 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
|
63
CloudronPackages/APISIX/apisix-source/t/lib/grafana_loki.lua
Normal file
63
CloudronPackages/APISIX/apisix-source/t/lib/grafana_loki.lua
Normal 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
|
136
CloudronPackages/APISIX/apisix-source/t/lib/keycloak.lua
Normal file
136
CloudronPackages/APISIX/apisix-source/t/lib/keycloak.lua
Normal 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("&", "&")
|
||||
|
||||
-- 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
|
215
CloudronPackages/APISIX/apisix-source/t/lib/keycloak_cas.lua
Normal file
215
CloudronPackages/APISIX/apisix-source/t/lib/keycloak_cas.lua
Normal 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("&", "&")
|
||||
|
||||
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
|
78
CloudronPackages/APISIX/apisix-source/t/lib/mock_layer4.lua
Normal file
78
CloudronPackages/APISIX/apisix-source/t/lib/mock_layer4.lua
Normal 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
|
128
CloudronPackages/APISIX/apisix-source/t/lib/pubsub.lua
Normal file
128
CloudronPackages/APISIX/apisix-source/t/lib/pubsub.lua
Normal 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
|
787
CloudronPackages/APISIX/apisix-source/t/lib/server.lua
Normal file
787
CloudronPackages/APISIX/apisix-source/t/lib/server.lua
Normal 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
|
272
CloudronPackages/APISIX/apisix-source/t/lib/test_admin.lua
Normal file
272
CloudronPackages/APISIX/apisix-source/t/lib/test_admin.lua
Normal 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
|
62
CloudronPackages/APISIX/apisix-source/t/lib/test_inspect.lua
Normal file
62
CloudronPackages/APISIX/apisix-source/t/lib/test_inspect.lua
Normal 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
|
Reference in New Issue
Block a user