comparison libgaim/protocols/oscar/flap_connection.c @ 15106:b2b0839f57d0

[gaim-migrate @ 17891] Outgoing message throttling. If we're sending messages too quickly, and Gaim thinks that sending another message will make AIM give us a warning, then delay sending the message a little bit. Currently only activated for IMs. Let me know if you see any problems. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Mon, 04 Dec 2006 07:47:50 +0000
parents 525607f86cce
children f41cd6f78c60
comparison
equal deleted inserted replaced
15105:cb7eef7bf550 15106:b2b0839f57d0
70 aim_tlvlist_free(&tl); 70 aim_tlvlist_free(&tl);
71 71
72 flap_connection_send(conn, frame); 72 flap_connection_send(conn, frame);
73 } 73 }
74 74
75 static void 75 static struct rateclass *
76 update_rate_class(FlapConnection *conn, guint16 family, guint16 subtype) 76 flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype)
77 { 77 {
78 GSList *tmp1, *tmp2; 78 GSList *tmp1, *tmp2;
79 79
80 for (tmp1 = conn->rateclasses; tmp1 != NULL; tmp1 = tmp1->next) 80 for (tmp1 = conn->rateclasses; tmp1 != NULL; tmp1 = tmp1->next)
81 { 81 {
86 { 86 {
87 struct snacpair *snacpair; 87 struct snacpair *snacpair;
88 snacpair = tmp2->data; 88 snacpair = tmp2->data;
89 if ((snacpair->group == family) && (snacpair->subtype == subtype)) 89 if ((snacpair->group == family) && (snacpair->subtype == subtype))
90 { 90 {
91 /* 91 return rateclass;
92 * We've found the rateclass for this SNAC family
93 * and subtype! Update our "current" average by
94 * calculating a rolling average. This is pretty
95 * shoddy. We should really keep track of the times
96 * when the last windowsize messages that were sent
97 * and just calculate the REAL average.
98 */
99 struct timeval now;
100 struct timezone tz;
101 unsigned long timediff; /* In milliseconds */
102
103 gettimeofday(&now, &tz);
104 timediff = MIN((now.tv_sec - rateclass->last.tv_sec) * 1000 + (now.tv_usec - rateclass->last.tv_usec) / 1000, rateclass->max);
105
106 /* This formula is taken from the joscar API docs. */
107 rateclass->current = MIN(((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize, rateclass->max);
108 rateclass->last.tv_sec = now.tv_sec;
109 rateclass->last.tv_usec = now.tv_usec;
110
111 return;
112 } 92 }
113 } 93 }
114 } 94 }
115 } 95
116 96 return NULL;
97 }
98
99 /*
100 * Attempt to calculate what our new current average would be if we
101 * were to send a SNAC in this rateclass at the given time.
102 */
103 static guint32
104 rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval now)
105 {
106 unsigned long timediff; /* In milliseconds */
107
108 timediff = (now.tv_sec - rateclass->last.tv_sec) * 1000 + (now.tv_usec - rateclass->last.tv_usec) / 1000;
109
110 /* This formula is taken from the joscar API docs. Preesh. */
111 return MIN(((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize, rateclass->max);
112 }
113
114 static gboolean flap_connection_send_queued(gpointer data)
115 {
116 FlapConnection *conn;
117 struct timeval now;
118
119 conn = data;
120 gettimeofday(&now, NULL);
121
122 while (conn->queued_snacs != NULL)
123 {
124 QueuedSnac *queued_snac;
125 struct rateclass *rateclass;
126
127 queued_snac = conn->queued_snacs->data;
128
129 rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype);
130 if (rateclass != NULL)
131 {
132 guint32 new_current;
133
134 new_current = rateclass_get_new_current(conn, rateclass, now);
135
136 if (new_current < rateclass->alert)
137 /* Not ready to send this SNAC yet--keep waiting. */
138 return TRUE;
139
140 rateclass->current = new_current;
141 rateclass->last.tv_sec = now.tv_sec;
142 rateclass->last.tv_usec = now.tv_usec;
143 }
144
145 flap_connection_send(conn, queued_snac->frame);
146 g_free(queued_snac);
147 conn->queued_snacs = g_slist_delete_link(conn->queued_snacs, conn->queued_snacs);
148 }
149
150 conn->outgoing_timeout = 0;
151 return FALSE;
152 }
117 153
118 /** 154 /**
119 * This sends a channel 2 FLAP containing a SNAC. The SNAC family and 155 * This sends a channel 2 FLAP containing a SNAC. The SNAC family and
120 * subtype are looked up in the rate info for this connection, and if 156 * subtype are looked up in the rate info for this connection, and if
121 * sending this SNAC will induce rate limiting then we delay sending 157 * sending this SNAC will induce rate limiting then we delay sending
122 * of the SNAC by putting it into an outgoing holding queue. 158 * of the SNAC by putting it into an outgoing holding queue.
159 *
160 * @param data The optional bytestream that makes up the data portion
161 * of this SNAC. For empty SNACs this should be NULL.
123 */ 162 */
124 void 163 void
125 flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data) 164 flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data)
126 { 165 {
127 FlapFrame *frame; 166 FlapFrame *frame;
128 guint32 length; 167 guint32 length;
168 gboolean enqueue = FALSE;
169 struct rateclass *rateclass;
129 170
130 length = data != NULL ? data->offset : 0; 171 length = data != NULL ? data->offset : 0;
131 172
132 frame = flap_frame_new(od, 0x02, 10 + length); 173 frame = flap_frame_new(od, 0x02, 10 + length);
133 aim_putsnac(&frame->data, family, subtype, flags, snacid); 174 aim_putsnac(&frame->data, family, subtype, flags, snacid);
136 { 177 {
137 byte_stream_rewind(data); 178 byte_stream_rewind(data);
138 byte_stream_putbs(&frame->data, data, length); 179 byte_stream_putbs(&frame->data, data, length);
139 } 180 }
140 181
141 /* TODO: Outgoing message throttling */ 182 if (conn->outgoing_timeout != 0)
142 update_rate_class(conn, family, subtype); 183 enqueue = TRUE;
184 else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL)
185 {
186 struct timeval now;
187 guint32 new_current;
188
189 gettimeofday(&now, NULL);
190 new_current = rateclass_get_new_current(conn, rateclass, now);
191
192 if (new_current < rateclass->alert)
193 {
194 enqueue = TRUE;
195 }
196 else
197 {
198 rateclass->current = new_current;
199 rateclass->last.tv_sec = now.tv_sec;
200 rateclass->last.tv_usec = now.tv_usec;
201 }
202 }
203
204 if (enqueue)
205 {
206 /* We've been sending too fast, so delay this message */
207 QueuedSnac *queued_snac;
208
209 queued_snac = g_new(QueuedSnac, 1);
210 queued_snac->family = family;
211 queued_snac->subtype = subtype;
212 queued_snac->frame = frame;
213 conn->queued_snacs = g_slist_append(conn->queued_snacs, queued_snac);
214
215 if (conn->outgoing_timeout == 0)
216 conn->outgoing_timeout = gaim_timeout_add(500, flap_connection_send_queued, conn);
217
218 return;
219 }
143 220
144 flap_connection_send(conn, frame); 221 flap_connection_send(conn, frame);
145 } 222 }
146 223
147 /** 224 /**
263 g_free(rateclass->members->data); 340 g_free(rateclass->members->data);
264 rateclass->members = g_slist_delete_link(rateclass->members, rateclass->members); 341 rateclass->members = g_slist_delete_link(rateclass->members, rateclass->members);
265 } 342 }
266 343
267 free(rateclass); 344 free(rateclass);
345 }
346
347 /**
348 * Free a FlapFrame
349 *
350 * @param frame The frame to free.
351 */
352 static void
353 flap_frame_destroy(FlapFrame *frame)
354 {
355 free(frame->data.data);
356 free(frame);
268 } 357 }
269 358
270 static gboolean 359 static gboolean
271 flap_connection_destroy_cb(gpointer data) 360 flap_connection_destroy_cb(gpointer data)
272 { 361 {
332 { 421 {
333 flap_connection_destroy_rateclass(conn->rateclasses->data); 422 flap_connection_destroy_rateclass(conn->rateclasses->data);
334 conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses); 423 conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses);
335 } 424 }
336 425
426 while (conn->queued_snacs != NULL)
427 {
428 QueuedSnac *queued_snac;
429 queued_snac = conn->queued_snacs->data;
430 flap_frame_destroy(queued_snac->frame);
431 g_free(queued_snac);
432 conn->queued_snacs = g_slist_delete_link(conn->queued_snacs, conn->queued_snacs);
433 }
434 if (conn->outgoing_timeout > 0)
435 gaim_timeout_remove(conn->outgoing_timeout);
436
337 g_free(conn); 437 g_free(conn);
338 438
339 return FALSE; 439 return FALSE;
340 } 440 }
341 441
525 625
526 if (datalen > 0) 626 if (datalen > 0)
527 byte_stream_new(&frame->data, datalen); 627 byte_stream_new(&frame->data, datalen);
528 628
529 return frame; 629 return frame;
530 }
531
532 /**
533 * Free a FlapFrame
534 *
535 * @param frame The frame to free.
536 * @return -1 on error; 0 on success.
537 */
538 static void
539 flap_frame_destroy(FlapFrame *frame)
540 {
541 free(frame->data.data);
542 free(frame);
543
544 return;
545 } 630 }
546 631
547 static void 632 static void
548 parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame) 633 parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
549 { 634 {