2017年10月

这两天想把基于 Flask 的 Telegram Bot 重新写一下,之前一把梭的太难看了。
顺便把一直想写的插件系统实现一下,这里单独把插件部分摘出来记录分享一下

./main.py

from flask import Flask, request, Response
import os, sys

app = Flask(__name__)
app.debug = True
sys.dont_write_bytecode = True

# 插件部分
print("Loading plugins...")
sys.path.append(os.getcwd() + '/plugins')  # 设置一下 module 的搜索路径
plugins = []
for file in os.listdir("./plugins"):
    filename = file.split('.')[0]
    plugin = __import__(filename)  # for 起来 magic 一把梭
    plugins.append(plugin)
    print "Loaded Plugin '%s' with priority %s." % (filename, plugin.priority)
plugins.sort(key=lambda x: x.priority)  # 一个一把梭的插件优先级实现
# 看一看最后 EventLoop 的处理顺序
print("Plugin order:")
for plugin in plugins:
    print(plugin.__name__)
print("Plugins ready.")
# 业务部分
@app.route("/")
def index():
    # EventLoop
    for plugin in plugins:
        plugin.plugin_main(request.data)
        if update.handled:
            print("meet handled, stopping EventLoop.")
            break
    return ""

./plugins/example.py

#!/usr/bin/env python
# coding:utf-8

priority = 100

def plugin_main(update):
    # Do something...
    update.handled = True  # 阻止优先级更低的插件接收到 request

if __name__ == "__main__":
    print("This is a plugin.")

终端输出

(.env) [email protected] /h/w/api.stone.moe> python dummy.py
Loading plugins...
Loaded Plugin 'msglog' with priority 0.
Loaded Plugin 'example' with priority 100.
Loaded Plugin 'cmdhandler' with priority 1.
Plugin order:
msglog
cmdhandler
example
Plugins ready.
 * Running on http://127.0.0.1:8081/ (Press CTRL+C to quit)

新增用户时指定用户所在的 Group

# ocpasswd -c /path/to/passwd/file -g "groupname,groupname2,groupxxx" username

ocserv.conf 里面几个关键配置

# 关掉自动选择
auto-select-group = false
# define 几个 group identify,bracket 里面可以写点description
select-group = all
select-group = aws[no bracket or add some description]
select-group = nochn[No CN route]
....
# 指定一个存 config-per-group 的路径
config-per-group = /etc/ocserv/config-per-group/

然后我们

# mkdir /etc/ocserv/config-per-group
# cd /etc/ocserv/config-per-group
# touch all aws nochn

然后在这几个文件里写上config,来覆盖掉 ocserv.conf 主配置文件的设置。
比如在 nochn 写上一点 no-route。
也可以在 aws 写上一些 route,让这个组的用户在连接后只接收一些到 aws 的 route table。

HTTP 本身,POST body是不限格式的。

然而没有标准怎么行,于是有好几种 Content-Type 和 encoding 方案给 POST 用。

0x01 HTTP 本身的编码相关问题
-

  1. HTTP spec 规定
HTTP Header 必须为 ASCII 编码

但是规定虽然是规定,还是得看BS两端具体能不能容错,再说 unicode 也兼容 ASCII,所以大部分时候没什么毛病。

(不过我们最好还是遵守 spec)

  1. 与 charset 相关的 header 有 Content-Type 里面的 charset 属性

    一般这样写:

Content-Type: text/html;charset=utf-8

似乎是指 Header 虽然要求 ASCII,可是 entity body 却可以指定其他编码(可能会要求兼容 ASCII)。
这部分读 spec 也没看太懂,期待大神指教。

0x02 再看 POST
-

几种常见/常用的 POST entity body Content-Type

application/x-www-form-urlencoded
multipart/form-data
application/json

然后从 Postman 里面又看到了几种平时没怎么见过的或者不常用的

text/plain
application/javascript
text/xml
application/xml
text/html

看到这里其实就明白了,归根结底还是来源于 MIME (rfc1341)

0x03 常见的 POST 都长什么样子
-

1.application/x-www-form-urlencoded

大概长这样:

POST /abc HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

最基础的 HTML Form 提交之后,浏览器就会使用x-www-form-urlencoded,是默认的表单格式。

除了 Header 指定的 Content-Type 之外:

  • 格式为 key=value&x=y
  • 使用 urlencode 编码
  • a[]=abc&a[]=123 可以用来表示 array

2.multipart/form-data

大概长这样:

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="submit-name"

Larry
--AaB03x
Content-Disposition: form-data; name="files"
Content-Type: multipart/mixed; boundary=BbC04y

--BbC04y
Content-Disposition: file; name="forminputname" filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--BbC04y
Content-Disposition: file; filename="file2.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary

...contents of file2.gif...
--BbC04y--
--AaB03x--

给 Form 设置 enctype="multipart/form-data" 之后,就可以使用 multipart 格式来上传文件了。

除了 Header 指定的 Content-Type 之外:

  • Content-Type 后面跟了个 boundary 属性来声明 multipart 的边界,作为多个 Form input 的分隔符
  • 每个部分的起始分隔符前面要加--
  • 每个部分的结束分隔符前后都要加--
  • 每个部分都能指定 Content 相关的 header,甚至能继续嵌套 multipart
  • 通过 Content-Disposition 提供 Form input 的 name 属性

这部分比较复杂,可以参考 SOF

3.application/json

大概长这样:

POST /efg HTTP/1.1
Content-Type: application/json;charset=utf-8

{"title":"test","sub":[1,2,3]}

没什么好说的,简单,好用。

如果BS两端都支持的话,非 ASCII 编码并不需要额外编码,JSON 本身并没有要求。
但通常为了更好的兼容性,会使用编码后的形式,而这里就是跟 javascript 的 escape 走了,并不是urlencode。
而编码后也很简单,直接使用 unicode,如 "\u0421\u043b\u043e\u0432\u043e",或者 "%u0421%u043b%u043e%u0432%u043e"

0x00 提出问题
-
一般遇到这个问题,我们会看到类似于这样的错误信息

The locale requested by LC_CTYPE=UTF-8 isn't available here.
Running `locale-gen UTF-8' may be necessary.

mosh-server needs a UTF-8 native locale to run.

Unfortunately, the local environment (LC_CTYPE=UTF-8) specifies
the character set "US-ASCII",

The client-supplied environment (LC_CTYPE=UTF-8) specifies
the character set "US-ASCII".

locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=
LANGUAGE=
LC_CTYPE=UTF-8
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
Connection to 128.199.. closed.
/usr/local/bin/mosh: Did not find mosh server startup message.

locale 就是指当前系统所使用的语言文件设定

0x01 发现问题
-
从错误信息和各种 Github Issue 中剖析可知,mosh 要正常工作,需要满足两个条件:

  1. 本地用的 locale,远端需要有对应的 locale 文件

    (Client 所使用的 locale 设置,会被 mosh 发送到 Server 进行比对,让 Server 设置到同一个 locale 上。)
    
  2. 目前所使用的 locale 需要是 UTF-8 的编码

0x02 解决问题
-

  1. 首先确认本地使用的是 UTF-8 的 locale 设置。

    使用 `locale` 来确认当前使用的 locale 设置,如果所有的参数值都带有 `.UTF-8` 字样,则说明没有问题。
    *( `LC_ALL` 是指全局的 locale 设置,其他的参数是不同情景的设置,比如可以让显示时间、货币等特殊字段时,使用不同的 locale。)*
    
  2. 如果不是 UTF-8 或者需要改个语言

    使用 `locale -a` 可以获取到所有已经安装的 locale,找一个自己需要语言的 UTF-8 版本的 locale,然后在环境变量中直接设置 `LC_ALL` 为对应的 locale 名字。

    Bash:export LC_ALL="en_US.UTF-8"

Fish:set -xg LC_ALL "en_US.UTF-8"

  1. 确认服务器上有这个 locale

    同样的,使用`locale -a`查看当前已安装的 locale 设置。
    
    如果没有,Ubuntu 可以使用 `locale-gen en_US.UTF-8` 生成一个。
    而 Debian 下这个命令虽然存在,但并不是用于生成新的 locale 文件,需要使用 `dpkg-reconfigure locales` 来对 locale 进行重新配置,来生成新的 locale 和设置默认的 locale。
    

0x04 吐槽
-
mosh 的好几个问题可以说是老生常谈了,比如 scroll 的支持,locale 的问题,都有许多的相关 issue。
感觉 mosh 的错误提示太模糊了,或者应该提供一个 ignore-locale 的选项。
然而,mosh 的 contributor 却丢一句

This looks like it is probably not a Mosh-specific problem.

emmm 总不能把这问题永远留在 issue 里面啊。

0x03 相关
-
https://unix.stackexchange.com/questions/246846/cant-generate-en-us-utf-8-locale

Debian, interactive: dpkg-reconfigure locales
Debian, automated: sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && locale-gen
Ubuntu, automated: locale-gen en_US.UTF-8

https://github.com/mobile-shell/mosh/issues/793
https://github.com/mobile-shell/mosh/issues/678
http://www.jianshu.com/p/386c80192f8e