complete state reset functionality
[oweals/gnunet.git] / src / transport / plugin_transport_http_server.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/plugin_transport_http.c
23  * @brief http transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "plugin_transport_http.h"
28
29 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
30 #define _RECEIVE 0
31 #define _SEND 1
32
33 struct ServerConnection
34 {
35   /* _RECV or _SEND */
36   int direction;
37
38   /* should this connection get disconnected? GNUNET_YES/NO  */
39   int disconnect;
40
41   struct Session *session;
42   struct MHD_Connection * mhd_conn;
43 };
44
45 /**
46  * Function that queries MHD's select sets and
47  * starts the task waiting for them.
48  * @param plugin plugin
49  * @param daemon_handle the MHD daemon handle
50  * @return gnunet task identifier
51  */
52 static GNUNET_SCHEDULER_TaskIdentifier
53 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, int now);
54
55 static void
56 server_log (void *arg, const char *fmt, va_list ap)
57 {
58   char text[1024];
59
60   vsnprintf (text, sizeof (text), fmt, ap);
61   va_end (ap);
62   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Server: %s\n", text);
63 }
64
65 /**
66  * Check if incoming connection is accepted.
67  * NOTE: Here every connection is accepted
68  * @param cls plugin as closure
69  * @param addr address of incoming connection
70  * @param addr_len address length of incoming connection
71  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
72  *
73  */
74 static int
75 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
76 {
77   struct Plugin * plugin = cls;
78
79   if (plugin->cur_connections <= plugin->max_connections)
80     return MHD_YES;
81   else
82   {
83     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Server: Cannot accept new connections\n");
84     return MHD_NO;
85   }
86 }
87
88
89 #if BUILD_HTTPS
90 static char *
91 server_load_file (const char *file)
92 {
93   struct GNUNET_DISK_FileHandle *gn_file;
94   struct stat fstat;
95   char *text = NULL;
96
97   if (0 != STAT (file, &fstat))
98     return NULL;
99   text = GNUNET_malloc (fstat.st_size + 1);
100   gn_file =
101       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
102                              GNUNET_DISK_PERM_USER_READ);
103   if (gn_file == NULL)
104   {
105     GNUNET_free (text);
106     return NULL;
107   }
108   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size))
109   {
110     GNUNET_free (text);
111     GNUNET_DISK_file_close (gn_file);
112     return NULL;
113   }
114   text[fstat.st_size] = '\0';
115   GNUNET_DISK_file_close (gn_file);
116   return text;
117 }
118 #endif
119
120
121 #if BUILD_HTTPS
122
123 static int
124 server_load_certificate (struct Plugin *plugin)
125 {
126   int res = GNUNET_OK;
127
128   char *key_file;
129   char *cert_file;
130
131   /* Get crypto init string from config
132    * If not present just use default values */
133
134   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
135                                          "CRYPTO_INIT", &plugin->crypto_init));
136
137   if (GNUNET_OK !=
138       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
139                                                "KEY_FILE", &key_file))
140   {
141     key_file = GNUNET_strdup ("https_key.key");
142   }
143
144   if (GNUNET_OK !=
145       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
146                                                "CERT_FILE", &cert_file))
147   {
148     GNUNET_asprintf(&cert_file, "%s", "https_cert.crt");
149   }
150
151   /* read key & certificates from file */
152 #if VERBOSE_SERVER
153   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
154               "Loading TLS certificate from key-file `%s' cert-file`%s'\n",
155               key_file, cert_file);
156 #endif
157
158   plugin->key = server_load_file (key_file);
159   plugin->cert = server_load_file (cert_file);
160
161   if ((plugin->key == NULL) || (plugin->cert == NULL))
162   {
163     struct GNUNET_OS_Process *cert_creation;
164
165     GNUNET_free_non_null (plugin->key);
166     plugin->key = NULL;
167     GNUNET_free_non_null (plugin->cert);
168     plugin->cert = NULL;
169
170 #if VERBOSE_SERVER
171     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
172                 "No usable TLS certificate found, creating certificate\n");
173 #endif
174     errno = 0;
175     cert_creation =
176         GNUNET_OS_start_process (NULL, NULL,
177                                  "gnunet-transport-certificate-creation",
178                                  "gnunet-transport-certificate-creation",
179                                  key_file, cert_file, NULL);
180     if (cert_creation == NULL)
181     {
182       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
183                        _
184                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
185       GNUNET_free (key_file);
186       GNUNET_free (cert_file);
187
188       GNUNET_free_non_null (plugin->key);
189       plugin->key = NULL;
190       GNUNET_free_non_null (plugin->cert);
191       plugin->cert = NULL;
192       GNUNET_free_non_null (plugin->crypto_init);
193       plugin->crypto_init = NULL;
194
195       return GNUNET_SYSERR;
196     }
197     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
198     GNUNET_OS_process_close (cert_creation);
199
200     plugin->key = server_load_file (key_file);
201     plugin->cert = server_load_file (cert_file);
202   }
203
204   if ((plugin->key == NULL) || (plugin->cert == NULL))
205   {
206     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
207                      _
208                      ("No usable TLS certificate found and creating one failed!\n"),
209                      "transport-https");
210     GNUNET_free (key_file);
211     GNUNET_free (cert_file);
212
213     GNUNET_free_non_null (plugin->key);
214     plugin->key = NULL;
215     GNUNET_free_non_null (plugin->cert);
216     plugin->cert = NULL;
217     GNUNET_free_non_null (plugin->crypto_init);
218     plugin->crypto_init = NULL;
219
220     return GNUNET_SYSERR;
221   }
222   GNUNET_free (key_file);
223   GNUNET_free (cert_file);
224 #if DEBUG_HTTP
225   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
226 #endif
227
228   return res;
229 }
230 #endif
231
232
233 /**
234  * Reschedule the execution of both IPv4 and IPv6 server
235  * @param plugin the plugin
236  * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait
237  * until timeout
238  */
239
240 static void
241 server_reschedule (struct Plugin *plugin, int now)
242 {
243   if (plugin->server_v4 != NULL)
244   {
245     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
246     {
247       GNUNET_SCHEDULER_cancel(plugin->server_v4_task);
248       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
249     }
250     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now);
251   }
252
253   if (plugin->server_v6 != NULL)
254   {
255   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
256    {
257      GNUNET_SCHEDULER_cancel(plugin->server_v6_task);
258      plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
259    }
260    plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now);
261   }
262 }
263
264 /**
265  * Callback called by MessageStreamTokenizer when a message has arrived
266  * @param cls current session as closure
267  * @param client clien
268  * @param message the message to be forwarded to transport service
269  */
270 static void
271 server_receive_mst_cb (void *cls, void *client,
272                   const struct GNUNET_MessageHeader *message)
273 {
274   struct Session *s = cls;
275 #if VERBOSE_SERVER
276   struct Plugin *plugin = s->plugin;
277 #endif
278   struct GNUNET_TIME_Relative delay;
279
280   delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen);
281
282   s->next_receive = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), delay);
283
284   if (delay.rel_value > 0)
285   {
286 #if VERBOSE_CLIENT
287     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Server: peer `%s' address `%s' next read delayed for %llu ms\n",
288                 GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), delay);
289 #endif
290   }
291 }
292
293 /**
294  * Callback called by MHD when it needs data to send
295  * @param cls current session
296  * @param pos position in buffer
297  * @param buf the buffer to write data to
298  * @param max max number of bytes available in buffer
299  * @return bytes written to buffer
300  */
301 static ssize_t
302 server_send_callback (void *cls, uint64_t pos, char *buf, size_t max)
303 {
304   struct Session *s = cls;
305
306   struct HTTP_Message *msg;
307   int bytes_read = 0;
308   //static int c = 0;
309   msg = s->msg_head;
310   if (msg != NULL)
311   {
312     /* sending */
313     if ((msg->size - msg->pos) <= max)
314     {
315       memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos));
316       bytes_read = msg->size - msg->pos;
317       msg->pos += (msg->size - msg->pos);
318     }
319     else
320     {
321       memcpy (buf, &msg->buf[msg->pos], max);
322       msg->pos += max;
323       bytes_read = max;
324     }
325
326     /* removing message */
327     if (msg->pos == msg->size)
328     {
329       if (NULL != msg->transmit_cont)
330         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
331       GNUNET_CONTAINER_DLL_remove(s->msg_head, s->msg_tail, msg);
332       GNUNET_free (msg);
333     }
334   }
335
336 #if VERBOSE_CLIENT
337   struct Plugin *plugin = s->plugin;
338   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
339                    "Server: %X: sent %u bytes\n",
340                    s, bytes_read);
341 #endif
342   return bytes_read;
343 }
344
345 static struct ServerConnection *
346 server_lookup_session (struct Plugin *plugin,
347     struct MHD_Connection *mhd_connection,
348     const char * url,
349     const char *method)
350 {
351   struct Session *s = NULL;
352   struct Session * t;
353   struct ServerConnection *sc = NULL;
354   const union MHD_ConnectionInfo *conn_info;
355
356   struct IPv4HttpAddress a4;
357   struct IPv6HttpAddress a6;
358   struct sockaddr_in * s4;
359   struct sockaddr_in6 * s6;
360   void * a;
361   size_t a_len;
362   struct GNUNET_PeerIdentity target;
363   int check = GNUNET_NO;
364   uint32_t tag = 0;
365   int direction;
366
367   conn_info = MHD_get_connection_info (mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
368   if ((conn_info->client_addr->sa_family != AF_INET) && (conn_info->client_addr->sa_family != AF_INET6))
369     return MHD_NO;
370
371   if ((strlen(&url[1]) >= 105)  && (url[104] == ';'))
372   {
373     char hash[104];
374     char * tagc = (char *) &url[105];
375     memcpy(&hash, &url[1], 103);
376     hash [103] = '\0';
377     if (GNUNET_OK == GNUNET_CRYPTO_hash_from_string ((const char *) &hash,
378           &(target.hashPubKey)))
379     {
380       tag = strtoul (tagc, NULL, 10);
381       if (tagc > 0)
382         check = GNUNET_YES;
383     }
384   }
385
386   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
387     direction = _RECEIVE;
388   if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
389     direction = _SEND;
390
391   if (check == GNUNET_NO)
392     goto error;
393
394   plugin->cur_connections++;
395
396 #if VERBOSE_SERVER
397   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
398                    "Server: New inbound connection from %s with tag %u\n", GNUNET_i2s(&target), tag);
399 #endif
400   /* find duplicate session */
401
402   t = plugin->head;
403
404   while (t != NULL)
405   {
406     if ((t->inbound) && (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) &&
407         /* FIXME add source address comparison */
408         (t->tag == tag))
409     break;
410     t = t->next;
411   }
412   if (t != NULL)
413   {
414 #if VERBOSE_SERVER
415     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
416                      "Server: Duplicate session, dismissing new connection from peer `%s'\n", GNUNET_i2s (&target));
417 #endif
418     goto error;
419   }
420
421   /* find semi-session */
422   t = plugin->server_semi_head;
423
424   while (t != NULL)
425   {
426     /* FIXME add source address comparison */
427     if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) &&
428         (t->tag == tag))
429     {
430       break;
431     }
432     t = t->next;
433   }
434
435   if (t == NULL)
436     goto create;
437
438 #if VERBOSE_SERVER
439   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
440                    "Server: Found existing semi-session for `%s'\n", GNUNET_i2s (&target));
441 #endif
442
443   if ((direction == _SEND) && (t->server_send != NULL))
444   {
445 #if VERBOSE_SERVER
446     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
447                      "Server: Duplicate GET session, dismissing new connection from peer `%s'\n", GNUNET_i2s (&target));
448 #endif
449     goto error;
450   }
451   else
452   {
453     s = t;
454     GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
455     GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
456 #if VERBOSE_SERVER
457     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
458                      "Server: Found matching semi-session, merging session for peer `%s'\n", GNUNET_i2s (&target));
459 #endif
460
461     goto found;
462   }
463   if ((direction == _RECEIVE) && (t->server_recv != NULL))
464   {
465 #if VERBOSE_SERVER
466     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
467                      "Server: Duplicate PUT session, dismissing new connection from peer `%s'\n", GNUNET_i2s (&target));
468 #endif
469     goto error;
470   }
471   else
472   {
473     s = t;
474     GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
475     GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
476 #if VERBOSE_SERVER
477     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
478                      "Server: Found matching semi-session, merging session for peer `%s'\n", GNUNET_i2s (&target));
479 #endif
480     goto found;
481   }
482
483 create:
484 /* create new session */
485 #if VERBOSE_SERVER
486   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
487                "Server: Creating new session for peer `%s' \n", GNUNET_i2s (&target));
488 #endif
489
490   switch (conn_info->client_addr->sa_family)
491   {
492   case (AF_INET):
493       s4 = ((struct sockaddr_in * ) conn_info->client_addr);
494       a4.u4_port = s4->sin_port;
495       memcpy (&a4.ipv4_addr, &s4->sin_addr,
496               sizeof (struct in_addr));
497       a = &a4;
498       a_len = sizeof (struct IPv4HttpAddress);
499       break;
500   case (AF_INET6):
501       s6 = ((struct sockaddr_in6 * ) conn_info->client_addr);
502       a6.u6_port = s6->sin6_port;
503       memcpy (&a6.ipv6_addr, &s6->sin6_addr,
504               sizeof (struct in6_addr));
505       a = &a6;
506       a_len = sizeof (struct IPv6HttpAddress);
507       break;
508   default:
509     GNUNET_break (0);
510     goto error;
511   }
512
513   s = create_session (plugin,
514                       &target,
515                       a,
516                       a_len,
517                       NULL,
518                       NULL);
519
520   s->inbound = GNUNET_YES;
521   s->next_receive = GNUNET_TIME_absolute_get_zero();
522   s->tag= tag;
523   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
524     s->server_recv = s;
525   if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
526     s->server_send = s;
527   GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, plugin->server_semi_tail, s);
528   goto found;
529
530 error:
531 #if VERBOSE_SERVER
532   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
533                "Server: Invalid connection request\n");
534 #endif
535   return NULL;
536
537 found:
538   sc = GNUNET_malloc (sizeof (struct ServerConnection));
539   sc->mhd_conn = mhd_connection;
540   sc->direction = direction;
541   sc->session = s;
542   if (direction == _SEND)
543     s->server_send = sc;
544   if (direction == _RECEIVE)
545     s->server_recv = sc;
546
547 #if MHD_VERSION >= 0x00090E00
548   int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
549 #if VERBOSE_SERVER
550   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
551                    "Server: Setting timeout for %X to %u sec.\n", sc, to);
552 #endif
553   MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to);
554   server_reschedule (plugin, GNUNET_NO);
555 #endif
556   return sc;
557 }
558
559 /**
560  * Process GET or PUT request received via MHD.  For
561  * GET, queue response that will send back our pending
562  * messages.  For PUT, process incoming data and send
563  * to GNUnet core.  In either case, check if a session
564  * already exists and create a new one if not.
565  */
566 static int
567 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
568                   const char *url, const char *method, const char *version,
569                   const char *upload_data, size_t * upload_data_size,
570                   void **httpSessionCache)
571 {
572
573   struct Plugin *plugin = cls;
574   struct ServerConnection *sc = *httpSessionCache;
575   struct Session *s = NULL;
576
577   int res = MHD_YES;
578   struct MHD_Response *response;
579
580   GNUNET_assert (cls != NULL);
581   /* new connection */
582   if (sc == NULL)
583   {
584     sc = server_lookup_session(plugin, mhd_connection, url, method);
585     if (sc != NULL)
586       (*httpSessionCache) = sc;
587     else
588     {
589       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
590       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
591       MHD_destroy_response (response);
592       return res;
593     }
594   }
595
596   /* existing connection */
597   sc = (*httpSessionCache);
598   s = sc->session;
599
600   /* connection is to be disconnected*/
601   if (sc->disconnect == GNUNET_YES)
602   {
603     response = MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", MHD_NO, MHD_NO);
604     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
605 #if VERBOSE_SERVER
606     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607                 "Sent HTTP/1.1: 200 OK as PUT Response\n");
608 #endif
609     MHD_destroy_response (response);
610     return MHD_YES;
611   }
612
613   GNUNET_assert (s != NULL);
614   if (sc->direction == _SEND)
615   {
616     response = MHD_create_response_from_callback (-1, 32 * 1024, &server_send_callback,
617                                            s, NULL);
618     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
619     MHD_destroy_response (response);
620     return MHD_YES;
621   }
622   if (sc->direction == _RECEIVE)
623   {
624     if (*upload_data_size == 0)
625     {
626 #if VERBOSE_SERVER
627   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
628                    "Server: Peer `%s' PUT on address `%s' connected\n",
629                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
630 #endif
631       return MHD_YES;
632     }
633
634     /* Recieving data */
635     if ((*upload_data_size > 0))
636     {
637 #if VERBOSE_SERVER
638   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
639                    "Server: peer `%s' PUT on address `%s' received %Zu bytes\n",
640                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), *upload_data_size);
641 #endif
642       struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
643       if (( s->next_receive.abs_value <= now.abs_value))
644       {
645 #if VERBOSE_SERVER
646         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
647                     "Server: %X: PUT with %u bytes forwarded to MST\n", s,
648                     *upload_data_size);
649 #endif
650         if (s->msg_tk == NULL)
651         {
652           s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
653         }
654         res = GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size, GNUNET_NO, GNUNET_NO);
655
656 #if MHD_VERSION >= 0x00090E00
657         int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
658         struct ServerConnection *t = NULL;
659
660 #if VERBOSE_SERVER
661         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
662                          "Server: Received %Zu bytes\n", *upload_data_size);
663 #endif
664
665         /* Setting timeouts for other connections */
666         if (s->server_recv != NULL)
667         {
668           t = s->server_recv;
669 #if VERBOSE_SERVER
670           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
671                      "Server: Setting timeout for %X to %u sec.\n", t, to);
672 #endif
673           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, to);
674         }
675         if (s->server_send != NULL)
676         {
677           t = s->server_send;
678 #if VERBOSE_SERVER
679           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
680                      "Server: Setting timeout for %X to %u sec.\n", t, to);
681 #endif
682           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, to);
683         }
684         server_reschedule (plugin, GNUNET_NO);
685 #endif
686         (*upload_data_size) = 0;
687       }
688       else
689       {
690 #if DEBUG_HTTP
691        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
692                   "Server: %X no inbound bandwidth available! Next read was delayed by %llu ms\n", s, now.abs_value - s->next_receive.abs_value);
693 #endif
694       }
695       return MHD_YES;
696     }
697     else
698       return MHD_NO;
699   }
700   return res;
701 }
702
703 static void
704 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
705                       void **httpSessionCache)
706 {
707   struct ServerConnection *sc = *httpSessionCache;
708   struct ServerConnection *tc = *httpSessionCache;
709   struct Session * s = NULL;
710   struct Session * t = NULL;
711   struct Plugin * plugin = NULL;
712
713   if (sc == NULL)
714     return;
715
716   s = sc->session;
717   plugin = s-> plugin;
718   if (sc->direction == _SEND)
719   {
720 #if VERBOSE_SERVER
721   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
722                    "Server: %X peer `%s' GET on address `%s' disconnected\n",
723                    s->server_send,
724                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
725 #endif
726     s->server_send = NULL;
727
728     if (s->server_recv != NULL)
729     {
730       tc = s->server_recv;
731       tc->disconnect = GNUNET_YES;
732 #if MHD_VERSION >= 0x00090E00
733       MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, 1);
734 #endif
735     }
736   }
737   if (sc->direction == _RECEIVE)
738   {
739 #if VERBOSE_SERVER
740   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
741                    "Server: %X peer `%s' PUT on address `%s' disconnected\n",
742                    s->server_recv,
743                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
744 #endif
745     s->server_recv = NULL;
746     if (s->server_send != NULL)
747     {
748       tc = s->server_send;
749       tc->disconnect = GNUNET_YES;
750 #if MHD_VERSION >= 0x00090E00
751       MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, 1);
752 #endif
753     }
754     if (s->msg_tk != NULL)
755     {
756        GNUNET_SERVER_mst_destroy(s->msg_tk);
757        s->msg_tk = NULL;
758     }
759   }
760   GNUNET_free (sc);
761
762   t = plugin->server_semi_head;
763   while (t != NULL)
764   {
765     if (t == s)
766     {
767       GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
768       GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
769       break;
770     }
771     t = t->next;
772   }
773   plugin->cur_connections--;
774
775   server_reschedule (plugin, GNUNET_NO);
776
777   if ((s->server_send == NULL) && (s->server_recv == NULL))
778   {
779 #if VERBOSE_SERVER
780   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
781                    "Server: peer `%s' on address `%s' disconnected\n",
782                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
783 #endif
784     if (s->msg_tk != NULL)
785     {
786        GNUNET_SERVER_mst_destroy(s->msg_tk);
787        s->msg_tk = NULL;
788     }
789
790     notify_session_end(s->plugin, &s->target, s);
791   }
792 }
793
794 int
795 server_disconnect (struct Session *s)
796 {
797   struct Plugin *plugin = s->plugin;
798   struct Session *t = plugin->head;
799
800   while (t != NULL)
801   {
802     if (t->inbound == GNUNET_YES)
803     {
804       if (t->server_send != NULL)
805       {
806         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
807       }
808       if (t->server_send != NULL)
809       {
810         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
811       }
812     }
813     t = t->next;
814   }
815
816
817   return GNUNET_OK;
818 }
819
820 int
821 server_send (struct Session *s, struct HTTP_Message * msg)
822 {
823   GNUNET_CONTAINER_DLL_insert (s->msg_head, s->msg_tail, msg);
824   server_reschedule (s->plugin, GNUNET_YES);
825   return GNUNET_OK;
826 }
827
828
829
830 /**
831  * Call MHD IPv4 to process pending requests and then go back
832  * and schedule the next run.
833  * @param cls plugin as closure
834  * @param tc task context
835  */
836 static void
837 server_v4_run (void *cls,
838                            const struct GNUNET_SCHEDULER_TaskContext *tc)
839 {
840   struct Plugin *plugin = cls;
841   GNUNET_assert (cls != NULL);
842
843   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
844
845   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
846     return;
847
848   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
849   if (plugin->server_v4 != NULL)
850     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, GNUNET_NO);
851 }
852
853
854 /**
855  * Call MHD IPv6 to process pending requests and then go back
856  * and schedule the next run.
857  * @param cls plugin as closure
858  * @param tc task context
859  */
860 static void
861 server_v6_run (void *cls,
862                            const struct GNUNET_SCHEDULER_TaskContext *tc)
863 {
864   struct Plugin *plugin = cls;
865   GNUNET_assert (cls != NULL);
866
867   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
868
869   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
870     return;
871
872   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
873   if (plugin->server_v6 != NULL)
874     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, GNUNET_NO);
875 }
876
877 /**
878  * Function that queries MHD's select sets and
879  * starts the task waiting for them.
880  * @param plugin plugin
881  * @param daemon_handle the MHD daemon handle
882  * @return gnunet task identifier
883  */
884 static GNUNET_SCHEDULER_TaskIdentifier
885 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, int now)
886 {
887   GNUNET_SCHEDULER_TaskIdentifier ret;
888   fd_set rs;
889   fd_set ws;
890   fd_set es;
891   struct GNUNET_NETWORK_FDSet *wrs;
892   struct GNUNET_NETWORK_FDSet *wws;
893   struct GNUNET_NETWORK_FDSet *wes;
894   int max;
895   unsigned long long timeout;
896   static unsigned long long last_timeout = 0;
897   int haveto;
898
899   struct GNUNET_TIME_Relative tv;
900
901   ret = GNUNET_SCHEDULER_NO_TASK;
902   FD_ZERO (&rs);
903   FD_ZERO (&ws);
904   FD_ZERO (&es);
905   wrs = GNUNET_NETWORK_fdset_create ();
906   wes = GNUNET_NETWORK_fdset_create ();
907   wws = GNUNET_NETWORK_fdset_create ();
908   max = -1;
909   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
910   haveto = MHD_get_timeout (daemon_handle, &timeout);
911   if (haveto == MHD_YES)
912     {
913     if (timeout != last_timeout)
914     {
915 #if VERBOSE_SERVER
916       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
917                        "SELECT Timeout changed from %llu to %llu\n",
918                        last_timeout, timeout);
919 #endif
920       last_timeout = timeout;
921     }
922     tv.rel_value = (uint64_t) timeout;
923     }
924   else
925     tv = GNUNET_TIME_UNIT_SECONDS;
926   /* Force immediate run, since we have outbound data to send */
927   if (now == GNUNET_YES)
928     tv = GNUNET_TIME_UNIT_MILLISECONDS;
929   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
930   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
931   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
932   if (daemon_handle == plugin->server_v4)
933   {
934     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
935     {
936       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
937       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
938     }
939
940     ret =
941         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
942                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
943                                      &server_v4_run, plugin);
944   }
945   if (daemon_handle == plugin->server_v6)
946   {
947     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
948     {
949       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
950       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
951     }
952
953     ret =
954         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
955                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
956                                      &server_v6_run, plugin);
957   }
958   GNUNET_NETWORK_fdset_destroy (wrs);
959   GNUNET_NETWORK_fdset_destroy (wws);
960   GNUNET_NETWORK_fdset_destroy (wes);
961   return ret;
962 }
963
964 int
965 server_start (struct Plugin *plugin)
966 {
967   int res = GNUNET_OK;
968   unsigned int timeout;
969
970 #if BUILD_HTTPS
971   res = server_load_certificate (plugin);
972   if (res == GNUNET_SYSERR)
973   {
974     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
975         "Could not load or create server certificate! Loading plugin failed!\n");
976     return res;
977   }
978 #endif
979
980
981 #if MHD_VERSION >= 0x00090E00
982   timeout = GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT.rel_value / 1000;
983   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
984       "MHD can set timeout per connection! Default time out %u sec.\n", timeout);
985 #else
986   timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000;
987   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
988       "MHD cannot set timeout per connection! Default time out %u sec.\n", timeout);
989 #endif
990   plugin->server_v4 = NULL;
991   if (plugin->ipv4 == GNUNET_YES)
992   {
993     plugin->server_v4 = MHD_start_daemon (
994 #if VERBOSE_SERVER
995                                            MHD_USE_DEBUG |
996 #endif
997 #if BUILD_HTTPS
998                                            MHD_USE_SSL |
999 #endif
1000                                            MHD_NO_FLAG, plugin->port,
1001                                            &server_accept_cb, plugin,
1002                                            &server_access_cb, plugin,
1003                                            MHD_OPTION_SOCK_ADDR,
1004                                            (struct sockaddr_in *)
1005                                            plugin->server_addr_v4,
1006                                            MHD_OPTION_CONNECTION_LIMIT,
1007                                            (unsigned int)
1008                                            plugin->max_connections,
1009 #if BUILD_HTTPS
1010                                            MHD_OPTION_HTTPS_PRIORITIES,
1011                                            plugin->crypto_init,
1012                                            MHD_OPTION_HTTPS_MEM_KEY,
1013                                            plugin->key,
1014                                            MHD_OPTION_HTTPS_MEM_CERT,
1015                                            plugin->cert,
1016 #endif
1017                                            MHD_OPTION_CONNECTION_TIMEOUT,
1018                                            timeout ,
1019                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1020                                            (size_t) (2 *
1021                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1022                                            MHD_OPTION_NOTIFY_COMPLETED,
1023                                            &server_disconnect_cb, plugin,
1024                                            MHD_OPTION_EXTERNAL_LOGGER,
1025                                            server_log, NULL, MHD_OPTION_END);
1026   }
1027   plugin->server_v6 = NULL;
1028   if (plugin->ipv6 == GNUNET_YES)
1029   {
1030     plugin->server_v6 = MHD_start_daemon (
1031 #if VERBOSE_SERVER
1032                                            MHD_USE_DEBUG |
1033 #endif
1034 #if BUILD_HTTPS
1035                                            MHD_USE_SSL |
1036 #endif
1037                                            MHD_USE_IPv6, plugin->port,
1038                                            &server_accept_cb, plugin,
1039                                            &server_access_cb, plugin,
1040                                            MHD_OPTION_SOCK_ADDR,
1041                                            (struct sockaddr_in6 *)
1042                                            plugin->server_addr_v6,
1043                                            MHD_OPTION_CONNECTION_LIMIT,
1044                                            (unsigned int)
1045                                            plugin->max_connections,
1046 #if BUILD_HTTPS
1047                                            MHD_OPTION_HTTPS_PRIORITIES,
1048                                            plugin->crypto_init,
1049                                            MHD_OPTION_HTTPS_MEM_KEY,
1050                                            plugin->key,
1051                                            MHD_OPTION_HTTPS_MEM_CERT,
1052                                            plugin->cert,
1053 #endif
1054                                            MHD_OPTION_CONNECTION_TIMEOUT,
1055                                            timeout,
1056                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1057                                            (size_t) (2 *
1058                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1059                                            MHD_OPTION_NOTIFY_COMPLETED,
1060                                            &server_disconnect_cb, plugin,
1061                                            MHD_OPTION_EXTERNAL_LOGGER,
1062                                            server_log, NULL, MHD_OPTION_END);
1063
1064   }
1065
1066   if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
1067   {
1068     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1069                      "Failed to start %s IPv4 server component on port %u\n", plugin->name,
1070                      plugin->port);
1071     return GNUNET_SYSERR;
1072   }
1073   if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
1074   {
1075     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1076                      "Failed to start %s IPv6 server component on port %u\n", plugin->name,
1077                      plugin->port);
1078     return GNUNET_SYSERR;
1079   }
1080
1081   server_reschedule (plugin, GNUNET_NO);
1082
1083 #if DEBUG_HTTP
1084   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1085                    "%s server component started on port %u\n", plugin->name,
1086                    plugin->port);
1087 #endif
1088   return res;
1089 }
1090
1091 void
1092 server_stop (struct Plugin *plugin)
1093 {
1094   struct Session *s = NULL;
1095   struct Session *t = NULL;
1096
1097   struct MHD_Daemon *server_v4_tmp = plugin->server_v4;
1098   plugin->server_v4 = NULL;
1099   struct MHD_Daemon *server_v6_tmp = plugin->server_v6;
1100   plugin->server_v6 = NULL;
1101
1102   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1103   {
1104     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1105     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1106   }
1107
1108   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1109   {
1110     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1111     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1112   }
1113
1114   if (server_v6_tmp != NULL)
1115   {
1116     MHD_stop_daemon (server_v4_tmp);
1117   }
1118   if (server_v6_tmp != NULL)
1119   {
1120     MHD_stop_daemon (server_v6_tmp);
1121   }
1122
1123   /* cleaning up semi-sessions never propagated */
1124   s = plugin->server_semi_head;
1125   while (s != NULL)
1126   {
1127     t = s->next;
1128     delete_session (s);
1129     s = t;
1130   }
1131
1132 #if BUILD_HTTPS
1133   GNUNET_free_non_null (plugin->crypto_init);
1134   GNUNET_free_non_null (plugin->cert);
1135   GNUNET_free_non_null (plugin->key);
1136 #endif
1137
1138 #if DEBUG_HTTP
1139   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1140                    "%s server component stopped\n", plugin->name);
1141 #endif
1142 }
1143
1144
1145
1146 /* end of plugin_transport_http.c */