You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ro...@apache.org on 2021/11/11 11:49:59 UTC

[iotdb] branch master updated: [IOTDB-1859] IoTDB REST Data Service: Framework (#4280)

This is an automated email from the ASF dual-hosted git repository.

rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new c4b9c2b  [IOTDB-1859] IoTDB REST Data Service: Framework (#4280)
c4b9c2b is described below

commit c4b9c2b087478a56061feb56ab09f6cbe054f053
Author: CloudWise-Lukemiao <76...@users.noreply.github.com>
AuthorDate: Thu Nov 11 19:49:32 2021 +0800

    [IOTDB-1859] IoTDB REST Data Service: Framework (#4280)
    
    Co-authored-by: Steve Yurong Su <ro...@apache.org>
---
 .../Communication-Service-Protocol/RestService.md  | 268 ++++++++++++++++++++
 openapi/pom.xml                                    | 124 +++++++++
 openapi/src/main/openapi3/iotdb-rest.yaml          | 167 ++++++++++++
 pom.xml                                            |   7 +-
 server/pom.xml                                     |   7 +
 .../resources/conf/iotdb-engine.properties         |   2 +-
 .../assembly/resources/conf/iotdb-rest.properties  |  55 ++++
 .../iotdb/db/conf/rest/IoTDBRestServiceCheck.java  |  65 +++++
 .../iotdb/db/conf/rest/IoTDBRestServiceConfig.java | 143 +++++++++++
 .../db/conf/rest/IoTDBRestServiceDescriptor.java   | 162 ++++++++++++
 .../java/org/apache/iotdb/db/rest/RestService.java | 156 ++++++++++++
 .../iotdb/db/rest/filter/ApiOriginFilter.java      |  45 ++++
 .../iotdb/db/rest/filter/AuthorizationFilter.java  | 125 +++++++++
 .../iotdb/db/rest/filter/BasicSecurityContext.java |  56 +++++
 .../java/org/apache/iotdb/db/rest/filter/User.java |  38 +++
 .../org/apache/iotdb/db/rest/filter/UserCache.java |  56 +++++
 .../db/rest/handler/AuthorizationHandler.java      |  52 ++++
 .../iotdb/db/rest/handler/ExceptionHandler.java    |  69 +++++
 .../handler/PhysicalPlanConstructionHandler.java   | 156 ++++++++++++
 .../iotdb/db/rest/handler/QueryDataSetHandler.java |  95 +++++++
 .../db/rest/handler/RequestValidationHandler.java  |  39 +++
 .../iotdb/db/rest/impl/PingApiServiceImpl.java     |  37 +++
 .../iotdb/db/rest/impl/RestApiServiceImpl.java     | 144 +++++++++++
 .../java/org/apache/iotdb/db/service/IoTDB.java    |   8 +-
 .../org/apache/iotdb/db/service/ServiceType.java   |   3 +-
 .../apache/iotdb/db/rest/IoTDBRestServiceIT.java   | 280 +++++++++++++++++++++
 server/src/test/resources/iotdb-rest.properties    |  55 ++++
 site/src/main/.vuepress/config.js                  |   1 +
 28 files changed, 2410 insertions(+), 5 deletions(-)

diff --git a/docs/zh/UserGuide/Communication-Service-Protocol/RestService.md b/docs/zh/UserGuide/Communication-Service-Protocol/RestService.md
new file mode 100644
index 0000000..37dbdd7
--- /dev/null
+++ b/docs/zh/UserGuide/Communication-Service-Protocol/RestService.md
@@ -0,0 +1,268 @@
+<!--
+
+    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.
+
+-->
+
+## RESTful 服务
+IoTDB 的 RESTful 服务可用于查询、写入和管理操作,它使用 OpenAPI 标准来定义接口并生成框架。
+
+
+
+### 鉴权
+RESTful 服务使用了基础(basic)鉴权,每次 URL 请求都需要在 header 中携带 `'Authorization': 'Basic ' + base64.encode(username + ':' + password)`。
+
+
+
+### 接口
+
+#### ping
+
+请求方式:`GET`
+
+请求路径:http://ip:port/ping
+
+示例中使用的用户名为:root,密码为:root
+
+请求示例:
+
+```shell
+$ curl -H "Authorization:Basic cm9vdDpyb2901" http://127.0.0.1:18080/ping
+```
+响应参数:
+
+|参数名称  |参数类型  |参数描述|
+| ------------ | ------------ | ------------|
+| code | integer |  状态码 |
+| message  |  string | 信息提示 |
+
+响应示例:
+```json
+{
+  "code": 200,
+  "message": "SUCCESS_STATUS"
+}
+```
+用户名密码认证失败示例:
+```json
+{
+  "code": 600,
+  "message": "WRONG_LOGIN_PASSWORD_ERROR"
+}
+```
+
+
+
+#### query
+
+请求方式:`POST`
+
+请求头:`application/json`
+
+请求路径:http://ip:port/rest/v1/query
+
+参数说明:
+
+|参数名称  |参数类型  |是否必填|参数描述|
+| ------------ | ------------ | ------------ |------------ |
+|  sql | string | 是  |   |
+
+请求示例:
+```shell
+curl -H "Content-Type:application/json" -H "Authorization:Basic cm9vdDpyb290" -X POST --data '{"sql":"select s3, s4, s3 + 1 from root.sg27 limit 2"}' http://127.0.0.1:18080/rest/v1/query
+```
+
+响应参数:
+
+|参数名称  |参数类型  |参数描述|
+| ------------ | ------------ | ------------|
+| expressions | array | 结果集列名的数组 |
+| timestamps | array | 时间戳列 |
+|values|array|值列数组,列数与结果集列名数组的长度相同|
+
+响应示例:
+
+```json
+{
+  "expressions": [
+    "root.sg27.s3",
+    "root.sg27.s4",
+    "root.sg27.s3 + 1"
+  ],
+  "timestamps": [1,2],
+  "values": [[11,12],[false,true],[12.0,23.0]]
+}
+```
+
+
+
+#### nonQuery
+
+请求方式:`POST`
+
+请求头:`application/json`
+
+请求路径:http://ip:port/rest/v1/nonQuery
+
+参数说明:
+
+|参数名称  |参数类型  |是否必填|参数描述|
+| ------------ | ------------ | ------------ |------------ |
+|  sql | string | 是  |   |
+
+请求示例:
+```shell
+curl -H "Content-Type:application/json" -H "Authorization:Basic cm9vdDpyb290" -X POST --data '{"sql":"set storage group to root.ln"}' http://127.0.0.1:18080/rest/v1/nonQuery
+```
+
+响应参数:
+
+|参数名称  |参数类型  |参数描述|
+| ------------ | ------------ | ------------|
+| code | integer |  状态码 |
+| message  |  string | 信息提示 |
+
+响应示例:
+```json
+{
+  "code": 200,
+  "message": "SUCCESS_STATUS"
+}
+```
+
+
+
+#### insertTablet
+
+请求方式:`POST`
+
+请求头:`application/json`
+
+请求路径:http://ip:port/rest/v1/insertTablet
+
+参数说明:
+
+|参数名称  |参数类型  |是否必填|参数描述|
+| ------------ | ------------ | ------------ |------------ |
+|  timestamps | array | 是 |  时间列  |
+|  measurements | array | 是  | 测点名称 |
+| dataTypes | array | 是  | 数据类型  |
+|  values | array | 是  | 值列,每一列中的值可以为 `null` |
+|  isAligned | boolean | 是  | 是否是对齐时间序列 |
+|  deviceId | boolean | 是  | 设备名称 |
+
+请求示例:
+```shell
+curl -H "Content-Type:application/json" -H "Authorization:Basic cm9vdDpyb290" -X POST --data '{"timestamps":[1635232143960,1635232153960],"measurements":["s3","s4"],"dataTypes":["INT32","BOOLEAN"],"values":[[11,null],[false,true]],"isAligned":false,"deviceId":"root.sg27"}' http://127.0.0.1:18080/rest/v1/insertTablet
+```
+
+响应参数:
+
+|参数名称  |参数类型  |参数描述|
+| ------------ | ------------ | ------------|
+| code | integer |  状态码 |
+| message  |  string | 信息提示 |
+
+响应示例:
+```json
+{
+  "code": 200,
+  "message": "SUCCESS_STATUS"
+}
+```
+
+
+
+### 配置
+
+配置位于 `iotdb-rest.properties` 中。
+
+
+
+* 将 `enable_rest_service` 设置为 `true` 以启用该模块,而将 `false` 设置为禁用该模块。默认情况下,该值为 `false`。
+
+```properties
+enable_rest_service=true
+```
+
+* 仅在 `enable_rest_service=true` 时生效。将 `rest_service_port `设置为数字(1025~65535),以自定义REST服务套接字端口。默认情况下,值为 `18080`。
+
+```properties
+rest_service_port=18080
+```
+
+* REST Service 是否开启 SSL 配置,将 `enable_https` 设置为 `true` 以启用该模块,而将 `false` 设置为禁用该模块。默认情况下,该值为 `false`。
+
+```properties
+enable_https=false
+```
+
+* keyStore 所在路径(非必填)
+
+```properties
+key_store_path=
+```
+
+
+* keyStore 密码(非必填)
+
+```properties
+key_store_pwd=
+```
+
+
+* trustStore 所在路径(非必填)
+
+```properties
+trust_store_path=
+```
+
+* trustStore 密码(非必填)
+
+```properties
+trust_store_pwd=
+```
+
+
+* SSL 超时时间,单位为秒
+
+```properties
+idle_timeout=5000
+```
+
+
+* 缓存客户登录信息的过期时间(用于加速用户鉴权的速度,单位为秒,默认是8个小时)
+
+```properties
+cache_expire=28800
+```
+
+
+* 缓存中存储的最大用户数量(默认是100)
+
+```properties
+cache_max_num=100
+```
+
+* 缓存初始容量(默认是10)
+
+```properties
+cache_init_num=10
+```
+
+
diff --git a/openapi/pom.xml b/openapi/pom.xml
new file mode 100644
index 0000000..e44e7da
--- /dev/null
+++ b/openapi/pom.xml
@@ -0,0 +1,124 @@
+<?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.
+
+-->
+<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/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>iotdb-parent</artifactId>
+        <groupId>org.apache.iotdb</groupId>
+        <version>0.13.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>openapi</artifactId>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.9.1</version>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.basedir}/target/generated-sources/java/src/gen/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-jersey2-jaxrs</artifactId>
+            <scope>compile</scope>
+            <version>${swagger.core.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet.api.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+    </dependencies>
+    <profiles>
+        <profile>
+            <id>openapi-generation</id>
+            <activation>
+                <file>
+                    <exists>src/main/openapi3</exists>
+                </file>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.openapitools</groupId>
+                        <artifactId>openapi-generator-maven-plugin</artifactId>
+                        <version>${openapi.generator.version}</version>
+                        <executions>
+                            <execution>
+                                <id>generate-java-rest-codes</id>
+                                <goals>
+                                    <goal>generate</goal>
+                                </goals>
+                                <configuration>
+                                    <inputSpec>${project.basedir}/src/main/openapi3/iotdb-rest.yaml</inputSpec>
+                                    <output>${project.build.directory}/generated-sources/java</output>
+                                    <apiPackage>org.apache.iotdb.db.rest</apiPackage>
+                                    <modelPackage>org.apache.iotdb.db.rest.model</modelPackage>
+                                    <invokerPackage>org.apache.iotdb.db.rest.invoker</invokerPackage>
+                                    <generatorName>jaxrs-jersey</generatorName>
+                                    <groupId>org.apache.iotdb</groupId>
+                                    <artifactId>iotdb-rest-service</artifactId>
+                                    <artifactVersion>${project.version}</artifactVersion>
+                                    <addCompileSourceRoot>true</addCompileSourceRoot>
+                                    <configOptions>
+                                        <licenseName>Apache License 2.0</licenseName>
+                                        <groupId>org.apache.iotdb</groupId>
+                                        <artifactId>iotdb-rest-service</artifactId>
+                                        <artifactVersion>${project.version}</artifactVersion>
+                                        <dateLibrary>java8</dateLibrary>
+                                        <useGzipFeature>true</useGzipFeature>
+                                    </configOptions>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/openapi/src/main/openapi3/iotdb-rest.yaml b/openapi/src/main/openapi3/iotdb-rest.yaml
new file mode 100644
index 0000000..c1d0315
--- /dev/null
+++ b/openapi/src/main/openapi3/iotdb-rest.yaml
@@ -0,0 +1,167 @@
+#
+# 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.
+#
+
+openapi: 3.0.0
+info:
+  title: iotdb-rest
+  description: IoTDB Rest API for Grafana, Prometheus, etc..
+  license:
+    name: Apache 2.0
+  version: 1.0.0
+servers:
+- url: http://127.0.0.1:18080/
+  description: api
+security:
+- basic: []
+paths:
+  /ping:
+    get:
+      responses:
+        "200":
+          description: ExecutionStatus
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ExecutionStatus'
+      operationId: tryPing
+
+  /rest/v1/insertTablet:
+    post:
+      summary: insertTablet
+      description: insertTablet
+      operationId: insertTablet
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/InsertTabletRequest'
+      responses:
+        "200":
+          description: ExecutionStatus
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ExecutionStatus'
+
+  /rest/v1/nonQuery:
+    post:
+      summary: executeNonQueryStatement
+      description: executeNonQueryStatement
+      operationId: executeNonQueryStatement
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/SQL'
+      responses:
+        "200":
+          description: ExecutionStatus
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ExecutionStatus'
+
+  /rest/v1/query:
+    post:
+      summary: executeQueryStatement
+      description: executeQueryStatement
+      operationId: executeQueryStatement
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/SQL'
+      responses:
+        "200":
+          description: QueryDataSet
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/QueryDataSet'
+
+components:
+  schemas:
+    SQL:
+      title: SQL
+      type: object
+      properties:
+        sql:
+          type: string
+
+    InsertTabletRequest:
+      title: InsertTabletRequest
+      type: object
+      properties:
+        timestamps:
+          type: array
+          items:
+            type: long
+        measurements:
+          type: array
+          items:
+            type: string
+        dataTypes:
+          type: array
+          items:
+            type: string
+        values:
+          type: array
+          items:
+            type: array
+            items:
+              type: object
+        isAligned:
+          type: boolean
+        deviceId:
+          type: string
+
+    ExecutionStatus:
+      type: object
+      properties:
+        code:
+          type: integer
+        message:
+          type: string
+
+    QueryDataSet:
+      type: object
+      properties:
+        expressions:
+          type: array
+          items:
+            type: string
+        timestamps:
+          type: array
+          items:
+            type: long
+        values:
+          type: array
+          items:
+            type: array
+            items:
+              type: object
+
+  securitySchemes:
+    basic:
+      type: http
+      scheme: basic
+    APIKey:
+      type: apiKey
+      name: API Key
+      in: header
diff --git a/pom.xml b/pom.xml
index f8101d7..ff7da57 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,7 @@
         <module>jdbc</module>
         <module>session</module>
         <module>cli</module>
+        <module>openapi</module>
         <module>server</module>
         <module>example</module>
         <module>grafana</module>
@@ -126,7 +127,7 @@
         <!-- keep consistent with client-cpp/tools/thrift/pom.xml-->
         <thrift.version>0.14.1</thrift.version>
         <airline.version>0.8</airline.version>
-        <jackson.version>2.11.0</jackson.version>
+        <jackson.version>2.10.0</jackson.version>
         <antlr4.version>4.8-1</antlr4.version>
         <common.cli.version>1.3.1</common.cli.version>
         <common.codec.version>1.13</common.codec.version>
@@ -161,6 +162,10 @@
         <spotless.version>2.4.2</spotless.version>
         <httpclient.version>4.5.13</httpclient.version>
         <httpcore.version>4.4.13</httpcore.version>
+        <!-- for REST service -->
+        <swagger.core.version>1.5.18</swagger.core.version>
+        <servlet.api.version>2.5</servlet.api.version>
+        <openapi.generator.version>5.0.0</openapi.generator.version>
     </properties>
     <!--
         if we claim dependencies in dependencyManagement, then we do not claim
diff --git a/server/pom.xml b/server/pom.xml
index 62910ab..a03a150 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -88,10 +88,17 @@
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
+            <version>${jetty.version}</version>
         </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-webapp</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.iotdb</groupId>
+            <artifactId>openapi</artifactId>
+            <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>io.dropwizard.metrics</groupId>
diff --git a/server/src/assembly/resources/conf/iotdb-engine.properties b/server/src/assembly/resources/conf/iotdb-engine.properties
index f306d17..ab779be 100644
--- a/server/src/assembly/resources/conf/iotdb-engine.properties
+++ b/server/src/assembly/resources/conf/iotdb-engine.properties
@@ -927,4 +927,4 @@ timestamp_precision=ms
 
 # admin password, default is root
 # Datatype: string
-# admin_password=root
+# admin_password=root
\ No newline at end of file
diff --git a/server/src/assembly/resources/conf/iotdb-rest.properties b/server/src/assembly/resources/conf/iotdb-rest.properties
new file mode 100644
index 0000000..af7d8de
--- /dev/null
+++ b/server/src/assembly/resources/conf/iotdb-rest.properties
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+####################
+### REST Service Configuration
+####################
+
+# Is the REST service enabled
+# enable_rest_service=false
+
+# the binding port of the REST service
+# rest_service_port=18080
+
+# is SSL enabled
+# enable_https=false
+
+# SSL key store path
+# key_store_path=
+
+# SSL key store password
+# key_store_pwd=
+
+# SSL trust store path
+# trust_store_path=
+
+# SSL trust store password.
+# trust_store_pwd=
+
+# SSL timeout (in seconds)
+# idle_timeout_in_seconds=50000
+
+# the expiration time of the user login infomation cache (in seconds)
+# cache_expire_in_seconds=28800
+
+# maximum number of users can be stored in the user login cache.
+# cache_max_num=100
+
+# init capacity of users can be stored in the user login cache.
+# cache_init_num=10
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceCheck.java b/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceCheck.java
new file mode 100644
index 0000000..2e413d3
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceCheck.java
@@ -0,0 +1,65 @@
+/*
+ * 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.iotdb.db.conf.rest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class IoTDBRestServiceCheck {
+  private static final Logger LOGGER = LoggerFactory.getLogger(IoTDBRestServiceCheck.class);
+  private static final IoTDBRestServiceConfig CONFIG =
+      IoTDBRestServiceDescriptor.getInstance().getConfig();
+
+  public static IoTDBRestServiceCheck getInstance() {
+    return IoTDBRestServiceConfigCheckHolder.INSTANCE;
+  }
+
+  private static class IoTDBRestServiceConfigCheckHolder {
+
+    private static final IoTDBRestServiceCheck INSTANCE = new IoTDBRestServiceCheck();
+  }
+
+  public void checkConfig() throws IOException {
+    if (CONFIG.getRestServicePort() > 65535 || CONFIG.getRestServicePort() < 1024) {
+      printErrorLogAndExit("rest_service_port");
+    }
+    if (CONFIG.getIdleTimeoutInSeconds() <= 0) {
+      printErrorLogAndExit("idle_timeout_in_seconds");
+    }
+    if (CONFIG.getCacheExpireInSeconds() <= 0) {
+      printErrorLogAndExit("cache_expire_in_seconds");
+    }
+    if (CONFIG.getCacheMaxNum() <= 0) {
+      printErrorLogAndExit("cache_max_num");
+    }
+    if (CONFIG.getCacheInitNum() <= 0) {
+      printErrorLogAndExit("cache_init_num");
+    }
+    if (CONFIG.getCacheInitNum() > CONFIG.getCacheMaxNum()) {
+      printErrorLogAndExit("cache_init_num");
+    }
+  }
+
+  private void printErrorLogAndExit(String property) {
+    LOGGER.error("Wrong config {}, please check!", property);
+    System.exit(-1);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java
new file mode 100644
index 0000000..80ef8d4
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java
@@ -0,0 +1,143 @@
+/*
+ * 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.iotdb.db.conf.rest;
+
+public class IoTDBRestServiceConfig {
+  static final String CONFIG_NAME = "iotdb-rest.properties";
+  /** if the enableRestService is true, we will start REST Service */
+  private boolean enableRestService = false;
+
+  /** set the REST Service port. */
+  private int restServicePort = 18080;
+
+  /** enable the REST Service ssl. */
+  private boolean enableHttps = false;
+
+  /** ssl key Store Path */
+  private String keyStorePath = "";
+
+  /** ssl trust Store Path */
+  private String trustStorePath = "";
+
+  /** ssl key Store password */
+  private String keyStorePwd = "";
+
+  /** ssl trust Store password */
+  private String trustStorePwd = "";
+
+  /** ssl timeout */
+  private int idleTimeoutInSeconds = 50000;
+
+  /** Session expiration time */
+  private int cacheExpireInSeconds = 28800;
+
+  /** maximum number of users stored in cache */
+  private int cacheMaxNum = 100;
+
+  /** init number of users stored in cache */
+  private int cacheInitNum = 10;
+
+  public String getTrustStorePwd() {
+    return trustStorePwd;
+  }
+
+  public void setTrustStorePwd(String trustStorePwd) {
+    this.trustStorePwd = trustStorePwd;
+  }
+
+  public int getIdleTimeoutInSeconds() {
+    return idleTimeoutInSeconds;
+  }
+
+  public void setIdleTimeoutInSeconds(int idleTimeoutInSeconds) {
+    this.idleTimeoutInSeconds = idleTimeoutInSeconds;
+  }
+
+  public String getKeyStorePath() {
+    return keyStorePath;
+  }
+
+  public void setKeyStorePath(String keyStorePath) {
+    this.keyStorePath = keyStorePath;
+  }
+
+  public String getTrustStorePath() {
+    return trustStorePath;
+  }
+
+  public void setTrustStorePath(String trustStorePath) {
+    this.trustStorePath = trustStorePath;
+  }
+
+  public String getKeyStorePwd() {
+    return keyStorePwd;
+  }
+
+  public void setKeyStorePwd(String keyStorePwd) {
+    this.keyStorePwd = keyStorePwd;
+  }
+
+  public int getRestServicePort() {
+    return restServicePort;
+  }
+
+  public void setRestServicePort(int restServicePort) {
+    this.restServicePort = restServicePort;
+  }
+
+  public boolean isEnableHttps() {
+    return enableHttps;
+  }
+
+  public void setEnableHttps(boolean enableHttps) {
+    this.enableHttps = enableHttps;
+  }
+
+  public boolean isEnableRestService() {
+    return enableRestService;
+  }
+
+  public void setEnableRestService(boolean enableRestService) {
+    this.enableRestService = enableRestService;
+  }
+
+  public int getCacheExpireInSeconds() {
+    return cacheExpireInSeconds;
+  }
+
+  public void setCacheExpireInSeconds(int cacheExpireInSeconds) {
+    this.cacheExpireInSeconds = cacheExpireInSeconds;
+  }
+
+  public int getCacheMaxNum() {
+    return cacheMaxNum;
+  }
+
+  public void setCacheMaxNum(int cacheMaxNum) {
+    this.cacheMaxNum = cacheMaxNum;
+  }
+
+  public int getCacheInitNum() {
+    return cacheInitNum;
+  }
+
+  public void setCacheInitNum(int cacheInitNum) {
+    this.cacheInitNum = cacheInitNum;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java b/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java
new file mode 100644
index 0000000..848c64e
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java
@@ -0,0 +1,162 @@
+/*
+ * 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.iotdb.db.conf.rest;
+
+import org.apache.iotdb.db.conf.IoTDBConfig;
+import org.apache.iotdb.db.conf.IoTDBConstant;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+public class IoTDBRestServiceDescriptor {
+  private static final Logger logger = LoggerFactory.getLogger(IoTDBRestServiceDescriptor.class);
+
+  private final IoTDBRestServiceConfig conf = new IoTDBRestServiceConfig();
+
+  protected IoTDBRestServiceDescriptor() {
+    loadProps();
+  }
+
+  public static IoTDBRestServiceDescriptor getInstance() {
+    return IoTDBRestServiceDescriptorHolder.INSTANCE;
+  }
+
+  /** load an property file. */
+  @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning
+  private void loadProps() {
+    URL url = getPropsUrl();
+    if (url == null) {
+      logger.warn("Couldn't load the REST Service configuration from any of the known sources.");
+      return;
+    }
+    try (InputStream inputStream = url.openStream()) {
+      logger.info("Start to read config file {}", url);
+      Properties properties = new Properties();
+      properties.load(inputStream);
+      conf.setEnableRestService(
+          Boolean.parseBoolean(
+              properties.getProperty(
+                  "enable_rest_service", Boolean.toString(conf.isEnableRestService()))));
+      conf.setRestServicePort(
+          Integer.parseInt(
+              properties.getProperty(
+                  "rest_service_port", Integer.toString(conf.getRestServicePort()))));
+
+      conf.setEnableHttps(
+          Boolean.parseBoolean(
+              properties.getProperty("enable_https", Boolean.toString(conf.isEnableHttps()))));
+      conf.setKeyStorePath(properties.getProperty("key_store_path", conf.getKeyStorePath()));
+      conf.setKeyStorePwd(properties.getProperty("key_store_pwd", conf.getKeyStorePwd()));
+      conf.setTrustStorePath(properties.getProperty("trust_store_path", conf.getTrustStorePath()));
+      conf.setTrustStorePwd(properties.getProperty("trust_store_pwd", conf.getTrustStorePwd()));
+      conf.setIdleTimeoutInSeconds(
+          Integer.parseInt(
+              properties.getProperty(
+                  "idle_timeout_in_seconds", Integer.toString(conf.getIdleTimeoutInSeconds()))));
+      conf.setCacheExpireInSeconds(
+          Integer.parseInt(
+              properties.getProperty(
+                  "cache_expire_in_seconds", Integer.toString(conf.getCacheExpireInSeconds()))));
+      conf.setCacheInitNum(
+          Integer.parseInt(
+              properties.getProperty("cache_init_num", Integer.toString(conf.getCacheInitNum()))));
+      conf.setCacheMaxNum(
+          Integer.parseInt(
+              properties.getProperty("cache_max_num", Integer.toString(conf.getCacheMaxNum()))));
+
+    } catch (FileNotFoundException e) {
+      logger.warn("REST service fail to find config file {}", url, e);
+    } catch (IOException e) {
+      logger.warn("REST service cannot load config file, use default configuration", e);
+    } catch (Exception e) {
+      logger.warn("REST service Incorrect format in config file, use default configuration", e);
+    }
+  }
+
+  /**
+   * get props url location
+   *
+   * @return url object if location exit, otherwise null.
+   */
+  public URL getPropsUrl() {
+    // Check if a config-directory was specified first.
+    String urlString = System.getProperty(IoTDBConstant.IOTDB_CONF, null);
+    // If it wasn't, check if a home directory was provided (This usually contains a config)
+    if (urlString == null) {
+      urlString = System.getProperty(IoTDBConstant.IOTDB_HOME, null);
+      if (urlString != null) {
+        urlString =
+            urlString
+                + File.separatorChar
+                + "conf"
+                + File.separatorChar
+                + "rest"
+                + File.separatorChar
+                + IoTDBRestServiceConfig.CONFIG_NAME;
+      } else {
+        // If this too wasn't provided, try to find a default config in the root of the classpath.
+        URL uri = IoTDBConfig.class.getResource("/" + IoTDBRestServiceConfig.CONFIG_NAME);
+        if (uri != null) {
+          return uri;
+        }
+        logger.warn(
+            "Cannot find IOTDB_HOME or IOTDB_CONF environment variable when loading "
+                + "config file {}, use default configuration",
+            IoTDBRestServiceConfig.CONFIG_NAME);
+        // update all data seriesPath
+        return null;
+      }
+    }
+    // If a config location was provided, but it doesn't end with a properties file,
+    // append the default location.
+    else if (!urlString.endsWith(".properties")) {
+      urlString += (File.separatorChar + IoTDBRestServiceConfig.CONFIG_NAME);
+    }
+
+    // If the url doesn't start with "file:" or "classpath:", it's provided as a no path.
+    // So we need to add it to make it a real URL.
+    if (!urlString.startsWith("file:") && !urlString.startsWith("classpath:")) {
+      urlString = "file:" + urlString;
+    }
+    try {
+      return new URL(urlString);
+    } catch (MalformedURLException e) {
+      return null;
+    }
+  }
+
+  public IoTDBRestServiceConfig getConfig() {
+    return conf;
+  }
+
+  private static class IoTDBRestServiceDescriptorHolder {
+
+    private static final IoTDBRestServiceDescriptor INSTANCE = new IoTDBRestServiceDescriptor();
+
+    private IoTDBRestServiceDescriptorHolder() {}
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/RestService.java b/server/src/main/java/org/apache/iotdb/db/rest/RestService.java
new file mode 100644
index 0000000..75928f7
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/RestService.java
@@ -0,0 +1,156 @@
+/*
+ * 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.iotdb.db.rest;
+
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceConfig;
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor;
+import org.apache.iotdb.db.exception.StartupException;
+import org.apache.iotdb.db.rest.filter.ApiOriginFilter;
+import org.apache.iotdb.db.service.IService;
+import org.apache.iotdb.db.service.ServiceType;
+
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.DispatcherType;
+
+import java.util.EnumSet;
+
+public class RestService implements IService {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(RestService.class);
+
+  private static Server server;
+
+  private void startSSL(
+      int port,
+      String keyStorePath,
+      String trustStorePath,
+      String keyStorePwd,
+      String trustStorePwd,
+      int idleTime) {
+    server = new Server();
+
+    HttpConfiguration httpsConfig = new HttpConfiguration();
+    httpsConfig.setSecurePort(port);
+    httpsConfig.addCustomizer(new SecureRequestCustomizer());
+
+    SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+    sslContextFactory.setKeyStorePath(keyStorePath);
+    sslContextFactory.setKeyStorePassword(keyStorePwd);
+    sslContextFactory.setTrustStorePath(trustStorePath);
+    sslContextFactory.setTrustStorePassword(trustStorePwd);
+
+    ServerConnector httpsConnector =
+        new ServerConnector(
+            server,
+            new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+            new HttpConnectionFactory(httpsConfig));
+    httpsConnector.setPort(port);
+    httpsConnector.setIdleTimeout(idleTime);
+    server.addConnector(httpsConnector);
+
+    server.setHandler(constructServletContextHandler());
+    serverStart();
+  }
+
+  private void startNonSSL(int port) {
+    server = new Server(port);
+    server.setHandler(constructServletContextHandler());
+    serverStart();
+  }
+
+  private ServletContextHandler constructServletContextHandler() {
+    ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+    context.addFilter(
+        ApiOriginFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST));
+    ServletHolder holder = context.addServlet(ServletContainer.class, "/*");
+    holder.setInitOrder(1);
+    holder.setInitParameter(
+        "jersey.config.server.provider.packages",
+        "io.swagger.jaxrs.listing, io.swagger.sample.resource, org.apache.iotdb.db.rest");
+    holder.setInitParameter(
+        "jersey.config.server.provider.classnames",
+        "org.glassfish.jersey.media.multipart.MultiPartFeature");
+    holder.setInitParameter("jersey.config.server.wadl.disableWadl", "true");
+    context.setContextPath("/");
+    return context;
+  }
+
+  private void serverStart() {
+    try {
+      server.start();
+    } catch (Exception e) {
+      LOGGER.warn("RestService failed to start: {}", e.getMessage());
+      server.destroy();
+    }
+  }
+
+  @Override
+  public void start() throws StartupException {
+    IoTDBRestServiceConfig config = IoTDBRestServiceDescriptor.getInstance().getConfig();
+    if (IoTDBRestServiceDescriptor.getInstance().getConfig().isEnableHttps()) {
+      startSSL(
+          config.getRestServicePort(),
+          config.getKeyStorePath(),
+          config.getTrustStorePath(),
+          config.getKeyStorePwd(),
+          config.getTrustStorePwd(),
+          config.getIdleTimeoutInSeconds());
+    } else {
+      startNonSSL(config.getRestServicePort());
+    }
+  }
+
+  @Override
+  public void stop() {
+    try {
+      server.stop();
+    } catch (Exception e) {
+      LOGGER.warn("RestService failed to stop: {}", e.getMessage());
+    } finally {
+      server.destroy();
+    }
+  }
+
+  @Override
+  public ServiceType getID() {
+    return ServiceType.REST_SERVICE;
+  }
+
+  public static RestService getInstance() {
+    return RestServiceHolder.INSTANCE;
+  }
+
+  private static class RestServiceHolder {
+
+    private static final RestService INSTANCE = new RestService();
+
+    private RestServiceHolder() {}
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/filter/ApiOriginFilter.java b/server/src/main/java/org/apache/iotdb/db/rest/filter/ApiOriginFilter.java
new file mode 100644
index 0000000..fb3f912
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/filter/ApiOriginFilter.java
@@ -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.
+ */
+
+package org.apache.iotdb.db.rest.filter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+public class ApiOriginFilter implements javax.servlet.Filter {
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+      throws IOException, ServletException {
+    HttpServletResponse res = (HttpServletResponse) response;
+    res.addHeader("Access-Control-Allow-Origin", "*");
+    res.addHeader("Access-Control-Allow-Methods", "GET, POST");
+    res.addHeader("Access-Control-Allow-Headers", "*");
+    chain.doFilter(request, response);
+  }
+
+  @Override
+  public void destroy() {}
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {}
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/filter/AuthorizationFilter.java b/server/src/main/java/org/apache/iotdb/db/rest/filter/AuthorizationFilter.java
new file mode 100644
index 0000000..da21e09
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/filter/AuthorizationFilter.java
@@ -0,0 +1,125 @@
+/*
+ * 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.iotdb.db.rest.filter;
+
+import org.apache.iotdb.db.auth.AuthException;
+import org.apache.iotdb.db.auth.authorizer.BasicAuthorizer;
+import org.apache.iotdb.db.auth.authorizer.IAuthorizer;
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor;
+import org.apache.iotdb.db.rest.model.ExecutionStatus;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.glassfish.jersey.internal.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.annotation.WebFilter;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.Provider;
+
+import java.io.IOException;
+
+@WebFilter("/*")
+@Provider
+public class AuthorizationFilter implements ContainerRequestFilter {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationFilter.class);
+
+  private final IAuthorizer authorizer = BasicAuthorizer.getInstance();
+  private final UserCache userCache = UserCache.getInstance();
+
+  public AuthorizationFilter() throws AuthException {}
+
+  @Override
+  public void filter(ContainerRequestContext containerRequestContext) throws IOException {
+    if ("OPTIONS".equals(containerRequestContext.getMethod())
+        || containerRequestContext.getUriInfo().getPath().equals("swagger.json")) {
+      return;
+    }
+
+    String authorizationHeader = containerRequestContext.getHeaderString("authorization");
+    User user = userCache.getUser(authorizationHeader);
+    if (user == null) {
+      user = checkLogin(containerRequestContext, authorizationHeader);
+      if (user == null) {
+        return;
+      } else {
+        userCache.setUser(authorizationHeader, user);
+      }
+    }
+
+    BasicSecurityContext basicSecurityContext =
+        new BasicSecurityContext(
+            user, IoTDBRestServiceDescriptor.getInstance().getConfig().isEnableHttps());
+    containerRequestContext.setSecurityContext(basicSecurityContext);
+  }
+
+  private User checkLogin(
+      ContainerRequestContext containerRequestContext, String authorizationHeader) {
+
+    String decoded = Base64.decodeAsString(authorizationHeader.replace("Basic ", ""));
+    // todo: support special chars in username and password
+    String[] split = decoded.split(":");
+    if (split.length != 2) {
+      Response resp =
+          Response.status(Status.BAD_REQUEST)
+              .type(MediaType.APPLICATION_JSON)
+              .entity(
+                  new ExecutionStatus()
+                      .code(TSStatusCode.SYSTEM_CHECK_ERROR.getStatusCode())
+                      .message(TSStatusCode.SYSTEM_CHECK_ERROR.name()))
+              .build();
+      containerRequestContext.abortWith(resp);
+      return null;
+    }
+
+    User user = new User();
+    user.setUsername(split[0]);
+    user.setPassword(split[1]);
+    try {
+      if (!authorizer.login(split[0], split[1])) {
+        Response resp =
+            Response.status(Status.OK)
+                .type(MediaType.APPLICATION_JSON)
+                .entity(
+                    new ExecutionStatus()
+                        .code(TSStatusCode.WRONG_LOGIN_PASSWORD_ERROR.getStatusCode())
+                        .message(TSStatusCode.WRONG_LOGIN_PASSWORD_ERROR.name()))
+                .build();
+        containerRequestContext.abortWith(resp);
+        return null;
+      }
+    } catch (AuthException e) {
+      LOGGER.warn(e.getMessage(), e);
+      Response resp =
+          Response.status(Status.INTERNAL_SERVER_ERROR)
+              .type(MediaType.APPLICATION_JSON)
+              .entity(
+                  new ExecutionStatus()
+                      .code(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
+                      .message(e.getMessage()))
+              .build();
+      containerRequestContext.abortWith(resp);
+      return null;
+    }
+    return user;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/filter/BasicSecurityContext.java b/server/src/main/java/org/apache/iotdb/db/rest/filter/BasicSecurityContext.java
new file mode 100644
index 0000000..b25443d
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/filter/BasicSecurityContext.java
@@ -0,0 +1,56 @@
+/*
+ * 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.iotdb.db.rest.filter;
+
+import javax.ws.rs.core.SecurityContext;
+
+import java.security.Principal;
+
+public class BasicSecurityContext implements SecurityContext {
+
+  private final User user;
+  private final boolean secure;
+
+  public BasicSecurityContext(User user, boolean secure) {
+    this.user = user;
+    this.secure = secure;
+  }
+
+  @Override
+  public Principal getUserPrincipal() {
+    return user::getUsername;
+  }
+
+  public User getUser() {
+    return user;
+  }
+
+  @Override
+  public boolean isUserInRole(String role) {
+    return true;
+  }
+
+  @Override
+  public boolean isSecure() {
+    return secure;
+  }
+
+  @Override
+  public String getAuthenticationScheme() {
+    return BASIC_AUTH;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/filter/User.java b/server/src/main/java/org/apache/iotdb/db/rest/filter/User.java
new file mode 100644
index 0000000..f0e505c
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/filter/User.java
@@ -0,0 +1,38 @@
+/*
+ * 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.iotdb.db.rest.filter;
+
+public class User {
+  private String username;
+  private String password;
+
+  public String getPassword() {
+    return password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  public String getUsername() {
+    return username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/filter/UserCache.java b/server/src/main/java/org/apache/iotdb/db/rest/filter/UserCache.java
new file mode 100644
index 0000000..e9204a5
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/filter/UserCache.java
@@ -0,0 +1,56 @@
+/*
+ * 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.iotdb.db.rest.filter;
+
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceConfig;
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+import java.util.concurrent.TimeUnit;
+
+public class UserCache {
+  private static final IoTDBRestServiceConfig CONFIG =
+      IoTDBRestServiceDescriptor.getInstance().getConfig();
+  private final Cache<String, User> cache;
+
+  private UserCache() {
+    cache =
+        Caffeine.newBuilder()
+            .initialCapacity(CONFIG.getCacheInitNum())
+            .maximumSize(CONFIG.getCacheMaxNum())
+            .expireAfterWrite(CONFIG.getCacheExpireInSeconds(), TimeUnit.SECONDS)
+            .build();
+  }
+
+  public static UserCache getInstance() {
+    return UserCacheHolder.INSTANCE;
+  }
+
+  public User getUser(String key) {
+    return cache.getIfPresent(key);
+  }
+
+  public void setUser(String key, User user) {
+    cache.put(key, user);
+  }
+
+  private static class UserCacheHolder {
+    private static final UserCache INSTANCE = new UserCache();
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/handler/AuthorizationHandler.java b/server/src/main/java/org/apache/iotdb/db/rest/handler/AuthorizationHandler.java
new file mode 100644
index 0000000..4404a0b
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/handler/AuthorizationHandler.java
@@ -0,0 +1,52 @@
+/*
+ * 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.iotdb.db.rest.handler;
+
+import org.apache.iotdb.db.auth.AuthException;
+import org.apache.iotdb.db.auth.AuthorityChecker;
+import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.db.rest.model.ExecutionStatus;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+public class AuthorizationHandler {
+  private AuthorizationHandler() {}
+
+  public static Response checkAuthority(
+      SecurityContext securityContext, PhysicalPlan physicalPlan) {
+    try {
+      if (!AuthorityChecker.check(
+          securityContext.getUserPrincipal().getName(),
+          physicalPlan.getAuthPaths(),
+          physicalPlan.getOperatorType(),
+          null)) {
+        return Response.ok()
+            .entity(
+                new ExecutionStatus()
+                    .code(TSStatusCode.NO_PERMISSION_ERROR.getStatusCode())
+                    .message(TSStatusCode.NO_PERMISSION_ERROR.name()))
+            .build();
+      }
+    } catch (AuthException e) {
+      return Response.ok().entity(ExceptionHandler.tryCatchException(e)).build();
+    }
+    return null;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/handler/ExceptionHandler.java b/server/src/main/java/org/apache/iotdb/db/rest/handler/ExceptionHandler.java
new file mode 100644
index 0000000..18a5263
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/handler/ExceptionHandler.java
@@ -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.iotdb.db.rest.handler;
+
+import org.apache.iotdb.db.auth.AuthException;
+import org.apache.iotdb.db.exception.IoTDBException;
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
+import org.apache.iotdb.db.exception.metadata.MetadataException;
+import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.rest.model.ExecutionStatus;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Response.Status;
+
+public class ExceptionHandler {
+  private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandler.class);
+
+  private ExceptionHandler() {}
+
+  public static ExecutionStatus tryCatchException(Exception e) {
+    ExecutionStatus responseResult = new ExecutionStatus();
+    if (e instanceof QueryProcessException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(((QueryProcessException) e).getErrorCode());
+    } else if (e instanceof StorageGroupNotSetException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(((StorageGroupNotSetException) e).getErrorCode());
+    } else if (e instanceof StorageEngineException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(((StorageEngineException) e).getErrorCode());
+    } else if (e instanceof AuthException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(Status.BAD_REQUEST.getStatusCode());
+    } else if (e instanceof IllegalPathException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(((IllegalPathException) e).getErrorCode());
+    } else if (e instanceof MetadataException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(((MetadataException) e).getErrorCode());
+    } else if (e instanceof IoTDBException) {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(((IoTDBException) e).getErrorCode());
+    } else {
+      responseResult.setMessage(e.getMessage());
+      responseResult.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
+    }
+    LOGGER.warn(e.getMessage(), e);
+    return responseResult;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/handler/PhysicalPlanConstructionHandler.java b/server/src/main/java/org/apache/iotdb/db/rest/handler/PhysicalPlanConstructionHandler.java
new file mode 100644
index 0000000..0f2ec43
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/handler/PhysicalPlanConstructionHandler.java
@@ -0,0 +1,156 @@
+/*
+ * 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.iotdb.db.rest.handler;
+
+import org.apache.iotdb.db.exception.WriteProcessRejectException;
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
+import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
+import org.apache.iotdb.db.rest.model.InsertTabletRequest;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.utils.Binary;
+import org.apache.iotdb.tsfile.utils.BitMap;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+public class PhysicalPlanConstructionHandler {
+  private PhysicalPlanConstructionHandler() {}
+
+  public static InsertTabletPlan constructInsertTabletPlan(InsertTabletRequest insertTabletRequest)
+      throws IllegalPathException, WriteProcessRejectException {
+    InsertTabletPlan insertTabletPlan =
+        new InsertTabletPlan(
+            new PartialPath(insertTabletRequest.getDeviceId()),
+            insertTabletRequest.getMeasurements());
+    List<List<Object>> rawData = insertTabletRequest.getValues();
+    List<String> rawDataType = insertTabletRequest.getDataTypes();
+
+    int rowSize = insertTabletRequest.getTimestamps().size();
+    int columnSize = rawDataType.size();
+
+    Object[] columns = new Object[columnSize];
+    BitMap[] bitMaps = new BitMap[columnSize];
+    TSDataType[] dataTypes = new TSDataType[columnSize];
+
+    for (int i = 0; i < columnSize; i++) {
+      dataTypes[i] = TSDataType.valueOf(rawDataType.get(i));
+    }
+
+    for (int columnIndex = 0; columnIndex < columnSize; columnIndex++) {
+      bitMaps[columnIndex] = new BitMap(rowSize);
+      switch (dataTypes[columnIndex]) {
+        case BOOLEAN:
+          boolean[] booleanValues = new boolean[rowSize];
+          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
+            if (rawData.get(columnIndex).get(rowIndex) == null) {
+              bitMaps[columnIndex].mark(rowIndex);
+            } else {
+              booleanValues[rowIndex] = (Boolean) rawData.get(columnIndex).get(rowIndex);
+            }
+          }
+          columns[columnIndex] = booleanValues;
+          break;
+        case INT32:
+          int[] intValues = new int[rowSize];
+          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
+            Object object = rawData.get(columnIndex).get(rowIndex);
+            if (object == null) {
+              bitMaps[columnIndex].mark(rowIndex);
+            } else if (object instanceof Integer) {
+              intValues[rowIndex] = (int) object;
+            } else {
+              throw new WriteProcessRejectException(
+                  "unsupported data type: " + object.getClass().toString());
+            }
+          }
+          columns[columnIndex] = intValues;
+          break;
+        case INT64:
+          long[] longValues = new long[rowSize];
+          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
+            Object object = rawData.get(columnIndex).get(rowIndex);
+            if (object == null) {
+              bitMaps[columnIndex].mark(rowIndex);
+            } else if (object instanceof Integer) {
+              longValues[rowIndex] = (int) object;
+            } else if (object instanceof Long) {
+              longValues[rowIndex] = (long) object;
+            } else {
+              throw new WriteProcessRejectException(
+                  "unsupported data type: " + object.getClass().toString());
+            }
+          }
+          columns[columnIndex] = longValues;
+          break;
+        case FLOAT:
+          float[] floatValues = new float[rowSize];
+          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
+            if (rawData.get(columnIndex).get(rowIndex) == null) {
+              bitMaps[columnIndex].mark(rowIndex);
+            } else {
+              floatValues[rowIndex] =
+                  ((Double) rawData.get(columnIndex).get(rowIndex)).floatValue();
+            }
+          }
+          columns[columnIndex] = floatValues;
+          break;
+        case DOUBLE:
+          double[] doubleValues = new double[rowSize];
+          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
+            if (rawData.get(columnIndex).get(rowIndex) == null) {
+              bitMaps[columnIndex].mark(rowIndex);
+            } else {
+              doubleValues[rowIndex] = (double) rawData.get(columnIndex).get(rowIndex);
+            }
+          }
+          columns[columnIndex] = doubleValues;
+          break;
+        case TEXT:
+          Binary[] binaryValues = new Binary[rowSize];
+          for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
+            if (rawData.get(columnIndex).get(rowIndex) == null) {
+              bitMaps[columnIndex].mark(rowIndex);
+              binaryValues[rowIndex] = new Binary("".getBytes(StandardCharsets.UTF_8));
+            } else {
+              binaryValues[rowIndex] =
+                  new Binary(
+                      rawData
+                          .get(columnIndex)
+                          .get(rowIndex)
+                          .toString()
+                          .getBytes(StandardCharsets.UTF_8));
+            }
+          }
+          columns[columnIndex] = binaryValues;
+          break;
+        default:
+          throw new IllegalArgumentException("Invalid input: " + rawDataType.get(columnIndex));
+      }
+    }
+
+    insertTabletPlan.setTimes(
+        insertTabletRequest.getTimestamps().stream().mapToLong(Long::longValue).toArray());
+    insertTabletPlan.setColumns(columns);
+    insertTabletPlan.setBitMaps(bitMaps);
+    insertTabletPlan.setRowCount(insertTabletRequest.getTimestamps().size());
+    insertTabletPlan.setDataTypes(dataTypes);
+    insertTabletPlan.setAligned(insertTabletRequest.getIsAligned());
+    return insertTabletPlan;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/handler/QueryDataSetHandler.java b/server/src/main/java/org/apache/iotdb/db/rest/handler/QueryDataSetHandler.java
new file mode 100644
index 0000000..e0ebb8b
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/handler/QueryDataSetHandler.java
@@ -0,0 +1,95 @@
+/*
+ * 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.iotdb.db.rest.handler;
+
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.metadata.MetadataException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.qp.executor.IPlanExecutor;
+import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.db.qp.physical.crud.QueryPlan;
+import org.apache.iotdb.db.query.context.QueryContext;
+import org.apache.iotdb.db.query.control.QueryResourceManager;
+import org.apache.iotdb.db.query.expression.ResultColumn;
+import org.apache.iotdb.tsfile.exception.filter.QueryFilterOptimizationException;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.common.Field;
+import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet;
+
+import org.apache.thrift.TException;
+
+import javax.ws.rs.core.Response;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class QueryDataSetHandler {
+
+  public static Response fillDateSet(QueryDataSet dataSet, QueryPlan queryPlan) {
+    org.apache.iotdb.db.rest.model.QueryDataSet queryDataSet =
+        new org.apache.iotdb.db.rest.model.QueryDataSet();
+
+    try {
+      List<ResultColumn> resultColumns = queryPlan.getResultColumns();
+      int[] dataSetIndexes = new int[resultColumns.size()];
+      Map<String, Integer> sourcePathToQueryDataSetIndex = queryPlan.getPathToIndex();
+      for (int i = 0; i < resultColumns.size(); i++) {
+        ResultColumn resultColumn = resultColumns.get(i);
+        queryDataSet.addExpressionsItem(resultColumn.getResultColumnName());
+        queryDataSet.addValuesItem(new ArrayList<>());
+        dataSetIndexes[i] = sourcePathToQueryDataSetIndex.get(resultColumn.getResultColumnName());
+      }
+
+      while (dataSet.hasNext()) {
+        RowRecord rowRecord = dataSet.next();
+        List<Field> fields = rowRecord.getFields();
+
+        queryDataSet.addTimestampsItem(rowRecord.getTimestamp());
+        for (int i = 0; i < resultColumns.size(); i++) {
+          List<Object> values = queryDataSet.getValues().get(i);
+          Field field = fields.get(dataSetIndexes[i]);
+          if (field == null || field.getDataType() == null) {
+            values.add(null);
+          } else {
+            values.add(
+                field.getDataType().equals(TSDataType.TEXT)
+                    ? field.getStringValue()
+                    : field.getObjectValue(field.getDataType()));
+          }
+        }
+      }
+    } catch (IOException e) {
+      return Response.ok().entity(ExceptionHandler.tryCatchException(e)).build();
+    }
+    return Response.ok().entity(queryDataSet).build();
+  }
+
+  public static QueryDataSet constructQueryDataSet(
+      IPlanExecutor executor, PhysicalPlan physicalPlan)
+      throws TException, StorageEngineException, QueryFilterOptimizationException,
+          MetadataException, IOException, InterruptedException, SQLException,
+          QueryProcessException {
+    long queryId = QueryResourceManager.getInstance().assignQueryId(true);
+    QueryContext context = new QueryContext(queryId);
+    return executor.processQuery(physicalPlan, context);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/handler/RequestValidationHandler.java b/server/src/main/java/org/apache/iotdb/db/rest/handler/RequestValidationHandler.java
new file mode 100644
index 0000000..27a06e1
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/handler/RequestValidationHandler.java
@@ -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.
+ */
+
+package org.apache.iotdb.db.rest.handler;
+
+import org.apache.iotdb.db.rest.model.InsertTabletRequest;
+import org.apache.iotdb.db.rest.model.SQL;
+
+import java.util.Objects;
+
+public class RequestValidationHandler {
+  private RequestValidationHandler() {}
+
+  public static void validateSQL(SQL sql) {
+    Objects.requireNonNull(sql.getSql(), "sql should not be null");
+  }
+
+  public static void validateInsertTabletRequest(InsertTabletRequest insertTabletRequest) {
+    Objects.requireNonNull(insertTabletRequest.getTimestamps(), "timestamps should not be null");
+    Objects.requireNonNull(insertTabletRequest.getIsAligned(), "isAligned should not be null");
+    Objects.requireNonNull(insertTabletRequest.getDeviceId(), "deviceId should not be null");
+    Objects.requireNonNull(insertTabletRequest.getDataTypes(), "dataTypes should not be null");
+    Objects.requireNonNull(insertTabletRequest.getValues(), "values should not be null");
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/impl/PingApiServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/rest/impl/PingApiServiceImpl.java
new file mode 100644
index 0000000..d4df79e
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/impl/PingApiServiceImpl.java
@@ -0,0 +1,37 @@
+/*
+ * 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.iotdb.db.rest.impl;
+
+import org.apache.iotdb.db.rest.PingApiService;
+import org.apache.iotdb.db.rest.model.ExecutionStatus;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+public class PingApiServiceImpl extends PingApiService {
+  @Override
+  public Response tryPing(SecurityContext securityContext) {
+    return Response.ok()
+        .entity(
+            new ExecutionStatus()
+                .code(TSStatusCode.SUCCESS_STATUS.getStatusCode())
+                .message(TSStatusCode.SUCCESS_STATUS.name()))
+        .build();
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/rest/impl/RestApiServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/rest/impl/RestApiServiceImpl.java
new file mode 100644
index 0000000..c5c5582
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/rest/impl/RestApiServiceImpl.java
@@ -0,0 +1,144 @@
+/*
+ * 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.iotdb.db.rest.impl;
+
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.qp.Planner;
+import org.apache.iotdb.db.qp.executor.IPlanExecutor;
+import org.apache.iotdb.db.qp.executor.PlanExecutor;
+import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
+import org.apache.iotdb.db.qp.physical.crud.QueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.FlushPlan;
+import org.apache.iotdb.db.qp.physical.sys.SetSystemModePlan;
+import org.apache.iotdb.db.rest.RestApiService;
+import org.apache.iotdb.db.rest.handler.AuthorizationHandler;
+import org.apache.iotdb.db.rest.handler.ExceptionHandler;
+import org.apache.iotdb.db.rest.handler.PhysicalPlanConstructionHandler;
+import org.apache.iotdb.db.rest.handler.QueryDataSetHandler;
+import org.apache.iotdb.db.rest.handler.RequestValidationHandler;
+import org.apache.iotdb.db.rest.model.ExecutionStatus;
+import org.apache.iotdb.db.rest.model.InsertTabletRequest;
+import org.apache.iotdb.db.rest.model.SQL;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+public class RestApiServiceImpl extends RestApiService {
+
+  protected final IPlanExecutor executor = new PlanExecutor(); // todo cluster
+  protected final Planner planner = new Planner();
+
+  public RestApiServiceImpl() throws QueryProcessException {}
+
+  @Override
+  public Response executeNonQueryStatement(SQL sql, SecurityContext securityContext) {
+    try {
+      RequestValidationHandler.validateSQL(sql);
+
+      PhysicalPlan physicalPlan = planner.parseSQLToPhysicalPlan(sql.getSql());
+      Response response = AuthorizationHandler.checkAuthority(securityContext, physicalPlan);
+      if (response != null) {
+        return response;
+      }
+      return Response.ok()
+          .entity(
+              executor.processNonQuery(physicalPlan)
+                  ? new ExecutionStatus()
+                      .code(TSStatusCode.SUCCESS_STATUS.getStatusCode())
+                      .message(TSStatusCode.SUCCESS_STATUS.name())
+                  : new ExecutionStatus()
+                      .code(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode())
+                      .message(TSStatusCode.EXECUTE_STATEMENT_ERROR.name()))
+          .build();
+    } catch (Exception e) {
+      return Response.ok().entity(ExceptionHandler.tryCatchException(e)).build();
+    }
+  }
+
+  @Override
+  public Response executeQueryStatement(SQL sql, SecurityContext securityContext) {
+    try {
+      RequestValidationHandler.validateSQL(sql);
+
+      PhysicalPlan physicalPlan = planner.parseSQLToPhysicalPlan(sql.getSql());
+      if (!(physicalPlan instanceof QueryPlan)) {
+        return Response.ok()
+            .entity(
+                new ExecutionStatus()
+                    .code(TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode())
+                    .message(TSStatusCode.EXECUTE_STATEMENT_ERROR.name()))
+            .build();
+      }
+
+      Response response = AuthorizationHandler.checkAuthority(securityContext, physicalPlan);
+      if (response != null) {
+        return response;
+      }
+      return QueryDataSetHandler.fillDateSet(
+          QueryDataSetHandler.constructQueryDataSet(executor, physicalPlan),
+          (QueryPlan) physicalPlan);
+    } catch (Exception e) {
+      return Response.ok().entity(ExceptionHandler.tryCatchException(e)).build();
+    }
+  }
+
+  @Override
+  public Response insertTablet(
+      InsertTabletRequest insertTabletRequest, SecurityContext securityContext) {
+    try {
+      RequestValidationHandler.validateInsertTabletRequest(insertTabletRequest);
+
+      InsertTabletPlan insertTabletPlan =
+          PhysicalPlanConstructionHandler.constructInsertTabletPlan(insertTabletRequest);
+
+      Response response = AuthorizationHandler.checkAuthority(securityContext, insertTabletPlan);
+      if (response != null) {
+        return response;
+      }
+
+      return Response.ok()
+          .entity(
+              executeNonQuery(insertTabletPlan)
+                  ? new ExecutionStatus()
+                      .code(TSStatusCode.SUCCESS_STATUS.getStatusCode())
+                      .message(TSStatusCode.SUCCESS_STATUS.name())
+                  : new ExecutionStatus()
+                      .code(TSStatusCode.WRITE_PROCESS_ERROR.getStatusCode())
+                      .message(TSStatusCode.WRITE_PROCESS_ERROR.name()))
+          .build();
+    } catch (Exception e) {
+      return Response.ok().entity(ExceptionHandler.tryCatchException(e)).build();
+    }
+  }
+
+  private boolean executeNonQuery(PhysicalPlan plan)
+      throws QueryProcessException, StorageGroupNotSetException, StorageEngineException {
+    if (!(plan instanceof SetSystemModePlan)
+        && !(plan instanceof FlushPlan)
+        && IoTDBDescriptor.getInstance().getConfig().isReadOnly()) {
+      throw new QueryProcessException(
+          "Current system mode is read-only, does not support non-query operation");
+    }
+    return executor.processNonQuery(plan);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
index 29afb31..6ad6d5d 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
@@ -22,6 +22,8 @@ import org.apache.iotdb.db.concurrent.IoTDBDefaultThreadExceptionHandler;
 import org.apache.iotdb.db.conf.IoTDBConfigCheck;
 import org.apache.iotdb.db.conf.IoTDBConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceCheck;
+import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor;
 import org.apache.iotdb.db.cost.statistic.Measurement;
 import org.apache.iotdb.db.cq.ContinuousQueryService;
 import org.apache.iotdb.db.engine.StorageEngine;
@@ -40,6 +42,7 @@ import org.apache.iotdb.db.query.udf.service.UDFRegistrationService;
 import org.apache.iotdb.db.rescon.PrimitiveArrayManager;
 import org.apache.iotdb.db.rescon.SystemInfo;
 import org.apache.iotdb.db.rescon.TVListAllocator;
+import org.apache.iotdb.db.rest.RestService;
 import org.apache.iotdb.db.sync.receiver.SyncServerManager;
 import org.apache.iotdb.db.writelog.manager.MultiFileLogNodeManager;
 
@@ -64,6 +67,7 @@ public class IoTDB implements IoTDBMBean {
   public static void main(String[] args) {
     try {
       IoTDBConfigCheck.getInstance().checkConfig();
+      IoTDBRestServiceCheck.getInstance().checkConfig();
     } catch (ConfigurationException | IOException e) {
       logger.error("meet error when doing start checking", e);
       System.exit(1);
@@ -139,7 +143,9 @@ public class IoTDB implements IoTDBMBean {
     if (IoTDBDescriptor.getInstance().getConfig().isEnableMQTTService()) {
       registerManager.register(MQTTService.getInstance());
     }
-
+    if (IoTDBRestServiceDescriptor.getInstance().getConfig().isEnableRestService()) {
+      registerManager.register(RestService.getInstance());
+    }
     logger.info("IoTDB is set up, now may some sgs are not ready, please wait several seconds...");
 
     while (!StorageEngine.getInstance().isAllSgReady()) {
diff --git a/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java b/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java
index e3c53f0..7513291 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java
@@ -56,7 +56,6 @@ public enum ServiceType {
   SYSTEMINFO_SERVICE("MemTable Monitor Service", "MemTable, Monitor"),
   CONTINUOUS_QUERY_SERVICE("Continuous Query Service", "Continuous Query Service"),
   CLUSTER_INFO_SERVICE("Cluster Monitor Service (thrift-based)", "Cluster Monitor-Thrift"),
-
   CLUSTER_RPC_SERVICE("Cluster RPC Service", "ClusterRPCService"),
   CLUSTER_META_RPC_SERVICE("Cluster Meta RPC Service", "ClusterMetaRPCService"),
   CLUSTER_META_HEART_BEAT_RPC_SERVICE(
@@ -66,7 +65,7 @@ public enum ServiceType {
       "Cluster Data Heartbeat RPC Service", "ClusterDataHeartbeatRPCService"),
   CLUSTER_META_ENGINE("Cluster Meta Engine", "ClusterMetaEngine"),
   CLUSTER_DATA_ENGINE("Cluster Data Engine", "ClusterDataEngine"),
-  ;
+  REST_SERVICE("REST Service", "REST Service");
 
   private final String name;
   private final String jmxName;
diff --git a/server/src/test/java/org/apache/iotdb/db/rest/IoTDBRestServiceIT.java b/server/src/test/java/org/apache/iotdb/db/rest/IoTDBRestServiceIT.java
new file mode 100644
index 0000000..c0496a8
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/rest/IoTDBRestServiceIT.java
@@ -0,0 +1,280 @@
+/*
+ * 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.iotdb.db.rest;
+
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class IoTDBRestServiceIT {
+  @Before
+  public void setUp() throws Exception {
+    EnvironmentUtils.closeStatMonitor();
+    EnvironmentUtils.envSetUp();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    EnvironmentUtils.cleanEnv();
+  }
+
+  private String getAuthorization(String username, String password) {
+    return Base64.getEncoder()
+        .encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
+  }
+
+  @Test
+  public void ping() {
+    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+    HttpGet httpGet = new HttpGet("http://127.0.0.1:18080/ping");
+    CloseableHttpResponse response = null;
+    try {
+      String authorization = getAuthorization("root", "root");
+      httpGet.setHeader("Authorization", authorization);
+      response = httpClient.execute(httpGet);
+      HttpEntity responseEntity = response.getEntity();
+      String message = EntityUtils.toString(responseEntity, "utf-8");
+      JsonObject result = JsonParser.parseString(message).getAsJsonObject();
+      assertEquals(200, Integer.parseInt(result.get("code").toString()));
+    } catch (IOException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    } finally {
+      try {
+        if (httpClient != null) {
+          httpClient.close();
+        }
+        if (response != null) {
+          response.close();
+        }
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail(e.getMessage());
+      }
+    }
+  }
+
+  private HttpPost getHttpPost(String url) {
+    HttpPost httpPost = new HttpPost(url);
+    httpPost.addHeader("Content-type", "application/json; charset=utf-8");
+    httpPost.setHeader("Accept", "application/json");
+    String authorization = getAuthorization("root", "root");
+    httpPost.setHeader("Authorization", authorization);
+    return httpPost;
+  }
+
+  public void rightInsertTablet(CloseableHttpClient httpClient) {
+    CloseableHttpResponse response = null;
+    try {
+      HttpPost httpPost = getHttpPost("http://127.0.0.1:18080/rest/v1/insertTablet");
+      String json =
+          "{\"timestamps\":[1635232143960,1635232153960],\"measurements\":[\"s3\",\"s4\",\"s5\",\"s6\",\"s7\",\"s8\"],\"dataTypes\":[\"TEXT\",\"INT32\",\"INT64\",\"FLOAT\",\"BOOLEAN\",\"DOUBLE\"],\"values\":[[\"2aa\",\"\"],[11,2],[1635000012345555,1635000012345556],[1.41,null],[null,false],[null,3.5555]],\"isAligned\":false,\"deviceId\":\"root.sg25\"}";
+      httpPost.setEntity(new StringEntity(json, Charset.defaultCharset()));
+      response = httpClient.execute(httpPost);
+      HttpEntity responseEntity = response.getEntity();
+      String message = EntityUtils.toString(responseEntity, "utf-8");
+      JsonObject result = JsonParser.parseString(message).getAsJsonObject();
+      assertEquals(200, Integer.parseInt(result.get("code").toString()));
+    } catch (IOException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    } finally {
+      try {
+        if (response != null) {
+          response.close();
+        }
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail(e.getMessage());
+      }
+    }
+  }
+
+  @Test
+  public void errorInsertTablet() {
+    CloseableHttpResponse response = null;
+    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+    try {
+      HttpPost httpPost = getHttpPost("http://127.0.0.1:18080/rest/v1/insertTablet");
+      String json =
+          "{\"timestamps\":[1635232143960,1635232153960],\"measurements\":[\"s3\",\"s4\",\"s5\",\"s6\",\"s7\",\"s8\"],\"dataTypes\":[\"TEXT\",\"INT32\",\"INT64\",\"FLOAT\",\"BOOLEAN\",\"DOUBLE\"],\"values\":[[\"2aa\",\"\"],[111111112312312442352545452323123,2],[16,15],[1.41,null],[null,false],[null,3.55555555555555555555555555555555555555555555312234235345123127318927461482308478123645555555555555555555555555555555555555555555531223423534512312731892746148230847812364]],\"isAligned\":fal [...]
+      httpPost.setEntity(new StringEntity(json, Charset.defaultCharset()));
+      response = httpClient.execute(httpPost);
+      HttpEntity responseEntity = response.getEntity();
+      String message = EntityUtils.toString(responseEntity, "utf-8");
+      JsonObject result = JsonParser.parseString(message).getAsJsonObject();
+      assertEquals(413, Integer.parseInt(result.get("code").toString()));
+    } catch (IOException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    } finally {
+      try {
+        if (response != null) {
+          response.close();
+        }
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail(e.getMessage());
+      }
+    }
+  }
+
+  @Test
+  public void insertAndQuery() {
+    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+    rightInsertTablet(httpClient);
+    query(httpClient);
+    try {
+      httpClient.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  public void query(CloseableHttpClient httpClient) {
+    CloseableHttpResponse response = null;
+    try {
+      HttpPost httpPost = getHttpPost("http://127.0.0.1:18080/rest/v1/query");
+      String sql = "{\"sql\":\"select *,s4+1,s4+1 from root.sg25\"}";
+      httpPost.setEntity(new StringEntity(sql, Charset.defaultCharset()));
+      response = httpClient.execute(httpPost);
+      HttpEntity responseEntity = response.getEntity();
+      String message = EntityUtils.toString(responseEntity, "utf-8");
+      ObjectMapper mapper = new ObjectMapper();
+      Map map = mapper.readValue(message, Map.class);
+      List<Long> timestampsResult = (List<Long>) map.get("timestamps");
+      List<Long> expressionsResult = (List<Long>) map.get("expressions");
+      List<List<Object>> valuesResult = (List<List<Object>>) map.get("values");
+      Assert.assertTrue(map.size() > 0);
+      List<Object> expressions =
+          new ArrayList<Object>() {
+            {
+              add("root.sg25.s3");
+              add("root.sg25.s4");
+              add("root.sg25.s5");
+              add("root.sg25.s6");
+              add("root.sg25.s7");
+              add("root.sg25.s8");
+              add("root.sg25.s4 + 1");
+              add("root.sg25.s4 + 1");
+            }
+          };
+      List<Object> timestamps =
+          new ArrayList<Object>() {
+            {
+              add(1635232143960l);
+              add(1635232153960l);
+            }
+          };
+      List<Object> values1 =
+          new ArrayList<Object>() {
+            {
+              add("2aa");
+              add("");
+            }
+          };
+      List<Object> values2 =
+          new ArrayList<Object>() {
+            {
+              add(11);
+              add(2);
+            }
+          };
+      List<Object> values3 =
+          new ArrayList<Object>() {
+            {
+              add(1635000012345555l);
+              add(1635000012345556l);
+            }
+          };
+
+      List<Object> values4 =
+          new ArrayList<Object>() {
+            {
+              add(1.41);
+              add(null);
+            }
+          };
+      List<Object> values5 =
+          new ArrayList<Object>() {
+            {
+              add(null);
+              add(false);
+            }
+          };
+      List<Object> values6 =
+          new ArrayList<Object>() {
+            {
+              add(null);
+              add(3.5555);
+            }
+          };
+
+      Assert.assertEquals(expressions, expressionsResult);
+      Assert.assertEquals(timestamps, timestampsResult);
+      Assert.assertEquals(values1, valuesResult.get(0));
+      Assert.assertEquals(values2, valuesResult.get(1));
+      Assert.assertEquals(values3, valuesResult.get(2));
+      Assert.assertEquals(values4, valuesResult.get(3));
+      Assert.assertEquals(values5, valuesResult.get(4));
+      Assert.assertEquals(values6, valuesResult.get(5));
+    } catch (IOException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    } finally {
+      try {
+        if (response != null) {
+          response.close();
+        }
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail(e.getMessage());
+      }
+    }
+  }
+}
diff --git a/server/src/test/resources/iotdb-rest.properties b/server/src/test/resources/iotdb-rest.properties
new file mode 100644
index 0000000..865f3ab
--- /dev/null
+++ b/server/src/test/resources/iotdb-rest.properties
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+####################
+### REST Service Configuration
+####################
+
+# Is the REST service enabled
+enable_rest_service=true
+
+# the binding port of the REST service
+# rest_service_port=18080
+
+# is SSL enabled
+# enable_https=false
+
+# SSL key store path
+# key_store_path=
+
+# SSL key store password
+# key_store_pwd=
+
+# SSL trust store path
+# trust_store_path=
+
+# SSL trust store password.
+# trust_store_pwd=
+
+# SSL timeout (in seconds)
+# idle_timeout_in_seconds=50000
+
+# the expiration time of the user login infomation cache (in seconds)
+# cache_expire_in_seconds=28800
+
+# maximum number of users can be stored in the user login cache.
+# cache_max_num=100
+
+# init capacity of users can be stored in the user login cache.
+# cache_init_num=10
diff --git a/site/src/main/.vuepress/config.js b/site/src/main/.vuepress/config.js
index c3ea509..78160ab 100644
--- a/site/src/main/.vuepress/config.js
+++ b/site/src/main/.vuepress/config.js
@@ -1524,6 +1524,7 @@ var config = {
 						children: [
 							['Communication-Service-Protocol/Programming-Thrift','Thrift'],
 							['Communication-Service-Protocol/Programming-MQTT','MQTT'],
+							['Communication-Service-Protocol/RestService','REST'],
 						]
 					},
 					{