HttpCookieManager.cpp 9.54 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
3
 *
4
 * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
5
 *
xiongziliang committed
6 7 8
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
9 10
 */

11
#include "HttpCookieManager.h"
12 13 14
#include "Common/config.h"
#include "Util/MD5.h"
#include "Util/util.h"
15

夏楚 committed
16 17 18
using namespace std;
using namespace toolkit;

19 20 21
namespace mediakit {

//////////////////////////////HttpServerCookie////////////////////////////////////
22 23 24
HttpServerCookie::HttpServerCookie(
    const std::shared_ptr<HttpCookieManager> &manager, const string &cookie_name, const string &uid,
    const string &cookie, uint64_t max_elapsed) {
25 26 27
    _uid = uid;
    _max_elapsed = max_elapsed;
    _cookie_uuid = cookie;
28
    _cookie_name = cookie_name;
29
    _manager = manager;
30
    manager->onAddCookie(_cookie_name, _uid, _cookie_uuid);
31 32
}

33
HttpServerCookie::~HttpServerCookie() {
34
    auto strongManager = _manager.lock();
35 36
    if (strongManager) {
        strongManager->onDelCookie(_cookie_name, _uid, _cookie_uuid);
37 38
    }
}
39

40
const string &HttpServerCookie::getUid() const {
41
    return _uid;
42 43
}

44 45 46 47
string HttpServerCookie::getCookie(const string &path) const {
    return (StrPrinter << _cookie_name << "=" << _cookie_uuid << ";expires=" << cookieExpireTime() << ";path=" << path);
}

48
const string &HttpServerCookie::getCookie() const {
49 50 51
    return _cookie_uuid;
}

52
const string &HttpServerCookie::getCookieName() const {
53
    return _cookie_name;
54 55
}

56
void HttpServerCookie::updateTime() {
57 58
    _ticker.resetTime();
}
59 60 61

bool HttpServerCookie::isExpired() {
    return _ticker.elapsedTime() > _max_elapsed * 1000;
62
}
63

64 65
void HttpServerCookie::setAttach(std::shared_ptr<void> attach) {
    _attach = std::move(attach);
66 67
}

68
string HttpServerCookie::cookieExpireTime() const {
69
    char buf[64];
70
    time_t tt = time(nullptr) + _max_elapsed;
71 72 73
    strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
    return buf;
}
74 75
//////////////////////////////CookieManager////////////////////////////////////
INSTANCE_IMP(HttpCookieManager);
76

77 78
HttpCookieManager::HttpCookieManager() {
    //定时删除过期的cookie,防止内存膨胀
79 80 81 82 83 84 85
    _timer = std::make_shared<Timer>(
        10.0f,
        [this]() {
            onManager();
            return true;
        },
        nullptr);
86 87
}

88 89
HttpCookieManager::~HttpCookieManager() {
    _timer.reset();
90 91
}

92 93 94
void HttpCookieManager::onManager() {
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    //先遍历所有类型
95
    for (auto it_name = _map_cookie.begin(); it_name != _map_cookie.end();) {
96
        //再遍历该类型下的所有cookie
97 98 99
        for (auto it_cookie = it_name->second.begin(); it_cookie != it_name->second.end();) {
            if (it_cookie->second->isExpired()) {
                // cookie过期,移除记录
100
                DebugL << it_cookie->second->getUid() << " cookie过期:" << it_cookie->second->getCookie();
101 102 103 104 105 106
                it_cookie = it_name->second.erase(it_cookie);
                continue;
            }
            ++it_cookie;
        }

107
        if (it_name->second.empty()) {
108 109
            //该类型下没有任何cookie记录,移除之
            DebugL << "该path下没有任何cookie记录:" << it_name->first;
110 111 112 113 114 115
            it_name = _map_cookie.erase(it_name);
            continue;
        }
        ++it_name;
    }
}
116

117 118
HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name, const string &uid_in,
                                                   uint64_t max_elapsed, std::shared_ptr<void> attach, int max_client) {
119
    lock_guard<recursive_mutex> lck(_mtx_cookie);
120
    auto cookie = _generator.obtain();
121 122 123
    auto uid = uid_in.empty() ? cookie : uid_in;
    auto oldCookie = getOldestCookie(cookie_name, uid, max_client);
    if (!oldCookie.empty()) {
124 125
        //假如该账号已经登录了,那么删除老的cookie。
        //目的是实现单账号多地登录时挤占登录
126
        delCookie(cookie_name, oldCookie);
127
    }
128 129
    HttpServerCookie::Ptr data(new HttpServerCookie(shared_from_this(), cookie_name, uid, cookie, max_elapsed));
    data->setAttach(std::move(attach));
130
    //保存该账号下的新cookie
131
    _map_cookie[cookie_name][cookie] = data;
132 133 134
    return data;
}

135
HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name, const string &cookie) {
136
    lock_guard<recursive_mutex> lck(_mtx_cookie);
137
    auto it_name = _map_cookie.find(cookie_name);
138
    if (it_name == _map_cookie.end()) {
139
        //不存在该类型的cookie
140 141
        return nullptr;
    }
142
    auto it_cookie = it_name->second.find(cookie);
143
    if (it_cookie == it_name->second.end()) {
144
        //该类型下没有对应的cookie
145 146
        return nullptr;
    }
147 148
    if (it_cookie->second->isExpired()) {
        // cookie过期
149
        DebugL << "cookie过期:" << it_cookie->second->getCookie();
150
        it_name->second.erase(it_cookie);
151 152 153 154 155
        return nullptr;
    }
    return it_cookie->second;
}

156
HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name, const StrCaseMap &http_header) {
157 158 159 160
    auto it = http_header.find("Cookie");
    if (it == http_header.end()) {
        return nullptr;
    }
xia-chu committed
161
    auto cookie = findSubString(it->second.data(), (cookie_name + "=").data(), ";");
162
    if (cookie.empty()) {
xia-chu committed
163
        cookie = findSubString(it->second.data(), (cookie_name + "=").data(), nullptr);
164
    }
165
    if (cookie.empty()) {
166 167
        return nullptr;
    }
168
    return getCookie(cookie_name, cookie);
169 170
}

171 172
HttpServerCookie::Ptr HttpCookieManager::getCookieByUid(const string &cookie_name, const string &uid) {
    if (cookie_name.empty() || uid.empty()) {
173 174
        return nullptr;
    }
175 176
    auto cookie = getOldestCookie(cookie_name, uid);
    if (cookie.empty()) {
177 178
        return nullptr;
    }
179
    return getCookie(cookie_name, cookie);
180 181
}

182
bool HttpCookieManager::delCookie(const HttpServerCookie::Ptr &cookie) {
183
    if (!cookie) {
184 185
        return false;
    }
186
    return delCookie(cookie->getCookieName(), cookie->getCookie());
187 188
}

189
bool HttpCookieManager::delCookie(const string &cookie_name, const string &cookie) {
190
    lock_guard<recursive_mutex> lck(_mtx_cookie);
191
    auto it_name = _map_cookie.find(cookie_name);
192
    if (it_name == _map_cookie.end()) {
193
        return false;
194
    }
195
    return it_name->second.erase(cookie);
196 197
}

198
void HttpCookieManager::onAddCookie(const string &cookie_name, const string &uid, const string &cookie) {
199 200 201
    //添加新的cookie,我们记录下这个uid下有哪些cookie,目的是实现单账号多地登录时挤占登录
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    //相同用户下可以存在多个cookie(意味多地登录),这些cookie根据登录时间的早晚依次排序
202
    _map_uid_to_cookie[cookie_name][uid][getCurrentMillisecond()] = cookie;
203
}
204 205

void HttpCookieManager::onDelCookie(const string &cookie_name, const string &uid, const string &cookie) {
206 207
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    //回收随机字符串
208
    _generator.release(cookie);
209

210
    auto it_name = _map_uid_to_cookie.find(cookie_name);
211
    if (it_name == _map_uid_to_cookie.end()) {
212
        //该类型下未有任意用户登录
213 214
        return;
    }
215
    auto it_uid = it_name->second.find(uid);
216
    if (it_uid == it_name->second.end()) {
217 218 219 220 221
        //该用户尚未登录
        return;
    }

    //遍历同一名用户下的所有客户端,移除命中的客户端
222 223
    for (auto it_cookie = it_uid->second.begin(); it_cookie != it_uid->second.end(); ++it_cookie) {
        if (it_cookie->second != cookie) {
224 225 226 227 228 229
            //不是该cookie
            continue;
        }
        //移除该用户名下的某个cookie,这个设备cookie将失效
        it_uid->second.erase(it_cookie);

230
        if (!it_uid->second.empty()) {
231 232 233 234
            break;
        }

        //该用户名下没有任何设备在线,移除之
235
        it_name->second.erase(it_uid);
236

237
        if (!it_name->second.empty()) {
238 239
            break;
        }
240 241
        //该类型下未有任何用户在线,移除之
        _map_uid_to_cookie.erase(it_name);
242 243 244 245
        break;
    }
}

246
string HttpCookieManager::getOldestCookie(const string &cookie_name, const string &uid, int max_client) {
247
    lock_guard<recursive_mutex> lck(_mtx_cookie);
248
    auto it_name = _map_uid_to_cookie.find(cookie_name);
249
    if (it_name == _map_uid_to_cookie.end()) {
250
        //不存在该类型的cookie
251 252
        return "";
    }
253
    auto it_uid = it_name->second.find(uid);
254
    if (it_uid == it_name->second.end()) {
255 256 257
        //该用户从未登录过
        return "";
    }
258
    if ((int)it_uid->second.size() < MAX(1, max_client)) {
259 260 261 262 263 264 265
        //同一名用户下,客户端个数还没达到限制个数
        return "";
    }
    //客户端个数超过限制,移除最先登录的客户端
    return it_uid->second.begin()->second;
}

266 267
/////////////////////////////////RandStrGenerator////////////////////////////////////
string RandStrGenerator::obtain() {
268
    //获取唯一的防膨胀的随机字符串
269
    while (true) {
270
        auto str = obtain_l();
271
        if (_obtained.find(str) == _obtained.end()) {
272 273 274 275 276 277
            //没有重复
            _obtained.emplace(str);
            return str;
        }
    }
}
278

279
void RandStrGenerator::release(const string &str) {
280 281 282 283
    //从防膨胀库中移除
    _obtained.erase(str);
}

284
string RandStrGenerator::obtain_l() {
285 286
    // 12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串
    auto str = makeRandStr(12, false);
287 288 289
    str.append((char *)&_index, sizeof(_index));
    ++_index;
    return MD5(str).hexdigest();
290 291
}

292
} // namespace mediakit