undo 17963, better solution ahead
[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   }
511
512   s = create_session (plugin,
513                       &target,
514                       a,
515                       a_len,
516                       NULL,
517                       NULL);
518
519   s->inbound = GNUNET_YES;
520   s->next_receive = GNUNET_TIME_absolute_get_zero();
521   s->tag= tag;
522   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
523     s->server_recv = s;
524   if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
525     s->server_send = s;
526   GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, plugin->server_semi_tail, s);
527   goto found;
528
529 error:
530 #if VERBOSE_SERVER
531   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
532                "Server: Invalid connection request\n");
533 #endif
534   return NULL;
535
536 found:
537   sc = GNUNET_malloc (sizeof (struct ServerConnection));
538   sc->mhd_conn = mhd_connection;
539   sc->direction = direction;
540   sc->session = s;
541   if (direction == _SEND)
542     s->server_send = sc;
543   if (direction == _RECEIVE)
544     s->server_recv = sc;
545
546 #if MHD_VERSION >= 0x00090E00
547   int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
548 #if VERBOSE_SERVER
549   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
550                    "Server: Setting timeout for %X to %u sec.\n", sc, to);
551 #endif
552   MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to);
553   server_reschedule (plugin, GNUNET_NO);
554 #endif
555   return sc;
556 }
557
558 /**
559  * Process GET or PUT request received via MHD.  For
560  * GET, queue response that will send back our pending
561  * messages.  For PUT, process incoming data and send
562  * to GNUnet core.  In either case, check if a session
563  * already exists and create a new one if not.
564  */
565 static int
566 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
567                   const char *url, const char *method, const char *version,
568                   const char *upload_data, size_t * upload_data_size,
569                   void **httpSessionCache)
570 {
571
572   struct Plugin *plugin = cls;
573   struct ServerConnection *sc = *httpSessionCache;
574   struct Session *s = NULL;
575
576   int res = MHD_YES;
577   struct MHD_Response *response;
578
579   GNUNET_assert (cls != NULL);
580   /* new connection */
581   if (sc == NULL)
582   {
583     sc = server_lookup_session(plugin, mhd_connection, url, method);
584     if (sc != NULL)
585       (*httpSessionCache) = sc;
586     else
587     {
588       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
589       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
590       MHD_destroy_response (response);
591       return res;
592     }
593   }
594
595   /* existing connection */
596   sc = (*httpSessionCache);
597   s = sc->session;
598
599   /* connection is to be disconnected*/
600   if (sc->disconnect == GNUNET_YES)
601   {
602     response = MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", MHD_NO, MHD_NO);
603     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
604 #if VERBOSE_SERVER
605     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606                 "Sent HTTP/1.1: 200 OK as PUT Response\n");
607 #endif
608     MHD_destroy_response (response);
609     return MHD_YES;
610   }
611
612   GNUNET_assert (s != NULL);
613   if (sc->direction == _SEND)
614   {
615     response = MHD_create_response_from_callback (-1, 32 * 1024, &server_send_callback,
616                                            s, NULL);
617     res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
618     MHD_destroy_response (response);
619     return MHD_YES;
620   }
621   if (sc->direction == _RECEIVE)
622   {
623     if (*upload_data_size == 0)
624     {
625 #if VERBOSE_SERVER
626   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
627                    "Server: Peer `%s' PUT on address `%s' connected\n",
628                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
629 #endif
630       return MHD_YES;
631     }
632
633     /* Recieving data */
634     if ((*upload_data_size > 0))
635     {
636 #if VERBOSE_SERVER
637   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
638                    "Server: peer `%s' PUT on address `%s' received %Zu bytes\n",
639                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), *upload_data_size);
640 #endif
641       struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get();
642       if (( s->next_receive.abs_value <= now.abs_value))
643       {
644 #if VERBOSE_SERVER
645         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
646                     "Server: %X: PUT with %u bytes forwarded to MST\n", s,
647                     *upload_data_size);
648 #endif
649         if (s->msg_tk == NULL)
650         {
651           s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
652         }
653         res = GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size, GNUNET_NO, GNUNET_NO);
654
655 #if MHD_VERSION >= 0x00090E00
656         int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
657         struct ServerConnection *t = NULL;
658
659 #if VERBOSE_SERVER
660         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
661                          "Server: Received %Zu bytes\n", *upload_data_size);
662 #endif
663
664         /* Setting timeouts for other connections */
665         if (s->server_recv != NULL)
666         {
667           t = s->server_recv;
668 #if VERBOSE_SERVER
669           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
670                      "Server: Setting timeout for %X to %u sec.\n", t, to);
671 #endif
672           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, to);
673         }
674         if (s->server_send != NULL)
675         {
676           t = s->server_send;
677 #if VERBOSE_SERVER
678           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
679                      "Server: Setting timeout for %X to %u sec.\n", t, to);
680 #endif
681           MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, to);
682         }
683         server_reschedule (plugin, GNUNET_NO);
684 #endif
685         (*upload_data_size) = 0;
686       }
687       else
688       {
689 #if DEBUG_HTTP
690        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691                   "Server: %X no inbound bandwidth available! Next read was delayed by %llu ms\n", s, now.abs_value - s->next_receive.abs_value);
692 #endif
693       }
694       return MHD_YES;
695     }
696     else
697       return MHD_NO;
698   }
699   return res;
700 }
701
702 static void
703 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
704                       void **httpSessionCache)
705 {
706   struct ServerConnection *sc = *httpSessionCache;
707   struct ServerConnection *tc = *httpSessionCache;
708   struct Session * s = NULL;
709   struct Session * t = NULL;
710   struct Plugin * plugin = NULL;
711
712   if (sc == NULL)
713     return;
714
715   s = sc->session;
716   plugin = s-> plugin;
717   if (sc->direction == _SEND)
718   {
719 #if VERBOSE_SERVER
720   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
721                    "Server: %X peer `%s' GET on address `%s' disconnected\n",
722                    s->server_send,
723                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
724 #endif
725     s->server_send = NULL;
726
727     if (s->server_recv != NULL)
728     {
729       tc = s->server_recv;
730       tc->disconnect = GNUNET_YES;
731 #if MHD_VERSION >= 0x00090E00
732       MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, 1);
733 #endif
734     }
735   }
736   if (sc->direction == _RECEIVE)
737   {
738 #if VERBOSE_SERVER
739   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
740                    "Server: %X peer `%s' PUT on address `%s' disconnected\n",
741                    s->server_recv,
742                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
743 #endif
744     s->server_recv = NULL;
745     if (s->server_send != NULL)
746     {
747       tc = s->server_send;
748       tc->disconnect = GNUNET_YES;
749 #if MHD_VERSION >= 0x00090E00
750       MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, 1);
751 #endif
752     }
753     if (s->msg_tk != NULL)
754     {
755        GNUNET_SERVER_mst_destroy(s->msg_tk);
756        s->msg_tk = NULL;
757     }
758   }
759   GNUNET_free (sc);
760
761   t = plugin->server_semi_head;
762   while (t != NULL)
763   {
764     if (t == s)
765     {
766       GNUNET_CONTAINER_DLL_remove(plugin->server_semi_head, plugin->server_semi_tail, s);
767       GNUNET_CONTAINER_DLL_insert(plugin->head, plugin->tail, s);
768       break;
769     }
770     t = t->next;
771   }
772   plugin->cur_connections--;
773
774   server_reschedule (plugin, GNUNET_NO);
775
776   if ((s->server_send == NULL) && (s->server_recv == NULL))
777   {
778 #if VERBOSE_SERVER
779   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
780                    "Server: peer `%s' on address `%s' disconnected\n",
781                    GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen));
782 #endif
783     if (s->msg_tk != NULL)
784     {
785        GNUNET_SERVER_mst_destroy(s->msg_tk);
786        s->msg_tk = NULL;
787     }
788
789     notify_session_end(s->plugin, &s->target, s);
790   }
791 }
792
793 int
794 server_disconnect (struct Session *s)
795 {
796   struct Plugin *plugin = s->plugin;
797   struct Session *t = plugin->head;
798
799   while (t != NULL)
800   {
801     if (t->inbound == GNUNET_YES)
802     {
803       if (t->server_send != NULL)
804       {
805         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
806       }
807       if (t->server_send != NULL)
808       {
809         ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES;
810       }
811     }
812     t = t->next;
813   }
814
815
816   return GNUNET_OK;
817 }
818
819 int
820 server_send (struct Session *s, struct HTTP_Message * msg)
821 {
822   GNUNET_CONTAINER_DLL_insert (s->msg_head, s->msg_tail, msg);
823   server_reschedule (s->plugin, GNUNET_YES);
824   return GNUNET_OK;
825 }
826
827
828
829 /**
830  * Call MHD IPv4 to process pending requests and then go back
831  * and schedule the next run.
832  * @param cls plugin as closure
833  * @param tc task context
834  */
835 static void
836 server_v4_run (void *cls,
837                            const struct GNUNET_SCHEDULER_TaskContext *tc)
838 {
839   struct Plugin *plugin = cls;
840   GNUNET_assert (cls != NULL);
841
842   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
843
844   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
845     return;
846
847   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
848   if (plugin->server_v4 != NULL)
849     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, GNUNET_NO);
850 }
851
852
853 /**
854  * Call MHD IPv6 to process pending requests and then go back
855  * and schedule the next run.
856  * @param cls plugin as closure
857  * @param tc task context
858  */
859 static void
860 server_v6_run (void *cls,
861                            const struct GNUNET_SCHEDULER_TaskContext *tc)
862 {
863   struct Plugin *plugin = cls;
864   GNUNET_assert (cls != NULL);
865
866   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
867
868   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
869     return;
870
871   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
872   if (plugin->server_v6 != NULL)
873     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, GNUNET_NO);
874 }
875
876 /**
877  * Function that queries MHD's select sets and
878  * starts the task waiting for them.
879  * @param plugin plugin
880  * @param daemon_handle the MHD daemon handle
881  * @return gnunet task identifier
882  */
883 static GNUNET_SCHEDULER_TaskIdentifier
884 server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, int now)
885 {
886   GNUNET_SCHEDULER_TaskIdentifier ret;
887   fd_set rs;
888   fd_set ws;
889   fd_set es;
890   struct GNUNET_NETWORK_FDSet *wrs;
891   struct GNUNET_NETWORK_FDSet *wws;
892   struct GNUNET_NETWORK_FDSet *wes;
893   int max;
894   unsigned long long timeout;
895   static unsigned long long last_timeout = 0;
896   int haveto;
897
898   struct GNUNET_TIME_Relative tv;
899
900   ret = GNUNET_SCHEDULER_NO_TASK;
901   FD_ZERO (&rs);
902   FD_ZERO (&ws);
903   FD_ZERO (&es);
904   wrs = GNUNET_NETWORK_fdset_create ();
905   wes = GNUNET_NETWORK_fdset_create ();
906   wws = GNUNET_NETWORK_fdset_create ();
907   max = -1;
908   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
909   haveto = MHD_get_timeout (daemon_handle, &timeout);
910   if (haveto == MHD_YES)
911     {
912     if (timeout != last_timeout)
913     {
914 #if VERBOSE_SERVER
915       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
916                        "SELECT Timeout changed from %llu to %llu\n",
917                        last_timeout, timeout);
918 #endif
919       last_timeout = timeout;
920     }
921     tv.rel_value = (uint64_t) timeout;
922     }
923   else
924     tv = GNUNET_TIME_UNIT_SECONDS;
925   /* Force immediate run, since we have outbound data to send */
926   if (now == GNUNET_YES)
927     tv = GNUNET_TIME_UNIT_MILLISECONDS;
928   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
929   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
930   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
931   if (daemon_handle == plugin->server_v4)
932   {
933     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
934     {
935       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
936       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
937     }
938
939     ret =
940         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
941                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
942                                      &server_v4_run, plugin);
943   }
944   if (daemon_handle == plugin->server_v6)
945   {
946     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
947     {
948       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
949       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
950     }
951
952     ret =
953         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
954                                      GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
955                                      &server_v6_run, plugin);
956   }
957   GNUNET_NETWORK_fdset_destroy (wrs);
958   GNUNET_NETWORK_fdset_destroy (wws);
959   GNUNET_NETWORK_fdset_destroy (wes);
960   return ret;
961 }
962
963 int
964 server_start (struct Plugin *plugin)
965 {
966   int res = GNUNET_OK;
967   unsigned int timeout;
968
969 #if BUILD_HTTPS
970   res = server_load_certificate (plugin);
971   if (res == GNUNET_SYSERR)
972   {
973     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
974         "Could not load or create server certificate! Loading plugin failed!\n");
975     return res;
976   }
977 #endif
978
979
980 #if MHD_VERSION >= 0x00090E00
981   timeout = GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT.rel_value / 1000;
982   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
983       "MHD can set timeout per connection! Default time out %u sec.\n", timeout);
984 #else
985   timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000;
986   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
987       "MHD cannot set timeout per connection! Default time out %u sec.\n", timeout);
988 #endif
989   plugin->server_v4 = NULL;
990   if (plugin->ipv4 == GNUNET_YES)
991   {
992     plugin->server_v4 = MHD_start_daemon (
993 #if VERBOSE_SERVER
994                                            MHD_USE_DEBUG |
995 #endif
996 #if BUILD_HTTPS
997                                            MHD_USE_SSL |
998 #endif
999                                            MHD_NO_FLAG, plugin->port,
1000                                            &server_accept_cb, plugin,
1001                                            &server_access_cb, plugin,
1002                                            MHD_OPTION_SOCK_ADDR,
1003                                            (struct sockaddr_in *)
1004                                            plugin->server_addr_v4,
1005                                            MHD_OPTION_CONNECTION_LIMIT,
1006                                            (unsigned int)
1007                                            plugin->max_connections,
1008 #if BUILD_HTTPS
1009                                            MHD_OPTION_HTTPS_PRIORITIES,
1010                                            plugin->crypto_init,
1011                                            MHD_OPTION_HTTPS_MEM_KEY,
1012                                            plugin->key,
1013                                            MHD_OPTION_HTTPS_MEM_CERT,
1014                                            plugin->cert,
1015 #endif
1016                                            MHD_OPTION_CONNECTION_TIMEOUT,
1017                                            timeout ,
1018                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1019                                            (size_t) (2 *
1020                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1021                                            MHD_OPTION_NOTIFY_COMPLETED,
1022                                            &server_disconnect_cb, plugin,
1023                                            MHD_OPTION_EXTERNAL_LOGGER,
1024                                            server_log, NULL, MHD_OPTION_END);
1025   }
1026   plugin->server_v6 = NULL;
1027   if (plugin->ipv6 == GNUNET_YES)
1028   {
1029     plugin->server_v6 = MHD_start_daemon (
1030 #if VERBOSE_SERVER
1031                                            MHD_USE_DEBUG |
1032 #endif
1033 #if BUILD_HTTPS
1034                                            MHD_USE_SSL |
1035 #endif
1036                                            MHD_USE_IPv6, plugin->port,
1037                                            &server_accept_cb, plugin,
1038                                            &server_access_cb, plugin,
1039                                            MHD_OPTION_SOCK_ADDR,
1040                                            (struct sockaddr_in6 *)
1041                                            plugin->server_addr_v6,
1042                                            MHD_OPTION_CONNECTION_LIMIT,
1043                                            (unsigned int)
1044                                            plugin->max_connections,
1045 #if BUILD_HTTPS
1046                                            MHD_OPTION_HTTPS_PRIORITIES,
1047                                            plugin->crypto_init,
1048                                            MHD_OPTION_HTTPS_MEM_KEY,
1049                                            plugin->key,
1050                                            MHD_OPTION_HTTPS_MEM_CERT,
1051                                            plugin->cert,
1052 #endif
1053                                            MHD_OPTION_CONNECTION_TIMEOUT,
1054                                            timeout,
1055                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1056                                            (size_t) (2 *
1057                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1058                                            MHD_OPTION_NOTIFY_COMPLETED,
1059                                            &server_disconnect_cb, plugin,
1060                                            MHD_OPTION_EXTERNAL_LOGGER,
1061                                            server_log, NULL, MHD_OPTION_END);
1062
1063   }
1064
1065   if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
1066   {
1067     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1068                      "Failed to start %s IPv4 server component on port %u\n", plugin->name,
1069                      plugin->port);
1070     return GNUNET_SYSERR;
1071   }
1072   if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
1073   {
1074     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1075                      "Failed to start %s IPv6 server component on port %u\n", plugin->name,
1076                      plugin->port);
1077     return GNUNET_SYSERR;
1078   }
1079
1080   server_reschedule (plugin, GNUNET_NO);
1081
1082 #if DEBUG_HTTP
1083   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1084                    "%s server component started on port %u\n", plugin->name,
1085                    plugin->port);
1086 #endif
1087   return res;
1088 }
1089
1090 void
1091 server_stop (struct Plugin *plugin)
1092 {
1093   struct Session *s = NULL;
1094   struct Session *t = NULL;
1095
1096   struct MHD_Daemon *server_v4_tmp = plugin->server_v4;
1097   plugin->server_v4 = NULL;
1098   struct MHD_Daemon *server_v6_tmp = plugin->server_v6;
1099   plugin->server_v6 = NULL;
1100
1101   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1102   {
1103     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1104     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1105   }
1106
1107   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1108   {
1109     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1110     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1111   }
1112
1113   if (server_v6_tmp != NULL)
1114   {
1115     MHD_stop_daemon (server_v4_tmp);
1116   }
1117   if (server_v6_tmp != NULL)
1118   {
1119     MHD_stop_daemon (server_v6_tmp);
1120   }
1121
1122   /* cleaning up semi-sessions never propagated */
1123   s = plugin->server_semi_head;
1124   while (s != NULL)
1125   {
1126     t = s->next;
1127     delete_session (s);
1128     s = t;
1129   }
1130
1131 #if BUILD_HTTPS
1132   GNUNET_free_non_null (plugin->crypto_init);
1133   GNUNET_free_non_null (plugin->cert);
1134   GNUNET_free_non_null (plugin->key);
1135 #endif
1136
1137 #if DEBUG_HTTP
1138   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1139                    "%s server component stopped\n", plugin->name);
1140 #endif
1141 }
1142
1143
1144
1145 /* end of plugin_transport_http.c */