2 This file is part of PulseAudio.
4 Copyright 2005-2009 Lennart Poettering
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31 #include <pulse/util.h>
32 #include <pulse/xmalloc.h>
33 #include <pulse/timeval.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/ioline.h>
37 #include <pulsecore/thread-mq.h>
38 #include <pulsecore/macro.h>
39 #include <pulsecore/log.h>
40 #include <pulsecore/namereg.h>
41 #include <pulsecore/cli-text.h>
42 #include <pulsecore/shared.h>
43 #include <pulsecore/core-error.h>
44 #include <pulsecore/mime-type.h>
46 #include "protocol-http.h"
48 /* Don't allow more than this many concurrent connections */
49 #define MAX_CONNECTIONS 10
52 #define URL_CSS "/style"
53 #define URL_STATUS "/status"
54 #define URL_LISTEN "/listen"
55 #define URL_LISTEN_SOURCE "/listen/source/"
57 #define MIME_HTML "text/html; charset=utf-8"
58 #define MIME_TEXT "text/plain; charset=utf-8"
59 #define MIME_CSS "text/css"
61 #define HTML_HEADER(t) \
62 "<?xml version=\"1.0\"?>\n" \
63 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
64 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" \
66 " <title>"t"</title>\n" \
67 " <link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/>\n" \
75 #define RECORD_BUFFER_SECONDS (5)
76 #define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
90 pa_http_protocol *protocol;
93 pa_memblockq *output_memblockq;
94 pa_source_output *source_output;
102 struct pa_http_protocol {
106 pa_idxset *connections;
112 SOURCE_OUTPUT_MESSAGE_POST_DATA = PA_SOURCE_OUTPUT_MESSAGE_MAX
115 /* Called from main context */
116 static void connection_unlink(struct connection *c) {
119 if (c->source_output) {
120 pa_source_output_unlink(c->source_output);
121 c->source_output->userdata = NULL;
122 pa_source_output_unref(c->source_output);
126 pa_client_free(c->client);
131 pa_ioline_unref(c->line);
134 pa_iochannel_free(c->io);
136 if (c->output_memblockq)
137 pa_memblockq_free(c->output_memblockq);
139 pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
144 /* Called from main context */
145 static int do_write(struct connection *c) {
152 if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
155 pa_assert(chunk.memblock);
156 pa_assert(chunk.length > 0);
158 p = pa_memblock_acquire(chunk.memblock);
159 r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
160 pa_memblock_release(chunk.memblock);
162 pa_memblock_unref(chunk.memblock);
166 if (errno == EINTR || errno == EAGAIN)
169 pa_log("write(): %s", pa_cstrerror(errno));
173 pa_memblockq_drop(c->output_memblockq, (size_t) r);
178 /* Called from main context */
179 static void do_work(struct connection *c) {
182 if (pa_iochannel_is_hungup(c->io))
185 if (pa_iochannel_is_writable(c->io))
192 connection_unlink(c);
195 /* Called from thread context, except when it is not */
196 static int source_output_process_msg(pa_msgobject *m, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
197 pa_source_output *o = PA_SOURCE_OUTPUT(m);
198 struct connection *c;
200 pa_source_output_assert_ref(o);
202 if (!(c = o->userdata))
207 case SOURCE_OUTPUT_MESSAGE_POST_DATA:
208 /* While this function is usually called from IO thread
209 * context, this specific command is not! */
210 pa_memblockq_push_align(c->output_memblockq, chunk);
215 return pa_source_output_process_msg(m, code, userdata, offset, chunk);
221 /* Called from thread context */
222 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
223 struct connection *c;
225 pa_source_output_assert_ref(o);
226 pa_assert_se(c = o->userdata);
229 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(o), SOURCE_OUTPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
232 /* Called from main context */
233 static void source_output_kill_cb(pa_source_output *o) {
236 pa_source_output_assert_ref(o);
237 pa_assert_se(c = o->userdata);
239 connection_unlink(c);
242 /* Called from main context */
243 static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
246 pa_source_output_assert_ref(o);
247 pa_assert_se(c = o->userdata);
249 return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
252 /*** client callbacks ***/
253 static void client_kill_cb(pa_client *client) {
257 pa_assert_se(c = client->userdata);
259 connection_unlink(c);
262 /*** pa_iochannel callbacks ***/
263 static void io_callback(pa_iochannel*io, void *userdata) {
264 struct connection *c = userdata;
272 static char *escape_html(const char *t) {
276 sb = pa_strbuf_new();
278 for (e = p = t; *p; p++) {
280 if (*p == '>' || *p == '<' || *p == '&') {
283 pa_strbuf_putsn(sb, e, p-e);
288 pa_strbuf_puts(sb, ">");
290 pa_strbuf_puts(sb, "<");
292 pa_strbuf_puts(sb, "&");
297 pa_strbuf_putsn(sb, e, p-e);
299 return pa_strbuf_tostring_free(sb);
302 static void http_response(
303 struct connection *c,
314 s = pa_sprintf_malloc(
316 "Connection: close\n"
318 "Cache-Control: no-cache\n"
320 "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
321 "\n", code, msg, mime);
322 pa_ioline_puts(c->line, s);
326 static void html_response(
327 struct connection *c,
335 http_response(c, code, msg, MIME_HTML);
337 if (c->method == METHOD_HEAD) {
338 pa_ioline_defer_close(c->line);
345 s = pa_sprintf_malloc(
351 pa_ioline_puts(c->line, s);
354 pa_ioline_defer_close(c->line);
357 static void html_print_field(pa_ioline *line, const char *left, const char *right) {
358 char *eleft, *eright;
360 eleft = escape_html(left);
361 eright = escape_html(right);
363 pa_ioline_printf(line,
364 "<tr><td><b>%s</b></td>"
365 "<td>%s</td></tr>\n", eleft, eright);
371 static void handle_root(struct connection *c) {
376 http_response(c, 200, "OK", MIME_HTML);
378 if (c->method == METHOD_HEAD) {
379 pa_ioline_defer_close(c->line);
383 pa_ioline_puts(c->line,
384 HTML_HEADER(PACKAGE_NAME" "PACKAGE_VERSION)
385 "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
388 t = pa_get_user_name_malloc();
389 html_print_field(c->line, "User Name:", t);
392 t = pa_get_host_name_malloc();
393 html_print_field(c->line, "Host name:", t);
397 html_print_field(c->line, "Machine ID:", t);
400 t = pa_uname_string();
401 html_print_field(c->line, "System:", t);
404 t = pa_sprintf_malloc("%lu", (unsigned long) getpid());
405 html_print_field(c->line, "Process ID:", t);
408 pa_ioline_puts(c->line,
410 "<p><a href=\"" URL_STATUS "\">Show an extensive server status report</a></p>\n"
411 "<p><a href=\"" URL_LISTEN "\">Monitor sinks and sources</a></p>\n"
414 pa_ioline_defer_close(c->line);
417 static void handle_css(struct connection *c) {
420 http_response(c, 200, "OK", MIME_CSS);
422 if (c->method == METHOD_HEAD) {
423 pa_ioline_defer_close(c->line);
427 pa_ioline_puts(c->line,
428 "body { color: black; background-color: white; }\n"
429 "a:link, a:visited { color: #900000; }\n"
430 "div.news-date { font-size: 80%; font-style: italic; }\n"
431 "pre { background-color: #f0f0f0; padding: 0.4cm; }\n"
432 ".grey { color: #8f8f8f; font-size: 80%; }"
433 "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
434 "td { padding-left:10px; padding-right:10px; }\n");
436 pa_ioline_defer_close(c->line);
439 static void handle_status(struct connection *c) {
444 http_response(c, 200, "OK", MIME_TEXT);
446 if (c->method == METHOD_HEAD) {
447 pa_ioline_defer_close(c->line);
451 r = pa_full_status_string(c->protocol->core);
452 pa_ioline_puts(c->line, r);
455 pa_ioline_defer_close(c->line);
458 static void handle_listen(struct connection *c) {
463 http_response(c, 200, "OK", MIME_HTML);
465 pa_ioline_puts(c->line,
466 HTML_HEADER("Listen")
470 if (c->method == METHOD_HEAD) {
471 pa_ioline_defer_close(c->line);
475 PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) {
478 t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
479 m = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
481 pa_ioline_printf(c->line,
482 "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
483 sink->monitor_source->name, m, t);
489 pa_ioline_puts(c->line,
494 PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) {
497 if (source->monitor_of)
500 t = escape_html(pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
501 m = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
503 pa_ioline_printf(c->line,
504 "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
512 pa_ioline_puts(c->line,
516 pa_ioline_defer_close(c->line);
519 static void line_drain_callback(pa_ioline *l, void *userdata) {
520 struct connection *c;
523 pa_assert_se(c = userdata);
525 /* We don't need the line reader anymore, instead we need a real
526 * binary io channel */
527 pa_assert_se(c->io = pa_ioline_detach_iochannel(c->line));
528 pa_iochannel_set_callback(c->io, io_callback, c);
530 pa_iochannel_socket_set_sndbuf(c->io, pa_memblockq_get_length(c->output_memblockq));
532 pa_ioline_unref(c->line);
536 static void handle_listen_prefix(struct connection *c, const char *source_name) {
538 pa_source_output_new_data data;
545 pa_assert(source_name);
550 if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
551 html_response(c, 404, "Source not found", NULL);
555 ss = source->sample_spec;
556 cm = source->channel_map;
558 pa_sample_spec_mimefy(&ss, &cm);
560 pa_source_output_new_data_init(&data);
561 data.driver = __FILE__;
562 data.module = c->module;
563 data.client = c->client;
564 pa_source_output_new_data_set_source(&data, source, FALSE);
565 pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
566 pa_source_output_new_data_set_sample_spec(&data, &ss);
567 pa_source_output_new_data_set_channel_map(&data, &cm);
569 pa_source_output_new(&c->source_output, c->protocol->core, &data);
570 pa_source_output_new_data_done(&data);
572 if (!c->source_output) {
573 html_response(c, 403, "Cannot create source output", NULL);
577 c->source_output->parent.process_msg = source_output_process_msg;
578 c->source_output->push = source_output_push_cb;
579 c->source_output->kill = source_output_kill_cb;
580 c->source_output->get_latency = source_output_get_latency_cb;
581 c->source_output->userdata = c;
583 pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
585 l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
586 c->output_memblockq = pa_memblockq_new(
587 "http protocol connection output_memblockq",
597 pa_source_output_put(c->source_output);
599 t = pa_sample_spec_to_mime_type(&ss, &cm);
600 http_response(c, 200, "OK", t);
603 if(c->method == METHOD_HEAD) {
604 connection_unlink(c);
607 pa_ioline_set_callback(c->line, NULL, NULL);
609 if (pa_ioline_is_drained(c->line))
610 line_drain_callback(c->line, c);
612 pa_ioline_set_drain_callback(c->line, line_drain_callback, c);
615 static void handle_url(struct connection *c) {
618 pa_log_debug("Request for %s", c->url);
620 if (pa_streq(c->url, URL_ROOT))
622 else if (pa_streq(c->url, URL_CSS))
624 else if (pa_streq(c->url, URL_STATUS))
626 else if (pa_streq(c->url, URL_LISTEN))
628 else if (pa_startswith(c->url, URL_LISTEN_SOURCE))
629 handle_listen_prefix(c, c->url + sizeof(URL_LISTEN_SOURCE)-1);
631 html_response(c, 404, "Not Found", NULL);
634 static void line_callback(pa_ioline *line, const char *s, void *userdata) {
635 struct connection *c = userdata;
641 connection_unlink(c);
646 case STATE_REQUEST_LINE: {
647 if (pa_startswith(s, "GET ")) {
648 c->method = METHOD_GET;
650 } else if (pa_startswith(s, "HEAD ")) {
651 c->method = METHOD_HEAD;
657 c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
658 c->state = STATE_MIME_HEADER;
662 case STATE_MIME_HEADER: {
664 /* Ignore MIME headers */
665 if (strcspn(s, " \r\n") != 0)
669 c->state = STATE_DATA;
682 html_response(c, 500, "Internal Server Error", NULL);
685 void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
686 struct connection *c;
687 pa_client_new_data client_data;
694 if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
695 pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
696 pa_iochannel_free(io);
700 c = pa_xnew0(struct connection, 1);
702 c->state = STATE_REQUEST_LINE;
705 c->line = pa_ioline_new(io);
706 pa_ioline_set_callback(c->line, line_callback, c);
708 pa_client_new_data_init(&client_data);
709 client_data.module = c->module;
710 client_data.driver = __FILE__;
711 pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
712 pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "HTTP client (%s)", pname);
713 pa_proplist_sets(client_data.proplist, "http-protocol.peer", pname);
714 c->client = pa_client_new(p->core, &client_data);
715 pa_client_new_data_done(&client_data);
720 c->client->kill = client_kill_cb;
721 c->client->userdata = c;
723 pa_idxset_put(p->connections, c, NULL);
729 connection_unlink(c);
732 void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
733 struct connection *c;
739 PA_IDXSET_FOREACH(c, p->connections, idx)
741 connection_unlink(c);
744 static pa_http_protocol* http_protocol_new(pa_core *c) {
749 p = pa_xnew0(pa_http_protocol, 1);
752 p->connections = pa_idxset_new(NULL, NULL);
754 pa_assert_se(pa_shared_set(c, "http-protocol", p) >= 0);
759 pa_http_protocol* pa_http_protocol_get(pa_core *c) {
762 if ((p = pa_shared_get(c, "http-protocol")))
763 return pa_http_protocol_ref(p);
765 return http_protocol_new(c);
768 pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p) {
770 pa_assert(PA_REFCNT_VALUE(p) >= 1);
777 void pa_http_protocol_unref(pa_http_protocol *p) {
778 struct connection *c;
781 pa_assert(PA_REFCNT_VALUE(p) >= 1);
783 if (PA_REFCNT_DEC(p) > 0)
786 while ((c = pa_idxset_first(p->connections, NULL)))
787 connection_unlink(c);
789 pa_idxset_free(p->connections, NULL, NULL);
791 pa_strlist_free(p->servers);
793 pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
798 void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name) {
800 pa_assert(PA_REFCNT_VALUE(p) >= 1);
803 p->servers = pa_strlist_prepend(p->servers, name);
806 void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name) {
808 pa_assert(PA_REFCNT_VALUE(p) >= 1);
811 p->servers = pa_strlist_remove(p->servers, name);
814 pa_strlist *pa_http_protocol_servers(pa_http_protocol *p) {
816 pa_assert(PA_REFCNT_VALUE(p) >= 1);