ynm3k(要你命3000)

ynm3k(要你命3000)是一个基于Python开发,以辅助调试为主要目的,其他各种莫名其妙的功能都有一些的http服务器,主要功能包括:

  • 直接通过json进行配置
  • 编写和服务mock数据
  • 提供反向代理
  • 静态文件服务器(包括把zip文件作为静态目录提供服务)

Contents

安装

可以通过pip直接安装ynm3k

pip install ynm3k

在权限不足时,可能需要加上sudo前缀

sudo pip install ynm3k

如果已经安装了ynm3k,可以通过如下的方法将ynm3k升级到最新版本

pip install --no-deps --upgrade ynm3k

命令行参数

ynm3k的在安装成功后,可以通过y3k命令启动的,y3k命令支持以下的基本参数:

  • –help: 显示帮助信息。
  • –version: 显示当前版本。
  • –host: 设置y3k绑定的地址,默认为0.0.0.0 。
  • –port: 设置y3k启动之后的端口号,默认为8080 。
  • --mock : 接受一个.json文件作为配置文件,具体的配置文件的格式详见配置文件一节中的内容。
  • --mock-prefix : 指定–mock的参数所启动的服务的路径前缀。
  • --reload : 让ynm3k进程监视所对应的文件或者路径,如果发生变化,则重新加载ynm3k服务器。比如,可以监控mock的配置文件,在配置发生变化时自动重新加载。
  • --server : 因为ynm3k是通过bottle.py启动的,默认的服务器为python自带的wsgiref中的simple-server,通过这个参数可以指定用来启动服务的web server,具体见 https://bottlepy.org/docs/dev/deployment.html#switching-the-server-backend 。

下面的参数主要用于单独启动各个功能模块(这些功能模块往往也可以作为–mock参数所引入的配置文件的一部分来加载):

static静态文件模块

  • –static : 把路径下的内容,作为一个静态文件服务提供出来。
  • –static-prefix : 启动–static参数后,指定这个模块的路径前缀。
  • –static-serve-dir: 启动–static参数后,如果提供这个参数,将允许把目录列表作为请求返回。

zip模块

  • –zip : 指定一个zip文件,把zip文件的内容作为一个静态文件服务提供出来。
  • –zip-prefix: 启动–zip参数后,指定这个模块的路径前缀。

配置文件

ynm3k可以通过–mock参数指定一个json(ynm3k的最新版本默认采用hjson作为配置文件的格式,兼容标准的json,并且可以支持注释,多行字符串等功能)格式的配置文件:

y3k --mock config.json

这个json文件的内容是一个list,list中每一项都是一个二元组,二元组的第一项为是http请求的描述(request spec),第二项为http响应的描述(response spec)。

ynm3k的在处理请求的时候,会按照配置文件的这个list的顺序,匹配当前请求是否满足每一个request spec的条件,如果找到了一个满足条件的request spec,那么会根据对应的response spec生成一个响应来返回。request spec和response spec的都既可以是一个dict object,也可以是一个字符串(仅仅应用于处理简单的反向代理的模式)。

配置文件的结构如下:

[
    [<Request Spec>, <Response Spec>],
    ...
]

配置文件的范例

  • 根据前缀转发请求至不同服务器
[
  ["/api/", "http://example.com/api/"],
  ["/static/my_work.html", "http://127.0.0.1/static/my_work.html"],
  ["/", "http://192.168.1.10:8080/"]
]
  • 为每个请求增加一个特殊的header,比如设置X-Forwarded-For
[
 [{"type": "prefix",
   "path": "/",
   "headers": {"X-Forwarded-For": "1.2.3.4"}},
  {"type": "remote",
   "url": "https://ifcfg.cn/"}
 ]
]

request和response的描述方法

通过y3k --mock config.json可以让ynm3k加载config.json中的配置,在这个配置中,每一项都是[, ]的二元组,其中request spec是用来匹配当前请求的。ynm3k会在config.json中找到可以匹配当前请求的第一个request spec的,然后根据response spec中的描述生成对当前请求的响应。

请求和响应的描述方法,都可以采用两种方式,一种是字符串的方式的描述,简单、直接,但是不能做比较精细的定制;另外一种是通过dict object来进行表述,就可以定制http method,http headers等属性。字符串方式的请求,都是一种dict object方式的简化写法。

request spec,请求的描述

字符串方式
  • 前缀类型: 直接写成请求的路径前缀。比如’/api/’,那么ynm3k会把路径前缀为’/api/’的请求匹配到这个request spec上。例子中的前缀类型的请求,如果改写为dict object,相当于
{
  "type": "prefix",
  "path": "/api/"
}
  • 完全匹配类型: 在字符串前面加一个’=’。比如’=/api/test’,ynm3k会把路径前缀完全等于’/api/test’的请求匹配到这个request spec上。例子中的完全匹配类型的请求,如果改写成dict object的方式,相当于
{
  "type": "exact",
  "path": "/api/test"
}
dict object方式

dict object方式request spec,支持指定如下的属性:

  • type: request spec本身的类型,必须提供,可以选择的值包括’exact’,’prefix’。
  • path: 请求的路径,会根据request spec的类型决定这个路径是前缀还是完全匹配。
  • method: 请求的方法,如’GET’, ‘POST’, ‘PUT’等,如果指定了这个属性,那么请求的方法必须和指定的一致才能匹配成功;如果不指定这个属性,那么匹配请求时,任何方法都是可以匹配成功的。

response spec,响应的描述

字符串类型
  • 远程方式,remote: 如果response spec的描述是以’https://’或者’http://’开头的远程地址,那么字符串方式的表述会被解析为remote方式的响应,也就是说请求会被反向代理到这个地址上。远程方式相当于这样的dict object
{
  "type": "remote",
  "url": "https://..."
}
  • 内容类型: 不满足远程方式条件的会被认为是内容方式,也就是直接把字符串的内容作为响应的body返回。相当于下面的dict object
{
  "type": "content",
  "body": "...",
  "headers": {
    "content-type": "text/plain"
  }
}
dict object方式

dict object方式的response spec,全部都可以支持的属性如下:

  • type: type属性是必须的,决定了response spec的返回响应的方式,也决定了这个dict object中,还需要配置哪些属性。
  • headers: headers属性可以是一个dict(比如 {“content-type”: “application/json”}),或者是一个2-tuple的list(比如 [[“content-type”, “application/json”]]),用户在返回的响应中设置自定义的headers。
  • status: status属性是int类型的,可以设置一个http返回码,用于设置或者覆盖当前响应的http return code。
  • operations: 在返回响应之前,可以通过在operations这个参数中定义的操作对返回进行后处理,operations可以为一个dict object或者dict object组成的list,也就是说可以支持一次或者多次的后处理操作。本参数需要0.4.0版本以后的版本支持。

ynm3k可以支持三种基本类型的response spec,即远程(remote),内容(content)和文件(file)。在ynm3k中提供的其它module,一般既可以通过命令行的方式启动,也可以在response spec中,作为一种特殊的类型来生成响应。

下面对基本类型做一些说明,其他模块的说明将在模块自己的文档中提供:

  • 远程,remote: 远程(type == ‘remote’)就是通过反向代理的方式,从远程的服务器获取当前的请求。在request spec的类型不同时,响应会有不同的行为,如果request spec的类型是’exact’,会直接返回远程地址的内容;如果request spec的类型是’prefix’,会把请求的后缀映射为远程地址的后缀,比如,如果request spec的path为’/test/’,远程请求的url为’https://www.example.com/product/’,那么会把到’/test/somewhere/inside’映射到远程的’https://www.example.com/product/somewhere/inside’。
    • url: 远程模式下,被反向代理到的地址
  • 内容,content: 内容(type == ‘content’)是直接把响应的body写在了response spec中。
    • body: 响应的内容。
  • 文件, file: 文件(type == ‘file’),是从文件中读取响应的内容,响应headers中的content-type会根据文件的扩展名来自动设置。
    • body: 文件的路径。

其他模块目前可以支持的响应内容包括:

  • static: 以静态文件的方式,返回一个文件夹下的内容。
  • zip: 以静态文件的方式,返回一个zip包中的内容。

对于response的个性化后处理

在response spec中,operations指定了一个或者多个对response进行的后处理操作,可以通过这些操作,对response进行个性化的加工,从而给一些特殊的调试场景提供支持。

operations参数可以是一个操作的列表(list),每个操作通过一个dict object进行定义;operations也可以是一个dict object,这时相当于只有一个个性化操作需要处理。每个操作的dict object格式为

{
  "type": "<操作类型>",
  ...  # 针对于不同操作类型的特殊参数
}

下面我们会根据不同的操作类型做详细的说明。

insert_adjacent_html,向dom中插入标签

insert_adjacent_html可以向响应body中的html dom中插入个性化标签代码,使用方法和js中insertAdjacentHTML比较相似,这种操作只有在响应的content type为text/html时才会生效。另外,如果要启用这个操作,必须要安装beautiful soup 4,安装方法为

# 在虚拟环境中
pip install bs4
# 需要root权限时
sudo pip install bs4

具体支持的参数包括

  • selector: 一个css选择器,比如”title”, “body a”等,插入标签操作讲作用于被选择到的所有dom元素上。
  • position: html代码插入的位置,支持”beforebegin”, “afterbegin”, “beforeend”, “afterend”这四个位置,具体位置对应的方式可以参考 https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML 中的说明。
  • html: 要插入的html代码。

比如,我们可以通过下面的配置,为转发的网页都加上vConsole,可以在不需要修改源代码的情况下直接调试移动端的页面,便于对生产和线上环境的通过手机对移动端页面进行调试和故障排查。

[
 ["/",
  {"type": "remote",
   "url": "https://m.baidu.com/",
   "operations": {"type": "insert_adjacent_html",
                  "selector": "title",
                  "position": "afterend",
                  "html": "<script src='https://res.wx.qq.com/mmbizwap/zh_CN/htmledition/js/vconsole/3.0.0/vconsole.min.js'></script><script>var vConsole = new VConsole();</script>"}
  }]
]

静态文件服务,static模块

static模块可以以静态文件的方式,针对文件系统中的目录提供服务。

在–mock的config.json中,需要设置以下的参数:

  • type: type == ‘static’时,表示采用static方式提供响应。
  • path: 要提供静态文件的路径,需要以’/’结尾。
  • serve_dir: boolean, 是否要在访问目录时,显示目录列表。
  • try_files: 一个默认文件的列表,比如可以提供[“index.html”, “index.htm”]。提供了这个参数是,如果访问的路径是一个目录,那么会在这个目录下寻找列表中的文件,如果存在,会返回这个文件的内容作为响应的内容。

举例:

[
  ["/img/",
   {"type": "static",
    "path": "/abc/img/"}
  ],
  ["/html/",
   {"type": "static",
    "path": "/cde/html/",
    "try_files": ["index.html"]}
  ],
  ["/download/",
   {"type": "static",
    "path": "/fgh/download/",
    "serve_dir": true}
  ]
]

通过y3k在命令行,可以以如下启动:

y3k --static <PATH> [--static-prefix <PREFIX>] [--static-serve-dir]

其中

  • –static : 把路径下的内容,作为一个静态文件服务提供出来。
  • –static-prefix : 启动–static参数后,指定这个模块的路径前缀。
  • –static-serve-dir: 启动–static参数后,如果提供这个参数,将允许把目录列表作为请求返回。

zip模块

zip模块可以把一个zip文件作为一个静态的目录来服务。

在通过–mock参数指定的config.json中,需要设置以下的参数:

  • type: type == ‘zip’时,表示采用zip模块来提供相应。
  • path: zip文件的路径。
  • encoding: 可选,zip文件的编码,默认值为utf-8,对于一些文件名编码为gbk文件,可以指定这个参数为’gbk’.

下面一个config.json的例子:

[
   ['/book/', '/home/ubuntu/book.zip']
]

如果采用y3k在命令行方式直接serve一个zip文件,可以以如下的方式启动:

y3k --zip <ZIP_FILE_NAME> [--zip-prefix <PATH_PREFIX>]

其中

  • –zip : 指定一个zip文件,把zip文件的内容作为一个静态文件服务提供出来。
  • –zip-prefix: 启动–zip参数后,指定这个模块的路径前缀,如果这个参数指定了’/audio/’,那么/audio/会对应这个zip文件的根路径。

使用范例

设置http header来强制浏览器不缓存资源

在下面的配置中,通过设置浏览器缓存相关的header,可以强制的要求浏览器不做任何缓存,每次请求都要刷新数据

[
   [
      "/",
      {
         "url" : "https://www.amazonaws.cn/",
         "type" : "remote",
         "headers" : {
            "Pragma" : "no-cache",
            "Expires" : "0",
            "Cache-Control" : "no-cache, no-store, must-revalidate"
         }
      }
   ]
]

通过设置http response status code为302,跳转至某一url

[
  [
    "/",
    {
      "type": "content",
      "status": 302,
      "headers": {
        "Location": "https://valarmorghulis.io"
      }
    }
  ]
]

FAQ

  • Q: 为什么有时候ynm3k的进程会卡住?
  • A: 因为ynm3k默认情况下使用的是的单线程http server,对于并发请求的处理效果并不好,可能会导致进程卡住,可以通过paste等http server来辅助解决这个问题。比如
pip install paste
ynm3k --mock mock.json --server paste

ynm3k的github地址为https://github.com/socrateslee/ynm3k