Browse Source

Reviewed http_service.md

gejun 6 years ago
parent
commit
af47c723b5
2 changed files with 184 additions and 138 deletions
  1. 79 58
      docs/cn/http_service.md
  2. 105 80
      docs/en/http_service.md

+ 79 - 58
docs/cn/http_service.md

@@ -1,12 +1,23 @@
-这里特指“纯粹"的HTTP service,而不是可通过HTTP访问的pb服务。虽然用不到pb消息,但“纯粹”的HTTP Service也必须定义在.proto文件中,只是request和response都是空的结构体。这么做是确保所有的服务声明集中在proto文件中,而不是散列在.proto、程序、配置等多个地方。示例代码见[http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp)。
+[English version](../en/http_service.md)
 
-# URL前缀为/ServiceName/MethodName
+这里指我们通常说的HTTP服务,而不是可通过HTTP访问的pb服务。
 
-所有pb service默认都能通过/ServiceName/MethodName来访问,其中ServiceName不包括package。对于公司内的纯HTTP服务,一般来说这种形式的URL也够用了。实现步骤如下:
+虽然用不到pb消息,但brpc中的HTTP服务接口也得定义在.proto文件中,只是request和response都是空的结构体。这确保了所有的服务声明集中在proto文件中,而不是散列在proto文件、程序、配置等多个地方。示例代码见[http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp)。
 
-1. 填写proto文件。
+# URL类型
+
+## 前缀为/ServiceName/MethodName
+
+定义一个service名为ServiceName(不包含package名), method名为MethodName的pb服务,且让request和reponse定义为空,则该服务默认在/ServiceName/MethodName上提供HTTP服务。
 
-   下面代码里的HttpRequest和HttpResponse都是空的,因为http数据在Controller中。http request的头在Controller.http_request()中,body在Controller.request_attachment()中。类似的,http response的头在Controller.http_response(),body在Controller.response_attachment()。
+request和response可为空是因为http数据在Controller中:
+
+* http request的header在Controller.http_request()中,body在Controller.request_attachment()中。
+* http response的header在Controller.http_response()中,body在Controller.response_attachment()中。
+
+实现步骤如下:
+
+1. 填写proto文件。
 
 ```protobuf
 option cc_generic_services = true;
@@ -19,7 +30,7 @@ service HttpService {
 };
 ```
 
-2. 实现Service。和其他pb service一样,也是继承定义在.pb.h中的service基类。
+2. 实现Service接口。和pb服务一样,也是继承定义在.pb.h中的service基类。
 
 ```c++
 class HttpServiceImpl : public HttpService {
@@ -32,10 +43,10 @@ public:
         brpc::ClosureGuard done_guard(done);
         brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);
  
-        // 这里返回纯文本。
+        // body是纯文本
         cntl->http_response().set_content_type("text/plain");
        
-        // 把请求的query-string和body打印出来,作为回复内容。
+        // 把请求的query-string和body打印结果作为回复内容。
         butil::IOBufBuilder os;
         os << "queries:";
         for (brpc::URI::QueryIterator it = cntl->http_request().uri().QueryBegin();
@@ -48,7 +59,7 @@ public:
 };
 ```
 
-3. 实现完毕插入Server后可通过如下URL访问,/HttpService/Echo后的部分在 cntl->http_request().unresolved_path()中,unresolved_path总是normalized
+3. 把实现好的服务插入Server后可通过如下URL访问,/HttpService/Echo后的部分在 cntl->http_request().unresolved_path()中。
 
 | URL                        | 访问方法             | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
 | -------------------------- | ---------------- | --------------------------------- | -------------------------------------- |
@@ -58,9 +69,9 @@ public:
 | /HttpService//Echo///Foo// | HttpService.Echo | "/HttpService//Echo///Foo//"      | "Foo"                                  |
 | /HttpService               | 访问错误             |                                   |                                        |
 
-# URL前缀为/ServiceName
+## 前缀为/ServiceName
 
-一些资源类的HTTP服务可能会需要这种类型的URL,比如FileService提供对文件的访问,/FileService/foobar.txt代表访问运行目录下的foobar.txt文件,而/FileService/app/data/boot.cfg代表app/data目录下的boot.cfg文件
+资源类的HTTP服务可能需要这样的URL,ServiceName后均为动态内容。比如/FileService/foobar.txt代表./foobar.txt,/FileService/app/data/boot.cfg代表./app/data/boot.cfg
 
 实现方法:
 
@@ -68,10 +79,10 @@ public:
 
 ```protobuf
 option cc_generic_services = true;
- 
+
 message HttpRequest { };
 message HttpResponse { };
- 
+
 service FileService {
       rpc default_method(HttpRequest) returns (HttpResponse);
 }
@@ -95,7 +106,7 @@ public:
 };
 ```
 
-3. 实现完毕插入Server后可通过如下URL访问,/FileService之后的路径在cntl->http_request().unresolved_path()中 ,unresolved_path总是normalized
+3. 实现完毕插入Server后可通过如下URL访问,/FileService之后的路径在cntl->http_request().unresolved_path()中i。
 
 | URL                             | 访问方法                       | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
 | ------------------------------- | -------------------------- | --------------------------------- | -------------------------------------- |
@@ -104,20 +115,20 @@ public:
 | /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
+## Restful URL
 
-brpc支持为service中的每个方法指定一个URL。接口如下:
+brpc支持为service中的每个方法指定一个URL。API如下:
 
 ```c++
-// 如果restful_mappings不为空, service中的方法可通过指定的URL被HTTP协议访问,而不是/ServiceName/MethodName. 
+// 如果restful_mappings不为空, service中的方法可通过指定的URL被HTTP协议访问,而不是/ServiceName/MethodName.
 // 映射格式:"PATH1 => NAME1, PATH2 => NAME2 ..."
-// PATHs是有效的HTTP路径, NAMEs是service中的方法名.                                                     
+// PATHs是有效的HTTP路径, NAMEs是service中的方法名.
 int AddService(google::protobuf::Service* service,
                ServiceOwnership ownership,
                butil::StringPiece restful_mappings);
 ```
 
-比如下面的QueueService包含多个http方法。
+下面的QueueService包含多个http方法。如果我们像之前那样把它插入server,那么只能通过`/QueueService/start, /QueueService/stop`等url来访问。
 
 ```protobuf
 service QueueService {
@@ -128,8 +139,6 @@ service QueueService {
 };
 ```
 
-如果我们像之前那样把它插入server,那么只能通过`/QueueService/start, /QueueService/stop`等url来访问。
-
 而在调用AddService时指定第三个参数(restful_mappings)就能定制URL了,如下所示:
 
 ```c++
@@ -162,7 +171,7 @@ if (server.AddService(&queue_svc,
 - 没有出现在映射中的方法仍旧通过/ServiceName/MethodName访问。出现在映射中的方法不再能通过/ServiceName/MethodName访问。
 - ==> ===> ...都是可以的。开头结尾的空格,额外的斜杠(/),最后多余的逗号,都不要紧。
 - PATH和PATH/*两者可以共存。
-- 星号后可以有更多字符,即支持后缀匹配
+- 支持后缀匹配: 星号后可以有更多字符。
 - 一个路径中只能出现一个星号。
 
 `cntl.http_request().unresolved_path()` 对应星号(*)匹配的部分,保证normalized:开头结尾都不包含斜杠(/),中间斜杠不重复。比如:
@@ -177,8 +186,6 @@ unresolved_path都是`"foo/bar"`,左右、中间多余的斜杠被移除了。
 
  注意:`cntl.http_request().uri().path()`不保证normalized,这两个例子中分别为`"//v1//queue//stats//foo///bar//////"`和`"//vars///foo////bar/////"`
 
- 
-
 /status页面上的方法名后会加上所有相关的URL,形式是:@URL1 @URL2 ...
 
 ![img](../images/restful_3.png)
@@ -189,7 +196,10 @@ unresolved_path都是`"foo/bar"`,左右、中间多余的斜杠被移除了。
 
 http header是一系列key/value对,有些由HTTP协议规定有特殊含义,其余则由用户自由设定。
 
-http headers易与query string混淆,后者是URL的一部分,常见形式是key1=value1&key2=value2&...,也可以表达key/value关系,且更容易在界面上操作。但用query string表达key/value并不是HTTP规范的一部分,更多是大家约定成俗的方式。就我的感受而言,由于http headers是协议的一部分,被所有http server认知,所以常用于机器接口,传递框架或协议层面的参数;而query string作为URL的一部分,很方便被人修改和阅读,常用于传递用户层面的参数。
+query string也是key/value对,http headers与query string的区别:
+
+* 虽然http headers由协议准确定义操作方式,但由于也不易在地址栏中被修改,常用于传递框架或协议层面的参数。
+* query string是URL的一部分,**常见**形式是key1=value1&key2=value2&…,易于阅读和修改,常用于传递应用层参数。但query string的具体格式并不是HTTP规范的一部分,只是约定成俗。
 
 ```c++
 // 获得header中"User-Agent"的值,大小写不敏感。
@@ -209,7 +219,7 @@ cntl->http_response().AppendHeader("Accept-encoding", "gzip");
 
 ## Content-Type
 
-Content-type记录body的类型,是一个使用频率较高的header,单独抽取出来方便使用,相应地,GetHeader()获取不到Content-Type
+Content-type记录body的类型,是一个使用频率较高的header。它在brpc中被特殊处理,需要通过cntl->http_request().content_type()来访问,cntl->GetHeader("Content-Type")是获取不到的
 
 ```c++
 // Get Content-Type
@@ -225,7 +235,7 @@ cntl->http_response().set_content_type("text/html");
 
 ## Status Code
 
-status code是http response特有的字段,标记http请求的完成情况。请使用定义在[http_status_code.h](https://github.com/brpc/brpc/blob/master/src/brpc/http_status_code.h)中的enum,遵守HTTP协议
+status code是http response特有的字段,标记http请求的完成情况。可能的值定义在[http_status_code.h](https://github.com/brpc/brpc/blob/master/src/brpc/http_status_code.h)中。
 
 ```c++
 // Get Status Code
@@ -238,7 +248,7 @@ 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...");
 ```
 
-以下代码在302错误时重定向:
+比如,以下代码以302错误实现重定向:
 
 ```c++
 cntl->http_response().set_status_code(brpc::HTTP_STATUS_FOUND);
@@ -249,30 +259,35 @@ cntl->http_response().SetHeader("Location", "http://bj.bs.bae.baidu.com/family/i
 
 ## Query String
 
-如上面的[HTTP headers](http_service.md#http-headers)中提到的那样,我们按约定成俗的方式来理解query string,即key1=value1&key2=value2&...。只有key而没有value也是可以的,仍然会被GetQuery查询到,只是值为空字符串,这常被用做bool型的开关。接口定义在[uri.h](https://github.com/brpc/brpc/blob/master/src/brpc/uri.h)。
+如上面的[HTTP headers](#http-headers)中提到的那样,我们按约定成俗的方式来理解query string,即key1=value1&key2=value2&...。只有key而没有value也是可以的,仍然会被GetQuery查询到,只是值为空字符串,这常被用做bool型的开关。接口定义在[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");
 ```
 
-# 查看server收到的请求和发出的回复
+# 调试
 
-打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可在stderr看到所有的http request和response,注意这应该只用于线下调试,而不是线上程序。 
+打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可在stderr看到所有的http request和response,注意这应该只用于线下调试,而不是线上程序。
 
 # 压缩response body
 
-http服务常对http body进行压缩,对于文本网页可以有效减少传输时间,加快页面的展现速度。
+http服务常对http body进行压缩,可以有效减少网页的传输时间,加快页面的展现速度。
 
-设置Controller::set_response_compress_type(baidu::rpc::COMPRESS_TYPE_GZIP)后将尝试用gzip压缩http body。“尝试“指的是压缩有可能不发生,条件有:
+设置Controller::set_response_compress_type(baidu::rpc::COMPRESS_TYPE_GZIP)后将**尝试**用gzip压缩http body。“尝试“指的是压缩有可能不发生,条件有:
 
 - 请求中没有设置Accept-encoding或不包含gzip。比如curl不加--compressed时是不支持压缩的,这时server总是会返回不压缩的结果。
-- body尺寸小于-http_body_compress_threshold指定的字节数,默认是512。这是因为gzip并不是一个很快的压缩算法,当body较小时,压缩增加的延时可能比网络传输省下的还多。
+
+- body尺寸小于-http_body_compress_threshold指定的字节数,默认是512。gzip并不是一个很快的压缩算法,当body较小时,压缩增加的延时可能比网络传输省下的还多。当包较小时不做压缩可能是个更好的选项。
+
+  | Name                         | Value | Description                              | Defined At                            |
+  | ---------------------------- | ----- | ---------------------------------------- | ------------------------------------- |
+  | http_body_compress_threshold | 512   | Not compress http body when it's less than so many bytes. | src/brpc/policy/http_rpc_protocol.cpp |
 
 # 解压request body
 
@@ -295,21 +310,21 @@ if (encoding != NULL && *encoding == "gzip") {
 
 # 开启HTTPS
 
-要开启HTTPS,首先确保代码依赖了最新的openssl库。如果openssl版本很旧,会有严重的安全漏洞,支持的加密算法也少,违背了开启SSL的初衷。然后设置ServerOptions中的SSLOptions
+要开启HTTPS,首先确保代码依赖了最新的openssl库。如果openssl版本很旧,会有严重的安全漏洞,支持的加密算法也少,违背了开启SSL的初衷。然后设置ServerOptions.ssl_options.
 ```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. 
+    // 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;
@@ -320,7 +335,7 @@ struct SSLOptions {
     // 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.
@@ -336,18 +351,15 @@ struct SSLOptions {
     // ... Other options
 };
 ```
-其余选项还包括:密钥套件选择(推荐密钥ECDHE-RSA-AES256-GCM-SHA384,chrome默认第一优先密钥,安全性很高,但比较耗性能)、session复用等,具体见server.h
-另外,开启HTTPS后,原先的HTTP请求也可以通过同一个端口来访问,Server会自动判断哪些是HTTP,哪些是HTTPS;用户也可以在callback中通过Controller接口来判断:
-```c++
-bool Controller::is_ssl() const;
-```
+其余选项还包括:密钥套件选择(推荐密钥ECDHE-RSA-AES256-GCM-SHA384,chrome默认第一优先密钥,安全性很高,但比较耗性能)、session复用等,具体见[server.h](https://github.com/brpc/brpc/blob/master/src/brpc/server.h)。
+开启HTTPS后,原先的HTTP请求仍可以通过同一个端口被访问,Server会自动判断哪些是HTTP,哪些是HTTPS;用户可通过Controller::is_ssl()判断是否是HTTPS。从这一点来说,brpc中的HTTPS更多是让server多支持一种协议,而不适合作为加密通道。
 
 # 性能
 
-没有极端性能要求的产品线都有使用HTTP协议的倾向,特别是移动产品线,所以我们很重视HTTP的实现质量,具体来说:
+没有极端性能要求的产品都有使用HTTP协议的倾向,特别是移动产品,所以我们很重视HTTP的实现质量,具体来说:
 
-- 使用了node.js的[http parser](https://github.com/brpc/brpc/blob/master/src/brpc/details/http_parser.h)(部分来自nginx)解析http消息,这是一个轻量、优秀的实现。
-- 使用[rapidjson](https://github.com/miloyip/rapidjson)解析json,这是一个主打性能的json库,由一位腾讯专家开发
+- 使用了node.js的[http parser](https://github.com/brpc/brpc/blob/master/src/brpc/details/http_parser.h)解析http消息,这是一个轻量、优秀、被广泛使用的实现。
+- 使用[rapidjson](https://github.com/miloyip/rapidjson)解析json,这是一个主打性能的json库。
 - 在最差情况下解析http请求的时间复杂度也是O(N),其中N是请求的字节数。反过来说,如果解析代码要求http请求是完整的,那么它可能会花费O(N^2)的时间。HTTP请求普遍较大,这一点意义还是比较大的。
 - 来自不同client的http消息是高度并发的,即使相当复杂的http消息也不会影响对其他客户端的响应。其他rpc和[基于单线程reactor](threading_overview.md#单线程reactor)的各类http server往往难以做到这一点。
 
@@ -355,24 +367,33 @@ bool Controller::is_ssl() const;
 
 brpc server支持发送超大或无限长的body。方法如下:
 
-1. 调用Controller::CreateProgressiveAttachment()创建可持续发送的body。
-  `boost::intrusive_ptr<brpc::ProgressiveAttachment> pa(cntl->CreateProgressiveAttachment());`
-  返回的ProgressiveAttachment对象需要用boost::intrusive_ptr<>管理,定义在`<brpc/progressive_attachment.h>`中。
+1. 调用Controller::CreateProgressiveAttachment()创建可持续发送的body。返回的ProgressiveAttachment对象需要用intrusive_ptr管理。
+  ```c++
+  #include <brpc/progressive_attachment.h>
+  ...
+  butil::intrusive_ptr<brpc::ProgressiveAttachment> pa(cntl->CreateProgressiveAttachment());
+  ```
+
+2. 调用ProgressiveAttachment::Write()发送数据。
 
-2. 调用ProgressiveAttachment::Write()发送数据。如果写入发生在server回调结束前,发送的数据将会被缓存直到回调结束发送了header部分后才会开始发送数据。如果写入发生在server回调结束后,发送的数据将立刻以chunked mode写出。 
-3. 发送完毕后确保所有的`boost::intrusive_ptr<brpc::ProgressiveAttachment>`都析构了。
+   * 如果写入发生在server-side done调用前,发送的数据将会被缓存直到回调结束后才会开始发送。
+   * 如果写入发生在server-side done调用后,发送的数据将立刻以chunked mode写出。
+
+3. 发送完毕后确保所有的`butil::intrusive_ptr<brpc::ProgressiveAttachment>`都析构以释放资源。
 
 # 持续接收
 
-目前brpc server不支持在接受完http请求的header部分就调用用户的服务回调,即brpc server不适合接收超长或无限长的body。
+目前brpc server不支持在收齐http请求的header部分后就调用服务回调,即brpc server不适合接收超长或无限长的body。
 
 # FAQ
 
-### Q: brpc前的nginx报了final fail (ff)
+### Q: brpc前的nginx报了final fail
+
+这个错误在于brpc server直接关闭了http连接而没有发送任何回复。
 
-brpc server同端口支持多种协议,当它遇到非法HTTP请求并解析失败后,无法说这个请求一定是HTTP。server会对query-string及之后出现解析错误的请求返回HTTP 400错误并关闭连接(因为有很大概率是HTTP请求),但如果是HTTP method错误,诸如出现GET、POST、HEAD等标准方法之外的东西或严重的格式错误(可能由HTTP client有bug导致),server仍会直接断开连接,导致nginx的ff。
+brpc server同端口支持多种协议,当它无法解析某个http请求时无法说这个请求一定是HTTP。server会对一些基本可确认是HTTP的请求返回HTTP 400错误并关闭连接,但如果是HTTP method错误(在http包开头)或严重的格式错误(可能由HTTP client的bug导致),server仍会直接断开连接,导致nginx的final fail
 
-解决方案: 在使用Nginx转发流量时,可以对$HTTP_method做一下过滤,只放行允许的方法。或者干脆在proxy时设置proxy_method为指定方法,来避免ff。 
+解决方案: 在使用Nginx转发流量时,通过指定$HTTP_method只放行允许的方法或者干脆设置proxy_method为指定方法。
 
 ### Q: brpc支持http chunked方式传输吗
 
@@ -391,6 +412,6 @@ brpc server同端口支持多种协议,当它遇到非法HTTP请求并解析
                    / "*" / "+" / "," / ";" / "="
 ```
 
-Base64 编码后的字符串中,会以"="或者"=="作为结尾(比如: ?wi=NDgwMDB8dGVzdA==&anothorkey=anothervalue), 这个字段可能会被正确解析,也可能不会,取决于具体实现,用户不应该做任何假设.
+Base64 编码后的字符串中,会以"="或者"=="作为结尾,比如: ?wi=NDgwMDB8dGVzdA==&anothorkey=anothervalue。这个字段可能会被正确解析,也可能不会,取决于具体实现,原则上不应做任何假设.
 
-一个解决方法是删除末尾的"=", 不影响Base64的[正常解码](http://en.wikipedia.org/wiki/Base64#Padding); 第二个方法是在这个URI在base64之后在使用%编码,使用的地方先进行%解码,然后再用base64解码.
+一个解决方法是删除末尾的"=", 不影响Base64的[正常解码](http://en.wikipedia.org/wiki/Base64#Padding); 第二个方法是在对这个URI做[percent encoding](https://en.wikipedia.org/wiki/Percent-encoding),解码时先做percent decoding再用Base64.

+ 105 - 80
docs/en/http_service.md

@@ -1,12 +1,21 @@
-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).
+[中文版](../cn/http_service.md)
 
-# URL Prefix: /ServiceName/MethodName
+This document talks about ordinary HTTP services rather than protobuf services accessible via HTTP. HTTP services in brpc have to declare interfaces with empty request and response in a .proto file. This requirement keeps all service declarations inside proto files rather than scattering in code, configurations, and proto files. Check [http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp) for an example.
 
-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:
+# URL types
 
-1. Add service declaration in the proto file.
+## /ServiceName/MethodName as the prefix
 
-   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()`.	
+Define a service named `ServiceName`(not including the package name), with a method named `MethodName` and empty request/response, the service will provide http service on `/ServiceName/MethodName` by default.
+
+The reason that request and response can be empty is that the HTTP data is in Controller:
+
+- Header of the http request is in Controller.http_request() and the body is in Controller.request_attachment().
+- Header of the http response is in Controller.http_response() and the body is in Controller.response_attachment().
+
+Implementation steps:
+
+1. Add the service declaration in a proto file.
 
 ```protobuf
 option cc_generic_services = true;
@@ -19,7 +28,7 @@ service HttpService {
 };
 ```
 
-2. Implement the service by inheriting the base class inside .pb.h which is the same process as other protobuf based services.
+2. Implement the service by inheriting the base class generated in .pb.h, which is same as protobuf services.
 
 ```c++
 class HttpServiceImpl : public HttpService {
@@ -32,11 +41,10 @@ public:
         brpc::ClosureGuard done_guard(done);
         brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);
  
-        // Return plain text
+        // body is plain text
         cntl->http_response().set_content_type("text/plain");
        
-        // Print the query string and the request body
-        // and use these as response
+        // Use printed query string and body as the response.
         butil::IOBufBuilder os;
         os << "queries:";
         for (brpc::URI::QueryIterator it = cntl->http_request().uri().QueryBegin();
@@ -49,7 +57,7 @@ public:
 };
 ```
 
-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):
+3. After adding the implemented instance into the server, the service is accessible via following URLs (Note that the 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() |
 | -------------------------- | ---------------- | --------------------------------- | -------------------------------------- |
@@ -59,20 +67,20 @@ public:
 | /HttpService//Echo///Foo// | HttpService.Echo | "/HttpService//Echo///Foo//"      | "Foo"                                  |
 | /HttpService               | No such method   |                                   |                                        |
 
-# URL Prefix: /ServiceName
+## /ServiceName as the prefix
 
-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`.
+HTTP services to manage resources may need this kind of URL, such as `/FileService/foobar.txt` represents `./foobar.txt` and `/FileService/app/data/boot.cfg` represents `./app/data/boot.cfg`.
 
-To implement this:
+Implementation steps:
 
-1. In the proto file, use `FileService` as the service name and `default_method` for the method name.
+1. Use `FileService` as the service name and `default_method` as the method name in the proto file.
 
 ```protobuf
 option cc_generic_services = true;
- 
+
 message HttpRequest { };
 message HttpResponse { };
- 
+
 service FileService {
       rpc default_method(HttpRequest) returns (HttpResponse);
 }
@@ -96,7 +104,7 @@ public:
 };
 ```
 
-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):
+3. After adding the implemented instance into the server, the service is accessible via following URLs (the path after `/FileService` is filled in `cntl->http_request().unresolved_path()`, which is always normalized):
 
 | URL                             | Protobuf Method            | cntl->http_request().uri().path() | cntl->http_request().unresolved_path() |
 | ------------------------------- | -------------------------- | --------------------------------- | -------------------------------------- |
@@ -105,21 +113,21 @@ public:
 | /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
+## Restful URL
 
-brpc also supports to specify a URL for each method in a service:
+brpc supports specifying a URL for each method in a service. The API is as follows:
 
 ```c++
 // If `restful_mappings' is non-empty, the method in service can
-// be accessed by the specified URL rather than /ServiceName/MethodName. 
+// 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.       
+// 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:
+`QueueService` defined below contains several HTTP methods. If the service is added into the server normally, it's accessible via URLs like `/QueueService/start` and ` /QueueService/stop`.
 
 ```protobuf
 service QueueService {
@@ -130,9 +138,7 @@ service QueueService {
 };
 ```
 
-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:
+By specifying the 3rd parameter `restful_mappings` to `AddService`, the URL can be customized:
 
 ```c++
 if (server.AddService(&queue_svc,
@@ -154,19 +160,19 @@ if (server.AddService(&queue_svc,
 }
 ```
 
-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. 
+There are 3 mappings separated by comma in the 3rd parameter (which is a string spanning 3 lines) to the `AddService`. Each mapping tells brpc to call the method at right side of the arrow if the left side matches the URL. The asterisk in `/v1/queue/stats/*` matches any string.
 
-More about the mapping rules:
+More about 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.
+- Multiple paths can be mapped to a same method.
+- Both HTTP and protobuf services are supported.
+- Un-mapped methods are still accessible via `/ServiceName/MethodName`. Mapped methods are **not** accessible via `/ServiceName/MethodName` anymore.
+- `==>` and ` ===>` are both OK, namely extra spaces at the beginning or the end, extra slashes, extra commas at the end, are all accepted.
+- Pattern `PATH` and `PATH/*` can coexist.
+- Support suffix matching: characters can appear after the asterisk.
+- At most one asterisk 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:
+The path after asterisk can be obtained by `cntl.http_request().unresolved_path()`, which is always normalized, namely no slashes at the beginning or the end, and no repeated slashes in the middle. For example:
 
 ![img](../images/restful_1.png)
 
@@ -174,11 +180,11 @@ or:
 
 ![img](../images/restful_2.png)
 
-will be normalized to `foo/bar`, where extra slashes from both sides and the middle are removed.
+in which unresolved_path are both `foo/bar`. The extra slashes at the left, the right, or 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` ...
+The built-in service page of `/status` shows customized URLs after the methods, in form of `@URL1 @URL2` ...
 
 ![img](../images/restful_3.png)
 
@@ -186,9 +192,12 @@ The built-in service page of `/status` shows all the methods along with their UR
 
 ## 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.
+HTTP headers are a series of key/value pairs, some of them are defined by the HTTP specification, while others are free to use.
+
+Query strings are also key/value pairs. Differences between HTTP headers and query strings:
 
-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.
+* Although operations on HTTP headers are accurately defined by the http specification, but http headers cannot be modified directly from an address bar, they are often used for passing parameters of a protocol or framework.
+* Query strings is part of the URL and **often** in form of `key1=value1&key2=value2&...`, which is easy to read and modify. They're often used for passing application-level parameters. However format of query strings is not defined in HTTP spec, just a convention.
 
 ```c++
 // Get value for header "User-Agent" (case insensitive)
@@ -202,14 +211,14 @@ if (user_agent_str != NULL) {  // has the header
 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 
+// 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()`.
+`Content-type` is a frequently used header for storing type of the HTTP body, and specially processed in brpc and accessible by `cntl->http_request().content_type()` . As a correspondence, `cntl->GetHeader("Content-Type")` returns nothing.
 
 ```c++
 // Get Content-Type
@@ -221,11 +230,11 @@ if (cntl->http_request().content_type() == "application/json") {
 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()`.
+If the RPC fails (`Controller` has been `SetFailed`), the framework overwrites `Content-Type` with `text/plain` and sets 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.
+Status code is a special field in HTTP response to store processing result of the http request. Possible values are defined in [http_status_code.h](https://github.com/brpc/brpc/blob/master/src/brpc/http_status_code.h).
 
 ```c++
 // Get Status Code
@@ -238,7 +247,7 @@ 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:
+For example, following code implements redirection with status code 302:
 
 ```c++
 cntl->http_response().set_status_code(brpc::HTTP_STATUS_FOUND);
@@ -249,31 +258,39 @@ cntl->http_response().SetHeader("Location", "http://bj.bs.bae.baidu.com/family/i
 
 ## 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).
+As mentioned in above [HTTP headers](#http-headers), query strings are interpreted in common convention, whose form is `key1=value1&key2=value2&…`. Keys without values are acceptable as well and accessible by `GetQuery` which returns an empty string. Such keys are often used as boolean flags. Full API are 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
+# Debugging
 
 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
+# Compress the response body
+
+HTTP services often compress http bodies to reduce transmission latency of web pages and speed up the presentations to end users.
+
+Call `Controller::set_response_compress_type(brpc::COMPRESS_TYPE_GZIP)` to **try to** compress the http body with gzip. "Try to" means the compression may not happen in following conditions:
 
-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. 
+* The request does not set `Accept-encoding` or the value does not contain "gzip". For example, curl does not support compression without option `--compressed`, in which case the server always returns uncompressed results.
 
-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.
+* Body size is less than the bytes specified by -http_body_compress_threshold (512 by default). gzip is not a very fast compression algorithm. When the body is small, the delay added by compression may be larger than the time saved by network transmission. No compression when the body is relatively small is probably a better choice.
 
-# Decompress request body
+  | Name                         | Value | Description                              | Defined At                            |
+  | ---------------------------- | ----- | ---------------------------------------- | ------------------------------------- |
+  | http_body_compress_threshold | 512   | Not compress http body when it's less than so many bytes. | src/brpc/policy/http_rpc_protocol.cpp |
 
-Due to generality concern, brpc won't decompress request body automatically. You can do it yourself as it's not that complicate:
+# Decompress the request body
+
+Due to generality, brpc does not decompress request bodies automatically, but users can do the job by themselves as follows:
 
 ```c++
 #include <brpc/policy/gzip_compress.h>
@@ -292,7 +309,7 @@ if (encoding != NULL && *encoding == "gzip") {
 
 # 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.
+Update openssl to the latest version before turning on HTTPS, since older versions of openssl may have severe security problems and support less encryption algorithms, which is against with the purpose of using SSL. Setup `ServerOptions.ssl_options` to turn on HTTPS.
 
 ```c++
 // Certificate structure
@@ -300,14 +317,14 @@ 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. 
+    // 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;
@@ -318,7 +335,7 @@ struct SSLOptions {
     // 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.
@@ -334,50 +351,58 @@ struct SSLOptions {
     // ... 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).
+Other options include: cipher suites (recommend using `ECDHE-RSA-AES256-GCM-SHA384` which is the default suite used by chrome, and one of the safest suites. The drawback is more CPU cost), session reuse and so on. Read [server.h](https://github.com/brpc/brpc/blob/master/src/brpc/server.h) for more information.
 
-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;
-```
+After turning on HTTPS, the service is still accessible by HTTP from the same port. The server identifies whether the request is HTTP or HTTPS automatically, and tell the result to users by `Controller::is_ssl()`. As you can see, the HTTPS in brpc is more like supporting an additional protocol, rather than providing an encrypted communication channel.
 
 # 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:
+Productions without extreme performance requirements tend to use HTTP protocol, especially mobile products. Thus we put great emphasis on implementation qualities 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).
+- Use [http parser](https://github.com/brpc/brpc/blob/master/src/brpc/details/http_parser.h) of node.js to parse http messages, which is a lightweight, well-written, and extensively used implementation.
+- Use [rapidjson](https://github.com/miloyip/rapidjson) to parse json, which is a json library focuses on performance.
+- In the worst case, the time complexity of parsing http requests is still O(N), where N is byte size of the request. As a contrast, parsing code that requires the http request to be complete, may cost O(N^2) time in the worst case. This feature is very helpful since many HTTP requests are large.
+- Processing HTTP messages from different clients is highly concurrent, even a pretty complicated http message does not block responding other clients. It's difficult to achieve this for other RPC implementations and http servers often based on [single-threaded reactor](threading_overview.md#single-threaded-reactor).
 
 # Progressive sending
 
-brpc server is also suitable for sending large or infinite size body. Uses the following method:
+brpc server is capable of sending large or infinite sized body, in following steps:
+
+1. Call `Controller::CreateProgressiveAttachment()` to create a body that can be written progressively. The returned `ProgressiveAttachment` object should be managed by `intrusive_ptr`
+  ```c++
+  #include <brpc/progressive_attachment.h>
+  ...
+  butil::intrusive_ptr<brpc::ProgressiveAttachment> pa (cntl->CreateProgressiveAttachment());
+  ```
+
+2. Call `ProgressiveAttachment::Write()` to send the data.
 
-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.
+   * If the write occurs before running of the server-side done, the sent data is cached until the done is called.
+   * If the write occurs after running of the server-side done, the sent data is written out in chunked mode immediately.
+
+3. After usage, destruct all `butil::intrusive_ptr<brpc::ProgressiveAttachment>` to release related resources.
 
 # 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.
+Currently brpc server doesn't support calling the service callback once header part in the http request is parsed. In other words, brpc server is not suitable for receiving large or infinite sized body.
 
 # FAQ
 
-### Q: nginx which uses brpc as upstream encounters final fail (ff)
+### Q: The nginx before brpc encounters final fail
+
+The error is caused by that brpc server closes the http connection directly without sending a response.
 
-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.
+brpc server supports a variety of protocols on the same port. When a request is failed to be parsed in HTTP, it's hard to tell that the request is definitely in HTTP. If the request is very likely to be one, the server sends HTTP 400 errors and closes the connection. However, if the error is caused HTTP method(at the beginning) or ill-formed serialization (may be caused by bugs at the HTTP client), the server still closes the connection without sending a response, which leads to "final fail" at nginx.
 
-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. 
+Solution: When using Nginx to forward traffic, set `$HTTP_method` to allowed HTTP methods or simply specify the HTTP method in `proxy_method`.
 
 ### Q: Does brpc support http chunked mode
 
-Yes
+Yes.
 
-### Q: Why does HTTP requests containing BASE64 encoded query string fail to parse sometimes?
+### Q: Why do 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 `%`.
+According to the [HTTP specification](http://tools.ietf.org/html/rfc3986#section-2.2), following characters need to be encoded with `%`.
 
 ```
        reserved    = gen-delims / sub-delims
@@ -388,6 +413,6 @@ According to the [HTTP specification](http://tools.ietf.org/html/rfc3986#section
                    / "*" / "+" / "," / ";" / "="
 ```
 
-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.
+Base64 encoded string may end with `=` which is a reserved character (take `?wi=NDgwMDB8dGVzdA==&anothorkey=anothervalue` as an example). The strings may be parsed successfully, or may be not, depending on the implementation which should not be assumed in principle.
 
-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. 
+One solution is to remove the trailing `=` which does not affect the [Base64 decoding](http://en.wikipedia.org/wiki/Base64#Padding). Another method is to [percent-encode](https://en.wikipedia.org/wiki/Percent-encoding) the URI, and do percent-decoding before Base64 decoding.