/* Attempts to push forward the reading 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 readable, read is complete, etc). */ static gboolean io_read (SoupMessage *msg, gboolean blocking, GCancellable *cancellable, GError **error) { SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg); SoupMessageIOData *io = priv->io_data; guchar *stack_buf = NULL; gssize nread; SoupBuffer *buffer; guint status; switch (io->read_state) { case SOUP_MESSAGE_IO_STATE_HEADERS: if (!read_headers (msg, blocking, cancellable, error)) return FALSE; status = io->parse_headers_cb (msg, (char *)io->read_header_buf->data, io->read_header_buf->len, &io->read_encoding, io->header_data, error); g_byte_array_set_size (io->read_header_buf, 0); if (status != SOUP_STATUS_OK) { /* Either we couldn't parse the headers, or they * indicated something that would mean we wouldn't * be able to parse the body. (Eg, unknown * Transfer-Encoding.). Skip the rest of the * reading, and make sure the connection gets * closed when we're done. */ soup_message_set_status (msg, status); soup_message_headers_append (msg->request_headers, "Connection", "close"); io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; break; } if (io->mode == SOUP_MESSAGE_IO_CLIENT && SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { if (msg->status_code == SOUP_STATUS_CONTINUE && io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) { /* Pause the reader, unpause the writer */ io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START; } else { /* Just stay in HEADERS */ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; } /* Informational responses have no bodies, so * bail out here rather than parsing encoding, etc */ soup_message_got_informational (msg); /* If this was "101 Switching Protocols", then * the session may have stolen the connection... */ if (io != priv->io_data) return FALSE; soup_message_cleanup_response (msg); break; } else if (io->mode == SOUP_MESSAGE_IO_SERVER && soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) { /* We must return a status code and response * headers to the client; either an error to * be set by a got-headers handler below, or * else %SOUP_STATUS_CONTINUE otherwise. */ io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; } else { io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START; /* If the client was waiting for a Continue * but got something else, then it's done * writing. */ if (io->mode == SOUP_MESSAGE_IO_CLIENT && io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; } if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) { SoupMessageHeaders *hdrs = (io->mode == SOUP_MESSAGE_IO_CLIENT) ? msg->response_headers : msg->request_headers; io->read_length = soup_message_headers_get_content_length (hdrs); if (io->mode == SOUP_MESSAGE_IO_CLIENT && !soup_message_is_keepalive (msg)) { /* Some servers suck and send * incorrect Content-Length values, so * allow EOF termination in this case * (iff the message is too short) too. */ io->read_encoding = SOUP_ENCODING_EOF; } } else io->read_length = -1; soup_message_got_headers (msg); break; case SOUP_MESSAGE_IO_STATE_BODY_START: if (!io->body_istream) { GInputStream *body_istream = soup_body_input_stream_new (G_INPUT_STREAM (io->istream), io->read_encoding, io->read_length); /* TODO: server-side messages do not have a io->item. This means * that we cannot use content processors for them right now. */ if (io->mode == SOUP_MESSAGE_IO_CLIENT) { io->body_istream = soup_message_setup_body_istream (body_istream, msg, io->item->session, SOUP_STAGE_MESSAGE_BODY); g_object_unref (body_istream); } else { io->body_istream = body_istream; } } if (priv->sniffer) { SoupContentSnifferStream *sniffer_stream = SOUP_CONTENT_SNIFFER_STREAM (io->body_istream); const char *content_type; GHashTable *params; if (!soup_content_sniffer_stream_is_ready (sniffer_stream, blocking, cancellable, error)) return FALSE; content_type = soup_content_sniffer_stream_sniff (sniffer_stream, ¶ms); soup_message_content_sniffed (msg, content_type, params); } io->read_state = SOUP_MESSAGE_IO_STATE_BODY; break; case SOUP_MESSAGE_IO_STATE_BODY: if (priv->chunk_allocator) { buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data); if (!buffer) { g_return_val_if_fail (!io->item || !io->item->new_api, FALSE); soup_message_io_pause (msg); return FALSE; } } else { if (!stack_buf) stack_buf = alloca (RESPONSE_BLOCK_SIZE); buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY, stack_buf, RESPONSE_BLOCK_SIZE); } nread = g_pollable_stream_read (io->body_istream, (guchar *)buffer->data, buffer->length, blocking, cancellable, error); if (nread > 0) { buffer->length = nread; soup_message_body_got_chunk (io->read_body, buffer); soup_message_got_chunk (msg, buffer); soup_buffer_free (buffer); break; } soup_buffer_free (buffer); if (nread == -1) return FALSE; /* else nread == 0 */ io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE; break; case SOUP_MESSAGE_IO_STATE_BODY_DONE: io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; soup_message_got_body (msg); break; case SOUP_MESSAGE_IO_STATE_FINISHING: io->read_state = SOUP_MESSAGE_IO_STATE_DONE; if (io->mode == SOUP_MESSAGE_IO_SERVER) io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; break; default: g_return_val_if_reached (FALSE); } return TRUE; }
static void io_read (SoupSocket *sock, SoupMessage *msg) { SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg); SoupMessageIOData *io = priv->io_data; guint status; read_more: switch (io->read_state) { case SOUP_MESSAGE_IO_STATE_NOT_STARTED: return; case SOUP_MESSAGE_IO_STATE_HEADERS: if (!read_metadata (msg, TRUE)) return; /* We need to "rewind" io->read_meta_buf back one line. * That SHOULD be two characters (CR LF), but if the * web server was stupid, it might only be one. */ if (io->read_meta_buf->len < 3 || io->read_meta_buf->data[io->read_meta_buf->len - 2] == '\n') io->read_meta_buf->len--; else io->read_meta_buf->len -= 2; io->read_meta_buf->data[io->read_meta_buf->len] = '\0'; status = io->parse_headers_cb (msg, (char *)io->read_meta_buf->data, io->read_meta_buf->len, &io->read_encoding, io->user_data); g_byte_array_set_size (io->read_meta_buf, 0); if (status != SOUP_STATUS_OK) { /* Either we couldn't parse the headers, or they * indicated something that would mean we wouldn't * be able to parse the body. (Eg, unknown * Transfer-Encoding.). Skip the rest of the * reading, and make sure the connection gets * closed when we're done. */ soup_message_set_status (msg, status); soup_message_headers_append (msg->request_headers, "Connection", "close"); io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; break; } if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) { SoupMessageHeaders *hdrs = (io->mode == SOUP_MESSAGE_IO_CLIENT) ? msg->response_headers : msg->request_headers; io->read_length = soup_message_headers_get_content_length (hdrs); } if (io->mode == SOUP_MESSAGE_IO_CLIENT && SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { if (msg->status_code == SOUP_STATUS_CONTINUE && io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) { /* Pause the reader, unpause the writer */ io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; io->write_state = io_body_state (io->write_encoding); } else { /* Just stay in HEADERS */ io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS; } } else if (io->mode == SOUP_MESSAGE_IO_SERVER && soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) { /* The client requested a Continue response. The * got_headers handler may change this to something * else though. */ soup_message_set_status (msg, SOUP_STATUS_CONTINUE); io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; io->read_state = SOUP_MESSAGE_IO_STATE_BLOCKING; } else { io->read_state = io_body_state (io->read_encoding); /* If the client was waiting for a Continue * but got something else, then it's done * writing. */ if (io->mode == SOUP_MESSAGE_IO_CLIENT && io->write_state == SOUP_MESSAGE_IO_STATE_BLOCKING) io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING; } if (io->mode == SOUP_MESSAGE_IO_CLIENT && SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) { SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_got_informational (msg); soup_message_cleanup_response (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; } else { SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_got_headers (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; } break; case SOUP_MESSAGE_IO_STATE_BLOCKING: io_write (sock, msg); /* As in the io_write case, we *must* return here. */ return; case SOUP_MESSAGE_IO_STATE_BODY: if (!read_body_chunk (msg)) return; got_body: if (!io_handle_sniffing (msg, TRUE)) { /* If the message was paused (as opposed to * cancelled), we need to make sure we wind up * back here when it's unpaused, even if it * was doing a chunked or EOF-terminated read * before. */ if (io == priv->io_data) { io->read_state = SOUP_MESSAGE_IO_STATE_BODY; io->read_encoding = SOUP_ENCODING_CONTENT_LENGTH; io->read_length = 0; } return; } io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING; SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK; soup_message_got_body (msg); SOUP_MESSAGE_IO_RETURN_IF_CANCELLED_OR_PAUSED; break; case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE: if (!read_metadata (msg, FALSE)) return; io->read_length = strtoul ((char *)io->read_meta_buf->data, NULL, 16); g_byte_array_set_size (io->read_meta_buf, 0); if (io->read_length > 0) io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK; else io->read_state = SOUP_MESSAGE_IO_STATE_TRAILERS; break; case SOUP_MESSAGE_IO_STATE_CHUNK: if (!read_body_chunk (msg)) return; io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_END; break; case SOUP_MESSAGE_IO_STATE_CHUNK_END: if (!read_metadata (msg, FALSE)) return; g_byte_array_set_size (io->read_meta_buf, 0); io->read_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE; break; case SOUP_MESSAGE_IO_STATE_TRAILERS: if (!read_metadata (msg, FALSE)) return; if (io->read_meta_buf->len <= SOUP_MESSAGE_IO_EOL_LEN) goto got_body; /* FIXME: process trailers */ g_byte_array_set_size (io->read_meta_buf, 0); break; case SOUP_MESSAGE_IO_STATE_FINISHING: if (io->read_tag) { g_signal_handler_disconnect (io->sock, io->read_tag); io->read_tag = 0; } io->read_state = SOUP_MESSAGE_IO_STATE_DONE; if (io->mode == SOUP_MESSAGE_IO_SERVER) { io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS; io_write (sock, msg); } else soup_message_io_finished (msg); return; case SOUP_MESSAGE_IO_STATE_DONE: default: g_return_if_reached (); } goto read_more; }
/* 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 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; }