> For the complete documentation index, see [llms.txt](https://meetbill.gitbook.io/butterfly-project-doc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://meetbill.gitbook.io/butterfly-project-doc/project-framework/how/api/butterfly-cookie.md).

# cookie

environ demo

```
environ={'HTTP_AUTHORIZATION': 'Bearer: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJidXR0ZXJmbHkiLCJpYXQiOjE1NzUwNDE1NTgsInVzZXJuYW1lIjoid2FuZ2JpbjM0IiwianRpIjoiMWVmMDI0MDQtM2Q3MC00ZmI4LTg2YzUtNTE3MWE0NzkwYTI0IiwiZXhwIjoxNTc1MDcwMzU4fQ.CgEUugRpKMvlCjlYmZ56Xx6G1m6z0bPcrw2u-FTifm4', 'HTTP_REFERER': 'http://127.0.0.1:8585/main', 'SERVER_SOFTWARE': 'CherryPy/3.2.0 Server', 'SCRIPT_NAME': '', 'ACTUAL_SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/api/get_jobs', 'SERVER_PROTOCOL': 'HTTP/1.1', 'QUERY_STRING': '', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', 'HTTP_CONNECTION': 'keep-alive', 'REMOTE_PORT': '51501', 'SERVER_NAME': 'B000000119924S', 'REMOTE_ADDR': '127.0.0.1', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '8585', 'HTTP_SEC_FETCH_MODE': 'cors', 'HTTP_HOST': '127.0.0.1:8585', 'wsgi.multithread': True, 'HTTP_SEC_FETCH_SITE': 'same-origin', 'REQUEST_URI': '/api/get_jobs', 'HTTP_ACCEPT': 'application/json, text/plain, */*', 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.multiprocess': False, 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_COOKIE': 'csrftoken=tcjr7Ps8pJKNrS8JNmB1h7YLz1XKUMWoVn02OoREBuVmSZHn5KLfScHSv7PthuCB'}
```

测试时可以先将部分 key 剔除

```
'wsgi.errors': <open file '<stderr>', mode 'w' at 0x107efa1e0>
'wsgi.input': <xlib.cherrypy_wsgiserver.KnownLengthRFile object at 0x108b0b250>
```

## 1 其他框架使用 Cookie

### 1.1 Django

#### 使用

Django 中 cookie 的保存和存取

> 保存 cookie： 通过 HttpResponse 类的对象的 set\_cookie 方法保存

```
response.set_cookie('键', '值'，max_age)
```

> 读取 cookie：通过 HttpRequest 请求对象的 COOKIES 属性（字典类型）读取

```
request.COOKIES['键']
# 或者：
request.COOKIES.get('键')
```

#### 获取 cookie

> django/core/handlers/wsgi.py

```python
@cached_property
def COOKIES(self):
    raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
    return http.parse_cookie(raw_cookie)

```

> * 解析 cookie：django/http/cookie.py:parse\_cookie
>   * django/http/cookie.py

Django 是获取到 cookie 字符串，然后将字符串解析为 Dict，不过 Django 在解析时，如果只指定了一个属性，则假定而不是，详情如下

```
https://bugzilla.mozilla.org/show_bug.cgi?id=169091
```

#### 设置 cookie

> django/core/handlers/wsgi.py:WSGIHandler

```
for c in response.cookies.values():
    response_headers.append((str('Set-Cookie'), str(c.output(header=''))))

```

> django/http/response.py:HttpResponseBase

```
self.cookies = SimpleCookie()
self.cookies[key] = value
```

### 1.2 Bottle

#### 使用

```python
import bottle
from bottle import template, Bottle, static_file, request, redirect

bottle.TEMPLATE_PATH.append("./templates/")  # 指定模板目录

root = Bottle()

@root.route("/index", method=["GET", "POST"])#method 默认 GET, 可以指定其他请求或者请求方式列表
def index():
    print (request.method)     # POST
    print (request.forms)      # post 请求信息
    print (request.query)      # get 请求数据
    print (request.body)       # POST 请求数据
    print (request.files)      # 上传的文件信息
    print (request.cookies)    # cookie 信息
    print (request.environ)    # 环境信息
    print (request.json)  #
    print (request.params)  #
    if request.method == "GET":
        return template("<h1> Hello {{ name }}</h1>", name="Bottle")
    elif request.method == "POST":
        return redirect("/index/")

@root.route("/static/<path:path>")
def callback(path):
    return static_file(path, "static") # 指定静态文件目录 static


root.run(host="localhost", port=8080)
```

Bottle 获取 cookie 时，是通过官方库 Cookie(Python 2.7 自带） 中 SimpleCookie 封装，然后再解析为 dict

## 2 分析

Cookie 是取的 environ\["HTTP\_COOKIE"] ，这个值是个 String ，很多框架获取 cookie 都是在 request 类中进行实现的

## 3 实现

### 3.1 服务端获取客户端的 Cookie

Butterfly 解析 Cookie 时也是放到了 request 类中实现，此处借鉴了 Django 的实现，不过解析到的 cookie item 中，如果 item 没有 "=" , 则会过滤掉

```
req.cookies()
```

### 3.2 服务器端向客户端发送 Cookie

响应首部 [Set-Cookie](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie) 被用来由服务器端向客户端发送 cookie

[Python 2.7 cookie 库](https://docs.python.org/2/library/cookie.html)

```python
>>> import Cookie
>>> C = Cookie.SimpleCookie()
>>> C["fig"] = "newton"
>>> C["sugar"] = "wafer"
>>> print C                                            # generate HTTP headers
Set-Cookie: fig=newton
Set-Cookie: sugar=wafer
>>> C.values()
[<Morsel: fig='newton'>, <Morsel: sugar='wafer'>]
#------------------------------------------------------------------------------------
>>> header_list = []                                   # 添加到 HTTP headers list
>>> for c in C.values():
...    header_list.append(('Set-Cookie', c.OutputString()))
...
>>> header_list
[('Set-Cookie', 'fig=newton'), ('Set-Cookie', 'sugar=wafer')]
#------------------------------------------------------------------------------------
>>> C["sugar"].keys()
['comment', 'domain', 'secure', 'expires', 'max-age', 'version', 'path', 'httponly']
>>> C["sugar"].values()
['', '', '', '', '', '', '', '']
```

> 标准的 Set-Cookie Header：

```
Set-Cookie: key=value; path=path; domain=domain; max-age=max-age-in-seconds; expires=date-in-GMTString-format; secure; httponly
```

> Cookies 可选属性

```
comment  :
domain   : cookie 的有效域 （有些网站有许多小的域名，例如百度可能还在"news.baidu.com" "zhidao.baidu.com" 和"v.baidu.com" 域名下有网页．如果想让"baidu.com"下的所有机器都可以读取该 cookie，必须在 cookie 中加入 "domain=.baidu.com")
secure   :
expires  : cookie 的生存周期
max-age  :
version  :
path     : cookie 的有效路径
httponly :
```
