1 /*
2 * statsgw.cpp
3 * HTTP server for showing some DL stats via SwarmPlayer 3000's webUI,
4 * libevent based
5 *
6 * Created by Victor Grishchenko, Arno Bakker
7 * Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
8 *
9 */
10
11 #include "swift.h"
12 #include <event2/http.h>
13
14 using namespace swift;
15
16 int statsgw_reqs_count = 0;
17
18
19 uint64_t statsgw_last_down;
20 uint64_t statsgw_last_up;
21 tint statsgw_last_time = 0;
22 bool statsgw_quit_process=false;
23 struct evhttp *statsgw_event;
24 struct evhttp_bound_socket *statsgw_handle;
25
26
27 const char *top_page = "<!doctype html> \
28 <html style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;\"> \
29 <head> \
30 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> \
31 <meta http-equiv=\"cache-control\" content=\"Private\" /> \
32 <meta http-equiv=\"Refresh\" content=\"2;url=http://127.0.0.1:6876/webUI\" /> \
33 <title>Swift Web Interface</title> \
34 </head> \
35 <body style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;padding:20px; margin:20px;\"> \
36 <div style=\"padding:40;\"> \
37 <div style=\"width:400; float:left; padding:20px\"> \
38 <h1 style=\"font-size: 20px;\"> Swift swarms: </h1>";
39
40 const char *swarm_page_templ = " \
41 <h2 style=\"font-size: 18px; padding-top: 10px; padding-left: 20px; margin-bottom: 0px; color:#30bf00;\">Root hash: %s</h2> \
42 <ul style=\"padding-left: 40px;\"> \
43 <li>Progress: %d%c \
44 <li>Download speed: %d KB/s \
45 <li>Upload speed: %d KB/s \
46 </ul>";
47
48
49 const char *bottom_page = " \
50 <button style=\"color:white; background-color:#4f84dc; width:80;height:50; font-size:18px; font-weight:bold; text-shadow: #6374AB 2px 2px 2px;\" \
51 onClick=\"window.location='http://127.0.0.1:6876/webUI/exit';\">Quit Swift</button> \
52 </div> \
53 </div> \
54 </body> \
55 </html>";
56
57
58 const char *exit_page = "<!doctype html> \
59 <html style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;\"> \
60 <head> \
61 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> \
62 <meta http-equiv=\"cache-control\" content=\"Private\" /> \
63 <title>Swift Web Interface</title> \
64 </head> \
65 <body style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;padding:20px; margin:20px;\"> \
66 <div style=\"padding:40;\"> \
67 <div style=\"width:400; float:left; padding:20px\"> \
68 <h1 style=\"font-size: 20px;\"> Swift is no longer running. </h1> \
69 </div> \
70 </div> \
71 </body> \
72 </html>";
73
74
75 static void StatsGwNewRequestCallback(struct evhttp_request *evreq, void *arg);
76
77
StatsExitCallback(struct evhttp_request * evreq)78 void StatsExitCallback(struct evhttp_request *evreq)
79 {
80 char contlenstr[1024];
81 sprintf(contlenstr,PRISIZET,strlen(exit_page));
82 struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
83 evhttp_add_header(headers, "Connection", "close");
84 evhttp_add_header(headers, "Content-Type", "text/html");
85 evhttp_add_header(headers, "Content-Length", contlenstr);
86 evhttp_add_header(headers, "Accept-Ranges", "none");
87
88 // Construct evbuffer and send via chunked encoding
89 struct evbuffer *evb = evbuffer_new();
90 int ret = evbuffer_add(evb,exit_page,strlen(exit_page));
91 if (ret < 0) {
92 print_error("statsgw: ExitCallback: error evbuffer_add");
93 return;
94 }
95
96 evhttp_send_reply(evreq, 200, "OK", evb);
97 evbuffer_free(evb);
98 }
99
100
StatsQuit()101 bool StatsQuit()
102 {
103 return statsgw_quit_process;
104 }
105
106
StatsOverviewCallback(struct evhttp_request * evreq)107 void StatsOverviewCallback(struct evhttp_request *evreq)
108 {
109 tint nu = NOW;
110 uint64_t down = Channel::global_raw_bytes_down;
111 uint64_t up = Channel::global_raw_bytes_up;
112
113 int dspeed = 0, uspeed = 0;
114 tint tdiff = (nu - statsgw_last_time)/1000000;
115 if (tdiff > 0) {
116 dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
117 uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
118 }
119 //statsgw_last_down = down;
120 //statsgw_last_up = up;
121
122
123 char bodystr[102400];
124 strcpy(bodystr,"");
125 strcat(bodystr,top_page);
126
127 tdlist_t tds = swift::GetTransferDescriptors();
128 tdlist_t::iterator iter;
129 for (iter = tds.begin(); iter != tds.end(); iter++) {
130 int td = *iter;
131 uint64_t total = (int)swift::Size(td);
132 uint64_t down = (int)swift::Complete(td);
133 int perc = (int)((down * 100) / total);
134
135 char roothashhexstr[1024];
136 sprintf(roothashhexstr,"%s", GetSwarmID(td).hex().c_str());
137
138 char templ[2048];
139 sprintf(templ,swarm_page_templ,roothashhexstr, perc, '%', dspeed, uspeed);
140 strcat(bodystr,templ);
141 }
142
143 strcat(bodystr,bottom_page);
144
145 char contlenstr[1024];
146 sprintf(contlenstr,PRISIZET,strlen(bodystr));
147 struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
148 evhttp_add_header(headers, "Connection", "close");
149 evhttp_add_header(headers, "Content-Type", "text/html");
150 evhttp_add_header(headers, "Content-Length", contlenstr);
151 evhttp_add_header(headers, "Accept-Ranges", "none");
152
153 // Construct evbuffer and send via chunked encoding
154 struct evbuffer *evb = evbuffer_new();
155 int ret = evbuffer_add(evb,bodystr,strlen(bodystr));
156 if (ret < 0) {
157 print_error("statsgw: OverviewCallback: error evbuffer_add");
158 return;
159 }
160
161 evhttp_send_reply(evreq, 200, "OK", evb);
162 evbuffer_free(evb);
163 }
164
165
StatsGetSpeedCallback(struct evhttp_request * evreq)166 void StatsGetSpeedCallback(struct evhttp_request *evreq)
167 {
168 if (statsgw_last_time == 0) {
169 statsgw_last_time = NOW-1000000;
170 }
171
172 tint nu = Channel::Time();
173 uint64_t down = Channel::global_raw_bytes_down;
174 uint64_t up = Channel::global_raw_bytes_up;
175
176 int dspeed = 0, uspeed = 0;
177 tint tdiff = (nu - statsgw_last_time)/1000000;
178 if (tdiff > 0) {
179 dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
180 uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
181 }
182 statsgw_last_down = down;
183 statsgw_last_up = up;
184 statsgw_last_time = nu;
185
186 // Arno: PDD+ wants content speeds too
187 double contentdownspeed = 0.0, contentupspeed = 0.0;
188 uint32_t nleech=0,nseed=0;
189
190 tdlist_t tds = swift::GetTransferDescriptors();
191 tdlist_t::iterator iter;
192 for (iter = tds.begin(); iter != tds.end(); iter++) {
193 int td = *iter;
194 contentdownspeed += swift::GetCurrentSpeed(td,DDIR_DOWNLOAD);
195 contentupspeed += swift::GetCurrentSpeed(td,DDIR_UPLOAD);
196 nleech += swift::GetNumLeechers(td);
197 nseed += swift::GetNumSeeders(td);
198 // TODO: Are these active leechers and seeders, or potential seeders and leechers? In the latter case these can be retrieved when cached peers are implemented
199 }
200 int cdownspeed = (int)(contentdownspeed/1024.0);
201 int cupspeed = (int)(contentupspeed/1024.0);
202
203 char speedstr[1024];
204 sprintf(speedstr,
205 "{\"downspeed\": %d, \"success\": \"true\", \"upspeed\": %d, \"cdownspeed\": %d, \"cupspeed\": %d, \"nleech\": %d, \"nseed\": %d}",
206 dspeed, uspeed, cdownspeed, cupspeed, nleech, nseed);
207
208 char contlenstr[1024];
209 sprintf(contlenstr,PRISIZET,strlen(speedstr));
210 struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
211 evhttp_add_header(headers, "Connection", "close");
212 evhttp_add_header(headers, "Content-Type", "application/json");
213 evhttp_add_header(headers, "Content-Length", contlenstr);
214 evhttp_add_header(headers, "Accept-Ranges", "none");
215
216 // Construct evbuffer and send via chunked encoding
217 struct evbuffer *evb = evbuffer_new();
218 int ret = evbuffer_add(evb,speedstr,strlen(speedstr));
219 if (ret < 0) {
220 print_error("statsgw: GetSpeedCallback: error evbuffer_add");
221 return;
222 }
223
224 evhttp_send_reply(evreq, 200, "OK", evb);
225 evbuffer_free(evb);
226 }
227
228
StatsGwNewRequestCallback(struct evhttp_request * evreq,void * arg)229 void StatsGwNewRequestCallback(struct evhttp_request *evreq, void *arg)
230 {
231
232 dprintf("%s @%i http new request\n",tintstr(),statsgw_reqs_count);
233 statsgw_reqs_count++;
234
235 if (evhttp_request_get_command(evreq) != EVHTTP_REQ_GET) {
236 return;
237 }
238
239 // Parse URI
240 const char *uri = evhttp_request_get_uri(evreq);
241 //struct evkeyvalq *headers = evhttp_request_get_input_headers(evreq);
242 //const char *contentrangestr =evhttp_find_header(headers,"Content-Range");
243
244 fprintf(stderr,"statsgw: GOT %s\n", uri);
245
246 if (strstr(uri,"get_speed_info") != NULL) {
247 StatsGetSpeedCallback(evreq);
248 } else if (!strncmp(uri,"/webUI/exit",strlen("/webUI/exit")) || statsgw_quit_process) {
249 statsgw_quit_process = true;
250 StatsExitCallback(evreq);
251 } else if (!strncmp(uri,"/webUI",strlen("/webUI"))) {
252 StatsOverviewCallback(evreq);
253 }
254 }
255
256
InstallStatsGateway(struct event_base * evbase,Address bindaddr)257 bool InstallStatsGateway(struct event_base *evbase,Address bindaddr)
258 {
259 // Arno, 2011-10-04: From libevent's http-server.c example
260
261 /* Create a new evhttp object to handle requests. */
262 statsgw_event = evhttp_new(evbase);
263 if (!statsgw_event) {
264 print_error("statsgw: evhttp_new failed");
265 return false;
266 }
267
268 /* Install callback for all requests */
269 evhttp_set_gencb(statsgw_event, StatsGwNewRequestCallback, NULL);
270
271 /* Now we tell the evhttp what port to listen on */
272 statsgw_handle = evhttp_bind_socket_with_handle(statsgw_event, bindaddr.ipstr().c_str(), bindaddr.port());
273 if (!statsgw_handle) {
274 print_error("statsgw: evhttp_bind_socket_with_handle failed");
275 return false;
276 }
277
278 return true;
279 }
280