HttpCookieManager.cpp 10.2 KB
Newer Older
xiongziliang committed
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 * MIT License
 *
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "Util/util.h"
#include "Util/MD5.h"
#include "Common/config.h"
30
#include "HttpCookieManager.h"
31

32 33 34 35 36 37 38 39
namespace mediakit {

//////////////////////////////HttpServerCookie////////////////////////////////////
HttpServerCookie::HttpServerCookie(const std::shared_ptr<HttpCookieManager> &manager,
                                   const string &cookie_name,
                                   const string &uid,
                                   const string &cookie,
                                   uint64_t max_elapsed){
40 41 42
    _uid = uid;
    _max_elapsed = max_elapsed;
    _cookie_uuid = cookie;
43
    _cookie_name = cookie_name;
44
    _manager = manager;
45
    manager->onAddCookie(_cookie_name,_uid,_cookie_uuid);
46 47
}

48
HttpServerCookie::~HttpServerCookie() {
49 50
    auto strongManager = _manager.lock();
    if(strongManager){
51
        strongManager->onDelCookie(_cookie_name,_uid,_cookie_uuid);
52 53
    }
}
54 55 56

const string & HttpServerCookie::getUid() const{
    return _uid;
57 58
}

59 60 61 62 63 64 65 66 67 68
string HttpServerCookie::getCookie(const string &path) const {
    return (StrPrinter << _cookie_name << "=" << _cookie_uuid << ";expires=" << cookieExpireTime() << ";path=" << path);
}

const string& HttpServerCookie::getCookie() const {
    return _cookie_uuid;
}

const string& HttpServerCookie::getCookieName() const{
    return _cookie_name;
69 70
}

71
void HttpServerCookie::updateTime() {
72 73
    _ticker.resetTime();
}
74 75 76

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

79 80 81 82
std::shared_ptr<lock_guard<mutex> > HttpServerCookie::getLock(){
    return std::make_shared<lock_guard<mutex> >(_mtx);
}

83
string HttpServerCookie::cookieExpireTime() const{
84 85 86 87 88
    char buf[64];
    time_t tt = time(NULL) + _max_elapsed;
    strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
    return buf;
}
89 90
//////////////////////////////CookieManager////////////////////////////////////
INSTANCE_IMP(HttpCookieManager);
91

92 93 94 95 96 97
HttpCookieManager::HttpCookieManager() {
    //定时删除过期的cookie,防止内存膨胀
    _timer = std::make_shared<Timer>(10,[this](){
        onManager();
        return true;
    }, nullptr);
98 99
}

100 101
HttpCookieManager::~HttpCookieManager() {
    _timer.reset();
102 103
}

104 105 106 107 108 109 110 111
void HttpCookieManager::onManager() {
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    //先遍历所有类型
    for(auto it_name = _map_cookie.begin() ; it_name != _map_cookie.end() ;){
        //再遍历该类型下的所有cookie
        for (auto it_cookie = it_name->second.begin() ; it_cookie != it_name->second.end() ; ){
            if(it_cookie->second->isExpired()){
                //cookie过期,移除记录
112
                DebugL << it_cookie->second->getUid() << " cookie过期:" << it_cookie->second->getCookie();
113 114 115 116 117 118 119 120
                it_cookie = it_name->second.erase(it_cookie);
                continue;
            }
            ++it_cookie;
        }

        if(it_name->second.empty()){
            //该类型下没有任何cooki记录,移除之
121
            DebugL << "该path下没有任何cooki记录:" << it_name->first;
122 123 124 125 126 127
            it_name = _map_cookie.erase(it_name);
            continue;
        }
        ++it_name;
    }
}
128

129
HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name,const string &uidIn,uint64_t max_elapsed,int max_client) {
130 131 132
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    auto cookie = _geneator.obtain();
    auto uid = uidIn.empty() ? cookie : uidIn;
133
    auto oldCookie = getOldestCookie(cookie_name , uid, max_client);
134 135 136
    if(!oldCookie.empty()){
        //假如该账号已经登录了,那么删除老的cookie。
        //目的是实现单账号多地登录时挤占登录
137
        delCookie(cookie_name,oldCookie);
138
    }
139
    HttpServerCookie::Ptr data(new HttpServerCookie(shared_from_this(),cookie_name,uid,cookie,max_elapsed));
140
    //保存该账号下的新cookie
141
    _map_cookie[cookie_name][cookie] = data;
142 143 144
    return data;
}

145
HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,const string &cookie) {
146
    lock_guard<recursive_mutex> lck(_mtx_cookie);
147 148 149
    auto it_name = _map_cookie.find(cookie_name);
    if(it_name == _map_cookie.end()){
        //不存在该类型的cookie
150 151
        return nullptr;
    }
152 153 154
    auto it_cookie = it_name->second.find(cookie);
    if(it_cookie == it_name->second.end()){
        //该类型下没有对应的cookie
155 156 157 158
        return nullptr;
    }
    if(it_cookie->second->isExpired()){
        //cookie过期
159
        DebugL << "cookie过期:" << it_cookie->second->getCookie();
160
        it_name->second.erase(it_cookie);
161 162 163 164 165
        return nullptr;
    }
    return it_cookie->second;
}

166
HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,const StrCaseMap &http_header) {
167 168 169 170 171 172 173 174 175 176 177
    auto it = http_header.find("Cookie");
    if (it == http_header.end()) {
        return nullptr;
    }
    auto cookie = FindField(it->second.data(), (cookie_name + "=").data(), ";");
    if (!cookie.size()) {
        cookie = FindField(it->second.data(), (cookie_name + "=").data(), nullptr);
    }
    if(cookie.empty()){
        return nullptr;
    }
178
    return HttpCookieManager::Instance().getCookie(cookie_name , cookie);
179 180
}

181 182 183 184 185 186 187 188 189 190 191
HttpServerCookie::Ptr HttpCookieManager::getCookieByUid(const string &cookie_name,const string &uid){
    if(cookie_name.empty() || uid.empty()){
        return nullptr;
    }
    auto cookie = getOldestCookie(cookie_name,uid);
    if(cookie.empty()){
        return nullptr;
    }
    return getCookie(cookie_name,cookie);
}

192 193 194 195 196
bool HttpCookieManager::delCookie(const HttpServerCookie::Ptr &cookie) {
    if(!cookie){
        return false;
    }
    return delCookie(cookie->getCookieName(),cookie->getCookie());
197 198
}

199
bool HttpCookieManager::delCookie(const string &cookie_name,const string &cookie) {
200
    lock_guard<recursive_mutex> lck(_mtx_cookie);
201 202 203
    auto it_name = _map_cookie.find(cookie_name);
    if(it_name == _map_cookie.end()){
        return false;
204
    }
205
    return it_name->second.erase(cookie);
206 207
}

208
void HttpCookieManager::onAddCookie(const string &cookie_name,const string &uid,const string &cookie){
209 210 211
    //添加新的cookie,我们记录下这个uid下有哪些cookie,目的是实现单账号多地登录时挤占登录
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    //相同用户下可以存在多个cookie(意味多地登录),这些cookie根据登录时间的早晚依次排序
212
    _map_uid_to_cookie[cookie_name][uid][getCurrentMillisecond()] = cookie;
213
}
214
void HttpCookieManager::onDelCookie(const string &cookie_name,const string &uid,const string &cookie){
215 216 217 218
    lock_guard<recursive_mutex> lck(_mtx_cookie);
    //回收随机字符串
    _geneator.release(cookie);

219 220 221
    auto it_name = _map_uid_to_cookie.find(cookie_name);
    if(it_name == _map_uid_to_cookie.end()){
        //该类型下未有任意用户登录
222 223
        return;
    }
224 225
    auto it_uid = it_name->second.find(uid);
    if(it_uid == it_name->second.end()){
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        //该用户尚未登录
        return;
    }

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

        if(it_uid->second.size() != 0) {
            break;
        }

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

246
        if(it_name->second.size() != 0) {
247 248
            break;
        }
249 250
        //该类型下未有任何用户在线,移除之
        _map_uid_to_cookie.erase(it_name);
251 252 253 254 255
        break;
    }

}

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

276 277
/////////////////////////////////RandStrGeneator////////////////////////////////////
string RandStrGeneator::obtain(){
278 279 280 281 282 283 284 285 286 287
    //获取唯一的防膨胀的随机字符串
    while (true){
        auto str = obtain_l();
        if(_obtained.find(str) == _obtained.end()){
            //没有重复
            _obtained.emplace(str);
            return str;
        }
    }
}
288
void RandStrGeneator::release(const string &str){
289 290 291 292
    //从防膨胀库中移除
    _obtained.erase(str);
}

293
string RandStrGeneator::obtain_l(){
294 295 296 297 298
    //12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串
    auto str = makeRandStr(12,false);
    str.append((char *)&_index, sizeof(_index));
    ++_index;
    return MD5(str).hexdigest();
299 300 301
}

}//namespace mediakit