/** * Reads data from the encoder (as much as available) and returns it * as a new #page object. */ static struct page * httpd_output_read_page(struct httpd_output *httpd) { size_t size = 0, nbytes; if (httpd->unflushed_input >= 65536) { /* we have fed a lot of input into the encoder, but it didn't give anything back yet - flush now to avoid buffer underruns */ encoder_flush(httpd->encoder, NULL); httpd->unflushed_input = 0; } do { nbytes = encoder_read(httpd->encoder, httpd->buffer + size, sizeof(httpd->buffer) - size); if (nbytes == 0) break; httpd->unflushed_input = 0; size += nbytes; } while (size < sizeof(httpd->buffer)); if (size == 0) return NULL; return page_new_copy(httpd->buffer, size); }
/** * Reads data from the encoder (as much as available) and returns it * as a new #page object. */ static struct page * httpd_output_read_page(struct httpd_output *httpd) { size_t size = 0, nbytes; do { nbytes = encoder_read(httpd->encoder, httpd->buffer + size, sizeof(httpd->buffer) - size); if (nbytes == 0) break; size += nbytes; } while (size < sizeof(httpd->buffer)); if (size == 0) return NULL; return page_new_copy(httpd->buffer, size); }
static gboolean httpd_client_out_event(GIOChannel *source, G_GNUC_UNUSED GIOCondition condition, gpointer data) { struct httpd_client *client = data; struct httpd_output *httpd = client->httpd; GError *error = NULL; GIOStatus status; gsize bytes_written; gint bytes_to_write; g_mutex_lock(httpd->mutex); assert(condition == G_IO_OUT); assert(client->state == RESPONSE); if (client->write_source_id == 0) { /* another thread has removed the event source while this thread was waiting for httpd->mutex */ g_mutex_unlock(httpd->mutex); return false; } if (client->current_page == NULL) { client->current_page = g_queue_pop_head(client->pages); client->current_position = 0; } bytes_to_write = bytes_left_till_metadata(client); if (bytes_to_write == 0) { gint metadata_to_write; metadata_to_write = client->metadata_current_position; if (!client->metadata_sent) { status = write_page_to_channel(source, client->metadata, metadata_to_write, &bytes_written, &error); client->metadata_current_position += bytes_written; if (client->metadata->size - client->metadata_current_position == 0) { client->metadata_fill = 0; client->metadata_current_position = 0; client->metadata_sent = true; } } else { struct page *empty_meta; guchar empty_data = 0; empty_meta = page_new_copy(&empty_data, 1); status = write_page_to_channel(source, empty_meta, metadata_to_write, &bytes_written, &error); client->metadata_current_position += bytes_written; if (empty_meta->size - client->metadata_current_position == 0) { client->metadata_fill = 0; client->metadata_current_position = 0; } } bytes_written = 0; } else { status = write_n_bytes_to_channel(source, client->current_page, client->current_position, bytes_to_write, &bytes_written, &error); } switch (status) { case G_IO_STATUS_NORMAL: client->current_position += bytes_written; assert(client->current_position <= client->current_page->size); if (client->metadata_requested) client->metadata_fill += bytes_written; if (client->current_position >= client->current_page->size) { page_unref(client->current_page); client->current_page = NULL; if (g_queue_is_empty(client->pages)) { /* all pages are sent: remove the event source */ client->write_source_id = 0; g_mutex_unlock(httpd->mutex); return false; } } g_mutex_unlock(httpd->mutex); return true; case G_IO_STATUS_AGAIN: g_mutex_unlock(httpd->mutex); return true; case G_IO_STATUS_EOF: /* client has disconnected */ httpd_client_close(client); g_mutex_unlock(httpd->mutex); return false; case G_IO_STATUS_ERROR: /* I/O error */ g_warning("failed to write to client: %s", error->message); g_error_free(error); httpd_client_close(client); g_mutex_unlock(httpd->mutex); return false; } /* unreachable */ httpd_client_close(client); g_mutex_unlock(httpd->mutex); return false; }
struct page* icy_server_metadata_page(const struct tag *tag, ...) { va_list args; const gchar *tag_items[TAG_NUM_OF_ITEM_TYPES]; gint last_item, item; guint position; gchar *icy_string; struct page *icy_metadata; gchar stream_title[(1 + 255 - 28) * 16]; // Length + Metadata - // "StreamTitle='';StreamUrl='';" // = 4081 - 28 last_item = -1; va_start(args, tag); while (1) { enum tag_type type; const gchar *tag_item; type = va_arg(args, enum tag_type); if (type == TAG_NUM_OF_ITEM_TYPES) break; tag_item = tag_get_value(tag, type); if (tag_item) tag_items[++last_item] = tag_item; } va_end(args); position = item = 0; while (position < sizeof(stream_title) && item <= last_item) { gint length = 0; length = g_strlcpy(stream_title + position, tag_items[item++], sizeof(stream_title) - position); position += length; if (item <= last_item) { length = g_strlcpy(stream_title + position, " - ", sizeof(stream_title) - position); position += length; } } icy_string = icy_server_metadata_string(stream_title, ""); if (icy_string == NULL) return NULL; icy_metadata = page_new_copy(icy_string, (icy_string[0] * 16) + 1); g_free(icy_string); return icy_metadata; }