core: call pa_sink_get_latency_within_thread() instead of going directly via process_...
[pulseaudio-mirror.git] / src / pulsecore / protocol-http.c
blob5220cc918543221bd653c8655ad91a1d99a1d95d
1 /***
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
19 USA.
20 ***/
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
31 #include <pulse/util.h>
32 #include <pulse/xmalloc.h>
33 #include <pulse/timeval.h>
35 #include <pulsecore/ioline.h>
36 #include <pulsecore/thread-mq.h>
37 #include <pulsecore/macro.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/namereg.h>
40 #include <pulsecore/cli-text.h>
41 #include <pulsecore/shared.h>
42 #include <pulsecore/core-error.h>
43 #include <pulsecore/mime-type.h>
45 #include "protocol-http.h"
47 /* Don't allow more than this many concurrent connections */
48 #define MAX_CONNECTIONS 10
50 #define URL_ROOT "/"
51 #define URL_CSS "/style"
52 #define URL_STATUS "/status"
53 #define URL_LISTEN "/listen"
54 #define URL_LISTEN_SOURCE "/listen/source/"
56 #define MIME_HTML "text/html; charset=utf-8"
57 #define MIME_TEXT "text/plain; charset=utf-8"
58 #define MIME_CSS "text/css"
60 #define HTML_HEADER(t) \
61 "<?xml version=\"1.0\"?>\n" \
62 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
63 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" \
64 " <head>\n" \
65 " <title>"t"</title>\n" \
66 " <link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/>\n" \
67 " </head>\n" \
68 " <body>\n"
70 #define HTML_FOOTER \
71 " </body>\n" \
72 "</html>\n"
74 #define RECORD_BUFFER_SECONDS (5)
75 #define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
77 enum state {
78 STATE_REQUEST_LINE,
79 STATE_MIME_HEADER,
80 STATE_DATA
83 struct connection {
84 pa_http_protocol *protocol;
85 pa_iochannel *io;
86 pa_ioline *line;
87 pa_memblockq *output_memblockq;
88 pa_source_output *source_output;
89 pa_client *client;
90 enum state state;
91 char *url;
92 pa_module *module;
95 struct pa_http_protocol {
96 PA_REFCNT_DECLARE;
98 pa_core *core;
99 pa_idxset *connections;
101 pa_strlist *servers;
104 enum {
105 SOURCE_OUTPUT_MESSAGE_POST_DATA = PA_SOURCE_OUTPUT_MESSAGE_MAX
108 /* Called from main context */
109 static void connection_unlink(struct connection *c) {
110 pa_assert(c);
112 if (c->source_output) {
113 pa_source_output_unlink(c->source_output);
114 c->source_output->userdata = NULL;
115 pa_source_output_unref(c->source_output);
118 if (c->client)
119 pa_client_free(c->client);
121 pa_xfree(c->url);
123 if (c->line)
124 pa_ioline_unref(c->line);
126 if (c->io)
127 pa_iochannel_free(c->io);
129 if (c->output_memblockq)
130 pa_memblockq_free(c->output_memblockq);
132 pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
134 pa_xfree(c);
137 /* Called from main context */
138 static int do_write(struct connection *c) {
139 pa_memchunk chunk;
140 ssize_t r;
141 void *p;
143 pa_assert(c);
145 if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
146 return 0;
148 pa_assert(chunk.memblock);
149 pa_assert(chunk.length > 0);
151 p = pa_memblock_acquire(chunk.memblock);
152 r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
153 pa_memblock_release(chunk.memblock);
155 pa_memblock_unref(chunk.memblock);
157 if (r < 0) {
159 if (errno == EINTR || errno == EAGAIN)
160 return 0;
162 pa_log("write(): %s", pa_cstrerror(errno));
163 return -1;
166 pa_memblockq_drop(c->output_memblockq, (size_t) r);
168 return 0;
171 /* Called from main context */
172 static void do_work(struct connection *c) {
173 pa_assert(c);
175 if (pa_iochannel_is_hungup(c->io))
176 goto fail;
178 if (pa_iochannel_is_writable(c->io))
179 if (do_write(c) < 0)
180 goto fail;
182 return;
184 fail:
185 connection_unlink(c);
188 /* Called from thread context, except when it is not */
189 static int source_output_process_msg(pa_msgobject *m, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
190 pa_source_output *o = PA_SOURCE_OUTPUT(m);
191 struct connection *c;
193 pa_source_output_assert_ref(o);
195 if (!(c = o->userdata))
196 return -1;
198 switch (code) {
200 case SOURCE_OUTPUT_MESSAGE_POST_DATA:
201 /* While this function is usually called from IO thread
202 * context, this specific command is not! */
203 pa_memblockq_push_align(c->output_memblockq, chunk);
204 do_work(c);
205 break;
207 default:
208 return pa_source_output_process_msg(m, code, userdata, offset, chunk);
211 return 0;
214 /* Called from thread context */
215 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
216 struct connection *c;
218 pa_source_output_assert_ref(o);
219 pa_assert_se(c = o->userdata);
220 pa_assert(chunk);
222 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(o), SOURCE_OUTPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
225 /* Called from main context */
226 static void source_output_kill_cb(pa_source_output *o) {
227 struct connection*c;
229 pa_source_output_assert_ref(o);
230 pa_assert_se(c = o->userdata);
232 connection_unlink(c);
235 /* Called from main context */
236 static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
237 struct connection*c;
239 pa_source_output_assert_ref(o);
240 pa_assert_se(c = o->userdata);
242 return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
245 /*** client callbacks ***/
246 static void client_kill_cb(pa_client *client) {
247 struct connection*c;
249 pa_assert(client);
250 pa_assert_se(c = client->userdata);
252 connection_unlink(c);
255 /*** pa_iochannel callbacks ***/
256 static void io_callback(pa_iochannel*io, void *userdata) {
257 struct connection *c = userdata;
259 pa_assert(c);
260 pa_assert(io);
262 do_work(c);
265 static char *escape_html(const char *t) {
266 pa_strbuf *sb;
267 const char *p, *e;
269 sb = pa_strbuf_new();
271 for (e = p = t; *p; p++) {
273 if (*p == '>' || *p == '<' || *p == '&') {
275 if (p > e) {
276 pa_strbuf_putsn(sb, e, p-e);
277 e = p + 1;
280 if (*p == '>')
281 pa_strbuf_puts(sb, "&gt;");
282 else if (*p == '<')
283 pa_strbuf_puts(sb, "&lt;");
284 else
285 pa_strbuf_puts(sb, "&amp;");
289 if (p > e)
290 pa_strbuf_putsn(sb, e, p-e);
292 return pa_strbuf_tostring_free(sb);
295 static void http_response(
296 struct connection *c,
297 int code,
298 const char *msg,
299 const char *mime) {
301 char *s;
303 pa_assert(c);
304 pa_assert(msg);
305 pa_assert(mime);
307 s = pa_sprintf_malloc(
308 "HTTP/1.0 %i %s\n"
309 "Connection: close\n"
310 "Content-Type: %s\n"
311 "Cache-Control: no-cache\n"
312 "Expires: 0\n"
313 "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
314 "\n", code, msg, mime);
315 pa_ioline_puts(c->line, s);
316 pa_xfree(s);
319 static void html_response(
320 struct connection *c,
321 int code,
322 const char *msg,
323 const char *text) {
325 char *s;
326 pa_assert(c);
328 http_response(c, code, msg, MIME_HTML);
330 if (!text)
331 text = msg;
333 s = pa_sprintf_malloc(
334 HTML_HEADER("%s")
335 "%s"
336 HTML_FOOTER,
337 text, text);
339 pa_ioline_puts(c->line, s);
340 pa_xfree(s);
342 pa_ioline_defer_close(c->line);
345 static void html_print_field(pa_ioline *line, const char *left, const char *right) {
346 char *eleft, *eright;
348 eleft = escape_html(left);
349 eright = escape_html(right);
351 pa_ioline_printf(line,
352 "<tr><td><b>%s</b></td>"
353 "<td>%s</td></tr>\n", eleft, eright);
355 pa_xfree(eleft);
356 pa_xfree(eright);
359 static void handle_root(struct connection *c) {
360 char *t;
362 pa_assert(c);
364 http_response(c, 200, "OK", MIME_HTML);
366 pa_ioline_puts(c->line,
367 HTML_HEADER(PACKAGE_NAME" "PACKAGE_VERSION)
368 "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
369 "<table>\n");
371 t = pa_get_user_name_malloc();
372 html_print_field(c->line, "User Name:", t);
373 pa_xfree(t);
375 t = pa_get_host_name_malloc();
376 html_print_field(c->line, "Host name:", t);
377 pa_xfree(t);
379 t = pa_machine_id();
380 html_print_field(c->line, "Machine ID:", t);
381 pa_xfree(t);
383 t = pa_uname_string();
384 html_print_field(c->line, "System:", t);
385 pa_xfree(t);
387 t = pa_sprintf_malloc("%lu", (unsigned long) getpid());
388 html_print_field(c->line, "Process ID:", t);
389 pa_xfree(t);
391 pa_ioline_puts(c->line,
392 "</table>\n"
393 "<p><a href=\"" URL_STATUS "\">Show an extensive server status report</a></p>\n"
394 "<p><a href=\"" URL_LISTEN "\">Monitor sinks and sources</a></p>\n"
395 HTML_FOOTER);
397 pa_ioline_defer_close(c->line);
400 static void handle_css(struct connection *c) {
401 pa_assert(c);
403 http_response(c, 200, "OK", MIME_CSS);
405 pa_ioline_puts(c->line,
406 "body { color: black; background-color: white; }\n"
407 "a:link, a:visited { color: #900000; }\n"
408 "div.news-date { font-size: 80%; font-style: italic; }\n"
409 "pre { background-color: #f0f0f0; padding: 0.4cm; }\n"
410 ".grey { color: #8f8f8f; font-size: 80%; }"
411 "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
412 "td { padding-left:10px; padding-right:10px; }\n");
414 pa_ioline_defer_close(c->line);
417 static void handle_status(struct connection *c) {
418 char *r;
420 pa_assert(c);
422 http_response(c, 200, "OK", MIME_TEXT);
423 r = pa_full_status_string(c->protocol->core);
424 pa_ioline_puts(c->line, r);
425 pa_xfree(r);
427 pa_ioline_defer_close(c->line);
430 static void handle_listen(struct connection *c) {
431 pa_source *source;
432 pa_sink *sink;
433 uint32_t idx;
435 http_response(c, 200, "OK", MIME_HTML);
437 pa_ioline_puts(c->line,
438 HTML_HEADER("Listen")
439 "<h2>Sinks</h2>\n"
440 "<p>\n");
442 PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) {
443 char *t, *m;
445 t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
446 m = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
448 pa_ioline_printf(c->line,
449 "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
450 sink->monitor_source->name, m, t);
452 pa_xfree(t);
453 pa_xfree(m);
456 pa_ioline_puts(c->line,
457 "</p>\n"
458 "<h2>Sources</h2>\n"
459 "<p>\n");
461 PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) {
462 char *t, *m;
464 if (source->monitor_of)
465 continue;
467 t = escape_html(pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
468 m = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
470 pa_ioline_printf(c->line,
471 "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
472 source->name, m, t);
474 pa_xfree(m);
475 pa_xfree(t);
479 pa_ioline_puts(c->line,
480 "</p>\n"
481 HTML_FOOTER);
483 pa_ioline_defer_close(c->line);
486 static void line_drain_callback(pa_ioline *l, void *userdata) {
487 struct connection *c;
489 pa_assert(l);
490 pa_assert_se(c = userdata);
492 /* We don't need the line reader anymore, instead we need a real
493 * binary io channel */
494 pa_assert_se(c->io = pa_ioline_detach_iochannel(c->line));
495 pa_iochannel_set_callback(c->io, io_callback, c);
497 pa_iochannel_socket_set_sndbuf(c->io, pa_memblockq_get_length(c->output_memblockq));
499 pa_ioline_unref(c->line);
500 c->line = NULL;
503 static void handle_listen_prefix(struct connection *c, const char *source_name) {
504 pa_source *source;
505 pa_source_output_new_data data;
506 pa_sample_spec ss;
507 pa_channel_map cm;
508 char *t;
509 size_t l;
511 pa_assert(c);
512 pa_assert(source_name);
514 pa_assert(c->line);
515 pa_assert(!c->io);
517 if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
518 html_response(c, 404, "Source not found", NULL);
519 return;
522 ss = source->sample_spec;
523 cm = source->channel_map;
525 pa_sample_spec_mimefy(&ss, &cm);
527 pa_source_output_new_data_init(&data);
528 data.driver = __FILE__;
529 data.module = c->module;
530 data.client = c->client;
531 data.source = source;
532 pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
533 pa_source_output_new_data_set_sample_spec(&data, &ss);
534 pa_source_output_new_data_set_channel_map(&data, &cm);
536 pa_source_output_new(&c->source_output, c->protocol->core, &data, 0);
537 pa_source_output_new_data_done(&data);
539 if (!c->source_output) {
540 html_response(c, 403, "Cannot create source output", NULL);
541 return;
544 c->source_output->parent.process_msg = source_output_process_msg;
545 c->source_output->push = source_output_push_cb;
546 c->source_output->kill = source_output_kill_cb;
547 c->source_output->get_latency = source_output_get_latency_cb;
548 c->source_output->userdata = c;
550 pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
552 l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
553 c->output_memblockq = pa_memblockq_new(
557 pa_frame_size(&ss),
561 NULL);
563 pa_source_output_put(c->source_output);
565 t = pa_sample_spec_to_mime_type(&ss, &cm);
566 http_response(c, 200, "OK", t);
567 pa_xfree(t);
569 pa_ioline_set_callback(c->line, NULL, NULL);
571 if (pa_ioline_is_drained(c->line))
572 line_drain_callback(c->line, c);
573 else
574 pa_ioline_set_drain_callback(c->line, line_drain_callback, c);
577 static void handle_url(struct connection *c) {
578 pa_assert(c);
580 pa_log_debug("Request for %s", c->url);
582 if (pa_streq(c->url, URL_ROOT))
583 handle_root(c);
584 else if (pa_streq(c->url, URL_CSS))
585 handle_css(c);
586 else if (pa_streq(c->url, URL_STATUS))
587 handle_status(c);
588 else if (pa_streq(c->url, URL_LISTEN))
589 handle_listen(c);
590 else if (pa_startswith(c->url, URL_LISTEN_SOURCE))
591 handle_listen_prefix(c, c->url + sizeof(URL_LISTEN_SOURCE)-1);
592 else
593 html_response(c, 404, "Not Found", NULL);
596 static void line_callback(pa_ioline *line, const char *s, void *userdata) {
597 struct connection *c = userdata;
598 pa_assert(line);
599 pa_assert(c);
601 if (!s) {
602 /* EOF */
603 connection_unlink(c);
604 return;
607 switch (c->state) {
608 case STATE_REQUEST_LINE: {
609 if (!pa_startswith(s, "GET "))
610 goto fail;
612 s +=4;
614 c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
615 c->state = STATE_MIME_HEADER;
616 break;
619 case STATE_MIME_HEADER: {
621 /* Ignore MIME headers */
622 if (strcspn(s, " \r\n") != 0)
623 break;
625 /* We're done */
626 c->state = STATE_DATA;
628 handle_url(c);
629 break;
632 default:
636 return;
638 fail:
639 html_response(c, 500, "Internal Server Error", NULL);
642 void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
643 struct connection *c;
644 pa_client_new_data client_data;
645 char pname[128];
647 pa_assert(p);
648 pa_assert(io);
649 pa_assert(m);
651 if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
652 pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
653 pa_iochannel_free(io);
654 return;
657 c = pa_xnew0(struct connection, 1);
658 c->protocol = p;
659 c->state = STATE_REQUEST_LINE;
660 c->module = m;
662 c->line = pa_ioline_new(io);
663 pa_ioline_set_callback(c->line, line_callback, c);
665 pa_client_new_data_init(&client_data);
666 client_data.module = c->module;
667 client_data.driver = __FILE__;
668 pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
669 pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "HTTP client (%s)", pname);
670 pa_proplist_sets(client_data.proplist, "http-protocol.peer", pname);
671 c->client = pa_client_new(p->core, &client_data);
672 pa_client_new_data_done(&client_data);
674 if (!c->client)
675 goto fail;
677 c->client->kill = client_kill_cb;
678 c->client->userdata = c;
680 pa_idxset_put(p->connections, c, NULL);
682 return;
684 fail:
685 if (c)
686 connection_unlink(c);
689 void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
690 struct connection *c;
691 uint32_t idx;
693 pa_assert(p);
694 pa_assert(m);
696 PA_IDXSET_FOREACH(c, p->connections, idx)
697 if (c->module == m)
698 connection_unlink(c);
701 static pa_http_protocol* http_protocol_new(pa_core *c) {
702 pa_http_protocol *p;
704 pa_assert(c);
706 p = pa_xnew0(pa_http_protocol, 1);
707 PA_REFCNT_INIT(p);
708 p->core = c;
709 p->connections = pa_idxset_new(NULL, NULL);
711 pa_assert_se(pa_shared_set(c, "http-protocol", p) >= 0);
713 return p;
716 pa_http_protocol* pa_http_protocol_get(pa_core *c) {
717 pa_http_protocol *p;
719 if ((p = pa_shared_get(c, "http-protocol")))
720 return pa_http_protocol_ref(p);
722 return http_protocol_new(c);
725 pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p) {
726 pa_assert(p);
727 pa_assert(PA_REFCNT_VALUE(p) >= 1);
729 PA_REFCNT_INC(p);
731 return p;
734 void pa_http_protocol_unref(pa_http_protocol *p) {
735 struct connection *c;
737 pa_assert(p);
738 pa_assert(PA_REFCNT_VALUE(p) >= 1);
740 if (PA_REFCNT_DEC(p) > 0)
741 return;
743 while ((c = pa_idxset_first(p->connections, NULL)))
744 connection_unlink(c);
746 pa_idxset_free(p->connections, NULL, NULL);
748 pa_strlist_free(p->servers);
750 pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
752 pa_xfree(p);
755 void pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name) {
756 pa_assert(p);
757 pa_assert(PA_REFCNT_VALUE(p) >= 1);
758 pa_assert(name);
760 p->servers = pa_strlist_prepend(p->servers, name);
763 void pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name) {
764 pa_assert(p);
765 pa_assert(PA_REFCNT_VALUE(p) >= 1);
766 pa_assert(name);
768 p->servers = pa_strlist_remove(p->servers, name);
771 pa_strlist *pa_http_protocol_servers(pa_http_protocol *p) {
772 pa_assert(p);
773 pa_assert(PA_REFCNT_VALUE(p) >= 1);
775 return p->servers;