First step for using multiple recvs from mptp.
[swifty.git] / src / libswift / statsgw.cpp
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
78 void StatsExitCallback(struct evhttp_request *evreq)
79 {
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" );
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
101 bool StatsQuit()
102 {
103         return statsgw_quit_process;
104 }
105
106
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     for (int i=0; i<swift::FileTransfer::files.size(); i++)
128     {
129         FileTransfer *ft = swift::FileTransfer::files[i];
130         if (ft != NULL)
131         {
132                 int fd = ft->fd();
133                         uint64_t total = (int)swift::Size(fd);
134                         uint64_t down  = (int)swift::Complete(fd);
135                         int perc = (int)((down * 100) / total);
136
137                         char roothashhexstr[256];
138                         sprintf(roothashhexstr,"%s", RootMerkleHash(fd).hex().c_str() );
139
140                         char templ[1024];
141                         sprintf(templ,swarm_page_templ,roothashhexstr, perc, '%', dspeed, uspeed );
142                         strcat(bodystr,templ);
143         }
144     }
145
146         strcat(bodystr,bottom_page);
147
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" );
155
156         // Construct evbuffer and send via chunked encoding
157         struct evbuffer *evb = evbuffer_new();
158         int ret = evbuffer_add(evb,bodystr,strlen(bodystr));
159         if (ret < 0) {
160                 print_error("statsgw: OverviewCallback: error evbuffer_add");
161                 return;
162         }
163
164         evhttp_send_reply(evreq, 200, "OK", evb);
165         evbuffer_free(evb);
166 }
167
168
169 void StatsGetSpeedCallback(struct evhttp_request *evreq)
170 {
171         if (statsgw_last_time == 0)
172         {
173                 statsgw_last_time = NOW-1000000;
174         }
175
176         tint nu = Channel::Time();
177         uint64_t down = Channel::global_raw_bytes_down;
178         uint64_t up = Channel::global_raw_bytes_up;
179
180         int dspeed = 0, uspeed = 0;
181         tint tdiff = (nu - statsgw_last_time)/1000000;
182         if (tdiff > 0) {
183                 dspeed = (int)(((down-statsgw_last_down)/1024) / tdiff);
184                 uspeed = (int)(((up-statsgw_last_up)/1024) / tdiff);
185         }
186     statsgw_last_down = down;
187     statsgw_last_up = up;
188     statsgw_last_time = nu;
189
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++)
194     {
195         FileTransfer *ft = swift::FileTransfer::files[i];
196         if (ft != NULL)
197         {
198                 contentdownspeed += ft->GetCurrentSpeed(DDIR_DOWNLOAD);
199                 contentupspeed += ft->GetCurrentSpeed(DDIR_UPLOAD);
200                 nleech += ft->GetNumLeechers();
201                 nseed += ft->GetNumSeeders();
202         }
203     }
204     int cdownspeed = (int)(contentdownspeed/1024.0);
205     int cupspeed = (int)(contentupspeed/1024.0);
206
207         char speedstr[1024];
208         sprintf(speedstr,"{\"downspeed\": %d, \"success\": \"true\", \"upspeed\": %d, \"cdownspeed\": %d, \"cupspeed\": %d, \"nleech\": %d, \"nseed\": %d}", dspeed, uspeed, cdownspeed, cupspeed, nleech, nseed );
209
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" );
217
218         // Construct evbuffer and send via chunked encoding
219         struct evbuffer *evb = evbuffer_new();
220         int ret = evbuffer_add(evb,speedstr,strlen(speedstr));
221         if (ret < 0) {
222                 print_error("statsgw: GetSpeedCallback: error evbuffer_add");
223                 return;
224         }
225
226         evhttp_send_reply(evreq, 200, "OK", evb);
227         evbuffer_free(evb);
228 }
229
230
231 void StatsGwNewRequestCallback (struct evhttp_request *evreq, void *arg) {
232
233     dprintf("%s @%i http new request\n",tintstr(),statsgw_reqs_count);
234     statsgw_reqs_count++;
235
236     if (evhttp_request_get_command(evreq) != EVHTTP_REQ_GET) {
237             return;
238     }
239
240     // Parse URI
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");
244
245     fprintf(stderr,"statsgw: GOT %s\n", uri);
246
247     if (strstr(uri,"get_speed_info") != NULL)
248     {
249         StatsGetSpeedCallback(evreq);
250     }
251     else if (!strncmp(uri,"/webUI/exit",strlen("/webUI/exit")) || statsgw_quit_process)
252     {
253         statsgw_quit_process = true;
254         StatsExitCallback(evreq);
255     }
256     else if (!strncmp(uri,"/webUI",strlen("/webUI")))
257     {
258         StatsOverviewCallback(evreq);
259     }
260 }
261
262
263 bool InstallStatsGateway (struct event_base *evbase,Address bindaddr) {
264         // Arno, 2011-10-04: From libevent's http-server.c example
265
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");
270                 return false;
271         }
272
273         /* Install callback for all requests */
274         evhttp_set_gencb(statsgw_event, StatsGwNewRequestCallback, NULL);
275
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");
280                 return false;
281         }
282
283         return true;
284 }