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;
}
Exemple #4
0
/**
 * 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;
}