static void write_ready_cb (GObject *source, GAsyncResult *result, SoupCacheInputStream *istream) { GOutputStream *ostream = G_OUTPUT_STREAM (source); SoupCacheInputStreamPrivate *priv = istream->priv; gssize write_size; gsize pending; GError *error = NULL; write_size = g_output_stream_write_finish (ostream, result, &error); if (error) { notify_and_clear (istream, error); g_object_unref (istream); return; } /* Check that we have written everything */ pending = priv->current_writing_buffer->length - write_size; if (pending) { SoupBuffer *subbuffer = soup_buffer_new_subbuffer (priv->current_writing_buffer, write_size, pending); g_queue_push_head (priv->buffer_queue, subbuffer); } priv->bytes_written += write_size; g_clear_pointer (&priv->current_writing_buffer, soup_buffer_free); try_write_next_buffer (istream); g_object_unref (istream); }
/** * soup_message_body_get_chunk: * @body: a #SoupMessageBody * @offset: an offset * * Gets a #SoupBuffer containing data from @body starting at @offset. * The size of the returned chunk is unspecified. You can iterate * through the entire body by first calling * soup_message_body_get_chunk() with an offset of 0, and then on each * successive call, increment the offset by the length of the * previously-returned chunk. * * If @offset is greater than or equal to the total length of @body, * then the return value depends on whether or not * soup_message_body_complete() has been called or not; if it has, * then soup_message_body_get_chunk() will return a 0-length chunk * (indicating the end of @body). If it has not, then * soup_message_body_get_chunk() will return %NULL (indicating that * @body may still potentially have more data, but that data is not * currently available). * * Return value: (nullable): a #SoupBuffer, or %NULL. **/ SoupBuffer * soup_message_body_get_chunk (SoupMessageBody *body, goffset offset) { SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body; GSList *iter; SoupBuffer *chunk = NULL; offset -= priv->base_offset; for (iter = priv->chunks; iter; iter = iter->next) { chunk = iter->data; if (offset < chunk->length || offset == 0) break; offset -= chunk->length; } if (!iter) return NULL; if (offset == 0) return soup_buffer_copy (chunk); else { return soup_buffer_new_subbuffer (chunk, offset, chunk->length - offset); } }
static void fillDataFromReadBuffer(SoupBuffer* readBuffer, size_t size, Data& data) { GRefPtr<SoupBuffer> buffer; if (size != readBuffer->length) { // The subbuffer does not copy the data. buffer = adoptGRef(soup_buffer_new_subbuffer(readBuffer, 0, size)); } else buffer = readBuffer; if (data.isNull()) { // First chunk, we need to force the data to be copied. data = { reinterpret_cast<const uint8_t*>(buffer->data), size }; } else { Data dataRead(WTF::move(buffer)); // Concatenate will copy the data. data = concatenate(data, dataRead); } }
static void outputStreamWriteReadyCallback(GOutputStream* stream, GAsyncResult* result, gpointer userData) { std::unique_ptr<WriteAsyncData> asyncData(static_cast<WriteAsyncData*>(userData)); gssize bytesWritten = g_output_stream_write_finish(stream, result, nullptr); if (bytesWritten == -1) { asyncData->completionHandler(-1); return; } gssize pendingBytesToWrite = asyncData->buffer->length - bytesWritten; if (!pendingBytesToWrite) { asyncData->completionHandler(0); return; } asyncData->buffer = adoptGRef(soup_buffer_new_subbuffer(asyncData->buffer.get(), bytesWritten, pendingBytesToWrite)); // Use a local variable for the data buffer to pass it to g_output_stream_write_async(), because WriteAsyncData is released. auto data = asyncData->buffer->data; g_output_stream_write_async(stream, data, pendingBytesToWrite, G_PRIORITY_DEFAULT, nullptr, reinterpret_cast<GAsyncReadyCallback>(outputStreamWriteReadyCallback), asyncData.release()); }
static gssize webkit_soup_directory_input_stream_read (GInputStream *input, void *buffer, gsize count, GCancellable *cancellable, GError **error) { WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input); gsize total, size; for (total = 0; total < count; total += size) { if (stream->buffer == NULL) { stream->buffer = webkit_soup_directory_input_stream_read_next_file (stream, cancellable, error); if (stream->buffer == NULL) { /* FIXME: Is this correct or should we forward the error? */ if (total) g_clear_error (error); return total; } } size = MIN (stream->buffer->length, count - total); memcpy ((char *)buffer + total, stream->buffer->data, size); if (size == stream->buffer->length) { soup_buffer_free (stream->buffer); stream->buffer = NULL; } else { SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer, size, stream->buffer->length - size); soup_buffer_free (stream->buffer); stream->buffer = sub; } } return total; }
/* Attempts to push forward the writing side of @msg's I/O. Returns * %TRUE if it manages to make some progress, and it is likely that * further progress can be made. Returns %FALSE if it has reached a * stopping point of some sort (need input from the application, * socket not writable, write is complete, etc). */ static gboolean io_write (SoupMessage *msg, gboolean blocking, GCancellable *cancellable, GError **error) { SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg); SoupMessageIOData *io = priv->io_data; SoupBuffer *chunk; gssize nwrote; if (io->async_close_error) { g_propagate_error (error, io->async_close_error); io->async_close_error = NULL; return FALSE; } else if (io->async_close_wait) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, _("Operation would block")); return FALSE; } switch (io->write_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: if (io->mode == SOUP_MESSAGE_IO_SERVER && io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && msg->status_code == 0) { /* Client requested "Expect: 100-continue", and * server did not set an error. */ soup_message_set_status (msg, SOUP_STATUS_CONTINUE); } if (!io->write_buf->len) { io->get_headers_cb (msg, io->write_buf, &io->write_encoding, io->header_data); } while (io->written < io->write_buf->len) { nwrote = g_pollable_stream_write (io->ostream, io->write_buf->str + io->written, io->write_buf->len - io->written, blocking, cancellable, error); if (nwrote == -1) return FALSE; io->written += nwrote; } io->written = 0; g_string_truncate (io->write_buf, 0); if (io->mode == SOUP_MESSAGE_IO_SERVER && SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { if (msg->status_code == SOUP_STATUS_CONTINUE) { /* Stop and wait for the body now */ io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; } else { /* We just wrote a 1xx response * header, so stay in STATE_HEADERS. * (The caller will pause us from the * wrote_informational callback if he * is not ready to send the final * response.) */ } soup_message_wrote_informational (msg); /* If this was "101 Switching Protocols", then * the server probably stole the connection... */ if (io != priv->io_data) return FALSE; soup_message_cleanup_response (msg); break; } if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) { SoupMessageHeaders *hdrs = (io->mode == SOUP_MESSAGE_IO_CLIENT) ? msg->request_headers : msg->response_headers; io->write_length = soup_message_headers_get_content_length (hdrs); } if (io->mode == SOUP_MESSAGE_IO_CLIENT && soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) { /* Need to wait for the Continue response */ io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; } else { io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START; /* If the client was waiting for a Continue * but we sent something else, then they're * now done writing. */ if (io->mode == SOUP_MESSAGE_IO_SERVER && io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING) io->read_state = SOUP_MESSAGE_IO_STATE_DONE; } soup_message_wrote_headers (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_START: io->body_ostream = soup_body_output_stream_new (io->ostream, io->write_encoding, io->write_length); io->write_state = SOUP_MESSAGE_IO_STATE_BODY; break; case SOUP_MESSAGE_IO_STATE_BODY: if (!io->write_length && io->write_encoding != SOUP_ENCODING_EOF && io->write_encoding != SOUP_ENCODING_CHUNKED) { io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; break; } if (!io->write_chunk) { io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset); if (!io->write_chunk) { g_return_val_if_fail (!io->item || !io->item->new_api, FALSE); soup_message_io_pause (msg); return FALSE; } if (!io->write_chunk->length) { io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; break; } } nwrote = g_pollable_stream_write (io->body_ostream, io->write_chunk->data + io->written, io->write_chunk->length - io->written, blocking, cancellable, error); if (nwrote == -1) return FALSE; chunk = soup_buffer_new_subbuffer (io->write_chunk, io->written, nwrote); io->written += nwrote; if (io->write_length) io->write_length -= nwrote; if (io->written == io->write_chunk->length) io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA; soup_message_wrote_body_data (msg, chunk); soup_buffer_free (chunk); break; case SOUP_MESSAGE_IO_STATE_BODY_DATA: io->written = 0; if (io->write_chunk->length == 0) { io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH; break; } if (io->mode == SOUP_MESSAGE_IO_SERVER || priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD) soup_message_body_wrote_chunk (io->write_body, io->write_chunk); io->write_body_offset += io->write_chunk->length; soup_buffer_free (io->write_chunk); io->write_chunk = NULL; io->write_state = SOUP_MESSAGE_IO_STATE_BODY; soup_message_wrote_chunk (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_FLUSH: if (io->body_ostream) { if (blocking || io->write_encoding != SOUP_ENCODING_CHUNKED) { if (!g_output_stream_close (io->body_ostream, cancellable, error)) return FALSE; g_clear_object (&io->body_ostream); } else { io->async_close_wait = g_cancellable_new (); if (io->async_context) g_main_context_push_thread_default (io->async_context); g_output_stream_close_async (io->body_ostream, G_PRIORITY_DEFAULT, cancellable, closed_async, g_object_ref (msg)); if (io->async_context) g_main_context_pop_thread_default (io->async_context); } } io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; break; case SOUP_MESSAGE_IO_STATE_BODY_DONE: io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; soup_message_wrote_body (msg); break; case SOUP_MESSAGE_IO_STATE_FINISHING: io->write_state = SOUP_MESSAGE_IO_STATE_DONE; if (io->mode == SOUP_MESSAGE_IO_CLIENT) io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; break; default: g_return_val_if_reached (FALSE); } return TRUE; }
static void handle_partial_get (SoupMessage *msg) { SoupRange *ranges; int nranges; SoupBuffer *full_response; /* Make sure the message is set up right for us to return a * partial response; it has to be a GET, the status must be * 200 OK (and in particular, NOT already 206 Partial * Content), and the SoupServer must have already filled in * the response body */ if (msg->method != SOUP_METHOD_GET || msg->status_code != SOUP_STATUS_OK || soup_message_headers_get_encoding (msg->response_headers) != SOUP_ENCODING_CONTENT_LENGTH || msg->response_body->length == 0 || !soup_message_body_get_accumulate (msg->response_body)) return; /* Oh, and there has to have been a valid Range header on the * request, of course. */ if (!soup_message_headers_get_ranges (msg->request_headers, msg->response_body->length, &ranges, &nranges)) return; full_response = soup_message_body_flatten (msg->response_body); if (!full_response) { soup_message_headers_free_ranges (msg->request_headers, ranges); return; } soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT); soup_message_body_truncate (msg->response_body); if (nranges == 1) { SoupBuffer *range_buf; /* Single range, so just set Content-Range and fix the body. */ soup_message_headers_set_content_range (msg->response_headers, ranges[0].start, ranges[0].end, full_response->length); range_buf = soup_buffer_new_subbuffer (full_response, ranges[0].start, ranges[0].end - ranges[0].start + 1); soup_message_body_append_buffer (msg->response_body, range_buf); soup_buffer_free (range_buf); } else { SoupMultipart *multipart; SoupMessageHeaders *part_headers; SoupBuffer *part_body; const char *content_type; int i; /* Multiple ranges, so build a multipart/byteranges response * to replace msg->response_body with. */ multipart = soup_multipart_new ("multipart/byteranges"); content_type = soup_message_headers_get_one (msg->response_headers, "Content-Type"); for (i = 0; i < nranges; i++) { part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART); if (content_type) { soup_message_headers_append (part_headers, "Content-Type", content_type); } soup_message_headers_set_content_range (part_headers, ranges[i].start, ranges[i].end, full_response->length); part_body = soup_buffer_new_subbuffer (full_response, ranges[i].start, ranges[i].end - ranges[i].start + 1); soup_multipart_append_part (multipart, part_headers, part_body); soup_message_headers_free (part_headers); soup_buffer_free (part_body); } soup_multipart_to_message (multipart, msg->response_headers, msg->response_body); soup_multipart_free (multipart); } soup_buffer_free (full_response); soup_message_headers_free_ranges (msg->request_headers, ranges); }
static void io_write (SoupSocket *sock, SoupMessage *msg) { SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg); SoupMessageIOData *io = priv->io_data; write_more: switch (io->write_state) { case SOUP_MESSAGE_IO_STATE_NOT_STARTED: return; case SOUP_MESSAGE_IO_STATE_HEADERS: if (!io->write_buf->len) { io->get_headers_cb (msg, io->write_buf, &io->write_encoding, io->user_data); if (!io->write_buf->len) { soup_message_io_pause (msg); return; } } if (!write_data (msg, io->write_buf->str, io->write_buf->len, FALSE)) return; g_string_truncate (io->write_buf, 0); if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) { SoupMessageHeaders *hdrs = (io->mode == SOUP_MESSAGE_IO_CLIENT) ? msg->request_headers : msg->response_headers; io->write_length = soup_message_headers_get_content_length (hdrs); } if (io->mode == SOUP_MESSAGE_IO_SERVER && SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { if (msg->status_code == SOUP_STATUS_CONTINUE) { /* Stop and wait for the body now */ io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->read_state = io_body_state (io->read_encoding); } else { /* We just wrote a 1xx response * header, so stay in STATE_HEADERS. * (The caller will pause us from the * wrote_informational callback if he * is not ready to send the final * response.) */ } } else if (io->mode == SOUP_MESSAGE_IO_CLIENT && soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) { /* Need to wait for the Continue response */ io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; } else { io->write_state = io_body_state (io->write_encoding); /* If the client was waiting for a Continue * but we sent something else, then they're * now done writing. */ if (io->mode == SOUP_MESSAGE_IO_SERVER && io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING) io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; } SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { soup_message_wrote_informational (msg); soup_message_cleanup_response (msg); } else soup_message_wrote_headers (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; break; case SOUP_MESSAGE_IO_STATE_BLOCKING: io_read (sock, msg); /* If io_read reached a point where we could write * again, it would have recursively called io_write. * So (a) we don't need to try to keep writing, and * (b) we can't anyway, because msg may have been * destroyed. */ return; case SOUP_MESSAGE_IO_STATE_BODY: if (!io->write_length && io->write_encoding != SOUP_ENCODING_EOF) { wrote_body: io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_wrote_body (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; break; } if (!io->write_chunk) { io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset); if (!io->write_chunk) { soup_message_io_pause (msg); return; } if (io->write_chunk->length > io->write_length && io->write_encoding != SOUP_ENCODING_EOF) { /* App is trying to write more than it * claimed it would; we have to truncate. */ SoupBuffer *truncated = soup_buffer_new_subbuffer (io->write_chunk, 0, io->write_length); soup_buffer_free (io->write_chunk); io->write_chunk = truncated; } else if (io->write_encoding == SOUP_ENCODING_EOF && !io->write_chunk->length) goto wrote_body; } if (!write_data (msg, io->write_chunk->data, io->write_chunk->length, TRUE)) return; if (io->mode == SOUP_MESSAGE_IO_SERVER) soup_message_body_wrote_chunk (io->write_body, io->write_chunk); io->write_body_offset += io->write_chunk->length; soup_buffer_free (io->write_chunk); io->write_chunk = NULL; SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_wrote_chunk (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; break; case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE: if (!io->write_chunk) { io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset); if (!io->write_chunk) { soup_message_io_pause (msg); return; } g_string_append_printf (io->write_buf, "%lx\r\n", (unsigned long) io->write_chunk->length); io->write_body_offset += io->write_chunk->length; } if (!write_data (msg, io->write_buf->str, io->write_buf->len, FALSE)) return; g_string_truncate (io->write_buf, 0); if (io->write_chunk->length == 0) { /* The last chunk has no CHUNK_END... */ io->write_state = SOUP_MESSAGE_IO_STATE_TRAILERS; break; } io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK; /* fall through */ case SOUP_MESSAGE_IO_STATE_CHUNK: if (!write_data (msg, io->write_chunk->data, io->write_chunk->length, TRUE)) return; if (io->mode == SOUP_MESSAGE_IO_SERVER) soup_message_body_wrote_chunk (io->write_body, io->write_chunk); soup_buffer_free (io->write_chunk); io->write_chunk = NULL; io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_END; SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_wrote_chunk (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; /* fall through */ case SOUP_MESSAGE_IO_STATE_CHUNK_END: if (!write_data (msg, SOUP_MESSAGE_IO_EOL, SOUP_MESSAGE_IO_EOL_LEN, FALSE)) return; io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE; break; case SOUP_MESSAGE_IO_STATE_TRAILERS: if (!write_data (msg, SOUP_MESSAGE_IO_EOL, SOUP_MESSAGE_IO_EOL_LEN, FALSE)) return; io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_wrote_body (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; /* fall through */ case SOUP_MESSAGE_IO_STATE_FINISHING: if (io->write_tag) { g_signal_handler_disconnect (io->sock, io->write_tag); io->write_tag = 0; } io->write_state = SOUP_MESSAGE_IO_STATE_DONE; if (io->mode == SOUP_MESSAGE_IO_CLIENT) { io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; io_read (sock, msg); } else soup_message_io_finished (msg); return; case SOUP_MESSAGE_IO_STATE_DONE: default: g_return_if_reached (); } goto write_more; }