1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 77    Delay Pools */
10 
11 #include "squid.h"
12 
13 #if USE_DELAY_POOLS && USE_AUTH
14 #include "auth/User.h"
15 #include "auth/UserRequest.h"
16 #include "comm/Connection.h"
17 #include "DelayUser.h"
18 #include "NullDelayId.h"
19 #include "Store.h"
20 
DelayUser()21 DelayUser::DelayUser()
22 {
23     DelayPools::registerForUpdates (this);
24 }
25 
26 static Splay<DelayUserBucket::Pointer>::SPLAYFREE DelayUserFree;
27 
~DelayUser()28 DelayUser::~DelayUser()
29 {
30     DelayPools::deregisterForUpdates (this);
31     buckets.destroy(DelayUserFree);
32 }
33 
34 static Splay<DelayUserBucket::Pointer>::SPLAYCMP DelayUserCmp;
35 
36 int
DelayUserCmp(DelayUserBucket::Pointer const & left,DelayUserBucket::Pointer const & right)37 DelayUserCmp(DelayUserBucket::Pointer const &left, DelayUserBucket::Pointer const &right)
38 {
39     /* Verify for re-currance of Bug 2127. either of these missing will crash strcasecmp() */
40     assert(left->authUser->username() != NULL);
41     assert(right->authUser->username() != NULL);
42 
43     /* for rate limiting, case insensitive */
44     return strcasecmp(left->authUser->username(), right->authUser->username());
45 }
46 
47 void
DelayUserFree(DelayUserBucket::Pointer &)48 DelayUserFree(DelayUserBucket::Pointer &)
49 {}
50 
51 void
DelayUserStatsWalkee(DelayUserBucket::Pointer const & current,void * state)52 DelayUserStatsWalkee(DelayUserBucket::Pointer const &current, void *state)
53 {
54     current->stats ((StoreEntry *)state);
55 }
56 
57 struct DelayUserStatsVisitor {
58     StoreEntry *se;
DelayUserStatsVisitorDelayUserStatsVisitor59     explicit DelayUserStatsVisitor(StoreEntry *s) : se(s) {}
operator ()DelayUserStatsVisitor60     void operator() (DelayUserBucket::Pointer const &current) {
61         current->stats(se);
62     }
63 };
64 
65 void
stats(StoreEntry * sentry)66 DelayUser::stats(StoreEntry * sentry)
67 {
68     spec.stats (sentry, "Per User");
69 
70     if (spec.restore_bps == -1)
71         return;
72 
73     storeAppendPrintf(sentry, "\t\tCurrent: ");
74 
75     if (buckets.empty()) {
76         storeAppendPrintf (sentry, "Not used yet.\n\n");
77         return;
78     }
79 
80     DelayUserStatsVisitor visitor(sentry);
81     buckets.visit(visitor);
82     storeAppendPrintf(sentry, "\n\n");
83 }
84 
85 void
dump(StoreEntry * entry) const86 DelayUser::dump(StoreEntry *entry) const
87 {
88     spec.dump(entry);
89 }
90 
91 struct DelayUserUpdater {
DelayUserUpdaterDelayUserUpdater92     DelayUserUpdater (DelaySpec &_spec, int _incr):spec(_spec),incr(_incr) {};
93 
94     DelaySpec spec;
95     int incr;
96 };
97 
98 void
DelayUserUpdateWalkee(DelayUserBucket::Pointer const & current,void * state)99 DelayUserUpdateWalkee(DelayUserBucket::Pointer const &current, void *state)
100 {
101     DelayUserUpdater *t = (DelayUserUpdater *)state;
102     /* This doesn't change the value of the DelayUserBucket, so is safe */
103     const_cast<DelayUserBucket *>(current.getRaw())->theBucket.update(t->spec, t->incr);
104 }
105 
106 struct DelayUserUpdateVisitor {
107     DelayUserUpdater *t;
DelayUserUpdateVisitorDelayUserUpdateVisitor108     DelayUserUpdateVisitor(DelayUserUpdater *updater) : t(updater) {}
operator ()DelayUserUpdateVisitor109     void operator() (DelayUserBucket::Pointer const &current) {
110         const_cast<DelayUserBucket *>(current.getRaw())->theBucket.update(t->spec, t->incr);
111     }
112 };
113 
114 void
update(int incr)115 DelayUser::update(int incr)
116 {
117     DelayUserUpdater updater(spec, incr);
118     DelayUserUpdateVisitor visitor(&updater);
119     buckets.visit(visitor);
120 }
121 
122 void
parse()123 DelayUser::parse()
124 {
125     spec.parse();
126 }
127 
128 DelayIdComposite::Pointer
id(CompositePoolNode::CompositeSelectionDetails & details)129 DelayUser::id(CompositePoolNode::CompositeSelectionDetails &details)
130 {
131     if (!details.user || !details.user->user() || !details.user->user()->username())
132         return new NullDelayId;
133 
134     debugs(77, 3, HERE << "Adding a slow-down for User '" << details.user->user()->username() << "'");
135     return new Id(this, details.user->user());
136 }
137 
DelayUserBucket(Auth::User::Pointer aUser)138 DelayUserBucket::DelayUserBucket(Auth::User::Pointer aUser) : authUser(aUser)
139 {
140     debugs(77, 3, "DelayUserBucket::DelayUserBucket");
141 }
142 
~DelayUserBucket()143 DelayUserBucket::~DelayUserBucket()
144 {
145     authUser = NULL;
146     debugs(77, 3, "DelayUserBucket::~DelayUserBucket");
147 }
148 
149 void
stats(StoreEntry * entry) const150 DelayUserBucket::stats (StoreEntry *entry) const
151 {
152     storeAppendPrintf(entry, " %s:", authUser->username());
153     theBucket.stats(entry);
154 }
155 
Id(DelayUser::Pointer aDelayUser,Auth::User::Pointer aUser)156 DelayUser::Id::Id(DelayUser::Pointer aDelayUser, Auth::User::Pointer aUser) : theUser(aDelayUser)
157 {
158     theBucket = new DelayUserBucket(aUser);
159     DelayUserBucket::Pointer const *existing = theUser->buckets.find(theBucket, DelayUserCmp);
160 
161     if (existing) {
162         theBucket = *existing;
163         return;
164     }
165 
166     theBucket->theBucket.init(theUser->spec);
167     theUser->buckets.insert (theBucket, DelayUserCmp);
168 }
169 
~Id()170 DelayUser::Id::~Id()
171 {
172     debugs(77, 3, "DelayUser::Id::~Id");
173 }
174 
175 int
bytesWanted(int min,int max) const176 DelayUser::Id::bytesWanted (int min, int max) const
177 {
178     return theBucket->theBucket.bytesWanted(min,max);
179 }
180 
181 void
bytesIn(int qty)182 DelayUser::Id::bytesIn(int qty)
183 {
184     theBucket->theBucket.bytesIn(qty);
185 }
186 
187 #endif /* USE_DELAY_POOLS && USE_AUTH */
188 
189