Witold Filipczyk <[EMAIL PROTECTED]> writes: > On Mon, May 12, 2008 at 12:14:52AM +0300, Kalle Olavi Niemitalo wrote: >> - The code is duplicated between src/protocol/file/cgi.c and >> src/protocol/http/http.c. This may be the best way but it >> looks a bit annoying. > > I have no idea how to make it better.
How about the following? I did not test this yet.
1008: Merge POST data sending of HTTP and local CGI.
To reduce code duplication, src/protocol/file/cgi.c no longer parses
connection->uri->post on its own but rather calls the new function
http_read_post_data(), provided by src/protocol/http/http.c. The same
code is now also used for POST requests that do not include files.
---
commit bd3745a8558bbebdbbe9d4195e095fd14adce24b
tree 05d3fab7c7e99630303bb8c6c73ec6519a7fa6e6
parent 181486696b58d0284fe64a8cadba0720ece87543
author Kalle Olavi Niemitalo <[EMAIL PROTECTED]> Sun, 18 May 2008 22:27:18 +0300
committer Kalle Olavi Niemitalo <[EMAIL PROTECTED]> Sun, 18 May 2008 22:34:16
+0300
src/protocol/file/cgi.c | 151 +++---------------------------
src/protocol/http/http.c | 230 ++++++++++++++++++++++++++--------------------
src/protocol/http/http.h | 3 -
3 files changed, 146 insertions(+), 238 deletions(-)
diff --git a/src/protocol/file/cgi.c b/src/protocol/file/cgi.c
index ac16335..f7cb873 100644
--- a/src/protocol/file/cgi.c
+++ b/src/protocol/file/cgi.c
@@ -82,161 +82,42 @@ close_pipe_and_read(struct socket *data_socket)
#define POST_BUFFER_SIZE 4096
-#define BIG_READ 65536
-static void send_files2(struct socket *socket);
static void
-send_files(struct socket *socket)
+send_more_post_data(struct socket *socket)
{
struct connection *conn = socket->conn;
struct http_connection_info *http = conn->info;
- unsigned char *post = http->post_data;
unsigned char buffer[POST_BUFFER_SIZE];
- unsigned char *file = strchr(post, FILE_CHAR);
- struct string data;
- int n = 0;
- int finish = 0;
-
- if (!init_string(&data)) {
- abort_connection(conn, S_OUT_OF_MEM);
- return;
- }
-
- if (!file) {
- finish = 1;
- file = strchr(post, '\0');
- }
-
- while (post < file) {
- int h1, h2;
-
- h1 = unhx(post[0]);
- assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d
(%d/%c)", h1, post[0], post[0]);
- if_assert_failed h1 = 0;
-
- h2 = unhx(post[1]);
- assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d
(%d/%c)", h2, post[1], post[1]);
- if_assert_failed h2 = 0;
-
- buffer[n++] = (h1<<4) + h2;
- post += 2;
- if (n == POST_BUFFER_SIZE) {
- add_bytes_to_string(&data, buffer, n);
- n = 0;
- }
- }
- if (n) add_bytes_to_string(&data, buffer, n);
-
- if (finish) {
- write_to_socket(socket, data.source, data.length, S_SENT,
- close_pipe_and_read);
- } else {
- unsigned char *end = strchr(file + 1, FILE_CHAR);
-
- assert(end);
- *end = '\0';
- conn->post_fd = open(file + 1, O_RDONLY);
- *end = FILE_CHAR;
- if (conn->post_fd < 0) {
- int errno_from_open = errno;
-
- done_string(&data);
- abort_connection(conn, -errno_from_open);
- return;
- }
- http->post_data = end + 1;
- socket->state = SOCKET_END_ONCLOSE;
- write_to_socket(socket, data.source, data.length, S_TRANS,
- send_files2);
- }
- done_string(&data);
-}
-
-static void
-send_files2(struct socket *socket)
-{
- struct connection *conn = socket->conn;
- unsigned char buffer[BIG_READ];
- int n = safe_read(conn->post_fd, buffer, BIG_READ);
-
- if (n > 0) {
- socket->state = SOCKET_END_ONCLOSE;
- write_to_socket(socket, buffer, n, S_TRANS,
- send_files2);
- } else {
- close(conn->post_fd);
- conn->post_fd = -1;
- send_files(socket);
+ int got;
+
+ got = http_read_post_data(socket, buffer, POST_BUFFER_SIZE);
+ if (got < 0) {
+ abort_connection(conn, -errno);
+ } else if (got > 0) {
+ write_to_socket(socket, buffer, got, S_TRANS,
+ send_more_post_data);
+ http->uploaded += got;
+ } else { /* got == 0, meaning end of data */
+ close_pipe_and_read(socket);
}
}
+#undef POST_BUFFER_SIZE
static void
send_post_data(struct connection *conn)
{
+ struct http_connection_info *http = conn->info;
unsigned char *post = conn->uri->post;
unsigned char *postend;
- unsigned char buffer[POST_BUFFER_SIZE];
- struct string data;
- int n = 0;
postend = strchr(post, '\n');
if (postend) post = postend + 1;
- if (post) {
- unsigned char *file = strchr(post, FILE_CHAR);
-
- if (file) {
- struct http_connection_info *http = conn->info;
-
- http->post_data = post;
- send_files(conn->data_socket);
- return;
- }
- }
-
-
- if (!init_string(&data)) {
- abort_connection(conn, S_OUT_OF_MEM);
- return;
- }
-
- /* FIXME: Code duplication with protocol/http/http.c! --witekfl */
- while (post[0] && post[1]) {
- int h1, h2;
-
- h1 = unhx(post[0]);
- assert(h1 >= 0 && h1 < 16);
- if_assert_failed h1 = 0;
-
- h2 = unhx(post[1]);
- assert(h2 >= 0 && h2 < 16);
- if_assert_failed h2 = 0;
-
- buffer[n++] = (h1<<4) + h2;
- post += 2;
- if (n == POST_BUFFER_SIZE) {
- add_bytes_to_string(&data, buffer, n);
- n = 0;
- }
- }
- if (n)
- add_bytes_to_string(&data, buffer, n);
-
-
- /* If we're submitting a form whose controls do not have
- * names, then the POST has a Content-Type but empty data,
- * and an assertion would fail in write_to_socket. */
- if (data.length)
- write_to_socket(conn->data_socket, data.source, data.length,
- S_SENT, close_pipe_and_read);
- else
- close_pipe_and_read(conn->data_socket);
-
- done_string(&data);
+ http->post_data = post;
+ send_more_post_data(conn->data_socket);
}
-#undef POST_BUFFER_SIZE
-#undef BIG_READ
static void
send_request(struct connection *conn)
diff --git a/src/protocol/http/http.c b/src/protocol/http/http.c
index 3dcf83b..d68ad7d 100644
--- a/src/protocol/http/http.c
+++ b/src/protocol/http/http.c
@@ -613,94 +613,151 @@ post_length(unsigned char *post_data, unsigned int
*count)
#define POST_BUFFER_SIZE 4096
#define BIG_READ 655360
-static void send_files2(struct socket *socket);
-
-static void
-send_files(struct socket *socket)
+static int
+http_read_post_data_inline(struct socket *socket,
+ unsigned char buffer[], int max)
{
- struct connection *conn = socket->conn;
- struct http_connection_info *http = conn->info;
+ struct connection *const conn = socket->conn;
+ struct http_connection_info *const http = conn->info;
unsigned char *post = http->post_data;
- unsigned char buffer[POST_BUFFER_SIZE];
- unsigned char *file = strchr(post, FILE_CHAR);
- struct string data;
- int n = 0;
- int finish = 0;
+ unsigned char *end = strchr(post, FILE_CHAR);
+ int total = 0;
- if (!init_string(&data)) {
- http_end_request(conn, S_OUT_OF_MEM, 0);
- return;
- }
+ assert(conn->post_fd < 0);
+ if_assert_failed { errno = EINVAL; return -1; }
- if (!file) {
- finish = 1;
- file = strchr(post, '\0');
- }
+ if (!end)
+ end = strchr(post, '\0');
- while (post < file) {
+ while (post < end && total < max) {
int h1, h2;
h1 = unhx(post[0]);
assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d
(%d/%c)", h1, post[0], post[0]);
if_assert_failed h1 = 0;
- h2 = unhx(post[1]);
- assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d
(%d/%c)", h2, post[1], post[1]);
+ h2 = unhx(post[0]);
+ assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d
(%d/%c)", h2, post[0], post[0]);
if_assert_failed h2 = 0;
- buffer[n++] = (h1<<4) + h2;
+ buffer[total++] = (h1<<4) + h2;
post += 2;
- if (n == POST_BUFFER_SIZE) {
- add_bytes_to_string(&data, buffer, n);
- n = 0;
- }
}
- if (n) add_bytes_to_string(&data, buffer, n);
+ if (post != end || *end != FILE_CHAR) {
+ http->post_data = post;
+ return total;
+ }
+
+ end = strchr(post + 1, FILE_CHAR);
+ assert(end);
+ *end = '\0';
+ conn->post_fd = open(post + 1, O_RDONLY);
+ /* Be careful not to change errno here. */
+ *end = FILE_CHAR;
+ if (conn->post_fd < 0) {
+ http->post_data = post;
+ if (total > 0)
+ return total; /* retry the open on the next call */
+ else
+ return -1; /* caller gets errno from open() */
+ }
+ http->post_data = end + 1;
+ return total;
+}
- if (finish) {
- http->uploaded += data.length;
- request_from_socket(socket, data.source, data.length, S_SENT,
- SOCKET_END_ONCLOSE, http_got_header);
- } else {
- unsigned char *end = strchr(file + 1, FILE_CHAR);
+static int
+http_read_post_data_fd(struct socket *socket,
+ unsigned char buffer[], int max)
+{
+ struct connection *const conn = socket->conn;
+ int ret;
- assert(end);
- *end = '\0';
- conn->post_fd = open(file + 1, O_RDONLY);
- *end = FILE_CHAR;
- if (conn->post_fd < 0) {
- int errno_from_open = errno;
+ /* safe_read() would set errno = EBADF anyway, but check this
+ * explicitly to make any such bugs easier to detect. */
+ assert(conn->post_fd >= 0);
+ if_assert_failed { errno = EBADF; return -1; }
- done_string(&data);
- http_end_request(conn, -errno_from_open, 0);
- return;
+ ret = safe_read(conn->post_fd, buffer, max);
+ if (ret <= 0) {
+ const int errno_from_read = errno;
+
+ close(conn->post_fd);
+ conn->post_fd = -1;
+
+ errno = errno_from_read;
+ }
+ return ret;
+}
+
+/** Read data from socket->conn->uri->post or from the files to which
+ * it refers.
+ *
+ * @return >0 if read that many bytes; 0 if EOF; -1 on error and set
+ * errno. */
+int
+http_read_post_data(struct socket *socket,
+ unsigned char buffer[], int max)
+{
+ struct connection *const conn = socket->conn;
+ int total = 0;
+
+ while (total < max) {
+ int chunk;
+ int post_fd = conn->post_fd;
+
+ if (post_fd < 0)
+ chunk = http_read_post_data_inline(socket,
+ buffer + total,
+ max - total);
+ else
+ chunk = http_read_post_data_fd(socket,
+ buffer + total,
+ max - total);
+ /* Be careful not to change errno here. */
+
+ if (chunk == 0 && conn->post_fd == post_fd)
+ return total; /* EOF */
+ if (chunk < 0) {
+ /* If some data has already been successfully
+ * read to buffer[], tell the caller about
+ * that and forget about the error. The next
+ * http_read_post_data() call will retry the
+ * operation that failed. */
+ if (total != 0)
+ return total;
+ else
+ return chunk; /* caller gets errno from above */
}
- http->post_data = end + 1;
- socket->state = SOCKET_END_ONCLOSE;
- http->uploaded += data.length;
- write_to_socket(socket, data.source, data.length, S_TRANS,
- send_files2);
+ total += chunk;
}
- done_string(&data);
+ return total;
}
static void
-send_files2(struct socket *socket)
+send_more_post_data(struct socket *socket)
{
struct connection *conn = socket->conn;
struct http_connection_info *http = conn->info;
- unsigned char buffer[BIG_READ];
- int n = safe_read(conn->post_fd, buffer, BIG_READ);
-
- if (n > 0) {
+ unsigned char buffer[POST_BUFFER_SIZE];
+ int got;
+
+ got = http_read_post_data(socket, buffer, POST_BUFFER_SIZE);
+ if (got < 0) {
+ http_end_request(conn, -errno, 0);
+ } else if (got > 0) {
+ write_to_socket(socket, buffer, got, S_TRANS,
+ send_more_post_data);
+ http->uploaded += got;
+ } else { /* got == 0, meaning end of data */
+ /* Can't use request_from_socket() because there's no
+ * more data to write. */
+ struct read_buffer *rb = alloc_read_buffer(socket);
+
socket->state = SOCKET_END_ONCLOSE;
- http->uploaded += n;
- write_to_socket(socket, buffer, n, S_TRANS,
- send_files2);
- } else {
- close(conn->post_fd);
- conn->post_fd = -1;
- send_files(socket);
+ if (rb)
+ read_from_socket(socket, rb, S_SENT, http_got_header);
+ else
+ http_end_request(conn, S_OUT_OF_MEM, 0);
}
}
@@ -1084,53 +1141,22 @@ http_send_header(struct socket *socket)
add_crlf_to_string(&header);
- if (files) {
- assert(!use_connect && post_data);
- assert(conn->post_fd == -1);
- http->post_data = post_data;
- socket->state = SOCKET_END_ONCLOSE;
- if (!conn->upload_progress)
- conn->upload_progress = init_progress(0);
- write_to_socket(socket, header.source, header.length, S_TRANS,
- send_files);
- done_string(&header);
- return;
- }
/* CONNECT: Any POST data is for the origin server only.
* This was already checked above and post_data is NULL
* in that case. Verified with an assertion below. */
if (post_data) {
- unsigned char *post = post_data;
- unsigned char buffer[POST_BUFFER_SIZE];
- int n = 0;
-
assert(!use_connect); /* see comment above */
+ assert(conn->post_fd == -1);
- while (post[0] && post[1]) {
- int h1, h2;
-
- h1 = unhx(post[0]);
- assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is
%d (%d/%c)", h1, post[0], post[0]);
- if_assert_failed h1 = 0;
-
- h2 = unhx(post[1]);
- assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is
%d (%d/%c)", h2, post[1], post[1]);
- if_assert_failed h2 = 0;
-
- buffer[n++] = (h1<<4) + h2;
- post += 2;
- if (n == POST_BUFFER_SIZE) {
- add_bytes_to_string(&header, buffer, n);
- n = 0;
- }
- }
-
- if (n)
- add_bytes_to_string(&header, buffer, n);
- }
-
- request_from_socket(socket, header.source, header.length, S_SENT,
- SOCKET_END_ONCLOSE, http_got_header);
+ http->post_data = post_data;
+ socket->state = SOCKET_END_ONCLOSE;
+ if (!conn->upload_progress && files)
+ conn->upload_progress = init_progress(0);
+ write_to_socket(socket, header.source, header.length, S_TRANS,
+ send_more_post_data);
+ } else
+ request_from_socket(socket, header.source, header.length,
S_SENT,
+ SOCKET_END_ONCLOSE, http_got_header);
done_string(&header);
}
diff --git a/src/protocol/http/http.h b/src/protocol/http/http.h
index c2950d9..305acaf 100644
--- a/src/protocol/http/http.h
+++ b/src/protocol/http/http.h
@@ -40,7 +40,8 @@ struct http_connection_info {
unsigned char *post_data;
};
-
+int http_read_post_data(struct socket *socket,
+ unsigned char buffer[], int max);
extern struct module http_protocol_module;
pgpsrLYoBEjNf.pgp
Description: PGP signature
_______________________________________________ elinks-dev mailing list [email protected] http://linuxfromscratch.org/mailman/listinfo/elinks-dev
