3 * HTTP server for showing some DL stats via SwarmPlayer 3000's webUI,
6 * Created by Victor Grishchenko, Arno Bakker
7 * Copyright 2010-2012 TECHNISCHE UNIVERSITEIT DELFT. All rights reserved.
12 #include <event2/http.h>
14 using namespace swift;
16 int statsgw_reqs_count = 0;
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;
27 const char *top_page = "<!doctype html> \
28 <html style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;\"> \
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> \
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>";
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;\"> \
44 <li>Download speed: %d KB/s \
45 <li>Upload speed: %d KB/s \
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> \
58 const char *exit_page = "<!doctype html> \
59 <html style=\"font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;background-color:white;\"> \
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> \
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> \
75 static void StatsGwNewRequestCallback (struct evhttp_request *evreq, void *arg);
78 void StatsExitCallback(struct evhttp_request *evreq)
80 char contlenstr[1024];
81 sprintf(contlenstr,"%i",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" );
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));
92 print_error("statsgw: ExitCallback: error evbuffer_add");
96 evhttp_send_reply(evreq, 200, "OK", evb);
103 return statsgw_quit_process;
107 void StatsOverviewCallback(struct evhttp_request *evreq)
110 uint64_t down = Channel::global_raw_bytes_down;
111 uint64_t up = Channel::global_raw_bytes_up;
113 int dspeed = 0, uspeed = 0;
114 tint tdiff = (nu - statsgw_last_time)/1000000;
116 dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
117 uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
119 //statsgw_last_down = down;
120 //statsgw_last_up = up;
123 char bodystr[102400];
125 strcat(bodystr,top_page);
127 for (int i=0; i<swift::FileTransfer::files.size(); i++)
129 FileTransfer *ft = swift::FileTransfer::files[i];
133 uint64_t total = (int)swift::Size(fd);
134 uint64_t down = (int)swift::Complete(fd);
135 int perc = (int)((down * 100) / total);
137 char roothashhexstr[256];
138 sprintf(roothashhexstr,"%s", RootMerkleHash(fd).hex().c_str() );
141 sprintf(templ,swarm_page_templ,roothashhexstr, perc, '%', dspeed, uspeed );
142 strcat(bodystr,templ);
146 strcat(bodystr,bottom_page);
148 char contlenstr[1024];
149 sprintf(contlenstr,"%i",strlen(bodystr));
150 struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
151 evhttp_add_header(headers, "Connection", "close" );
152 evhttp_add_header(headers, "Content-Type", "text/html" );
153 evhttp_add_header(headers, "Content-Length", contlenstr );
154 evhttp_add_header(headers, "Accept-Ranges", "none" );
156 // Construct evbuffer and send via chunked encoding
157 struct evbuffer *evb = evbuffer_new();
158 int ret = evbuffer_add(evb,bodystr,strlen(bodystr));
160 print_error("statsgw: OverviewCallback: error evbuffer_add");
164 evhttp_send_reply(evreq, 200, "OK", evb);
169 void StatsGetSpeedCallback(struct evhttp_request *evreq)
171 if (statsgw_last_time == 0)
173 statsgw_last_time = NOW-1000000;
176 tint nu = Channel::Time();
177 uint64_t down = Channel::global_raw_bytes_down;
178 uint64_t up = Channel::global_raw_bytes_up;
180 int dspeed = 0, uspeed = 0;
181 tint tdiff = (nu - statsgw_last_time)/1000000;
183 dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
184 uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
186 statsgw_last_down = down;
187 statsgw_last_up = up;
188 statsgw_last_time = nu;
190 // Arno: PDD+ wants content speeds too
191 double contentdownspeed = 0.0, contentupspeed = 0.0;
192 uint32_t nleech=0,nseed=0;
193 for (int i=0; i<swift::FileTransfer::files.size(); i++)
195 FileTransfer *ft = swift::FileTransfer::files[i];
198 contentdownspeed += ft->GetCurrentSpeed(DDIR_DOWNLOAD);
199 contentupspeed += ft->GetCurrentSpeed(DDIR_UPLOAD);
200 nleech += ft->GetNumLeechers();
201 nseed += ft->GetNumSeeders();
204 int cdownspeed = (int)(contentdownspeed/1024.0);
205 int cupspeed = (int)(contentupspeed/1024.0);
208 sprintf(speedstr,"{\"downspeed\": %d, \"success\": \"true\", \"upspeed\": %d, \"cdownspeed\": %d, \"cupspeed\": %d, \"nleech\": %d, \"nseed\": %d}", dspeed, uspeed, cdownspeed, cupspeed, nleech, nseed );
210 char contlenstr[1024];
211 sprintf(contlenstr,"%i",strlen(speedstr));
212 struct evkeyvalq *headers = evhttp_request_get_output_headers(evreq);
213 evhttp_add_header(headers, "Connection", "close" );
214 evhttp_add_header(headers, "Content-Type", "application/json" );
215 evhttp_add_header(headers, "Content-Length", contlenstr );
216 evhttp_add_header(headers, "Accept-Ranges", "none" );
218 // Construct evbuffer and send via chunked encoding
219 struct evbuffer *evb = evbuffer_new();
220 int ret = evbuffer_add(evb,speedstr,strlen(speedstr));
222 print_error("statsgw: GetSpeedCallback: error evbuffer_add");
226 evhttp_send_reply(evreq, 200, "OK", evb);
231 void StatsGwNewRequestCallback (struct evhttp_request *evreq, void *arg) {
233 dprintf("%s @%i http new request\n",tintstr(),statsgw_reqs_count);
234 statsgw_reqs_count++;
236 if (evhttp_request_get_command(evreq) != EVHTTP_REQ_GET) {
241 const char *uri = evhttp_request_get_uri(evreq);
242 //struct evkeyvalq *headers = evhttp_request_get_input_headers(evreq);
243 //const char *contentrangestr =evhttp_find_header(headers,"Content-Range");
245 fprintf(stderr,"statsgw: GOT %s\n", uri);
247 if (strstr(uri,"get_speed_info") != NULL)
249 StatsGetSpeedCallback(evreq);
251 else if (!strncmp(uri,"/webUI/exit",strlen("/webUI/exit")) || statsgw_quit_process)
253 statsgw_quit_process = true;
254 StatsExitCallback(evreq);
256 else if (!strncmp(uri,"/webUI",strlen("/webUI")))
258 StatsOverviewCallback(evreq);
263 bool InstallStatsGateway (struct event_base *evbase,Address bindaddr) {
264 // Arno, 2011-10-04: From libevent's http-server.c example
266 /* Create a new evhttp object to handle requests. */
267 statsgw_event = evhttp_new(evbase);
268 if (!statsgw_event) {
269 print_error("statsgw: evhttp_new failed");
273 /* Install callback for all requests */
274 evhttp_set_gencb(statsgw_event, StatsGwNewRequestCallback, NULL);
276 /* Now we tell the evhttp what port to listen on */
277 statsgw_handle = evhttp_bind_socket_with_handle(statsgw_event, bindaddr.ipv4str(), bindaddr.port());
278 if (!statsgw_handle) {
279 print_error("statsgw: evhttp_bind_socket_with_handle failed");