Skip to content

Conversation

@vii
Copy link
Contributor

@vii vii commented Dec 1, 2017

Add callback to allow HTTP servers to decline (and thereby close) incoming connections.

This is important, as otherwise clients can easily exhaust the file
descriptors available on a libevent HTTP server, which can cause
problems in other code which does not handle EMFILE well: for example,
see bitcoin/bitcoin#11368

@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) to 80.602% when pulling 13885ab on vii:master into c2c08e0 on libevent:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) to 80.602% when pulling 13885ab on vii:master into c2c08e0 on libevent:master.

@vii
Copy link
Contributor Author

vii commented Dec 4, 2017

I notice this failure on one configuration - however, all the other HTTP tests seem to have failed too? Is there anything I can do?

 FAIL test/regress_http.c:4620: assert(state->connections_throttled >= state->connections_error): 15 vs 16http/throttlereqcb: 
  FAIL test/regress_http.c:4631: (Failed.)
  [throttlereqcb FAILED]
FAILED

Copy link
Member

@azat azat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for delay, could you take a look at the comments?

http.c Outdated
case 3:
/* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clean this patch from this whitespace fixes (I know I know, it looks better now, but it pollutes the patch)


/* Older versions of libevent did not have this callback, so make it
* possible for users to detect it */
#define EVENT2_HTTP_HAS_THROTTLECB
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like it here, we should either move it into some *-config.h or just let user detect this by himself, using his build system (well it is simpler to just #ifdef but we don't have such defines for other functions). Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plus I guess it should be defined as EVENT2_HTTP_HAS_THROTTLEREQCB to avoid different namings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this should be renamed with the other rename.

My suggestion here is that we should make presence of this callback easy to detect at runtime.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is not "at runtime", and there is nothing that we can do here

const char *address, ev_uint16_t port);

/**
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well this is definitely a bad hunk (even though previous whitespace hunks were fine, at least they make sense)

listener.c Outdated
void *user_data;
LOCK(lev);
while (1) {
while (lev->enabled) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what this for? even though this makes sense, this should be a separate patch, otherwise it will became too tricky to track changes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the intention was to immediately act, if a cb decides to disable the listener, to prevent it draining the queue.

If you would accept this as a separate PR I would add it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, just submit another PR, since this is totally different thing

return;
end:
tt_fail();
exit(17);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that tt_abort() is better instead, no? or does the semantic differs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tt_abort ends up doing goto end - I copied this more drastic exit from elsewhere in the file

struct http_throttlereqcb_test_state* state = arg;
state->connections_noticed ++;
http_throttlereqcb_test_state_check(state);
if (1 == state->connections_noticed%7) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh, why do people prefer 1 == some_var instead of some_var == 1, never mind.

Copy link
Contributor Author

@vii vii Dec 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fewer typos!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, don't see your point

http.c Outdated
}

void
evhttp_set_throttlereqcb(struct evhttp *http,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this could be renamed, since this is useful not only for throttling, but also for some kind of whitelist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azat exactly - happy to rename it, what would you like me to call it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azat called it newreqcb

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sounds more generic

http.c Outdated

TAILQ_INSERT_TAIL(&evcon->requests, req, next);
if (http->throttlereqcb) {
if (http->throttlereqcb(req, http->throttlereqcbarg)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace issues, we use tabs, not spaces

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and please check all other places)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plus I guess this can be rewritten with if (http->throttlereqcb && http->throttlereqcb(...)) { ... }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done! Hope the whitespace is better now!

http_request_error_throttlereqcb(enum evhttp_request_error err, void *arg)
{
struct http_throttlereqcb_test_state* state = arg;
state->connections_error ++;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what space for?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and in other places too)

end:
evhttp_free(http);

for (n = 0; n < sizeof(evcons)/sizeof(evcons[0]); ++n) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is ARRAY_SIZE() in regress_dnc.c, could be moved into some header and re-used here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would that really be clearer? People unfamiliar with the project would have to understand which kind of size is being referred to by that macro

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty common macro I suppose, but I'm fine with that sizeof/sizeof

@azat
Copy link
Member

azat commented Dec 11, 2017

I notice this failure on one configuration - however, all the other HTTP tests seem to have failed too? Is there anything I can do?

could give the link to this failures?

@vii
Copy link
Contributor Author

vii commented Dec 12, 2017

the failures I am referring to are in this run https://travis-ci.org/libevent/libevent/jobs/309815275

Sorry for the whitespace changes. I'll try to go through and fix them.

@vii vii force-pushed the master branch 2 times, most recently from 6e1a7e1 to 1e1f699 Compare December 16, 2017 17:48
@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 80.528% when pulling 1e1f699 on vii:master into 65eb529 on libevent:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 80.528% when pulling 1e1f699 on vii:master into 65eb529 on libevent:master.

@azat
Copy link
Member

azat commented Dec 16, 2017

Sorry for the whitespace changes. I'll try to go through and fix them.

yep, now it looks cleaner, thanks!

@azat
Copy link
Member

azat commented Dec 16, 2017

the failures I am referring to are in this run https://travis-ci.org/libevent/libevent/jobs/309815275

grep connections_throttled doesn't show anything, and your tests passes there, while you reported such trace before:

 FAIL test/regress_http.c:4620: assert(state->connections_throttled >= state->connections_error): 15 vs 16http/throttlereqcb: 
  FAIL test/regress_http.c:4631: (Failed.)
  [throttlereqcb FAILED]
FAILED

*/
EVENT2_EXPORT_SYMBOL
void evhttp_set_newreqcb(struct evhttp *http,
int (*cb)(struct evhttp_request*, void *), void *arg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about returning enum instead, but don't have a clear idea what we can add in future, plus all other callbacks returns int. At least let's make well-defined return code, let's say "If the callback returns -1, the connection will be terminated"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 80.527% when pulling 1e1f699 on vii:master into 65eb529 on libevent:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 80.527% when pulling 1e1f699 on vii:master into 65eb529 on libevent:master.

…oming connections.

This is important, as otherwise clients can easily exhaust the file
descriptors available on a libevent HTTP server, which can cause
problems in other code which does not handle EMFILE well: for example,
see bitcoin/bitcoin#11368
@vii
Copy link
Contributor Author

vii commented Dec 17, 2017

as far as I can tell, all the HTTP regression tests fail in that CI mode so the new one failing too is to be expected

Would you like any more changes @azat ?

@azat
Copy link
Member

azat commented Dec 17, 2017

as far as I can tell, all the HTTP regression tests fail in that CI mode so the new one failing too is to be expected

Well most of the time tests passes.
But there are flacky tests that we should address.

Would you like any more changes @azat ?

Nope, I will take a look through this again today, thanks for fixing all notes!

@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 80.51% when pulling 40ea136 on vii:master into 6e5c15d on libevent:master.

1 similar comment
@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 80.51% when pulling 40ea136 on vii:master into 6e5c15d on libevent:master.

@azat azat closed this in 727bcea Dec 18, 2017
@azat
Copy link
Member

azat commented Dec 18, 2017

Applied, thanks! (but one small comment, please take a look at the differences between upstream commit and your patch, since I had been fixed small coding style issues)

@vii
Copy link
Contributor Author

vii commented Dec 18, 2017

appreciate the help with the coding style @azat! BTW, here is the other small change, that was omitted in the final version of this #581

azat pushed a commit that referenced this pull request Jan 4, 2018
This is a refined version of the logic previously in #578

The rationale is that the consumer of sockets may wish to temporarily
delay accepting for some reason (e.g. being out of file-descriptors).
The kernel will then queue them up. The kernel queue is bounded and
programs like NodeJS will actually try to quickly accept and then close
(as the current behaviour before this PR).

However, it seems that libevent should allow the user to choose whether
to accept and respond correctly if the listener is disabled.
@fjahr fjahr mentioned this pull request May 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants