static gboolean httpd_client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition, gpointer data) { struct httpd_client *client = data; struct httpd_output *httpd = client->httpd; bool ret; g_mutex_lock(httpd->mutex); if (condition == G_IO_IN && httpd_client_read(client)) { ret = true; } else { httpd_client_close(client); ret = false; } g_mutex_unlock(httpd->mutex); return ret; }
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; }
/** * Sends the status line and response headers to the client. */ static bool httpd_client_send_response(struct httpd_client *client) { char buffer[1024]; GError *error = NULL; GIOStatus status; gsize bytes_written; assert(client != NULL); assert(client->state == RESPONSE); if (!client->metadata_requested) { g_snprintf(buffer, sizeof(buffer), "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Connection: close\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache, no-store\r\n" "\r\n", client->httpd->content_type); } else { gchar *metadata_header; metadata_header = icy_server_metadata_header( client->httpd->name, client->httpd->genre, client->httpd->website, client->httpd->content_type, client->metaint); g_strlcpy(buffer, metadata_header, sizeof(buffer)); g_free(metadata_header); } status = g_io_channel_write_chars(client->channel, buffer, strlen(buffer), &bytes_written, &error); switch (status) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: return true; case G_IO_STATUS_EOF: /* client has disconnected */ httpd_client_close(client); 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); return false; } /* unreachable */ httpd_client_close(client); return false; }
/** * Sends the status line and response headers to the client. */ static bool httpd_client_send_response(struct httpd_client *client) { char buffer[1024]; GError *error = NULL; GIOStatus status; gsize bytes_written; assert(client != NULL); assert(client->state == RESPONSE); if (client->dlna_streaming_requested) { g_snprintf(buffer, sizeof(buffer), "HTTP/1.1 206 OK\r\n" "Content-Type: %s\r\n" "Content-Length: 10000\r\n" "Content-RangeX: 0-1000000/1000000\r\n" "transferMode.dlna.org: Streaming\r\n" "Accept-Ranges: bytes\r\n" "Connection: close\r\n" "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n" "\r\n", client->httpd->content_type); } else if (client->metadata_requested) { gchar *metadata_header; metadata_header = icy_server_metadata_header( client->httpd->name, client->httpd->genre, client->httpd->website, client->httpd->content_type, client->metaint); g_strlcpy(buffer, metadata_header, sizeof(buffer)); g_free(metadata_header); } else { /* revert to a normal HTTP request */ g_snprintf(buffer, sizeof(buffer), "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Connection: close\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache, no-store\r\n" "\r\n", client->httpd->content_type); } status = g_io_channel_write_chars(client->channel, buffer, strlen(buffer), &bytes_written, &error); switch (status) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: return true; case G_IO_STATUS_EOF: /* client has disconnected */ httpd_client_close(client); 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); return false; } /* unreachable */ httpd_client_close(client); return false; }