comparison src/neon/neon.c @ 1719:29c35cb8873e

- Add neon HTTP transport plugin sources (for real)
author Ralf Ertzinger <ralf@skytale.net>
date Mon, 17 Sep 2007 21:46:53 +0200
parents
children 2e33cfa6a872
comparison
equal deleted inserted replaced
1718:892deefba58d 1719:29c35cb8873e
1 #include <audacious/vfs.h>
2 #include <audacious/plugin.h>
3
4 #include <ne_socket.h>
5 #include <ne_utils.h>
6 #include <ne_redirect.h>
7 #include <ne_request.h>
8
9 #include "debug.h"
10 #include "neon.h"
11 #include "rb.h"
12
13 #define NBUFSIZ (128u*1024u)
14 #define NETBLKSIZ (4096u)
15
16 DECLARE_PLUGIN(neon, init, fini)
17
18 VFSConstructor neon_http_const = {
19 "http://",
20 neon_vfs_fopen_impl,
21 neon_vfs_fclose_impl,
22 neon_vfs_fread_impl,
23 neon_vfs_fwrite_impl,
24 neon_vfs_getc_impl,
25 neon_vfs_ungetc_impl,
26 neon_vfs_fseek_impl,
27 neon_vfs_rewind_impl,
28 neon_vfs_ftell_impl,
29 neon_vfs_feof_impl,
30 neon_vfs_truncate_impl,
31 neon_vfs_fsize_impl,
32 neon_vfs_metadata_impl
33 };
34
35 /*
36 * ========
37 */
38
39 static struct neon_handle* handle_init(void) {
40
41 struct neon_handle* h;
42
43 _ENTER;
44
45 if (NULL == (h = malloc(sizeof(struct neon_handle)))) {
46 _ERROR("Could not allocate memory for handle");
47 _LEAVE NULL;
48 }
49
50 if (0 != init_rb(&(h->rb), NBUFSIZ)) {
51 _ERROR("Could not initialize buffer");
52 free(h);
53 _LEAVE NULL;
54 }
55
56 h->url = NULL;
57 h->purl = &purl;
58 memset(h->purl, 0, sizeof(ne_uri));
59 h->session = NULL;
60 h->request = NULL;
61 h->redircount = 0;
62 h->pos = 0;
63 h->content_length = -1;
64 h->can_ranges = FALSE;
65 h->reader = NULL;
66 h->reader_status.mutex = g_mutex_new();
67 h->reader_status.cond = g_cond_new();
68 h->reader_status.reading = FALSE;
69 h->reader_status.status = NEON_READER_INIT;
70 h->eof = FALSE;
71
72 _LEAVE h;
73 }
74
75 /*
76 * -----
77 */
78
79 static void handle_free(struct neon_handle* h) {
80
81 _ENTER;
82
83 ne_uri_free(h->purl);
84 destroy_rb(&h->rb);
85 free(h);
86
87 _LEAVE;
88 }
89
90 /*
91 * ----
92 */
93
94 static void init(void) {
95
96 int ret;
97
98 _ENTER;
99
100 if (0 != (ret = ne_sock_init())) {
101 _ERROR("Could not initialize neon library: %d\n", ret);
102 _LEAVE;
103 }
104
105 vfs_register_transport(&neon_http_const);
106
107 if (0 != ne_has_support(NE_FEATURE_SSL)) {
108 _DEBUG("neon compiled with thread-safe SSL, enabling https:// transport");
109 }
110
111 _LEAVE;
112 }
113
114 /*
115 * -----
116 */
117
118 static void fini(void) {
119
120 _ENTER;
121
122 ne_sock_exit();
123
124 _LEAVE;
125 }
126
127 /*
128 * -----
129 */
130
131 static void kill_reader(struct neon_handle* h) {
132
133 _ENTER;
134
135 _DEBUG("Signaling reader thread to terminate");
136 g_mutex_lock(h->reader_status.mutex);
137 h->reader_status.reading = FALSE;
138 g_cond_signal(h->reader_status.cond);
139 g_mutex_unlock(h->reader_status.mutex);
140
141 _DEBUG("Waiting for reader thread to die...");
142 g_thread_join(h->reader);
143 _DEBUG("Reader thread has died");
144 h->reader = NULL;
145 }
146
147 /*
148 * -----
149 */
150
151 static void handle_headers(struct neon_handle* h) {
152
153 const gchar* name;
154 const gchar* value;
155 void* cursor = NULL;
156 long len;
157 gchar* endptr;
158
159 _ENTER;
160
161 _DEBUG("Header responses:");
162 while(NULL != (cursor = ne_response_header_iterate(h->request, cursor, &name, &value))) {
163 _DEBUG("HEADER: %s: %s", name, value);
164 if (0 == g_ascii_strncasecmp("accept-ranges", name, 13)) {
165 /*
166 * The server advertises range capability. we need "bytes"
167 */
168 if (NULL != g_strrstr(value, "bytes")) {
169 _DEBUG("server can_ranges");
170 h->can_ranges = TRUE;
171 }
172 }
173
174 if (0 == g_ascii_strncasecmp("content-length", name, 14)) {
175 /*
176 * The server sent us the content length. Parse and store.
177 */
178 len = strtol(value, &endptr, 10);
179 if ((*value != '\0') && (*endptr == '\0')) {
180 /*
181 * Valid data.
182 */
183 _DEBUG("Content length as advertised by server: %d", len);
184 h->content_length = len;
185 }
186 }
187 }
188
189 _LEAVE;
190 }
191
192 /*
193 * -----
194 */
195
196 static int open_request(struct neon_handle* handle, unsigned long startbyte) {
197
198 int ret;
199
200 _ENTER;
201
202 handle->request = ne_request_create(handle->session, "GET", handle->purl->path);
203 ne_print_request_header(handle->request, "Range", "bytes=%ld-", startbyte);
204
205 /*
206 * Try to connect to the server.
207 */
208 _DEBUG("Connecting...");
209 ret = ne_begin_request(handle->request);
210
211 switch (ret) {
212 case NE_OK:
213 /* URL opened OK */
214 _DEBUG("URL opened OK");
215 handle->content_start = startbyte;
216 handle->pos = startbyte;
217 handle_headers(handle);
218 _LEAVE 0;
219 break;
220
221 case NE_REDIRECT:
222 /* We hit a redirect. Handle it. */
223 _DEBUG("Redirect encountered");
224 handle->redircount += 1;
225 handle->purl = ne_redirect_location(handle->session);
226 ne_request_destroy(handle->request);
227 if (NULL == handle->purl) {
228 _ERROR("Could not parse redirect response");
229 _LEAVE -1;
230 }
231 break;
232
233 default:
234 /* Something went wrong. */
235 _ERROR("Could not open URL: %d", ret);
236 if (1 == ret) {
237 _ERROR("neon error string: %s", ne_get_error(handle->session));
238 }
239 ne_request_destroy(handle->request);
240 _LEAVE -1;
241 break;
242 }
243 }
244
245 /*
246 * -----
247 */
248
249 static int open_handle(struct neon_handle* handle, unsigned long startbyte) {
250
251 int ret;
252
253 _ENTER;
254
255 handle->redircount = 0;
256
257 _DEBUG("Parsing URL");
258 if (0 != ne_uri_parse(handle->url, handle->purl)) {
259 _ERROR("Could not parse URL '%s'", handle->url);
260 _LEAVE -1;
261 }
262
263 if (0 == handle->purl->port) {
264 handle->purl->port = 80;
265 }
266
267 while (handle->redircount < 10) {
268
269 _DEBUG("Creating session");
270 handle->session = ne_session_create(handle->purl->scheme, handle->purl->host, handle->purl->port);
271 ne_set_session_flag(handle->session, NE_SESSFLAG_ICYPROTO, 1);
272 ne_set_session_flag(handle->session, NE_SESSFLAG_PERSIST, 0);
273 ne_redirect_register(handle->session);
274
275 _DEBUG("Creating request");
276 ret = open_request(handle, startbyte);
277
278 if (0 == ret) {
279 _LEAVE 0;
280 } else if (-1 == ret) {
281 ne_session_destroy(handle->session);
282 _LEAVE -1;
283 }
284 }
285
286 /*
287 * If we get here, our redirect count exceeded
288 */
289
290 _ERROR("Redirect count exceeded for URL");
291
292 _LEAVE 1;
293 }
294
295 /*
296 * -----
297 */
298
299 static int fill_buffer(struct neon_handle* h) {
300
301 ssize_t bsize;
302 char buffer[NETBLKSIZ];
303 ssize_t to_read;
304
305 _ENTER;
306
307 bsize = free_rb(&h->rb);
308 to_read = MIN(bsize, NETBLKSIZ);
309
310 _DEBUG("%d bytes free in buffer, trying to read %d bytes max", bsize, to_read);
311
312 _DEBUG("Reading from the network....");
313 if (0 >= (bsize = ne_read_response_block(h->request, buffer, to_read))) {
314 if (0 == bsize) {
315 _DEBUG("End of file encountered");
316 _LEAVE 1;
317 } else {
318 _ERROR("Error while reading from the network");
319 _LEAVE -1;
320 }
321 }
322 _DEBUG("Read %d bytes from the network", bsize);
323
324 if (0 != write_rb(&(h->rb), buffer, bsize)) {
325 _ERROR("Error putting data into buffer");
326 _LEAVE -1;
327 }
328
329 _LEAVE 0;
330 }
331
332 /*
333 * -----
334 */
335
336 static int fill_buffer_limit(struct neon_handle* h, unsigned int maxfree) {
337
338 ssize_t bfree;
339 int ret;
340
341 _ENTER;
342
343 bfree = free_rb(&h->rb);
344 _DEBUG("Filling buffer up to max %d bytes free, %d bytes free now", maxfree, bfree);
345
346 while (bfree > maxfree) {
347 ret = fill_buffer(h);
348 if (-1 == ret) {
349 _ERROR("Error while filling buffer");
350 _LEAVE ret;
351 } else if (1 == ret) {
352 /*
353 * EOF while filling the buffer. Return what we have.
354 */
355 _LEAVE 0;
356 }
357
358 bfree = free_rb(&h->rb);
359 }
360
361 _LEAVE 0;
362 }
363
364 /*
365 * -----
366 */
367
368 static gpointer reader_thread(void* data) {
369
370 struct neon_handle* h = (struct neon_handle*)data;
371 int ret;
372
373 _ENTER;
374
375 g_mutex_lock(h->reader_status.mutex);
376
377 while(h->reader_status.reading) {
378 g_mutex_unlock(h->reader_status.mutex);
379
380 /*
381 * Hit the network only if we have more than NETBLKSIZ of free buffer
382 */
383 if (NETBLKSIZ < free_rb(&h->rb)) {
384
385 _DEBUG("Filling buffer...");
386 ret = fill_buffer(h);
387
388 g_mutex_lock(h->reader_status.mutex);
389 if (-1 == ret) {
390 /*
391 * Error encountered while reading from the network.
392 * Set the error flag and terminate the
393 * reader thread.
394 */
395 _DEBUG("Error while reading from the network. Terminating reader thread");
396 h->reader_status.status = NEON_READER_ERROR;
397 g_mutex_unlock(h->reader_status.mutex);
398 _LEAVE NULL;
399 } else if (1 == ret) {
400 /*
401 * EOF encountered while reading from the
402 * network. Set the EOF status and exit.
403 */
404 _DEBUG("EOF encountered while reading from the network. Terminating reader thread");
405 h->reader_status.status = NEON_READER_EOF;
406 g_mutex_unlock(h->reader_status.mutex);
407 _LEAVE NULL;
408 }
409
410 /*
411 * So we actually got some data out of the stream.
412 */
413 _DEBUG("Network read succeeded");
414 } else {
415 /*
416 * Not enough free space in the buffer.
417 * Sleep until the main thread wakes us up.
418 */
419 g_mutex_lock(h->reader_status.mutex);
420 if (h->reader_status.reading) {
421 _DEBUG("Reader thread going to sleep");
422 g_cond_wait(h->reader_status.cond, h->reader_status.mutex);
423 _DEBUG("Reader thread woke up");
424 } else {
425 /*
426 * Main thread has ordered termination of this thread.
427 * Leave the loop.
428 */
429 break;
430 }
431 }
432 }
433
434 _DEBUG("Reader thread terminating gracefully");
435 h->reader_status.status = NEON_READER_TERM;
436 g_mutex_unlock(h->reader_status.mutex);
437
438 _LEAVE NULL;
439 }
440
441 /*
442 * -----
443 */
444
445 VFSFile* neon_vfs_fopen_impl(const gchar* path, const gchar* mode) {
446
447 VFSFile* file;
448 struct neon_handle* handle;
449
450 _ENTER;
451
452 _DEBUG("Trying to open '%s' with neon", path);
453
454 if (NULL == (file = malloc(sizeof(VFSFile)))) {
455 _ERROR("Could not allocate memory for filehandle");
456 _LEAVE NULL;
457 }
458
459 if (NULL == (handle = handle_init())) {
460 _ERROR("Could not allocate memory for neon handle");
461 free(file);
462 _LEAVE NULL;
463 }
464
465 if (NULL == (handle->url = strdup(path))) {
466 _ERROR("Could not copy URL string");
467 handle_free(handle);
468 free(file);
469 _LEAVE NULL;
470 }
471
472 if (0 != open_handle(handle, 0)) {
473 _ERROR("Could not open URL");
474 handle_free(handle);
475 free(file);
476 _LEAVE NULL;
477 }
478
479 file->handle = handle;
480 file->base = &neon_http_const;
481
482 _LEAVE file;
483 }
484
485 /*
486 * ----
487 */
488
489 gint neon_vfs_fclose_impl(VFSFile* file) {
490
491 struct neon_handle* h = (struct neon_handle *)file->handle;
492
493 _ENTER;
494
495 if (NULL != h->reader) {
496 kill_reader(h);
497 }
498
499 _DEBUG("Destroying request");
500 if (NULL != h->request) {
501 ne_request_destroy(h->request);
502 }
503
504 _DEBUG("Destroying session");
505 ne_session_destroy(h->session);
506
507 handle_free(h);
508
509 _LEAVE 0;
510 }
511
512 /*
513 * -----
514 */
515
516 size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file) {
517
518 struct neon_handle* h = (struct neon_handle*)file->handle;
519 int belem;
520 int ret;
521
522 _ENTER;
523
524 if (NULL == h->request) {
525 _ERROR("No request to read from, seek gone wrong?");
526 _LEAVE 0;
527 }
528
529 _DEBUG("Requesting %d elements of %d bytes size each (%d bytes total), to be stored at %p",
530 nmemb, size, (nmemb*size), ptr_);
531
532 /*
533 * Look how much data is in the buffer
534 */
535 belem = used_rb(&h->rb) / size;
536
537 if ((NULL != h->reader) && (0 == belem)) {
538 /*
539 * There is a reader thread, but the buffer is empty.
540 * If we are running normally we will have to rebuffer.
541 * Kill the reader thread and restart.
542 */
543 g_mutex_lock(h->reader_status.mutex);
544 if (NEON_READER_RUN == h->reader_status.status) {
545 g_mutex_unlock(h->reader_status.mutex);
546 _ERROR("Buffer underrun, trying rebuffering");
547 kill_reader(h);
548 } else {
549 g_mutex_unlock(h->reader_status.mutex);
550 }
551 }
552
553 if (NULL == h->reader) {
554 /*
555 * There is no reader thread yet. Read the first bytes from
556 * the network ourselves, and then fire up the reader thread
557 * to keep the buffer filled up.
558 */
559 _DEBUG("Doing initial buffer fill");
560 ret = fill_buffer_limit(h, NBUFSIZ/2);
561
562 if (-1 == ret) {
563 _ERROR("Error while reading from the network");
564 _LEAVE 0;
565 } else if (1 == ret) {
566 _ERROR("EOF during initial read");
567 _LEAVE 0;
568 }
569
570 /*
571 * We have some data in the buffer now.
572 * Start the reader thread.
573 */
574 h->reader_status.reading = TRUE;
575 if (NULL == (h->reader = g_thread_create(reader_thread, h, TRUE, NULL))) {
576 h->reader_status.reading = FALSE;
577 _ERROR("Error creating reader thread!");
578 _LEAVE 0;
579 }
580 g_mutex_lock(h->reader_status.mutex);
581 h->reader_status.status = NEON_READER_RUN;
582 g_mutex_unlock(h->reader_status.mutex);
583 } else {
584 /*
585 * There already is a reader thread. Look if it is in good
586 * shape.
587 */
588 g_mutex_lock(h->reader_status.mutex);
589 _DEBUG("Reader thread status: %d", h->reader_status.status);
590 switch (h->reader_status.status) {
591 case NEON_READER_INIT:
592 case NEON_READER_RUN:
593 /*
594 * All is well, nothing to be done.
595 */
596 break;
597 case NEON_READER_EOF:
598 /*
599 * If there still is data in the buffer, carry on.
600 * If not, terminate the reader thread and return 0.
601 */
602 if (0 == used_rb(&h->rb)) {
603 _DEBUG("Reached end of stream");
604 g_mutex_unlock(h->reader_status.mutex);
605 kill_reader(h);
606 h->eof = TRUE;
607 _LEAVE 0;
608 }
609 break;
610 case NEON_READER_ERROR:
611 /* Terminate the reader and return 0 */
612 g_mutex_unlock(h->reader_status.mutex);
613 kill_reader(h);
614 _LEAVE 0;
615 break;
616 case NEON_READER_TERM:
617 /*
618 * The reader thread terminated gracefully, most
619 * likely on our own request.
620 * We should not get here.
621 */
622 _ERROR("Reader thread terminated and fread() called. How did we get here?");
623 g_mutex_unlock(h->reader_status.mutex);
624 kill_reader(h);
625 _LEAVE 0;
626 }
627 g_mutex_unlock(h->reader_status.mutex);
628 }
629
630 /*
631 * Deliver data from the buffer
632 */
633 belem = used_rb(&h->rb) / size;
634
635 if (0 == belem) {
636 /*
637 * The buffer is empty, we can deliver no data!
638 */
639 _ERROR("Buffer still underrun, fatal.");
640 _LEAVE 0;
641 }
642
643 _DEBUG("%d elements of data in the buffer", belem);
644 read_rb(&h->rb, ptr_, MIN(belem, nmemb)*size);
645
646 /*
647 * Signal the network thread to continue reading
648 */
649 _DEBUG("Waking up reader thread");
650 g_mutex_lock(h->reader_status.mutex);
651 g_cond_signal(h->reader_status.cond);
652 g_mutex_unlock(h->reader_status.mutex);
653
654 h->pos += (MIN(belem, nmemb)*size);
655
656 _DEBUG("Returning %d elements", MIN(belem, nmemb));
657
658 _LEAVE MIN(belem, nmemb);
659 }
660
661
662 /*
663 * -----
664 */
665
666 size_t neon_vfs_fwrite_impl(gconstpointer ptr, size_t size, size_t nmemb, VFSFile* file) {
667
668 _ENTER;
669
670 _ERROR("NOT IMPLEMENTED");
671
672 _LEAVE 0;
673 }
674
675 /*
676 * -----
677 */
678
679 gint neon_vfs_getc_impl(VFSFile* file) {
680
681 gchar c;
682
683 _ENTER;
684
685 if (1 != neon_vfs_fread_impl(&c, 1, 1, file)) {
686 _ERROR("Could not getc()!");
687 _LEAVE -1;
688 }
689
690 _LEAVE c;
691 }
692
693 /*
694 * -----
695 */
696
697 gint neon_vfs_ungetc_impl(gint c, VFSFile* stream) {
698
699 _ENTER;
700
701 _ERROR("NOT IMPLEMENTED");
702
703 _LEAVE 0;
704 }
705
706 /*
707 * -----
708 */
709
710 void neon_vfs_rewind_impl(VFSFile* file) {
711
712 _ENTER;
713
714 (void)neon_vfs_fseek_impl(file, 0L, SEEK_SET);
715
716 _LEAVE;
717 }
718
719 /*
720 * -----
721 */
722
723 glong neon_vfs_ftell_impl(VFSFile* file) {
724
725 struct neon_handle* h = (struct neon_handle *)file->handle;
726
727 _ENTER;
728
729 _DEBUG("Current file position: %d", h->pos);
730
731 _LEAVE h->pos;
732 }
733
734 /*
735 * -----
736 */
737
738 gboolean neon_vfs_feof_impl(VFSFile* file) {
739
740 struct neon_handle* h = (struct neon_handle*)file->handle;
741
742 _ENTER;
743
744 _LEAVE h->eof;
745 }
746
747 /*
748 * -----
749 */
750
751 gint neon_vfs_truncate_impl(VFSFile* file, glong size) {
752
753 _ENTER;
754
755 _ERROR("NOT IMPLEMENTED");
756
757 _LEAVE 0;
758 }
759
760 /*
761 * -----
762 */
763
764 gint neon_vfs_fseek_impl(VFSFile* file, glong offset, gint whence) {
765
766 struct neon_handle* h = (struct neon_handle*)file->handle;
767 long newpos;
768 long content_length;
769
770 _ENTER;
771
772 _DEBUG("Seek requested: offset %ld, whence %d", offset, whence);
773 /*
774 * Two things must be satisfied for us to be able to seek:
775 * - the server must advertise a content-length
776 * - the server must advertise accept-ranges: bytes
777 */
778 if ((-1 == h->content_length) || !h->can_ranges) {
779 _DEBUG("Can not seek due to server restrictions");
780 _LEAVE -1;
781 }
782
783 content_length = h->content_length + h->content_start;
784
785 switch (whence) {
786 case SEEK_SET:
787 newpos = offset;
788 break;
789 case SEEK_CUR:
790 newpos = h->pos + offset;
791 break;
792 case SEEK_END:
793 newpos = content_length + offset;
794 break;
795 default:
796 _ERROR("Invalid whence specified");
797 _LEAVE -1;
798 }
799
800 _DEBUG("Position to seek to: %ld, current: %ld", newpos, h->pos);
801 if (0 > newpos) {
802 _ERROR("Can not seek before start of stream");
803 _LEAVE -1;
804 }
805
806 if (newpos > content_length) {
807 _ERROR("Can not seek beyond end of stream");
808 _LEAVE -1;
809 }
810
811 if (newpos == h->pos) {
812 _LEAVE 0;
813 }
814
815 /*
816 * To seek to the new position we have to
817 * - stop the current reader thread, if there is one
818 * - destroy the current request
819 * - dump all data currently in the ringbuffer
820 * - create a new request starting at newpos
821 */
822 if (NULL != h->reader) {
823 /*
824 * There may be a thread still running.
825 */
826 kill_reader(h);
827 }
828
829 ne_request_destroy(h->request);
830 ne_session_destroy(h->session);
831 reset_rb(&h->rb);
832
833 if (0 != open_handle(h, newpos)) {
834 /*
835 * Something went wrong while creating the new request.
836 * There is not much we can do now, we'll set the request
837 * to NULL, so that fread() will error out on the next
838 * read request
839 */
840 _ERROR("Error while creating new request!");
841 h->request = NULL;
842 _LEAVE -1;
843 }
844
845 /*
846 * Things seem to have worked. The next read request will start
847 * the reader thread again.
848 */
849
850 _LEAVE 0;
851 }
852
853 /*
854 * -----
855 */
856
857 gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field) {
858
859 _ENTER;
860
861 _ERROR("NOT IMPLEMENTED");
862
863 _LEAVE NULL;
864 }
865
866 /*
867 * -----
868 */
869
870 off_t neon_vfs_fsize_impl(VFSFile* file) {
871
872 struct neon_handle* h = (struct neon_handle*)file->handle;
873
874 _ENTER;
875
876 if (-1 == h->content_length) {
877 _DEBUG("Unknown content length");
878 _LEAVE 0;
879 }
880
881 _LEAVE (h->content_start + h->content_length);
882 }