On Thu, Apr 08, 2021 at 07:43:47PM +0200, Theo Buehler wrote:
> On Thu, Apr 08, 2021 at 07:18:39PM +0200, Claudio Jeker wrote:
> > On Thu, Apr 08, 2021 at 06:22:16PM +0200, Theo Buehler wrote:
> > > On Thu, Apr 08, 2021 at 04:47:15PM +0200, Claudio Jeker wrote:
> > > > This diff is a first step in tightening the code in http.c
> > > > It should cleanup the poll handling and make adds some code to ensure
> > > > that
> > > > only expected results are returned. The goal is that http_handle() only
> > > > does IO processing and http_nextstep() is used for transitions into new
> > > > states.
> > >
> > > I like this direction.
> > >
> > > > I did shuffle the code around a bit. So that similar functions are
> > > > together.
> > > > Also by shuffling the poll loop now connections are added after the all
> > > > active connections have been processed.
> > >
> > > This added quite a bit of noise to the diff (especially some parts
> > > shuffling _write() functions are hard to read).
> > >
> > > I'm ok with this, one small comment below.
> > >
> >
> > Sorry about that. I committed the deck chair shuffle and some other minor
> > bits. Here is the part that replaces the regular and chunked write
> > functions with a single data_write one.
> >
> > I made the strtoul() check look like the man page example to detect
> > overflows. There is some off_t vs size_t casts required but in that case
> > the size limited bufpos is casted.
>
>
> > On Thu, Apr 08, 2021 at 06:22:16PM +0200, Theo Buehler wrote:
> > > On Thu, Apr 08, 2021 at 04:47:15PM +0200, Claudio Jeker wrote:
> > > > This diff is a first step in tightening the code in http.c
> > > > It should cleanup the poll handling and make adds some code to ensure
> > > > that
> > > > only expected results are returned. The goal is that http_handle() only
> > > > does IO processing and http_nextstep() is used for transitions into new
> > > > states.
> > >
> > > I like this direction.
> > >
> > > > I did shuffle the code around a bit. So that similar functions are
> > > > together.
> > > > Also by shuffling the poll loop now connections are added after the all
> > > > active connections have been processed.
> > >
> > > This added quite a bit of noise to the diff (especially some parts
> > > shuffling _write() functions are hard to read).
> > >
> > > I'm ok with this, one small comment below.
> > >
> >
> > Sorry about that. I committed the deck chair shuffle and some other minor
> > bits. Here is the part that replaces the regular and chunked write
> > functions with a single data_write one.
> >
> > I made the strtoul() check look like the man page example to detect
> > overflows. There is some off_t vs size_t casts required but in that case
> > the size limited bufpos is casted.
>
> Thanks. Still looks good. Tiny suggestion below, but ok either way.
Yes, your right. I over optimised it a bit and triggered on the less
common iosz == 0 first.
Also here is the last bit of the http work. This changes http_handle() and
ensures that http_nextstep() never returns 0. For http_tls_connect() this
requires a fall through to the next stage in case it returns 0.
Also http_redirect() now calls http_connect() directly and no longer
requires an extra state machine step.
--
:wq Claudio
? obj
Index: http.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/http.c,v
retrieving revision 1.26
diff -u -p -r1.26 http.c
--- http.c 8 Apr 2021 18:35:02 -0000 1.26
+++ http.c 8 Apr 2021 18:38:22 -0000
@@ -410,6 +410,8 @@ http_connect(struct http_connection *con
{
const char *cause = NULL;
+ conn->state = STATE_CONNECT;
+
if (conn->fd != -1) {
close(conn->fd);
conn->fd = -1;
@@ -702,7 +704,7 @@ http_redirect(struct http_connection *co
if (http_resolv(conn, host, port) == -1)
return -1;
- return 0;
+ return http_connect(conn);
}
static int
@@ -995,12 +997,34 @@ data_write(struct http_connection *conn)
return WANT_POLLOUT;
}
+/*
+ * Do one IO call depending on the connection state.
+ * Return WANT_POLLIN or WANT_POLLOUT to poll for more data.
+ * If 0 is returned this stage is finished and the protocol should move
+ * to the next stage by calling http_nextstep(). On error return -1.
+ */
static int
-http_handle(struct http_connection *conn)
+http_handle(struct http_connection *conn, int events)
{
+ /* special case for INIT, there is no event to start a request */
+ if (conn->state == STATE_INIT)
+ return http_connect(conn);
+
+ if (events & POLLHUP) {
+ if (conn->state == STATE_WRITE_DATA)
+ warnx("%s: output pipe closed", http_info(conn->url));
+ else
+ warnx("%s: server closed connection",
+ http_info(conn->url));
+ return -1;
+ }
+
+ if ((events & conn->events) == 0) {
+ warnx("%s: unexpected event", http_info(conn->url));
+ return -1;
+ }
+
switch (conn->state) {
- case STATE_INIT:
- return 0;
case STATE_CONNECT:
if (http_finish_connect(conn) == -1)
/* something went wrong, try other host */
@@ -1019,22 +1043,31 @@ http_handle(struct http_connection *conn
return data_write(conn);
case STATE_DONE:
return http_close(conn);
+ case STATE_INIT:
case STATE_FREE:
errx(1, "bad http state");
}
errx(1, "unknown http state");
}
+/*
+ * Move the state machine forward until IO needs to happen.
+ * Returns either WANT_POLLIN or WANT_POLLOUT or -1 on error.
+ */
static int
http_nextstep(struct http_connection *conn)
{
+ int r;
+
switch (conn->state) {
case STATE_INIT:
- conn->state = STATE_CONNECT;
- return http_connect(conn);
+ errx(1, "bad http state");
case STATE_CONNECT:
conn->state = STATE_TLSCONNECT;
- return http_tls_connect(conn);
+ r = http_tls_connect(conn);
+ if (r != 0)
+ return r;
+ /* FALLTHROUGH */
case STATE_TLSCONNECT:
conn->state = STATE_REQUEST;
return http_request(conn);
@@ -1082,9 +1115,9 @@ http_nextstep(struct http_connection *co
}
static int
-http_do(struct http_connection *conn)
+http_do(struct http_connection *conn, int events)
{
- switch (http_handle(conn)) {
+ switch (http_handle(conn, events)) {
case -1:
/* connection failure */
if (conn->state != STATE_DONE)
@@ -1104,6 +1137,9 @@ http_do(struct http_connection *conn)
http_fail(conn->id);
http_free(conn);
return -1;
+ case 0:
+ errx(1, "%s: http_nextstep returned 0, state %d",
+ http_info(conn->url), conn->state);
}
break;
case WANT_POLLIN:
@@ -1194,18 +1230,22 @@ proc_http(char *bind_addr, int fd)
err(1, "write");
}
}
+
+ /* process active http requests */
for (i = 0; i < MAX_CONNECTIONS; i++) {
struct http_connection *conn = http_conns[i];
if (conn == NULL)
continue;
/* event not ready */
- if (!(pfds[i].revents & (conn->events | POLLHUP)))
+ if (pfds[i].revents == 0)
continue;
- if (http_do(conn) == -1)
+ if (http_do(conn, pfds[i].revents) == -1)
http_conns[i] = NULL;
}
+
+ /* process new requests last */
if (pfds[MAX_CONNECTIONS].revents & POLLIN) {
struct http_connection *h;
size_t id;
@@ -1223,7 +1263,7 @@ proc_http(char *bind_addr, int fd)
if (http_conns[i] != NULL)
continue;
http_conns[i] = h;
- if (http_do(h) == -1)
+ if (http_do(h, 0) == -1)
http_conns[i] = NULL;
break;
}