Browse Source

Translate document http_service to English

old-bear 6 years ago
parent
commit
92d802b0d8
2 changed files with 439 additions and 37 deletions
  1. 46 37
      docs/cn/http_service.md
  2. 393 0
      docs/en/http_service.md

+ 46 - 37
docs/cn/http_service.md

@@ -6,7 +6,7 @@
 
 1. 填写proto文件。
 
-下面代码里的HttpRequest和HttpResponse都是空的,因为http数据在Controller中。http request的头在Controller.http_request()中,body在Controller.request_attachment()中。类似的,http response的头在Controller.http_response(),body在Controller.response_attachment()。
+   下面代码里的HttpRequest和HttpResponse都是空的,因为http数据在Controller中。http request的头在Controller.http_request()中,body在Controller.request_attachment()中。类似的,http response的头在Controller.http_response(),body在Controller.response_attachment()。
 
 ```protobuf
 option cc_generic_services = true;
@@ -48,7 +48,7 @@ public:
 };
 ```
 
-实现完毕插入Server后可通过如下URL访问,/HttpService/Echo后的部分在 cntl->http_request().unresolved_path()中,unresolved_path总是normalized。
+3. 实现完毕插入Server后可通过如下URL访问,/HttpService/Echo后的部分在 cntl->http_request().unresolved_path()中,unresolved_path总是normalized。
 
 | URL                        | 访问方法             | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
 | -------------------------- | ---------------- | --------------------------------- | -------------------------------------- |
@@ -95,7 +95,7 @@ public:
 };
 ```
 
-实现完毕插入Server后可通过如下URL访问,/FileService之后的路径在cntl->http_request().unresolved_path()中 (r32097前被称为method_path),unresolved_path总是normalized。
+3. 实现完毕插入Server后可通过如下URL访问,/FileService之后的路径在cntl->http_request().unresolved_path()中 ,unresolved_path总是normalized。
 
 | URL                             | 访问方法                       | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
 | ------------------------------- | -------------------------- | --------------------------------- | -------------------------------------- |
@@ -106,7 +106,7 @@ public:
 
 # Restful URL
 
-r32097后,brpc支持为service中的每个方法指定一个URL。接口如下:
+brpc支持为service中的每个方法指定一个URL。接口如下:
 
 ```c++
 // 如果restful_mappings不为空, service中的方法可通过指定的URL被HTTP协议访问,而不是/ServiceName/MethodName. 
@@ -128,12 +128,11 @@ service QueueService {
 };
 ```
 
-如果我们像之前那样把它插入server,那么只能通过`/QueueService/start, /QueueService/stop等url来访问`
+如果我们像之前那样把它插入server,那么只能通过`/QueueService/start, /QueueService/stop`等url来访问。
 
 而在调用AddService时指定第三个参数(restful_mappings)就能定制URL了,如下所示:
 
 ```c++
-// r33521前星号只能出现在最后
 if (server.AddService(&queue_svc,
                       brpc::SERVER_DOESNT_OWN_SERVICE,
                       "/v1/queue/start   => start,"
@@ -143,7 +142,7 @@ if (server.AddService(&queue_svc,
     return -1;
 }
  
-// r33521后星号可出现在中间
+// 星号可出现在中间
 if (server.AddService(&queue_svc,
                       brpc::SERVER_DOESNT_OWN_SERVICE,
                       "/v1/*/start   => start,"
@@ -154,7 +153,7 @@ if (server.AddService(&queue_svc,
 }
 ```
 
-上面代码中AddService的第三个参数分了三行,但实际上是一个字符串。这个字符串包含以逗号(,)分隔的三个映射关系,每个映射告诉brpc:在遇到箭头左侧的URL时调用右侧的方法。"/v1/queue/stats/*"中的星号可匹配任意字串。在r33521前星号只能加在URL最后。
+上面代码中AddService的第三个参数分了三行,但实际上是一个字符串。这个字符串包含以逗号(,)分隔的三个映射关系,每个映射告诉brpc:在遇到箭头左侧的URL时调用右侧的方法。"/v1/queue/stats/*"中的星号可匹配任意字串。
 
 关于映射规则:
 
@@ -162,8 +161,8 @@ if (server.AddService(&queue_svc,
 - service不要求是纯HTTP,pb service也支持。
 - 没有出现在映射中的方法仍旧通过/ServiceName/MethodName访问。出现在映射中的方法不再能通过/ServiceName/MethodName访问。
 - ==> ===> ...都是可以的。开头结尾的空格,额外的斜杠(/),最后多余的逗号,都不要紧。
-- r33521前PATH和PATH/* 是冲突的,不能同时出现在一个字符串中。r33521后两者可以共存。
-- r33521前星号后不能有更多字符,r33521后可以,即支持后缀匹配。
+- PATH和PATH/*两者可以共存。
+- 星号后可以有更多字符,即支持后缀匹配。
 - 一个路径中只能出现一个星号。
 
 `cntl.http_request().unresolved_path()` 对应星号(*)匹配的部分,保证normalized:开头结尾都不包含斜杠(/),中间斜杠不重复。比如:
@@ -294,37 +293,47 @@ if (encoding != NULL && *encoding == "gzip") {
 // cntl->request_attachment()中已经是解压后的数据了
 ```
 
-
-
 # 开启HTTPS
 
 要开启HTTPS,首先确保代码依赖了最新的openssl库。如果openssl版本很旧,会有严重的安全漏洞,支持的加密算法也少,违背了开启SSL的初衷。然后设置ServerOptions中的SSLOptions
 ```c++
-// 证书结构
+// Certificate structure
 struct CertInfo {
-    // PEM格式证书文件
-    // 当存在证书链时, 将所有证书链上的证书append为一个文件
-    std::string certificate_file;
-  
-    // PEM格式的密钥文件
-    std::string private_key_file;
-  
-    // 指定该证书绑定的域名,首字符支持通配符(类似*.abc.com)
-    // 访问这些域名的请求,会使用该证书进行SSL握手,在client最终显示该证书的信息
-    // 如果没指定此字段,程序会自动尝试从证书文件中提取域名信息
-    std::vector<std::string> sni_filters;
+    // Certificate in PEM format.
+    // Note that CN and alt subjects will be extracted from the certificate,
+    // and will be used as hostnames. Requests to this hostname (provided SNI
+    // extension supported) will be encrypted using this certifcate. 
+    // Supported both file path and raw string
+    std::string certificate;
+
+    // Private key in PEM format.
+    // Supported both file path and raw string based on prefix:
+    std::string private_key;
+        
+    // Additional hostnames besides those inside the certificate. Wildcards
+    // are supported but it can only appear once at the beginning (i.e. *.xxx.com).
+    std::vector<std::string> sni_filters;
 };
+
 struct SSLOptions {
-    // 要加载的所有证书
-    std::vector<CertInfo> certs;
-    // 当HTTPS请求到来时,会自动根据访问域名找相应的证书
-    // 如果没有找到相匹配的证书,默认情况使用certs中的第一张证书
-    // 除非开启strict_sni,则此时会拒绝该请求
-    bool strict_sni;
+    // Default certificate which will be loaded into server. Requests
+    // without hostname or whose hostname doesn't have a corresponding
+    // certificate will use this certificate. MUST be set to enable SSL.
+    CertInfo default_cert;
+    
+    // Additional certificates which will be loaded into server. These
+    // provide extra bindings between hostnames and certificates so that
+    // we can choose different certificates according to different hostnames.
+    // See `CertInfo' for detail.
+    std::vector<CertInfo> certs;
+
+    // When set, requests without hostname or whose hostname can't be found in
+    // any of the cerficates above will be dropped. Otherwise, `default_cert'
+    // will be used.
+    // Default: false
+    bool strict_sni;
  
-    // ... 其他选项
+    // ... Other options
 };
 ```
 其余选项还包括:密钥套件选择(推荐密钥ECDHE-RSA-AES256-GCM-SHA384,chrome默认第一优先密钥,安全性很高,但比较耗性能)、session复用等,具体见server.h
@@ -344,14 +353,14 @@ bool Controller::is_ssl() const;
 
 # 持续发送
 
-r33796前brpc server不适合发送超大或无限长的body。r33796后brpc server支持。方法如下:
+brpc server支持发送超大或无限长的body。方法如下:
 
 1. 调用Controller::CreateProgressiveAttachment()创建可持续发送的body。
   `boost::intrusive_ptr<brpc::ProgressiveAttachment> pa(cntl->CreateProgressiveAttachment());`
-  返回的ProgressiveAttachment对象需要用boost::intrusive_ptr<>管理,定义在brpc/progressive_attachment.h>中。
+  返回的ProgressiveAttachment对象需要用boost::intrusive_ptr<>管理,定义在`<brpc/progressive_attachment.h>`中。
 
 2. 调用ProgressiveAttachment::Write()发送数据。如果写入发生在server回调结束前,发送的数据将会被缓存直到回调结束发送了header部分后才会开始发送数据。如果写入发生在server回调结束后,发送的数据将立刻以chunked mode写出。 
-3. 发送完毕后确保所有的boost::intrusive_ptr<brpc::ProgressiveAttachment>都析构了。
+3. 发送完毕后确保所有的`boost::intrusive_ptr<brpc::ProgressiveAttachment>`都析构了。
 
 # 持续接收
 
@@ -361,7 +370,7 @@ r33796前brpc server不适合发送超大或无限长的body。r33796后brpc ser
 
 ### Q: brpc前的nginx报了final fail (ff)
 
-brpc server同端口支持多种协议,当它遇到非法HTTP请求并解析失败后,无法说这个请求一定是HTTP。在r31355之后,server会对query-string及之后出现解析错误的请求返回HTTP 400错误并关闭连接(因为有很大概率是HTTP请求),但如果是HTTP method错误,诸如出现GET、POST、HEAD等标准方法之外的东西或严重的格式错误(可能由HTTP client有bug导致),server仍会直接断开连接,导致nginx的ff。
+brpc server同端口支持多种协议,当它遇到非法HTTP请求并解析失败后,无法说这个请求一定是HTTP。server会对query-string及之后出现解析错误的请求返回HTTP 400错误并关闭连接(因为有很大概率是HTTP请求),但如果是HTTP method错误,诸如出现GET、POST、HEAD等标准方法之外的东西或严重的格式错误(可能由HTTP client有bug导致),server仍会直接断开连接,导致nginx的ff。
 
 解决方案: 在使用Nginx转发流量时,可以对$HTTP_method做一下过滤,只放行允许的方法。或者干脆在proxy时设置proxy_method为指定方法,来避免ff。 
 

+ 393 - 0
docs/en/http_service.md

@@ -0,0 +1,393 @@
+This document describes the "pure" HTTP service rather than protobuf based ones (use pure pb as input/output format). The HTTP service declaration inside the .proto file is still necessary although the request/response structure is empty. The reason is to keep all the service declaration inside proto files rather than scattering in code, conf, proto and other places. For examples please refer to [http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp).
+
+# URL Prefix: /ServiceName/MethodName
+
+All protobuf based services can be accessed through URL `/ServiceName/MethodName` by default, where the `ServiceName` does not contain package name. This URL rule should cover many cases in general. The detail is as follows:
+
+1. Add service declaration in the proto file.
+
+   Note that `HttpRequest` and `HttpResponse` are empty in the proto because the HTTP request body is inside  `Controller.request_attachment()` while the HTTP request header is inside `Controller.http_request()`. Similarly, those for the HTTP response locate in `Controller.response_attachment()` and `Controller.http_response()`.	
+
+```protobuf
+option cc_generic_services = true;
+message HttpRequest { };
+message HttpResponse { };
+service HttpService {
+      rpc Echo(HttpRequest) returns (HttpResponse);
+};
+```
+
+2. Implement the service by inheriting the base class inside .pb.h which is the same process as other protobuf based services.
+
+```c++
+class HttpServiceImpl : public HttpService {
+public:
+    ...
+    virtual void Echo(google::protobuf::RpcController* cntl_base,
+                      const HttpRequest* /*request*/,
+                      HttpResponse* /*response*/,
+                      google::protobuf::Closure* done) {
+        brpc::ClosureGuard done_guard(done);
+        brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);
+        // Return plain text
+        cntl->http_response().set_content_type("text/plain");
+       
+        // Print the query string and the request body
+        // and use these as response
+        butil::IOBufBuilder os;
+        os << "queries:";
+        for (brpc::URI::QueryIterator it = cntl->http_request().uri().QueryBegin();
+                it != cntl->http_request().uri().QueryEnd(); ++it) {
+            os << ' ' << it->first << '=' << it->second;
+        }
+        os << "\nbody: " << cntl->request_attachment() << '\n';
+        os.move_to(cntl->response_attachment());
+    }
+};
+```
+
+3. Add the implemented service into Server and then access it using the following URL (Note that path 									after `/HttpService/Echo ` is filled into `cntl->http_request().unresolved_path()`, which is always 			normalized):
+
+| URL                        | Protobuf Method  | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
+| -------------------------- | ---------------- | --------------------------------- | -------------------------------------- |
+| /HttpService/Echo          | HttpService.Echo | "/HttpService/Echo"               | ""                                     |
+| /HttpService/Echo/Foo      | HttpService.Echo | "/HttpService/Echo/Foo"           | "Foo"                                  |
+| /HttpService/Echo/Foo/Bar  | HttpService.Echo | "/HttpService/Echo/Foo/Bar"       | "Foo/Bar"                              |
+| /HttpService//Echo///Foo// | HttpService.Echo | "/HttpService//Echo///Foo//"      | "Foo"                                  |
+| /HttpService               | No such method   |                                   |                                        |
+
+# URL Prefix: /ServiceName
+
+Some resource management HTTP services may need this URL rule, such as a FileService to provide access to files: Use `/FileService/foobar.txt` to represent file `foobar.txt` in the working directory, or `/FileService/app/data/boot.cfg` to represent file `boot.cfg` in directory `app/data`.
+
+To implement this:
+
+1. In the proto file, use `FileService` as the service name and `default_method` for the method name.
+
+```protobuf
+option cc_generic_services = true;
+ 
+message HttpRequest { };
+message HttpResponse { };
+ 
+service FileService {
+      rpc default_method(HttpRequest) returns (HttpResponse);
+}
+```
+
+2. Implement the service.
+
+```c++
+class FileServiceImpl: public FileService {
+public:
+    ...
+    virtual void default_method(google::protobuf::RpcController* cntl_base,
+                                const HttpRequest* /*request*/,
+                                HttpResponse* /*response*/,
+                                google::protobuf::Closure* done) {
+        brpc::ClosureGuard done_guard(done);
+        brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);
+        cntl->response_attachment().append("Getting file: ");
+        cntl->response_attachment().append(cntl->http_request().unresolved_path());
+    }
+};
+```
+
+3. Add the implemented service into Server and then access it using the following URL (the path after `/FileService` locates in `cntl->http_request().unresolved_path()`, which is always normalized):
+
+| URL                             | Protobuf Method            | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
+| ------------------------------- | -------------------------- | --------------------------------- | -------------------------------------- |
+| /FileService                    | FileService.default_method | "/FileService"                    | ""                                     |
+| /FileService/123.txt            | FileService.default_method | "/FileService/123.txt"            | "123.txt"                              |
+| /FileService/mydir/123.txt      | FileService.default_method | "/FileService/mydir/123.txt"      | "mydir/123.txt"                        |
+| /FileService//mydir///123.txt// | FileService.default_method | "/FileService//mydir///123.txt//" | "mydir/123.txt"                        |
+
+# Restful URL
+
+brpc also supports to specify a URL for each method in a service:
+
+```c++
+// If `restful_mappings' is non-empty, the method in service can
+// be accessed by the specified URL rather than /ServiceName/MethodName. 
+// Mapping rules: "PATH1 => NAME1, PATH2 => NAME2 ..."
+// where `PATH' is a valid HTTP path and `NAME' is the method name.       
+int AddService(google::protobuf::Service* service,
+               ServiceOwnership ownership,
+               butil::StringPiece restful_mappings);
+```
+
+For example, the following `QueueService` contains several HTTP methods:
+
+```protobuf
+service QueueService {
+    rpc start(HttpRequest) returns (HttpResponse);
+    rpc stop(HttpRequest) returns (HttpResponse);
+    rpc get_stats(HttpRequest) returns (HttpResponse);
+    rpc download_data(HttpRequest) returns (HttpResponse);
+};
+```
+
+If we add it into the server as before, it could only be accessed by URLs such as `/QueueService/start` or ` /QueueService/stop`.
+
+However, adding the third parameter `restful_mappings` in `AddService` allows us to customize URL:
+
+```c++
+if (server.AddService(&queue_svc,
+                      brpc::SERVER_DOESNT_OWN_SERVICE,
+                      "/v1/queue/start   => start,"
+                      "/v1/queue/stop    => stop,"
+                      "/v1/queue/stats/* => get_stats") != 0) {
+    LOG(ERROR) << "Fail to add queue_svc";
+    return -1;
+}
+if (server.AddService(&queue_svc,
+                      brpc::SERVER_DOESNT_OWN_SERVICE,
+                      "/v1/*/start   => start,"
+                      "/v1/*/stop    => stop,"
+                      "*.data        => download_data") != 0) {
+    LOG(ERROR) << "Fail to add queue_svc";
+    return -1;
+}
+```
+
+There are 3 mappings in the third parameter (which is a string separated by comma) of `AddService` above. Each tells bpc to call the method at the right side of the arrow when it sees a incoming URL matching the left side. The star in `/v1/queue/stats/*` matches any string. 
+
+More about the mapping rules:
+
+- Multiple paths can map to the same method.
+- Besides pure HTTP services, protobuf based ones are also supported.
+- Methods that are not in the mapping can still be accessed by `/ServiceName/MethodName`. Otherwise, this rule will be disabled.
+- The lexical rules are relax. For example,  `==>` and ` ===>` the same. Extra spaces from the beginning and the end of the mapping string, extra slashes in the URL, and extra commas at the end, are ignored automatically.
+- The URL pattern `PATH` and `PATH/*` can coexist.
+- Characters can appear after asterisk, which means suffix matching is supported.
+- At most one star is allowed in a path.
+
+The extra part after the asterisk can be obtained by `cntl.http_request().unresolved_path()`, which is ensured to be normalized: No slash from the beginning and the end. No repeated slash in the middle. For example, the following URL:
+
+![img](../images/restful_1.png)
+
+or:
+
+![img](../images/restful_2.png)
+
+will be normalized to `foo/bar`, where extra slashes from both sides and the middle are removed.
+
+Note that `cntl.http_request().uri().path()` is not ensured to be normalized, which is `"//v1//queue//stats//foo///bar//////"` and `"//vars///foo////bar/////"` respectively in the above example.
+
+The built-in service page of `/status` shows all the methods along with their URLs, whose form are: `@URL1 @URL2` ...
+
+![img](../images/restful_3.png)
+
+# HTTP Parameters
+
+## HTTP headers
+
+HTTP headers are a series of key value pairs, some of which are defined by the HTTP specification, while others are free to use.
+
+It's easy to confuse HTTP headers with query string, which is part of the URL. Query string can also express the key value relationship using the form `key1=value1&key2=value2&...`, and it's simpler to manipulate on GUI such as browsers. However, this usage is more of a custom convention rather than part of the HTTP specification. According our observation, in general, HTTP headers are used for passing framework and protocol level parameters since they are part of the specification which will be recognized by all http servers, while query string, as part of the URL, is suitable for user-level parameters as it's easy to read and modify.
+
+```c++
+// Get value for header "User-Agent" (case insensitive)
+const std::string* user_agent_str = cntl->http_request().GetHeader("User-Agent");
+if (user_agent_str != NULL) {  // has the header
+    LOG(TRACE) << "User-Agent is " << *user_agent_str;
+}
+...
+// Add a header "Accept-encoding: gzip" (case insensitive)
+cntl->http_response().SetHeader("Accept-encoding", "gzip");
+// Overwrite the previous header "Accept-encoding: deflate"
+cntl->http_response().SetHeader("Accept-encoding", "deflate");
+// Append value to the previous header so that it becomes 
+// "Accept-encoding: deflate,gzip" (values separated by comma)
+cntl->http_response().AppendHeader("Accept-encoding", "gzip");
+```
+
+## Content-Type
+
+As a frequently used header, `Content-type` marks the type of the HTTP body, and it can be fetched through a specialized interface. Accordingly, it can't be fetched by `GetHeader()`.
+
+```c++
+// Get Content-Type
+if (cntl->http_request().content_type() == "application/json") {
+    ...
+}
+...
+// Set Content-Type
+cntl->http_response().set_content_type("text/html");
+```
+
+If RPC fails (`Controller` has been `SetFailed`), the framework will overwrite `Content-Type` with `text/plain` and fill the response body with `Controller::ErrorText()`.
+
+## Status Code
+
+Status code is a special field for HTTP response, which marks the completion status of the http request. Please use the `enums` defined in [http_status_code.h](https://github.com/brpc/brpc/blob/master/src/brpc/http_status_code.h) to follow the HTTP specification.
+
+```c++
+// Get Status Code
+if (cntl->http_response().status_code() == brpc::HTTP_STATUS_NOT_FOUND) {
+    LOG(FATAL) << "FAILED: " << controller.http_response().reason_phrase();
+}
+...
+// Set Status code
+cntl->http_response().set_status_code(brpc::HTTP_STATUS_INTERNAL_SERVER_ERROR);
+cntl->http_response().set_status_code(brpc::HTTP_STATUS_INTERNAL_SERVER_ERROR, "My explanation of the error...");
+```
+
+The following code implements server's redirection with status code 302:
+
+```c++
+cntl->http_response().set_status_code(brpc::HTTP_STATUS_FOUND);
+cntl->http_response().SetHeader("Location", "http://bj.bs.bae.baidu.com/family/image001(4979).jpg");
+```
+
+![img](../images/302.png)
+
+## Query String
+
+As mentioned above in the [HTTP headers](http_service.md#http-headers), brpc interpret query string as in a custom convention, whose form is `key1=value1&key2=value2&…`. Keys without value are also acceptable and accessible through `GetQuery` (returns an empty string), which is often used as bool-type switch. The interface is defined in [uri.h](https://github.com/brpc/brpc/blob/master/src/brpc/uri.h).
+
+```c++
+const std::string* time_value = cntl->http_request().uri().GetQuery("time");
+if (time_value != NULL) {  // the query string is present
+    LOG(TRACE) << "time = " << *time_value;
+}
+ 
+...
+cntl->http_request().uri().SetQuery("time", "2015/1/2");
+```
+
+# Debug
+
+Turn on [-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose) to print contents of all http requests and responses to stderr. Note that this should only be used for debugging rather than online services.
+
+# Compress response body
+
+HTTP services usually apply compression on the http body in order to reduce the transmission time of text-based pages and thus speed up the loading process effectively. 
+
+Calls to `Controller::set_response_compress_type(brpc::COMPRESS_TYPE_GZIP)` will try to compress the http body using gzip and set `Content-Encoding` to `gzip`. It has no effect when request does not specify the `Accept-encoding` or value does not contain gzip. For example, curl does not support compression without option `—compressed`, thus the server will always return the uncompressed results.
+
+# Decompress request body
+
+Due to generality concern, brpc won't decompress request body automatically. You can do it yourself as it's not that complicate:
+
+```c++
+#include <brpc/policy/gzip_compress.h>
+...
+const std::string* encoding = cntl->http_request().GetHeader("Content-Encoding");
+if (encoding != NULL && *encoding == "gzip") {
+    butil::IOBuf uncompressed;
+    if (!brpc::policy::GzipDecompress(cntl->request_attachment(), &uncompressed)) {
+        LOG(ERROR) << "Fail to un-gzip request body";
+        return;
+    }
+    cntl->request_attachment().swap(uncompressed);
+}
+// cntl->request_attachment() contains the data after decompression
+```
+
+# Turn on HTTPS
+
+Make sure to update openssl library to the latest version before turning on HTTPS. Old verions of openssl have severe security problems and support only a few encryption algorithms, which conflicts with the purpose of using SSL. Set the `SSLOptions` inside `ServerOptions` to turn on HTTPS.
+
+```c++
+// Certificate structure
+struct CertInfo {
+    // Certificate in PEM format.
+    // Note that CN and alt subjects will be extracted from the certificate,
+    // and will be used as hostnames. Requests to this hostname (provided SNI
+    // extension supported) will be encrypted using this certifcate. 
+    // Supported both file path and raw string
+    std::string certificate;
+
+    // Private key in PEM format.
+    // Supported both file path and raw string based on prefix:
+    std::string private_key;
+        
+    // Additional hostnames besides those inside the certificate. Wildcards
+    // are supported but it can only appear once at the beginning (i.e. *.xxx.com).
+    std::vector<std::string> sni_filters;
+};
+
+struct SSLOptions {
+    // Default certificate which will be loaded into server. Requests
+    // without hostname or whose hostname doesn't have a corresponding
+    // certificate will use this certificate. MUST be set to enable SSL.
+    CertInfo default_cert;
+    
+    // Additional certificates which will be loaded into server. These
+    // provide extra bindings between hostnames and certificates so that
+    // we can choose different certificates according to different hostnames.
+    // See `CertInfo' for detail.
+    std::vector<CertInfo> certs;
+
+    // When set, requests without hostname or whose hostname can't be found in
+    // any of the cerficates above will be dropped. Otherwise, `default_cert'
+    // will be used.
+    // Default: false
+    bool strict_sni;
+    // ... Other options
+};
+```
+Other options include: cipher suites (`ECDHE-RSA-AES256-GCM-SHA384` is recommended since it's the default suite used by chrome with the highest priority. It‘s one of the safest suites but costs more CPU), session reuse and so on. For more information please refer to [server.h](https://github.com/brpc/brpc/blob/master/src/brpc/server.h).
+
+After turning on HTTPS, you can still send HTTP requests to the same port. Server will identify whether it's HTTP or HTTPS automatically. The result can be fetched through `Controller` in service callback: 
+
+```c++
+bool Controller::is_ssl() const;
+```
+
+# Performance
+
+Productions without extreme performance requirements tend to use HTTP protocol, especially those mobile products. As a result, we put great emphasis on the implementation quality of HTTP. To be more specific:
+
+- Use [http parser](https://github.com/brpc/brpc/blob/master/src/brpc/details/http_parser.h) (some part comes from nginx) of node.js to parse http message, which is a lightweight, excellent implementation.
+- Use [rapidjson](https://github.com/miloyip/rapidjson) to parse json, which is a json library developed by a Tencent expert for extreme performance.
+- In the worst case, the time complexity of parsing http requests is still O(N), where N is the number of request bytes. In other words, if the parsing code requires the http request to be complete, then it may cost O(N^2). This feature is very helpful since most HTTP requests have large body.
+- The process of multiple HTTP messages from different clients is highly concurrent. Even a complicate http messages won't affect response to other clients. It's difficult to achieve this for other rpc implementations and http servers based on [single-threaded reactor](threading_overview.md#单线程reactor).
+
+# Progressive sending
+
+brpc server is also suitable for sending large or infinite size body. Uses the following method:
+
+1. Call `Controller::CreateProgressiveAttachment()` to create a body that can ben sent progressively. Use `boost::intrusive_ptr<>` to manage the returning `ProgressiveAttachment` object: `boost::intrusive_ptr <brpc::ProgressiveAttachment> pa (cntl->CreateProgressiveAttachment());`. The detail is defined in `<brpc/progressive_attachment.h>`.
+2. Call `ProgressiveAttachment::Write()` to send the data. If the write occurs before the end of server callback, the sent data will be cached until the server callback ends. It starts writing after the header portion is sent. If the write occurs after the server callback, the data sent will be written in chunked mode immediately.
+3. After sending, make sure that all `boost::intrusive_ptr<brpc::ProgressiveAttachment>` have been  destructed.
+
+# Progressive receiving
+
+Currently brpc server doesn't support calling user's callback once it finishes parsing the header portion of the http request. In other words, it's not suitable for receiving large or infinite size body.
+
+# FAQ
+
+### Q: nginx which uses brpc as upstream encounters final fail (ff)
+
+brpc server supports running a variety of protocols on the same port. As a result, when it encounters an illegal HTTP request due to parsing failure, it can not be certain that this request is using HTTP. The server treats errors after paring the query-string portion with an HTTP 400 followed and then closes the connection, since it has a large probability that this is an HTTP request. However, for HTTP method errors (such as invalid methods other than GET, POST, HEAD, etc), or other serious format errors (may be caused by HTTP client bug), the server will close the connection immediately, which leads to nginx ff.
+
+Solution: When using Nginx to forward traffic, filter out the unexpected HTTP method using `$HTTP_method`, or simply specify the HTTP method using `proxy_method` to avoid ff. 
+
+### Q: Does brpc support http chunked mode
+
+Yes
+
+### Q: Why does HTTP requests containing BASE64 encoded query string fail to parse sometimes?
+
+According to the [HTTP specification](http://tools.ietf.org/html/rfc3986#section-2.2), the following characters need to be encoded using `%`.
+
+```
+       reserved    = gen-delims / sub-delims
+
+       gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+
+       sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
+                   / "*" / "+" / "," / ";" / "="
+```
+
+Base64 encoded string may end with `=` or `==` (for example, `?wi=NDgwMDB8dGVzdA==&anothorkey=anothervalue`). These string may be parsed successfully, or may be not. It depends on the implementation which users should not assume.
+
+One solution is to remove the trailing `=` since it won't affect the [Base64 decode](http://en.wikipedia.org/wiki/Base64#Padding). Another way is to encode the URI using Base64 followed by `%`, and then decode it using `%` followed by Base64 before access.