]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Rewrite HTTP server so it can defer replies
authorAndreas Öman <andreas@lonelycoder.com>
Mon, 14 Apr 2008 18:59:31 +0000 (18:59 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Mon, 14 Apr 2008 18:59:31 +0000 (18:59 +0000)
ajaxui/ajaxui.c
ajaxui/ajaxui.h
ajaxui/ajaxui_channels.c
ajaxui/ajaxui_config.c
ajaxui/ajaxui_config_channels.c
ajaxui/ajaxui_config_dvb.c
ajaxui/ajaxui_config_transport.c
http.c
http.h
tcp.c

index 7265298683d4ce3f66d3de31b745ca7a9a76b331..0da4b569188fc3964eeb7b6a66b7cd0033e28ddd 100644 (file)
@@ -268,16 +268,15 @@ ajax_a_jsfunc(tcp_queue_t *tq, const char *innerhtml, const char *func,
  * Titlebar AJAX page
  */
 static int
-ajax_page_titlebar(http_connection_t *hc, const char *remain, void *opaque)
+ajax_page_titlebar(http_connection_t *hc, http_reply_t *hr, 
+                  const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
-  
   if(remain == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
-  ajax_menu_bar_from_array(&tq, "top", ajax_tabnames, AJAX_TABS, atoi(remain));
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  ajax_menu_bar_from_array(&hr->hr_tq, "top", 
+                          ajax_tabnames, AJAX_TABS, atoi(remain));
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -287,20 +286,18 @@ ajax_page_titlebar(http_connection_t *hc, const char *remain, void *opaque)
  * About
  */
 static int
-ajax_about_tab(http_connection_t *hc)
+ajax_about_tab(http_connection_t *hc, http_reply_t *hr)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   
-  tcp_init_queue(&tq, -1);
-
-  tcp_qprintf(&tq, "<center>");
-  tcp_qprintf(&tq, "<div style=\"padding: auto; width: 400px\">");
+  tcp_qprintf(tq, "<center>");
+  tcp_qprintf(tq, "<div style=\"padding: auto; width: 400px\">");
 
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, "About");
+  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, "About");
 
-  tcp_qprintf(&tq, "<div style=\"text-align: center\">");
+  tcp_qprintf(tq, "<div style=\"text-align: center\">");
 
-  tcp_qprintf(&tq, 
+  tcp_qprintf(tq, 
              "<p>HTS / Tvheadend</p>"
              "<p>(c) 2006-2008 Andreas \303\226man</p>"
              "<p>Latest release and information is available at:</p>"
@@ -317,12 +314,12 @@ ajax_about_tab(http_connection_t *hc)
              "<p><a href=\"http://www.ffmpeg.org/\">FFmpeg</a></p>"
              );
 
-  tcp_qprintf(&tq, "</div>");
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
-  tcp_qprintf(&tq, "</div>");
-  tcp_qprintf(&tq, "</center>");
+  tcp_qprintf(tq, "</div>");
+  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
+  tcp_qprintf(tq, "</div>");
+  tcp_qprintf(tq, "</center>");
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -334,7 +331,8 @@ ajax_about_tab(http_connection_t *hc)
  * Find the 'tab' id and continue with tab specific code
  */
 static int
-ajax_page_tab(http_connection_t *hc, const char *remain, void *opaque)
+ajax_page_tab(http_connection_t *hc, http_reply_t *hr, 
+             const char *remain, void *opaque)
 {
   int tab;
 
@@ -345,13 +343,13 @@ ajax_page_tab(http_connection_t *hc, const char *remain, void *opaque)
 
   switch(tab) {
   case AJAX_TAB_CHANNELS:
-    return ajax_channelgroup_tab(hc);
+    return ajax_channelgroup_tab(hc, hr);
 
   case AJAX_TAB_CONFIGURATION:
-    return ajax_config_tab(hc);
+    return ajax_config_tab(hc, hr);
 
   case AJAX_TAB_ABOUT:
-    return ajax_about_tab(hc);
+    return ajax_about_tab(hc, hr);
 
   default:
     return HTTP_STATUS_NOT_FOUND;
@@ -365,13 +363,12 @@ ajax_page_tab(http_connection_t *hc, const char *remain, void *opaque)
  * Root page
  */
 static int
-ajax_page_root(http_connection_t *hc, const char *remain, void *opaque)
+ajax_page_root(http_connection_t *hc, http_reply_t *hr, 
+              const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
 
-  tcp_init_queue(&tq, -1);
-
-  tcp_qprintf(&tq, 
+  tcp_qprintf(tq, 
              "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\r\n"
              "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
              /*
@@ -388,110 +385,50 @@ ajax_page_root(http_connection_t *hc, const char *remain, void *opaque)
              "content=\"text/html; charset=utf-8\">\r\n");
 
 
-  tcp_qprintf(&tq, 
+  tcp_qprintf(tq, 
              "<link href=\"/ajax/ajaxui.css\" rel=\"stylesheet\" "
              "type=\"text/css\">\r\n");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<script src=\"/ajax/prototype.js\" type=\"text/javascript\">"
              "</script>\r\n");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<script src=\"/ajax/effects.js\" type=\"text/javascript\">"
              "</script>\r\n");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<script src=\"/ajax/dragdrop.js\" type=\"text/javascript\">"
              "</script>\r\n");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<script src=\"/ajax/controls.js\" type=\"text/javascript\">"
              "</script>\r\n");
 
- tcp_qprintf(&tq,
+ tcp_qprintf(tq,
              "<script src=\"/ajax/tvheadend.js\" type=\"text/javascript\">"
              "</script>\r\n");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "</head>");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<body>");
 
 
-  ajax_box_begin(&tq, AJAX_BOX_FILLED, "topmenu", NULL, NULL);
-  ajax_box_end(&tq, AJAX_BOX_FILLED);
+  ajax_box_begin(tq, AJAX_BOX_FILLED, "topmenu", NULL, NULL);
+  ajax_box_end(tq, AJAX_BOX_FILLED);
 
-  tcp_qprintf(&tq, "<div id=\"topdeck\"></div>");
+  tcp_qprintf(tq, "<div id=\"topdeck\"></div>");
   
-  ajax_js(&tq, "switchtab('top', '0')");
-
-  tcp_qprintf(&tq, "</body></html>");
-
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
-  return 0;
-}
-
-#if 0
-void blah(void) {
-  tcp_qprintf(&tq,
-             "<ul id=\"list\">"
-             "<li id=\"item_1\">I'm number 1 <a href=\"/\">a link</a></li>"
-             "<li id=\"item_2\">");
-  ajax_box_top(&tq, NULL, NULL);
-  tcp_qprintf(&tq,
-             "I'm number 2");
-
-  ajax_box_bottom(&tq);
-
-  tcp_qprintf(&tq,
-             "</li>"
-             "<li id=\"item_3\">I'm number 3</li>"
-             "<li id=\"item_4\">I'm number 4</li>"
-             "</ul>"
-             "\r\n"
-             "<p id=\"list-info\"></p>\r\n");
-
-
-  tcp_qprintf(&tq,
-             "<script type=\"text/javascript\">\r\n"
-             "//<![CDATA[\r\n"
-             "Sortable.create(\"list\", {onUpdate:function(){new Ajax.Updater('list-info', '/ajax/order', {asynchronous:true, evalScripts:true, onComplete:function(request){new Effect.Highlight(\"list\",{});}, parameters:Sortable.serialize(\"list\")})}})\r\n"
-             "//]]>\r\n"
-             "</script>\r\n");
+  ajax_js(tq, "switchtab('top', '0')");
 
+  tcp_qprintf(tq, "</body></html>");
 
- tcp_qprintf(&tq,
-            "<div id=\"updateDiv\"></div>\r\n"
-            "<form id=\"testform\">\n"
-            "<input name=\"name\" type=\"text\"><br>\r\n"
-            "<textarea name=\"comment\" cols=\"20\" rows=\"3\">"
-            "</textarea><br>\r\n"
-            "<input name=\"sendbutton\" type=\"button\" value=\"Send\""
-            " onClick=\"new Ajax.Updater('updateDiv', '/ajax/formupdate', "
-            "{asynchronous:true, "
-            " parameters:Form.serialize($('testform'))})\">\r\n"
-            "</form>\r\n");
-
-
- for(i = 0; i < 40; i++) {
-   tcp_qprintf(&tq,
-              "<div style=\"float: left\" class=\"sidebox\">\r\n"
-              " <div class=\"boxhead\"><h2>Discovery</h2></div>\r\n"
-              "  <div class=\"boxbody\">"
-              "   <p>Idag e det bra saker pa TV</p>"
-              "   <p>Imorgon vet vi inte</p>"
-              "</div></div>");
- }
-
-  tcp_qprintf(&tq,
-             "</body></html>");
-
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
-#endif
 
 
 /**
index 42fe3c642bc18eaf718005ed164a55a6b901e372..61b4813d7b4faa3dff6eebf39c4bdfdfea75d309 100644 (file)
@@ -54,13 +54,13 @@ void ajax_menu_bar_from_array(tcp_queue_t *tq, const char *name,
 void ajax_a_jsfunc(tcp_queue_t *tq, const char *innerhtml, const char *func,
                   const char *trailer);
 
-int ajax_channelgroup_tab(http_connection_t *hc);
-int ajax_config_tab(http_connection_t *hc);
+int ajax_channelgroup_tab(http_connection_t *hc, http_reply_t *hr);
+int ajax_config_tab(http_connection_t *hc, http_reply_t *hr);
 
-int ajax_config_channels_tab(http_connection_t *hc);
+int ajax_config_channels_tab(http_connection_t *hc, http_reply_t *hr);
 void ajax_config_channels_init(void);
 
-int ajax_config_dvb_tab(http_connection_t *hc);
+int ajax_config_dvb_tab(http_connection_t *hc, http_reply_t *hr);
 void ajax_config_dvb_init(void);
 void ajax_config_transport_init(void);
 
index e968f73f5f7ef2a753c83c1cbd5a0356be6460f1..b37434f3ed904c693906e35a0e9a7b8ae0fe0680 100644 (file)
@@ -58,17 +58,16 @@ ajax_channelgroupmenu_content(tcp_queue_t *tq, int current)
  * Channelgroup menu bar
  */
 static int
-ajax_channelgroup_menu(http_connection_t *hc, const char *remain, void *opaque)
+ajax_channelgroup_menu(http_connection_t *hc, http_reply_t *hr, 
+                      const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   
   if(remain == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
-
-  ajax_channelgroupmenu_content(&tq, atoi(remain));
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  ajax_channelgroupmenu_content(tq, atoi(remain));
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -82,9 +81,10 @@ ajax_channelgroup_menu(http_connection_t *hc, const char *remain, void *opaque)
  * Group is given by 'tag' as an ASCII string in remain
  */
 int
-ajax_channel_tab(http_connection_t *hc, const char *remain, void *opaque)
+ajax_channel_tab(http_connection_t *hc, http_reply_t *hr,
+                const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  //  tcp_queue_t *tq = &hr->hr_tq;
   th_channel_t *ch;
   th_channel_group_t *tcg;
 
@@ -94,8 +94,6 @@ ajax_channel_tab(http_connection_t *hc, const char *remain, void *opaque)
   if((tcg = channel_group_by_tag(atoi(remain))) == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
-
   TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
     if(LIST_FIRST(&ch->ch_transports) == NULL)
       continue;
@@ -108,7 +106,7 @@ ajax_channel_tab(http_connection_t *hc, const char *remain, void *opaque)
 #endif
   }
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -121,23 +119,21 @@ ajax_channel_tab(http_connection_t *hc, const char *remain, void *opaque)
  * This is the top level menu for this c-file
  */
 int
-ajax_channelgroup_tab(http_connection_t *hc)
+ajax_channelgroup_tab(http_connection_t *hc, http_reply_t *hr)
 {
-  tcp_queue_t tq;
-
-  tcp_init_queue(&tq, -1);
+  tcp_queue_t *tq = &hr->hr_tq;
 
-  ajax_box_begin(&tq, AJAX_BOX_FILLED, "channelgroupmenu", NULL, NULL);
-  ajax_box_end(&tq, AJAX_BOX_FILLED);
+  ajax_box_begin(tq, AJAX_BOX_FILLED, "channelgroupmenu", NULL, NULL);
+  ajax_box_end(tq, AJAX_BOX_FILLED);
 
-  tcp_qprintf(&tq, "<div id=\"channelgroupdeck\"></div>");
+  tcp_qprintf(tq, "<div id=\"channelgroupdeck\"></div>");
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<script type=\"text/javascript\">"
              "switchtab('channelgroup', '0')"
              "</script>");
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
index 5806ad2776067afb35ef9c50b6a45319f79cfa5a..e6075a9df34b175d158b8edd0f94cbc7989a6fb0 100644 (file)
@@ -43,18 +43,18 @@ const char *ajax_config_tabnames[] = {
  * Titlebar AJAX page
  */
 static int
-ajax_config_menu(http_connection_t *hc, const char *remain, void *opaque)
+ajax_config_menu(http_connection_t *hc, http_reply_t *hr, 
+                const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   
   if(remain == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
-  ajax_menu_bar_from_array(&tq, "config", 
+  ajax_menu_bar_from_array(tq, "config", 
                           ajax_config_tabnames, AJAX_CONFIG_TABS,
                           atoi(remain));
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -64,7 +64,8 @@ ajax_config_menu(http_connection_t *hc, const char *remain, void *opaque)
  * Switch to different tabs
  */
 static int
-ajax_config_dispatch(http_connection_t *hc, const char *remain, void *opaque)
+ajax_config_dispatch(http_connection_t *hc, http_reply_t *hr,
+                    const char *remain, void *opaque)
 {
   int tab;
 
@@ -75,9 +76,9 @@ ajax_config_dispatch(http_connection_t *hc, const char *remain, void *opaque)
 
   switch(tab) {
   case AJAX_CONFIG_TAB_CHANNELS:
-    return ajax_config_channels_tab(hc);
+    return ajax_config_channels_tab(hc, hr);
   case AJAX_CONFIG_TAB_DVB:
-    return ajax_config_dvb_tab(hc);
+    return ajax_config_dvb_tab(hc, hr);
 
   default:
     return HTTP_STATUS_NOT_FOUND;
@@ -94,23 +95,21 @@ ajax_config_dispatch(http_connection_t *hc, const char *remain, void *opaque)
  * This is the top level menu for this c-file
  */
 int
-ajax_config_tab(http_connection_t *hc)
+ajax_config_tab(http_connection_t *hc, http_reply_t *hr)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
 
-  tcp_init_queue(&tq, -1);
+  ajax_box_begin(tq, AJAX_BOX_FILLED, "configmenu", NULL, NULL);
+  ajax_box_end(tq, AJAX_BOX_FILLED);
 
-  ajax_box_begin(&tq, AJAX_BOX_FILLED, "configmenu", NULL, NULL);
-  ajax_box_end(&tq, AJAX_BOX_FILLED);
+  tcp_qprintf(tq, "<div id=\"configdeck\"></div>");
 
-  tcp_qprintf(&tq, "<div id=\"configdeck\"></div>");
-
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<script type=\"text/javascript\">"
              "switchtab('config', '0')"
              "</script>");
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
index 799ac6e819e81ebd881a80cb9d19568937c0d4c3..7d8b8534e73d144f464d80609676b862ff871ba5 100644 (file)
@@ -71,15 +71,13 @@ ajax_chgroup_build(tcp_queue_t *tq, th_channel_group_t *tcg)
  * Update order of channel groups
  */
 static int
-ajax_chgroup_updateorder(http_connection_t *hc, const char *remain,
-                        void *opaque)
+ajax_chgroup_updateorder(http_connection_t *hc, http_reply_t *hr, 
+                        const char *remain, void *opaque)
 {
   th_channel_group_t *tcg;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   http_arg_t *ra;
 
-  tcp_init_queue(&tq, -1);
-
   TAILQ_FOREACH(ra, &hc->hc_req_args, link) {
     if(strcmp(ra->key, "channelgrouplist[]") ||
        (tcg = channel_group_by_tag(atoi(ra->val))) == NULL)
@@ -91,9 +89,9 @@ ajax_chgroup_updateorder(http_connection_t *hc, const char *remain,
 
   channel_group_settings_write();
 
-  tcp_qprintf(&tq, "<span id=\"updatedok\">Updated on server</span>");
-  ajax_js(&tq, "Effect.Fade('updatedok')");
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  tcp_qprintf(tq, "<span id=\"updatedok\">Updated on server</span>");
+  ajax_js(tq, "Effect.Fade('updatedok')");
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -103,14 +101,13 @@ ajax_chgroup_updateorder(http_connection_t *hc, const char *remain,
  * Add a new channel group
  */
 static int
-ajax_chgroup_add(http_connection_t *hc, const char *remain, void *opaque)
+ajax_chgroup_add(http_connection_t *hc, http_reply_t *hr, 
+                const char *remain, void *opaque)
 {
   th_channel_group_t *tcg;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   const char *name;
   
-  tcp_init_queue(&tq, -1);
-
   if((name = http_arg_get(&hc->hc_req_args, "name")) != NULL) {
 
     TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link)
@@ -120,13 +117,13 @@ ajax_chgroup_add(http_connection_t *hc, const char *remain, void *opaque)
     if(tcg == NULL) {
       tcg = channel_group_find(name, 1);
 
-      ajax_chgroup_build(&tq, tcg);
+      ajax_chgroup_build(tq, tcg);
 
       /* We must recreate the Sortable object */
 
-      ajax_js(&tq, "Sortable.destroy(\"channelgrouplist\")");
+      ajax_js(tq, "Sortable.destroy(\"channelgrouplist\")");
 
-      ajax_js(&tq, "Sortable.create(\"channelgrouplist\", "
+      ajax_js(tq, "Sortable.create(\"channelgrouplist\", "
              "{onUpdate:function(){updatelistonserver("
              "'channelgrouplist', "
              "'/ajax/chgroup_updateorder', "
@@ -134,7 +131,7 @@ ajax_chgroup_add(http_connection_t *hc, const char *remain, void *opaque)
              ")}});");
     }
   }
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -144,10 +141,11 @@ ajax_chgroup_add(http_connection_t *hc, const char *remain, void *opaque)
  * Delete a channel group
  */
 static int
-ajax_chgroup_del(http_connection_t *hc, const char *remain, void *opaque)
+ajax_chgroup_del(http_connection_t *hc, http_reply_t *hr, 
+                const char *remain, void *opaque)
 {
   th_channel_group_t *tcg;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   const char *id;
   
   if((id = http_arg_get(&hc->hc_req_args, "id")) == NULL)
@@ -156,12 +154,10 @@ ajax_chgroup_del(http_connection_t *hc, const char *remain, void *opaque)
   if((tcg = channel_group_by_tag(atoi(id))) == NULL)
     return HTTP_STATUS_BAD_REQUEST;
 
-  tcp_init_queue(&tq, -1);
-  tcp_qprintf(&tq, "$('chgrp_%d').remove();", tcg->tcg_tag);
-  http_output_queue(hc, &tq, "text/javascript; charset=UTF-8", 0);
+  tcp_qprintf(tq, "$('chgrp_%d').remove();", tcg->tcg_tag);
+  http_output(hc, hr, "text/javascript; charset=UTF-8", NULL, 0);
 
   channel_group_destroy(tcg);
-
   return 0;
 }
 
@@ -171,32 +167,30 @@ ajax_chgroup_del(http_connection_t *hc, const char *remain, void *opaque)
  * Channel group & channel configuration
  */
 int
-ajax_config_channels_tab(http_connection_t *hc)
+ajax_config_channels_tab(http_connection_t *hc, http_reply_t *hr)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_channel_group_t *tcg;
 
-  tcp_init_queue(&tq, -1);
-  
-  tcp_qprintf(&tq, "<div style=\"float: left; width: 40%\">");
+  tcp_qprintf(tq, "<div style=\"float: left; width: 40%\">");
 
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, "channelgroups",
+  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, "channelgroups",
                 NULL, "Channel groups");
 
-  tcp_qprintf(&tq, "<div style=\"height:15px; text-align:center\" "
+  tcp_qprintf(tq, "<div style=\"height:15px; text-align:center\" "
              "id=\"list-info\"></div>");
    
-  tcp_qprintf(&tq, "<ul id=\"channelgrouplist\" class=\"draglist\">");
+  tcp_qprintf(tq, "<ul id=\"channelgrouplist\" class=\"draglist\">");
 
   TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
     if(tcg->tcg_hidden)
       continue;
-    ajax_chgroup_build(&tq, tcg);
+    ajax_chgroup_build(tq, tcg);
   }
 
-  tcp_qprintf(&tq, "</ul>");
+  tcp_qprintf(tq, "</ul>");
  
-  ajax_js(&tq, "Sortable.create(\"channelgrouplist\", "
+  ajax_js(tq, "Sortable.create(\"channelgrouplist\", "
          "{onUpdate:function(){updatelistonserver("
          "'channelgrouplist', "
          "'/ajax/chgroup_updateorder', "
@@ -206,9 +200,9 @@ ajax_config_channels_tab(http_connection_t *hc)
   /**
    * Add new group
    */
-  ajax_box_begin(&tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
+  ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<div style=\"height: 20px\">"
              "<div style=\"float: left\">"
              "<input class=\"textinput\" type=\"text\" id=\"newchgrp\">"
@@ -220,21 +214,18 @@ ajax_config_channels_tab(http_connection_t *hc)
              "Add</a></div>"
              "</div>");
     
-  ajax_box_end(&tq, AJAX_BOX_BORDER);
+  ajax_box_end(tq, AJAX_BOX_BORDER);
     
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
-  tcp_qprintf(&tq, "</div>");
+  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
+  tcp_qprintf(tq, "</div>");
 
-  tcp_qprintf(&tq, 
+  tcp_qprintf(tq, 
              "<div id=\"groupeditortab\" "
              "style=\"height: 600px; overflow: auto; "
              "float: left; width: 60%\">");
 
-
-  tcp_qprintf(&tq, "</div>");
-
-
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  tcp_qprintf(tq, "</div>");
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -242,46 +233,45 @@ ajax_config_channels_tab(http_connection_t *hc)
  * Display all channels within the group
  */
 static int
-ajax_chgroup_editor(http_connection_t *hc, const char *remain, void *opaque)
+ajax_chgroup_editor(http_connection_t *hc, http_reply_t *hr,
+                   const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_channel_t *ch;
   th_channel_group_t *tcg;
 
   if(remain == NULL || (tcg = channel_group_by_tag(atoi(remain))) == NULL)
     return HTTP_STATUS_BAD_REQUEST;
 
-  tcp_init_queue(&tq, -1);
-
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, tcg->tcg_name);
+  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, tcg->tcg_name);
 
-  tcp_qprintf(&tq, "<ul id=\"channellist\" class=\"draglist\">");
+  tcp_qprintf(tq, "<ul id=\"channellist\" class=\"draglist\">");
 
   TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
-    tcp_qprintf(&tq, "<li id=\"ch_%d\">", ch->ch_tag);
+    tcp_qprintf(tq, "<li id=\"ch_%d\">", ch->ch_tag);
   
-    ajax_box_begin(&tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
+    ajax_box_begin(tq, AJAX_BOX_BORDER, NULL, NULL, NULL);
   
-    tcp_qprintf(&tq, "<div style=\"overflow: auto; width: 100%\">");
+    tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
   
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div style=\"float: left; width: 100%\">"
                "<a href=\"javascript:void(0)\" >"
                "%s</a>"
                "</div>",
                ch->ch_name);
 
-    tcp_qprintf(&tq, "</div>");
-    ajax_box_end(&tq, AJAX_BOX_BORDER);
-    tcp_qprintf(&tq, "</li>");
+    tcp_qprintf(tq, "</div>");
+    ajax_box_end(tq, AJAX_BOX_BORDER);
+    tcp_qprintf(tq, "</li>");
   }
 
-  tcp_qprintf(&tq, "</ul>");
+  tcp_qprintf(tq, "</ul>");
 
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
 
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
index 3c8a8413d1b7a69574e7a9d3eeb77a3f301b3f1a..0d73dbaf6069a9f31b0e80f8c85c8d9e9479879b 100644 (file)
@@ -68,38 +68,37 @@ tdmi_displayname(th_dvb_mux_instance_t *tdmi, char *buf, size_t len)
  * Adapter summary
  */
 static int
-ajax_adaptersummary(http_connection_t *hc, const char *remain, void *opaque)
+ajax_adaptersummary(http_connection_t *hc, http_reply_t *hr, 
+                   const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_dvb_adapter_t *tda;
   char dispname[20];
 
   if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
-
   snprintf(dispname, sizeof(dispname), "%s", tda->tda_displayname);
   strcpy(dispname + sizeof(dispname) - 4, "...");
 
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, dispname);
+  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, dispname);
 
-  tcp_qprintf(&tq, "<div class=\"infoprefix\">Status:</div>"
+  tcp_qprintf(tq, "<div class=\"infoprefix\">Status:</div>"
              "<div>%s</div>",
              val2str(tda->tda_state, adapterstatus) ?: "invalid");
-  tcp_qprintf(&tq, "<div class=\"infoprefix\">Type:</div>"
+  tcp_qprintf(tq, "<div class=\"infoprefix\">Type:</div>"
              "<div>%s</div>", 
              dvb_adaptertype_to_str(tda->tda_fe_info->type));
  
-  tcp_qprintf(&tq, "<div style=\"text-align: center\">"
+  tcp_qprintf(tq, "<div style=\"text-align: center\">"
              "<a href=\"javascript:void(0);\" "
              "onClick=\"new Ajax.Updater('dvbadaptereditor', "
              "'/ajax/dvbadaptereditor/%s', "
              "{method: 'get', evalScripts: true})\""
              "\">Edit</a></div>", tda->tda_identifier);
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -108,29 +107,27 @@ ajax_adaptersummary(http_connection_t *hc, const char *remain, void *opaque)
  * DVB configuration
  */
 int
-ajax_config_dvb_tab(http_connection_t *hc)
+ajax_config_dvb_tab(http_connection_t *hc, http_reply_t *hr)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_dvb_adapter_t *tda;
 
-  tcp_init_queue(&tq, -1);
-
-  tcp_qprintf(&tq, "<div style=\"overflow: auto; width: 100%\">");
+  tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
 
   LIST_FOREACH(tda, &dvb_adapters, tda_global_link) {
 
-    tcp_qprintf(&tq, "<div id=\"summary_%s\" "
+    tcp_qprintf(tq, "<div id=\"summary_%s\" "
                "style=\"float:left; width: 250px\"></div>",
                tda->tda_identifier);
 
-    ajax_js(&tq, "new Ajax.Updater('summary_%s', "
+    ajax_js(tq, "new Ajax.Updater('summary_%s', "
            "'/ajax/dvbadaptersummary/%s', {method: 'get'})",
            tda->tda_identifier, tda->tda_identifier);
 
   }
-  tcp_qprintf(&tq, "</div>");
-  tcp_qprintf(&tq, "<div id=\"dvbadaptereditor\"></div>");
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  tcp_qprintf(tq, "</div>");
+  tcp_qprintf(tq, "<div id=\"dvbadaptereditor\"></div>");
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -159,28 +156,27 @@ dvb_make_add_link(tcp_queue_t *tq, th_dvb_adapter_t *tda, const char *result)
  * DVB adapter editor pane
  */
 static int
-ajax_adaptereditor(http_connection_t *hc, const char *remain, void *opaque)
+ajax_adaptereditor(http_connection_t *hc, http_reply_t *hr, 
+                  const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_dvb_adapter_t *tda;
   float a, b, c;
 
   if(remain == NULL || (tda = dvb_adapter_find_by_identifier(remain)) == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
+  ajax_box_begin(tq, AJAX_BOX_FILLED, NULL, NULL, NULL);
 
-  ajax_box_begin(&tq, AJAX_BOX_FILLED, NULL, NULL, NULL);
-
-  tcp_qprintf(&tq, 
+  tcp_qprintf(tq, 
              "<div style=\"text-align: center; font-weight: bold\">%s</div>",
              tda->tda_displayname);
   
-  ajax_box_end(&tq, AJAX_BOX_FILLED);
+  ajax_box_end(tq, AJAX_BOX_FILLED);
 
   /* Type */
 
-  tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Model:</div>"
+  tcp_qprintf(tq, "<div class=\"infoprefixwide\">Model:</div>"
              "<div>%s (%s)</div>", 
              tda->tda_fe_info->name,
              dvb_adaptertype_to_str(tda->tda_fe_info->type));
@@ -200,13 +196,13 @@ ajax_adaptereditor(http_connection_t *hc, const char *remain, void *opaque)
     break;
   }
 
-  tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Freq. Range:</div>"
+  tcp_qprintf(tq, "<div class=\"infoprefixwide\">Freq. Range:</div>"
              "<div>%.2f - %.2f kHz, in steps of %.2f kHz</div>",
              a, b, c);
 
 
   if(tda->tda_fe_info->symbol_rate_min) {
-    tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Symbolrate:</div>"
+    tcp_qprintf(tq, "<div class=\"infoprefixwide\">Symbolrate:</div>"
                "<div>%d - %d BAUD</div>",
                tda->tda_fe_info->symbol_rate_min,
                tda->tda_fe_info->symbol_rate_max);
@@ -216,34 +212,34 @@ ajax_adaptereditor(http_connection_t *hc, const char *remain, void *opaque)
   /* Capabilities */
 
 
-  //  tcp_qprintf(&tq, "<div class=\"infoprefixwide\">Capabilities:</div>");
+  //  tcp_qprintf(tq, "<div class=\"infoprefixwide\">Capabilities:</div>");
 
-  tcp_qprintf(&tq, "<div style=\"float: left; width:45%\">");
+  tcp_qprintf(tq, "<div style=\"float: left; width:45%\">");
 
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, "Multiplexes");
+  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, "Multiplexes");
 
-  tcp_qprintf(&tq, "<div id=\"dvbmuxlist%s\"></div>",
+  tcp_qprintf(tq, "<div id=\"dvbmuxlist%s\"></div>",
              tda->tda_identifier);
 
-  ajax_js(&tq, 
+  ajax_js(tq, 
          "new Ajax.Updater('dvbmuxlist%s', "
          "'/ajax/dvbadaptermuxlist/%s', {method: 'get'}) ",
          tda->tda_identifier, tda->tda_identifier);
 
-  tcp_qprintf(&tq, "<hr><div id=\"addmux\">");
-  dvb_make_add_link(&tq, tda, NULL);
-  tcp_qprintf(&tq, "</div>");
+  tcp_qprintf(tq, "<hr><div id=\"addmux\">");
+  dvb_make_add_link(tq, tda, NULL);
+  tcp_qprintf(tq, "</div>");
 
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
-  tcp_qprintf(&tq, "</div>");
+  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
+  tcp_qprintf(tq, "</div>");
 
   /* Div for displaying services */
 
-  tcp_qprintf(&tq, "<div id=\"servicepane\" "
+  tcp_qprintf(tq, "<div id=\"servicepane\" "
              "style=\"float: left; width:55%\">");
-  tcp_qprintf(&tq, "</div>");
+  tcp_qprintf(tq, "</div>");
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -252,9 +248,10 @@ ajax_adaptereditor(http_connection_t *hc, const char *remain, void *opaque)
  * DVB adapter add mux
  */
 static int
-ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
+ajax_adapteraddmux(http_connection_t *hc, http_reply_t *hr, 
+                  const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_dvb_adapter_t *tda;
   int caps;
   int fetype;
@@ -266,13 +263,11 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
   caps   = tda->tda_fe_info->caps;
   fetype = tda->tda_fe_info->type;
 
-  tcp_init_queue(&tq, -1);
-
-  tcp_qprintf(&tq, "<div style=\"text-align: center; font-weight: bold\">"
+  tcp_qprintf(tq, "<div style=\"text-align: center; font-weight: bold\">"
              "Add new %s mux</div>",
              dvb_adaptertype_to_str(tda->tda_fe_info->type));
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<div class=\"infoprefixwidefat\">Frequency (%s):</div>"
              "<div>"
              "<input class=\"textinput\" type=\"text\" id=\"freq\">"
@@ -287,7 +282,7 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
 
   if(fetype == FE_QAM || fetype == FE_QPSK) {
  
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Symbolrate:</div>"
                "<div>"
                "<input class=\"textinput\" type=\"text\" id=\"symrate\">"
@@ -300,15 +295,15 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
   /* Bandwidth */
 
   if(fetype == FE_OFDM) {
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Bandwidth:</div>"
                "<div><select id=\"bw\" class=\"textinput\">");
 
-    add_option(&tq, caps & FE_CAN_BANDWIDTH_AUTO, "AUTO");
-    add_option(&tq, 1                           , "8MHz");
-    add_option(&tq, 1                           , "7MHz");
-    add_option(&tq, 1                           , "6MHz");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_BANDWIDTH_AUTO, "AUTO");
+    add_option(tq, 1                           , "8MHz");
+    add_option(tq, 1                           , "7MHz");
+    add_option(tq, 1                           , "6MHz");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", bw: $F('bw')");
@@ -318,19 +313,19 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
   /* Constellation */
 
   if(fetype == FE_QAM || fetype == FE_OFDM) {
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Constellation:</div>"
                "<div><select id=\"const\" class=\"textinput\">");
 
-    add_option(&tq, caps & FE_CAN_QAM_AUTO,  "AUTO");
-    add_option(&tq, caps & FE_CAN_QPSK,      "QPSK");
-    add_option(&tq, caps & FE_CAN_QAM_16,    "QAM16");
-    add_option(&tq, caps & FE_CAN_QAM_32,    "QAM32");
-    add_option(&tq, caps & FE_CAN_QAM_64,    "QAM64");
-    add_option(&tq, caps & FE_CAN_QAM_128,   "QAM128");
-    add_option(&tq, caps & FE_CAN_QAM_256,   "QAM256");
+    add_option(tq, caps & FE_CAN_QAM_AUTO,  "AUTO");
+    add_option(tq, caps & FE_CAN_QPSK,      "QPSK");
+    add_option(tq, caps & FE_CAN_QAM_16,    "QAM16");
+    add_option(tq, caps & FE_CAN_QAM_32,    "QAM32");
+    add_option(tq, caps & FE_CAN_QAM_64,    "QAM64");
+    add_option(tq, caps & FE_CAN_QAM_128,   "QAM128");
+    add_option(tq, caps & FE_CAN_QAM_256,   "QAM256");
 
-    tcp_qprintf(&tq, "</select></div>");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", const: $F('const')");
@@ -340,33 +335,33 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
   /* FEC */
 
   if(fetype == FE_QAM || fetype == FE_QPSK) {
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">FEC:</div>"
                "<div><select id=\"fec\" class=\"textinput\">");
 
-    add_option(&tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
-    add_option(&tq, caps & FE_CAN_FEC_1_2,   "1/2");
-    add_option(&tq, caps & FE_CAN_FEC_2_3,   "2/3");
-    add_option(&tq, caps & FE_CAN_FEC_3_4,   "3/4");
-    add_option(&tq, caps & FE_CAN_FEC_4_5,   "4/5");
-    add_option(&tq, caps & FE_CAN_FEC_5_6,   "5/6");
-    add_option(&tq, caps & FE_CAN_FEC_6_7,   "6/7");
-    add_option(&tq, caps & FE_CAN_FEC_7_8,   "7/8");
-    add_option(&tq, caps & FE_CAN_FEC_8_9,   "8/9");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
+    add_option(tq, caps & FE_CAN_FEC_1_2,   "1/2");
+    add_option(tq, caps & FE_CAN_FEC_2_3,   "2/3");
+    add_option(tq, caps & FE_CAN_FEC_3_4,   "3/4");
+    add_option(tq, caps & FE_CAN_FEC_4_5,   "4/5");
+    add_option(tq, caps & FE_CAN_FEC_5_6,   "5/6");
+    add_option(tq, caps & FE_CAN_FEC_6_7,   "6/7");
+    add_option(tq, caps & FE_CAN_FEC_7_8,   "7/8");
+    add_option(tq, caps & FE_CAN_FEC_8_9,   "8/9");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", fec: $F('fec')");
   }
 
   if(fetype == FE_QPSK) {
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Polarisation:</div>"
                "<div><select id=\"pol\" class=\"textinput\">");
 
-    add_option(&tq, 1,  "Vertical");
-    add_option(&tq, 1,  "Horizontal");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, 1,  "Vertical");
+    add_option(tq, 1,  "Horizontal");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", pol: $F('pol')");
@@ -376,44 +371,44 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
 
 
   if(fetype == FE_OFDM) {
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Transmission mode:</div>"
                "<div><select id=\"tmode\" class=\"textinput\">");
     
-    add_option(&tq, caps & FE_CAN_TRANSMISSION_MODE_AUTO, "AUTO");
-    add_option(&tq, 1                                   , "2k");
-    add_option(&tq, 1                                   , "8k");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_TRANSMISSION_MODE_AUTO, "AUTO");
+    add_option(tq, 1                                   , "2k");
+    add_option(tq, 1                                   , "8k");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", tmode: $F('tmode')");
 
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Guard interval:</div>"
                "<div><select id=\"guard\" class=\"textinput\">");
     
-    add_option(&tq, caps & FE_CAN_GUARD_INTERVAL_AUTO, "AUTO");
-    add_option(&tq, 1                                , "1/32");
-    add_option(&tq, 1                                , "1/16");
-    add_option(&tq, 1                                , "1/8");
-    add_option(&tq, 1                                , "1/4");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_GUARD_INTERVAL_AUTO, "AUTO");
+    add_option(tq, 1                                , "1/32");
+    add_option(tq, 1                                , "1/16");
+    add_option(tq, 1                                , "1/8");
+    add_option(tq, 1                                , "1/4");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", guard: $F('guard')");
 
 
 
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">Hierarchy:</div>"
                "<div><select id=\"hier\" class=\"textinput\">");
     
-    add_option(&tq, caps & FE_CAN_HIERARCHY_AUTO, "AUTO");
-    add_option(&tq, 1                           , "1");
-    add_option(&tq, 1                           , "2");
-    add_option(&tq, 1                           , "4");
-    add_option(&tq, 1                           , "NONE");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_HIERARCHY_AUTO, "AUTO");
+    add_option(tq, 1                           , "1");
+    add_option(tq, 1                           , "2");
+    add_option(tq, 1                           , "4");
+    add_option(tq, 1                           , "NONE");
+    tcp_qprintf(tq, "</select></div>");
 
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
@@ -421,45 +416,45 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
 
 
 
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">FEC Hi:</div>"
                "<div><select id=\"fechi\" class=\"textinput\">");
     
-    add_option(&tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
-    add_option(&tq, caps & FE_CAN_FEC_1_2,   "1/2");
-    add_option(&tq, caps & FE_CAN_FEC_2_3,   "2/3");
-    add_option(&tq, caps & FE_CAN_FEC_3_4,   "3/4");
-    add_option(&tq, caps & FE_CAN_FEC_4_5,   "4/5");
-    add_option(&tq, caps & FE_CAN_FEC_5_6,   "5/6");
-    add_option(&tq, caps & FE_CAN_FEC_6_7,   "6/7");
-    add_option(&tq, caps & FE_CAN_FEC_7_8,   "7/8");
-    add_option(&tq, caps & FE_CAN_FEC_8_9,   "8/9");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
+    add_option(tq, caps & FE_CAN_FEC_1_2,   "1/2");
+    add_option(tq, caps & FE_CAN_FEC_2_3,   "2/3");
+    add_option(tq, caps & FE_CAN_FEC_3_4,   "3/4");
+    add_option(tq, caps & FE_CAN_FEC_4_5,   "4/5");
+    add_option(tq, caps & FE_CAN_FEC_5_6,   "5/6");
+    add_option(tq, caps & FE_CAN_FEC_6_7,   "6/7");
+    add_option(tq, caps & FE_CAN_FEC_7_8,   "7/8");
+    add_option(tq, caps & FE_CAN_FEC_8_9,   "8/9");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", fechi: $F('fechi')");
 
 
-    tcp_qprintf(&tq,
+    tcp_qprintf(tq,
                "<div class=\"infoprefixwidefat\">FEC Low:</div>"
                "<div><select id=\"feclo\" class=\"textinput\">");
     
-    add_option(&tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
-    add_option(&tq, caps & FE_CAN_FEC_1_2,   "1/2");
-    add_option(&tq, caps & FE_CAN_FEC_2_3,   "2/3");
-    add_option(&tq, caps & FE_CAN_FEC_3_4,   "3/4");
-    add_option(&tq, caps & FE_CAN_FEC_4_5,   "4/5");
-    add_option(&tq, caps & FE_CAN_FEC_5_6,   "5/6");
-    add_option(&tq, caps & FE_CAN_FEC_6_7,   "6/7");
-    add_option(&tq, caps & FE_CAN_FEC_7_8,   "7/8");
-    add_option(&tq, caps & FE_CAN_FEC_8_9,   "8/9");
-    tcp_qprintf(&tq, "</select></div>");
+    add_option(tq, caps & FE_CAN_FEC_AUTO,  "AUTO");
+    add_option(tq, caps & FE_CAN_FEC_1_2,   "1/2");
+    add_option(tq, caps & FE_CAN_FEC_2_3,   "2/3");
+    add_option(tq, caps & FE_CAN_FEC_3_4,   "3/4");
+    add_option(tq, caps & FE_CAN_FEC_4_5,   "4/5");
+    add_option(tq, caps & FE_CAN_FEC_5_6,   "5/6");
+    add_option(tq, caps & FE_CAN_FEC_6_7,   "6/7");
+    add_option(tq, caps & FE_CAN_FEC_7_8,   "7/8");
+    add_option(tq, caps & FE_CAN_FEC_8_9,   "8/9");
+    tcp_qprintf(tq, "</select></div>");
 
     snprintf(params + strlen(params), sizeof(params) - strlen(params), 
             ", feclo: $F('feclo')");
   }
 
-  tcp_qprintf(&tq,
+  tcp_qprintf(tq,
              "<div style=\"text-align: center\">"
              "<input type=\"button\" value=\"Create\" "
              "onClick=\"new Ajax.Updater('addmux', "
@@ -468,7 +463,7 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
              "\">"
              "</div>", tda->tda_identifier, params);
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -476,13 +471,12 @@ ajax_adapteraddmux(http_connection_t *hc, const char *remain, void *opaque)
  *
  */
 static int
-ajax_adaptercreatemux_fail(http_connection_t *hc, th_dvb_adapter_t *tda, 
-                          const char *errmsg)
+ajax_adaptercreatemux_fail(http_connection_t *hc, http_reply_t *hr,
+                          th_dvb_adapter_t *tda, const char *errmsg)
 {
-  tcp_queue_t tq;
-  tcp_init_queue(&tq, -1);
-  dvb_make_add_link(&tq, tda, errmsg);
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  tcp_queue_t *tq = &hr->hr_tq;
+  dvb_make_add_link(tq, tda, errmsg);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -490,9 +484,10 @@ ajax_adaptercreatemux_fail(http_connection_t *hc, th_dvb_adapter_t *tda,
  * DVB adapter create mux (come here on POST after addmux query)
  */
 static int
-ajax_adaptercreatemux(http_connection_t *hc, const char *remain, void *opaque)
+ajax_adaptercreatemux(http_connection_t *hc, http_reply_t *hr, 
+                     const char *remain, void *opaque)
 {
-  tcp_queue_t tq;
+  tcp_queue_t *tq;
   th_dvb_adapter_t *tda;
   const char *v;
 
@@ -514,17 +509,17 @@ ajax_adaptercreatemux(http_connection_t *hc, const char *remain, void *opaque)
                         http_arg_get(&hc->hc_req_args, "port"), 1);
 
   if(v != NULL)
-    return ajax_adaptercreatemux_fail(hc, tda, v);
+    return ajax_adaptercreatemux_fail(hc, hr, tda, v);
 
-  tcp_init_queue(&tq, -1);
-  dvb_make_add_link(&tq, tda, "Successfully created");
+  tq = &hr->hr_tq;
+  dvb_make_add_link(tq, tda, "Successfully created");
 
-  ajax_js(&tq, 
+  ajax_js(tq, 
          "new Ajax.Updater('dvbmuxlist%s', "
          "'/ajax/dvbadaptermuxlist/%s', {method: 'get'}) ",
          tda->tda_identifier, tda->tda_identifier);
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -533,10 +528,11 @@ ajax_adaptercreatemux(http_connection_t *hc, const char *remain, void *opaque)
  * Return a list of all muxes on the given adapter
  */
 static int
-ajax_adaptermuxlist(http_connection_t *hc, const char *remain, void *opaque)
+ajax_adaptermuxlist(http_connection_t *hc, http_reply_t *hr,
+                   const char *remain, void *opaque)
 {
   th_dvb_mux_instance_t *tdmi;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   th_dvb_adapter_t *tda;
   char buf[50], buf2[500], buf3[20];
   const char *txt;
@@ -555,32 +551,30 @@ ajax_adaptermuxlist(http_connection_t *hc, const char *remain, void *opaque)
 
   fetype = tda->tda_fe_info->type;
 
-  tcp_init_queue(&tq, -1);
-
   /* List of muxes */
   
   LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
     nmuxes++;
   
-  ajax_table_header(hc, &tq,
+  ajax_table_header(hc, tq,
                    (const char *[])
                    {"Freq", "Status", "State", "Name", "Services", NULL},
                    (int[]){3,3,2,4,2},
                    nmuxes > displines,
                    csize);
 
-  tcp_qprintf(&tq, "<hr>");
+  tcp_qprintf(tq, "<hr>");
 
   v = displines;
   if(nmuxes < displines)
     v = nmuxes;
 
-  tcp_qprintf(&tq, "<div id=\"dvbmuxlist%s\" "
+  tcp_qprintf(tq, "<div id=\"dvbmuxlist%s\" "
              "style=\"height: %dpx; overflow: auto\" class=\"normallist\">",
              tda->tda_identifier, v * 14);
 
   if(nmuxes == 0) {
-    tcp_qprintf(&tq, "<div style=\"text-align: center\">"
+    tcp_qprintf(tq, "<div style=\"text-align: center\">"
                "No muxes configured</div>");
   } else LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
 
@@ -620,12 +614,12 @@ ajax_adaptermuxlist(http_connection_t *hc, const char *remain, void *opaque)
     cells[4] = buf3;
     cells[5] = NULL;
 
-    ajax_table_row(&tq, cells, csize, &o);
+    ajax_table_row(tq, cells, csize, &o);
 
   }
-  tcp_qprintf(&tq, "</div>");
+  tcp_qprintf(tq, "</div>");
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
@@ -642,10 +636,11 @@ dvbsvccmp(th_transport_t *a, th_transport_t *b)
  * Display detailes about a mux
  */
 static int
-ajax_dvbmuxeditor(http_connection_t *hc, const char *remain, void *opaque)
+ajax_dvbmuxeditor(http_connection_t *hc, http_reply_t *hr, 
+                 const char *remain, void *opaque)
 {
   th_dvb_mux_instance_t *tdmi;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   char buf[1000];
   th_transport_t *t;
   struct th_transport_list head;
@@ -654,8 +649,6 @@ ajax_dvbmuxeditor(http_connection_t *hc, const char *remain, void *opaque)
   if(remain == NULL || (tdmi = dvb_mux_find_by_identifier(remain)) == NULL)
     return HTTP_STATUS_NOT_FOUND;
 
-  tcp_init_queue(&tq, -1);
-
   tdmi_displayname(tdmi, buf, sizeof(buf));
 
   LIST_INIT(&head);
@@ -667,11 +660,11 @@ ajax_dvbmuxeditor(http_connection_t *hc, const char *remain, void *opaque)
     }
   }
 
-  ajax_box_begin(&tq, AJAX_BOX_SIDEBOX, NULL, NULL, buf);
-  ajax_transport_build_list(hc, &tq, &head, n);
-  ajax_box_end(&tq, AJAX_BOX_SIDEBOX);
+  ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, buf);
+  ajax_transport_build_list(hc, tq, &head, n);
+  ajax_box_end(tq, AJAX_BOX_SIDEBOX);
 
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   return 0;
 }
 
index 33d165af6d21092b9cb1c9aee9278ab766c734e4..aae34d97c8d9fdbd491bfdd07f3b43c816a024d0 100644 (file)
@@ -282,12 +282,12 @@ ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq,
  *  Rename of unmapped channel
  */
 static int
-ajax_transport_rename_channel(http_connection_t *hc, 
+ajax_transport_rename_channel(http_connection_t *hc, http_reply_t *hr,
                              const char *remain, void *opaque)
 {
   th_transport_t *t;
   const char *newname, *v;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   char buf[1000];
 
   if(remain == NULL || (t = transport_find_by_identifier(remain)) == NULL)
@@ -299,8 +299,6 @@ ajax_transport_rename_channel(http_connection_t *hc,
   free((void *)t->tht_channelname);
   t->tht_channelname = strdup(newname);
 
-  tcp_init_queue(&tq, -1);
-
   v = newname;
 
   snprintf(buf, sizeof(buf), 
@@ -308,9 +306,9 @@ ajax_transport_rename_channel(http_connection_t *hc,
           "'/ajax/transport_rename_channel/%s', '%s')",
           t->tht_identifier, t->tht_identifier, v);
   
-  ajax_a_jsfunc(&tq, v, buf, "");
+  ajax_a_jsfunc(tq, v, buf, "");
       
-  http_output_queue(hc, &tq, "text/html; charset=UTF-8", 0);
+  http_output_html(hc, hr);
   t->tht_config_change(t);
   return 0;
 }
@@ -361,10 +359,11 @@ dvb_unmap_channel(th_transport_t *t, tcp_queue_t *tq)
  *
  */
 int
-ajax_transport_op(http_connection_t *hc, const char *remain, void *opaque)
+ajax_transport_op(http_connection_t *hc, http_reply_t *hr, 
+                 const char *remain, void *opaque)
 {
   th_transport_t *t;
-  tcp_queue_t tq;
+  tcp_queue_t *tq = &hr->hr_tq;
   const char *op;
 
   if(remain == NULL || (t = transport_find_by_identifier(remain)) == NULL)
@@ -373,20 +372,18 @@ ajax_transport_op(http_connection_t *hc, const char *remain, void *opaque)
   if((op = http_arg_get(&hc->hc_req_args, "action")) == NULL)
     return HTTP_STATUS_BAD_REQUEST;
 
-  tcp_init_queue(&tq, -1);
-
   if(!strcmp(op, "toggle")) {
     if(t->tht_channel)
-      dvb_unmap_channel(t, &tq);
+      dvb_unmap_channel(t, tq);
     else
-      dvb_map_channel(t, &tq);
+      dvb_map_channel(t, tq);
   } else if(!strcmp(op, "map") && t->tht_channel == NULL) {
-    dvb_map_channel(t, &tq);
+    dvb_map_channel(t, tq);
   } else if(!strcmp(op, "unmap") && t->tht_channel != NULL) {
-    dvb_unmap_channel(t, &tq);
+    dvb_unmap_channel(t, tq);
   }
 
-  http_output_queue(hc, &tq, "text/javascript; charset=UTF-8", 0);
+  http_output(hc, hr, "text/javascript; charset=UTF8", NULL, 0);
 
   t->tht_config_change(t);
   return 0;
diff --git a/http.c b/http.c
index 4b1df7133c14679a6afe2dc0f43bbcc99c2e8849..6704c5dab86df8199675bf7b4605ffe24d5ef8be 100644 (file)
--- a/http.c
+++ b/http.c
@@ -149,218 +149,269 @@ static const char *cachemonths[12] = {
 };
 
 /**
- * If current version mandates it, send a HTTP reply header back
+ *
  */
 static void
-http_output_reply_header(http_connection_t *hc, int rc, int maxage)
+http_destroy_reply(http_connection_t *hc, http_reply_t *hr)
+{
+  if(hr->hr_destroy != NULL)
+    hr->hr_destroy(hr, hr->hr_opaque);
+
+  TAILQ_REMOVE(&hc->hc_replies, hr, hr_link);
+  free(hr->hr_location);
+  tcp_flush_queue(&hr->hr_tq);
+  free(hr);
+}
+
+/**
+ * Transmit a HTTP reply
+ *
+ * Return non-zero if we should disconnect (no more keep-alive)
+ */
+static int
+http_send_reply(http_connection_t *hc, http_reply_t *hr)
 {
   struct tm tm0, *tm;
   time_t t;
+  tcp_queue_t *tq = &hr->hr_tq;
+  int r;
 
-  if(hc->hc_version < HTTP_VERSION_1_0)
-    return;
-  
-  http_printf(hc, "%s %d %s\r\n", val2str(hc->hc_version, HTTP_versiontab),
-             rc, http_rc2str(rc));
+  if(hr->hr_version > HTTP_VERSION_1_0) {
+    http_printf(hc, "%s %d %s\r\n", val2str(hr->hr_version, HTTP_versiontab),
+               hr->hr_rc, http_rc2str(hr->hr_rc));
 
-  if(maxage == 0) {
-    http_printf(hc, "Cache-Control: no-cache\r\n");
-  } else {
-    t = dispatch_clock;
+    http_printf(hc, "Server: HTS/tvheadend\r\n");
 
-    tm = gmtime_r(&t, &tm0);
-    http_printf(hc, 
-               "Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
-               cachedays[tm->tm_wday], tm->tm_year + 1900,
-               cachemonths[tm->tm_mon], tm->tm_mday,
-               tm->tm_hour, tm->tm_min, tm->tm_sec);
+    if(hr->hr_maxage == 0) {
+      http_printf(hc, "Cache-Control: no-cache\r\n");
+    } else {
+      t = dispatch_clock;
+      
+      tm = gmtime_r(&t, &tm0);
+      http_printf(hc, 
+                 "Last-Modified: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
+                 cachedays[tm->tm_wday],       tm->tm_year + 1900,
+                 cachemonths[tm->tm_mon], tm->tm_mday,
+                 tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+      t += hr->hr_maxage;
+
+      tm = gmtime_r(&t, &tm0);
+      http_printf(hc, 
+                 "Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
+                 cachedays[tm->tm_wday],       tm->tm_year + 1900,
+                 cachemonths[tm->tm_mon], tm->tm_mday,
+                 tm->tm_hour, tm->tm_min, tm->tm_sec);
+      
+      http_printf(hc, "Cache-Control: max-age=%d\r\n", hr->hr_maxage);
+    }
 
-    t += maxage;
+    http_printf(hc, "Connection: %s\r\n", 
+               hr->hr_keep_alive ? "Keep-Alive" : "Close");
 
-    tm = gmtime_r(&t, &tm0);
-    http_printf(hc, 
-               "Expires: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
-               cachedays[tm->tm_wday], tm->tm_year + 1900,
-               cachemonths[tm->tm_mon], tm->tm_mday,
-               tm->tm_hour, tm->tm_min, tm->tm_sec);
+    if(hr->hr_rc == HTTP_STATUS_UNAUTHORIZED)
+      http_printf(hc, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n");
+
+    if(hr->hr_encoding != NULL)
+      http_printf(hc, "Content-Encoding: %s\r\n", hr->hr_encoding);
 
-    http_printf(hc, "Cache-Control: max-age=%d\r\n", maxage);
+    if(hr->hr_location != NULL)
+      http_printf(hc, "Location: %s\r\n", hr->hr_location);
+
+    http_printf(hc, 
+               "Content-Type: %s\r\n"
+               "Content-Length: %d\r\n"
+               "\r\n",
+               hr->hr_content, tq->tq_depth);
   }
+  tcp_output_queue(&hc->hc_tcp_session, NULL, tq);
 
-  http_printf(hc, "Server: HTS/tvheadend\r\n");
-  http_printf(hc, "Connection: %s\r\n", 
-             hc->hc_keep_alive ? "Keep-Alive" : "Close");
+  r = !hr->hr_keep_alive;
+
+  http_destroy_reply(hc, hr);
+  return r;
 }
 
 
 /**
- * Send HTTP error back
+ * Send HTTP replies
+ *
+ * Return non-zero if we should disconnect
  */
-void
-http_error(http_connection_t *hc, int error)
+static int
+http_xmit_queue(http_connection_t *hc)
 {
-  char ret[300];
-  const char *errtxt = http_rc2str(error);
+  http_reply_t *hr;
 
-  http_output_reply_header(hc, error, 0);
+  while((hr = TAILQ_FIRST(&hc->hc_replies)) != NULL) {
+    if(hr->hr_destroy != NULL)
+      break; /* Pending reply, cannot send this yet */
+    if(http_send_reply(hc, hr))
+      return 1;
+  }
+  return 0;
+}
 
-  snprintf(ret, sizeof(ret),
-          "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-          "<HTML><HEAD>\r\n"
-          "<TITLE>%d %s</TITLE>\r\n"
-          "</HEAD><BODY>\r\n"
-          "<H1>%d %s</H1>\r\n"
-          "</BODY></HTML>\r\n",
-          error, errtxt,
-          error, errtxt);
 
-  if(hc->hc_version >= HTTP_VERSION_1_0) {
-    if(error == HTTP_STATUS_UNAUTHORIZED)
-      http_printf(hc, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n");
 
-    http_printf(hc, "Content-Type: text/html\r\n");
-    http_printf(hc, "Content-Length: %d\r\n", strlen(ret));
-    http_printf(hc, "\r\n");
-  }
-  http_printf(hc, "%s", ret);
+/**
+ * Send HTTP error back
+ */
+void
+http_error(http_connection_t *hc, http_reply_t *hr, int error)
+{
+  const char *errtxt = http_rc2str(error);
+  tcp_queue_t *tq = &hr->hr_tq;
+
+  tcp_flush_queue(tq);
+
+  tcp_qprintf(tq,
+             "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+             "<HTML><HEAD>\r\n"
+             "<TITLE>%d %s</TITLE>\r\n"
+             "</HEAD><BODY>\r\n"
+             "<H1>%d %s</H1>\r\n"
+             "</BODY></HTML>\r\n",
+             error, errtxt,  error, errtxt);
+
+  hr->hr_destroy = NULL;
+  hr->hr_rc = error;
+  hr->hr_content = "text/html";
 }
 
 /**
- * Send an HTTP OK and post data from a tcp queue
+ * Send an HTTP OK
  */
 void
-http_output_queue(http_connection_t *hc, tcp_queue_t *tq, const char *content,
-                 int maxage)
+http_output(http_connection_t *hc, http_reply_t *hr, const char *content,
+           const char *encoding, int maxage)
 {
-  http_output_reply_header(hc, 200, maxage);
-
-  if(hc->hc_version >= HTTP_VERSION_1_0) {
-    http_printf(hc, 
-               "Content-Type: %s\r\n"
-               "Content-Length: %d\r\n"
-               "\r\n",
-               content, tq->tq_depth);
-  }
-
-  tcp_output_queue(&hc->hc_tcp_session, NULL, tq);
+  hr->hr_encoding = encoding;
+  hr->hr_content = content;
+  hr->hr_maxage = maxage;
 }
 
 /**
- * Send an HTTP OK and post data from a tcp queue
+ * Send an HTTP OK, simple version for text/html
  */
-static void
-http_output_queue_encoding(http_connection_t *hc, tcp_queue_t *tq,
-                          const char *content, int maxage,
-                          const char *encoding)
+void
+http_output_html(http_connection_t *hc, http_reply_t *hr)
 {
-  http_output_reply_header(hc, 200, maxage);
+  hr->hr_content = "text/html; charset=UTF-8";
+}
 
-  if(hc->hc_version >= HTTP_VERSION_1_0) {
-    http_printf(hc, 
-               "Content-Encoding: %s\r\n"
-               "Content-Type: %s\r\n"
-               "Content-Length: %d\r\n"
-               "\r\n",
-               encoding, content, tq->tq_depth);
-  }
 
-  tcp_output_queue(&hc->hc_tcp_session, NULL, tq);
+
+/**
+ * Send an HTTP REDIRECT
+ */
+void
+http_redirect(http_connection_t *hc, http_reply_t *hr, const char *location)
+{
+  tcp_queue_t *tq = &hr->hr_tq;
+
+  tcp_qprintf(tq,
+             "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+             "<HTML><HEAD>\r\n"
+             "<TITLE>Redirect</TITLE>\r\n"
+             "</HEAD><BODY>\r\n"
+             "Please follow <a href=\"%s\">%s</a>\r\n"
+             "</BODY></HTML>\r\n",
+             location, location);
+
+  hr->hr_location = strdup(location);
+  hr->hr_rc = 303;
+  hr->hr_content = "text/html";
 }
 
 
 /**
- * Send an HTTP REDIRECT
+ * Execute url callback
+ *
+ * Returns 1 if we should disconnect
+ * 
  */
-int
-http_redirect(http_connection_t *hc, const char *location)
+static int
+http_exec(http_connection_t *hc, http_path_t *hp, char *remain, int err)
 {
-  tcp_queue_t tq;
+  http_reply_t *hr = calloc(1, sizeof(http_reply_t));
 
-  http_output_reply_header(hc, 303, 0);
+  /* Insert reply in order */
+  TAILQ_INSERT_TAIL(&hc->hc_replies, hr, hr_link);
+  
+  tcp_init_queue(&hr->hr_tq, -1);
+  hr->hr_version    = hc->hc_version;
+  hr->hr_keep_alive = hc->hc_keep_alive;
 
-  if(hc->hc_version < HTTP_VERSION_1_0)
-    return -1;
+  if(!err)
+    err = hp->hp_callback(hc, hr, remain, hp->hp_opaque);
 
-  tcp_init_queue(&tq, -1);
+  if(err)
+    http_error(hc, hr, err);
 
-  tcp_qprintf(&tq, "Please follow <a href=\"%s\"\"></a>", location);
+  if(hr->hr_destroy != NULL)
+    return 0; /* New entry is delayed, do not transmit anything */
 
-  http_printf(hc,
-             "Location: %s\r\n"
-             "Content-Type: text/html\r\n"
-             "Content-Length: %d\r\n"
-             "\r\n", location, tq.tq_depth);
-  tcp_output_queue(&hc->hc_tcp_session, NULL, &tq);
-  return 0;
+  return http_xmit_queue(hc);
 }
 
 
+
 /**
  * HTTP GET
  */
-static void
+static int
 http_cmd_get(http_connection_t *hc)
 {
   http_path_t *hp;
   char *remain;
   char *args;
-  int err;
 
   hp = http_resolve(hc, &remain, &args);
-  if(hp == NULL) {
-    http_error(hc, HTTP_STATUS_NOT_FOUND);
-    return;
-  }
+  if(hp == NULL)
+    return http_exec(hc, NULL, NULL, HTTP_STATUS_NOT_FOUND);
 
   if(args != NULL)
     http_parse_get_args(hc, args);
 
-  err = hp->hp_callback(hc, remain, hp->hp_opaque);
-  if(err)
-    http_error(hc, err);
+  return http_exec(hc, hp, remain, 0);
 }
 
 
 /**
  * Check if a HTTP POST is fully received, and if so, continue processing
+ *
+ * Return non-zero if we should disconnect
  */
-static void
+static int
 http_post_check(http_connection_t *hc)
 {
   http_path_t *hp;
   char *remain, *args, *v, *argv[2];
-  int err, n;
+  int n;
 
   if(hc->hc_post_ptr != hc->hc_post_len)
-    return;
+    return 0;
 
   hc->hc_state = HTTP_CON_WAIT_REQUEST;
 
   /* Parse content-type */
   v = http_arg_get(&hc->hc_args, "Content-Type");
-  if(v == NULL) {
-    http_error(hc, HTTP_STATUS_BAD_REQUEST);
-    return;
-  }
+  if(v == NULL)
+    return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
 
   n = http_tokenize(v, argv, 2, ';');
-  if(n == 0) {
-    http_error(hc, HTTP_STATUS_BAD_REQUEST);
-    return;
-  }
+  if(n == 0)
+    return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
 
   if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
     http_parse_get_args(hc, hc->hc_post_data);
 
   hp = http_resolve(hc, &remain, &args);
-  if(hp == NULL) {
-    http_error(hc, HTTP_STATUS_NOT_FOUND);
-    return;
-  }
+  if(hp == NULL)
+    return http_exec(hc, NULL, NULL, HTTP_STATUS_NOT_FOUND);
 
-  err = hp->hp_callback(hc, remain, hp->hp_opaque);
-  if(err)
-    http_error(hc, err);
+  return http_exec(hc, hp, remain, 0);
 }
 
 
@@ -368,27 +419,23 @@ http_post_check(http_connection_t *hc)
 
 
 /**
- * HTTP POST
+ * Initial processing of HTTP POST
+ *
+ * Return non-zero if we should disconnect
  */
-static void
+static int
 http_cmd_post(http_connection_t *hc)
 {
   char *v;
 
   /* Set keep-alive status */
   v = http_arg_get(&hc->hc_args, "Content-Length");
-  if(v == NULL) {
-    /* No content length in POST, make us disconnect */
-    hc->hc_keep_alive = 0;
-    return;
-  }
+  if(v == NULL)
+    return 1; /* No content length in POST, make us disconnect */
   
   hc->hc_post_len = atoi(v);
-  if(hc->hc_post_len > 16 * 1024 * 1024) {
-    /* Bail out if POST data > 16 Mb */
-    hc->hc_keep_alive = 0;
-    return;
-  }
+  if(hc->hc_post_len > 16 * 1024 * 1024)
+    return 1; /* Bail out if POST data > 16 Mb */
 
   hc->hc_state = HTTP_CON_POST_DATA;
 
@@ -404,8 +451,7 @@ http_cmd_post(http_connection_t *hc)
 
   hc->hc_post_ptr = tcp_line_drain(&hc->hc_tcp_session, hc->hc_post_data,
                                   hc->hc_post_len);
-
-  http_post_check(hc);
+  return http_post_check(hc);
 }
 
 
@@ -426,25 +472,23 @@ http_consume_post_data(http_connection_t *hc)
   }
 
   hc->hc_post_ptr += r;
-  http_post_check(hc);
+  if(http_post_check(hc))
+    tcp_disconnect(tcp, 0);
 }
 
 /**
  * Process a HTTP request
  */
-static void
+static int
 http_process_request(http_connection_t *hc)
 {
   switch(hc->hc_cmd) {
   default:
-    http_error(hc, HTTP_STATUS_BAD_REQUEST);
-    break;
+    return http_exec(hc, NULL, NULL, HTTP_STATUS_BAD_REQUEST);
   case HTTP_CMD_GET:
-    http_cmd_get(hc); 
-    break;
+    return http_cmd_get(hc); 
   case HTTP_CMD_POST:
-    http_cmd_post(hc); 
-    break;
+    return http_cmd_post(hc); 
   }
 }
 
@@ -518,22 +562,22 @@ process_request(http_connection_t *hc)
   switch(hc->hc_version) {
   case RTSP_VERSION_1_0:
     rtsp_process_request(hc);
-    break;
+    return 0;
 
   case HTTP_VERSION_0_9:
   case HTTP_VERSION_1_0:
   case HTTP_VERSION_1_1:
-    http_process_request(hc);
-    break;
+    return http_process_request(hc) ? -1 : 0;
   }
-
-  return hc->hc_keep_alive == 0 ? -1 : 0;
+  return -1;
 }
 
 
 
 /*
  * HTTP connection state machine & parser
+ *
+ * If we return non zero we will disconnect
  */
 static int
 http_con_parse(void *aux, char *buf)
@@ -615,6 +659,11 @@ http_con_parse(void *aux, char *buf)
 static void
 http_disconnect(http_connection_t *hc)
 {
+  http_reply_t *hr;
+
+  while((hr = TAILQ_FIRST(&hc->hc_replies)) != NULL)
+    http_destroy_reply(hc, hr);
+
   free(hc->hc_post_data);
   free(hc->hc_username);
   free(hc->hc_password);
@@ -636,6 +685,7 @@ http_tcp_callback(tcpevent_t event, void *tcpsession)
 
   switch(event) {
   case TCP_CONNECT:
+    TAILQ_INIT(&hc->hc_replies);
     TAILQ_INIT(&hc->hc_args);
     TAILQ_INIT(&hc->hc_req_args);
     break;
@@ -865,14 +915,13 @@ typedef struct http_resource {
 } http_resource_t;
 
 static int
-deliver_resource(http_connection_t *hc, const char *remain, void *opaque)
+deliver_resource(http_connection_t *hc, http_reply_t *hr, 
+                const char *remain, void *opaque)
 {
   http_resource_t *hres = opaque;
-  tcp_queue_t tq;
 
-  tcp_init_queue(&tq, -1);
-  tcp_qput(&tq, hres->data, hres->len);
-  http_output_queue_encoding(hc, &tq, hres->content, 15, hres->encoding);
+  tcp_qput(&hr->hr_tq, hres->data, hres->len);
+  http_output(hc, hr, hres->content, hres->encoding, 15);
   return 0;
 }
 
diff --git a/http.h b/http.h
index f0f1b08042f4e528c20656c195e847b46a821a84..b8aaf7541851750d50ad29be9ec0c3e4e0a5db53 100644 (file)
--- a/http.h
+++ b/http.h
@@ -42,11 +42,37 @@ typedef struct http_arg {
 } http_arg_t;
 
 
+TAILQ_HEAD(http_reply_queue, http_reply);
+
+typedef struct http_reply {
+  TAILQ_ENTRY(http_reply) hr_link;
+
+  void *hr_opaque;
+
+  void (*hr_destroy)(struct http_reply *hr, void *opaque);
+
+  int hr_version; /* HTTP version */
+  int hr_keep_alive;
+  
+  char *hr_location;
+
+  int hr_rc;      /* Return code */
+  int hr_maxage;
+  const char *hr_encoding;
+  const char *hr_content;
+
+  tcp_queue_t hr_tq;
+
+} http_reply_t;
+
+
 typedef struct http_connection {
   tcp_session_t hc_tcp_session; /* Must be first */
   char *hc_url;
   int hc_keep_alive;
 
+  struct http_reply_queue hc_replies;
+
   struct http_arg_list hc_args;
 
   struct http_arg_list hc_req_args; /* Argumets from GET or POST request */
@@ -103,15 +129,18 @@ void http_arg_set(struct http_arg_list *list, char *key, char *val);
 
 int http_tokenize(char *buf, char **vec, int vecsize, int delimiter);
 
-void http_error(http_connection_t *hc, int error);
+void http_error(http_connection_t *hc, http_reply_t *hr, int error);
+
+void http_output(http_connection_t *hc, http_reply_t *hr,
+                const char *content, const char *encoding, int maxage);
 
-void http_output_queue(http_connection_t *hc, tcp_queue_t *tq,
-                      const char *content, int maxage);
+void http_output_html(http_connection_t *hc, http_reply_t *hr);
 
-int http_redirect(http_connection_t *hc, const char *location);
+void http_redirect(http_connection_t *hc, http_reply_t *hr, 
+                  const char *location);
 
-typedef int (http_callback_t)(http_connection_t *hc, const char *remain,
-                             void *opaque);
+typedef int (http_callback_t)(http_connection_t *hc, http_reply_t *hr, 
+                             const char *remain, void *opaque);
 
 typedef struct http_path {
   LIST_ENTRY(http_path) hp_link;
diff --git a/tcp.c b/tcp.c
index e7e9319fd55d3f804b2d1f1667a69d50143bf9c3..c77c808c238e4a630b2e5effc0cdfb5cadd400d1 100644 (file)
--- a/tcp.c
+++ b/tcp.c
@@ -351,7 +351,7 @@ tcp_output_queue(tcp_session_t *ses, tcp_queue_t *dst, tcp_queue_t *src)
     dst->tq_depth += s;
   }
 
-  if(!ses->tcp_blocked)
+  if(ses != NULL && !ses->tcp_blocked)
     tcp_transmit(ses);
 
   src->tq_depth = 0;