/** * cockpit_web_response_queue: * @self: the response * @block: the block of data to queue * * Queue a single block of data on the response. Will be sent * during the main loop. * * See cockpit_web_response_content() for a simple way to * avoid queueing individual blocks. * * If this function returns %FALSE, then the response has failed * or has been completed elsewhere. The block was ignored and * queuing more blocks doesn't makes sense. * * After done queuing all your blocks call * cockpit_web_response_complete(). * * Returns: Whether queuing more blocks makes sense */ gboolean cockpit_web_response_queue (CockpitWebResponse *self, GBytes *block) { gchar *data; GBytes *bytes; gsize length; g_return_val_if_fail (block != NULL, FALSE); g_return_val_if_fail (self->complete == FALSE, FALSE); if (self->failed) { g_debug ("%s: ignoring queued block after failure", self->logname); return FALSE; } length = g_bytes_get_size (block); g_debug ("%s: queued %d bytes", self->logname, (int)length); /* * We cannot queue chunks of length zero. Besides being silly, this * messes with chunked encoding. The 0 length block means end of * response. */ if (length == 0) return TRUE; if (!self->chunked) { queue_bytes (self, block); } else { /* Required for chunked transfer encoding. */ data = g_strdup_printf ("%x\r\n", (unsigned int)g_bytes_get_size (block)); bytes = g_bytes_new_take (data, strlen (data)); queue_bytes (self, bytes); g_bytes_unref (bytes); queue_bytes (self, block); bytes = g_bytes_new_static ("\r\n", 2); queue_bytes (self, bytes); g_bytes_unref (bytes); } return TRUE; }
/** * cockpit_web_response_headers: * @self: the response * @status: the HTTP status code * @reason: the HTTP reason * @length: the combined length of data blocks to follow, or -1 * @headers: headers to include or NULL * * See cockpit_web_response_content() for an easy to use function. * * Queue the headers of the response. No data blocks must yet be * queued on the response. * * Don't put Content-Length or Connection in @headers. * * If @length is zero or greater, then it must represent the * number of queued blocks to follow. */ void cockpit_web_response_headers_full (CockpitWebResponse *self, guint status, const gchar *reason, gssize length, GHashTable *headers) { GString *string; GBytes *block; g_return_if_fail (COCKPIT_IS_WEB_RESPONSE (self)); if (self->count > 0) { g_critical ("Headers should be sent first. This is a programmer error."); return; } string = begin_headers (self, status, reason); block = finish_headers (self, string, length, status, append_table (string, headers)); queue_bytes (self, block); g_bytes_unref (block); }
/** * cockpit_web_response_complete: * @self: the response * * See cockpit_web_response_content() for easy to use stuff. * * Tell the response that all the data has been queued. * The response will hold a reference to itself until the * data is actually sent, so you can unref it. */ void cockpit_web_response_complete (CockpitWebResponse *self) { GBytes *bytes; g_return_if_fail (COCKPIT_IS_WEB_RESPONSE (self)); g_return_if_fail (self->complete == FALSE); if (self->failed) return; /* Hold a reference until cockpit_web_response_done() */ g_object_ref (self); self->complete = TRUE; if (self->chunked) { bytes = g_bytes_new_static ("0\r\n\r\n", 5); queue_bytes (self, bytes); g_bytes_unref (bytes); } if (self->source) { g_debug ("%s: queueing complete", self->logname); } else { g_debug ("%s: complete closing io", self->logname); g_output_stream_flush_async (G_OUTPUT_STREAM (self->out), G_PRIORITY_DEFAULT, NULL, on_output_flushed, g_object_ref (self)); } }
/** * cockpit_web_response_headers: * @self: the response * @status: the HTTP status code * @reason: the HTTP reason * @length: the combined length of data blocks to follow, or -1 * * See cockpit_web_response_content() for an easy to use function. * * Queue the headers of the response. No data blocks must yet be * queued on the response. * * Specify header name/value pairs in the var args, and end with * a NULL name. If value is NULL, then that header won't be sent. * * Don't specify Content-Length or Connection headers. * * If @length is zero or greater, then it must represent the * number of queued blocks to follow. */ void cockpit_web_response_headers (CockpitWebResponse *self, guint status, const gchar *reason, gssize length, ...) { GString *string; GBytes *block; va_list va; if (self->count > 0) { g_critical ("Headers should be sent first. This is a programmer error."); return; } string = begin_headers (self, status, reason); va_start (va, length); block = finish_headers (self, string, length, status >= 200 && status <= 299, append_va (string, va)); va_end (va); queue_bytes (self, block); g_bytes_unref (block); }
/** * cockpit_web_response_queue: * @self: the response * @block: the block of data to queue * * Queue a single block of data on the response. Will be sent * during the main loop. * * See cockpit_web_response_content() for a simple way to * avoid queueing individual blocks. * * If this function returns %FALSE, then the response has failed * or has been completed elsewhere. The block was ignored and * queuing more blocks doesn't makes sense. * * After done queuing all your blocks call * cockpit_web_response_complete(). * * Returns: Whether queuing more blocks makes sense */ gboolean cockpit_web_response_queue (CockpitWebResponse *self, GBytes *block) { gchar *data; GBytes *bytes; g_return_val_if_fail (block != NULL, FALSE); g_return_val_if_fail (self->complete == FALSE, FALSE); if (self->failed) { g_debug ("%s: ignoring queued block after failure", self->logname); return FALSE; } g_debug ("%s: queued %d bytes", self->logname, (int)g_bytes_get_size (block)); if (!self->chunked) { queue_bytes (self, block); } else { /* Required for chunked transfer encoding. */ data = g_strdup_printf ("%x\r\n", (unsigned int)g_bytes_get_size (block)); bytes = g_bytes_new_take (data, strlen (data)); queue_bytes (self, bytes); g_bytes_unref (bytes); queue_bytes (self, block); bytes = g_bytes_new_static ("\r\n", 2); queue_bytes (self, bytes); g_bytes_unref (bytes); } return TRUE; }
static void queue_block (CockpitWebResponse *self, GBytes *block) { gsize length = g_bytes_get_size (block); GBytes *bytes; gchar *data; /* * We cannot queue chunks of length zero. Besides being silly, this * messes with chunked encoding. The 0 length block means end of * response. */ if (length == 0) return; g_debug ("%s: queued %d bytes", self->logname, (int)length); if (!self->chunked) { queue_bytes (self, block); } else { /* Required for chunked transfer encoding. */ data = g_strdup_printf ("%x\r\n", (unsigned int)length); bytes = g_bytes_new_take (data, strlen (data)); queue_bytes (self, bytes); g_bytes_unref (bytes); queue_bytes (self, block); bytes = g_bytes_new_static ("\r\n", 2); queue_bytes (self, bytes); g_bytes_unref (bytes); } }