-
Notifications
You must be signed in to change notification settings - Fork 37
relpsess: skip graceful close for half-open clients #289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -220,6 +220,62 @@ relpRetVal relpEngineSetOnErr(relpEngine_t *pThis, | |
| void (*pCB)(void*pUsr, char *objinfo, char*errmsg, relpRetVal errcode) ); | ||
| relpRetVal relpEngineSetOnGenericErr(relpEngine_t *pThis, | ||
| void (*pCB)(char *objinfo, char*errmsg, relpRetVal errcode) ); | ||
| /** | ||
| * Set a session-open callback. | ||
| * | ||
| * Callback signature: | ||
| * void cb(void *pUsr, const relpSess_t *pSess) | ||
| * | ||
| * The callback is invoked after a server-side RELP session successfully | ||
| * completes the open handshake and is ready for traffic. The callback runs | ||
| * on the relpEngineRun() thread. The relpSess_t pointer is only valid for the | ||
| * duration of the callback and must not be retained. | ||
| * | ||
| * Callback parameters: | ||
| * | ||
| * pUsr - user pointer from relpSrvSetUsrPtr() | ||
| * pSess - session being opened (read-only, ephemeral) | ||
| */ | ||
| relpRetVal relpEngineSetOnSessOpen(relpEngine_t *pThis, | ||
| void (*pCB)(void*pUsr, const relpSess_t *pSess) ); | ||
| /** | ||
| * Set a session-close callback. | ||
| * | ||
| * Callback signature: | ||
| * void cb(void *pUsr, const relpSess_t *pSess, relpRetVal reason) | ||
| * | ||
| * The callback fires when a session is removed from the engine, regardless | ||
| * of the close cause (protocol close, I/O error, or engine shutdown). The | ||
| * reason parameter contains the relpRetVal that triggered teardown; engine | ||
| * shutdown uses RELP_RET_SESSION_CLOSED. The callback runs on the | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The Prompt for AI agents |
||
| * relpEngineRun() thread and pSess is valid only during the callback. | ||
| * | ||
| * Callback parameters: | ||
| * | ||
| * pUsr - user pointer from relpSrvSetUsrPtr() | ||
| * pSess - session being closed (read-only, ephemeral) | ||
| * reason - relpRetVal that caused teardown | ||
| */ | ||
| relpRetVal relpEngineSetOnSessClose(relpEngine_t *pThis, | ||
| void (*pCB)(void*pUsr, const relpSess_t *pSess, relpRetVal reason) ); | ||
| /** | ||
| * Set a session-open-failed callback. | ||
| * | ||
| * Callback signature: | ||
| * void cb(void *pUsr, const relpSess_t *pSess, relpRetVal reason) | ||
| * | ||
| * The callback fires when the server-side open handshake fails. It is | ||
| * invoked before the error response is sent. The callback runs on the | ||
| * relpEngineRun() thread and pSess is valid only during the callback. | ||
| * | ||
| * Callback parameters: | ||
| * | ||
| * pUsr - user pointer from relpSrvSetUsrPtr() | ||
| * pSess - session that failed to open (read-only, ephemeral) | ||
| * reason - relpRetVal describing the failure | ||
| */ | ||
| relpRetVal relpEngineSetOnSessOpenFail(relpEngine_t *pThis, | ||
| void (*pCB)(void*pUsr, const relpSess_t *pSess, relpRetVal reason) ); | ||
|
|
||
| /* exposed server property set functions */ | ||
| relpRetVal relpSrvSetLstnPort(relpSrv_t *pThis, unsigned char *pLstnPort); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,6 +89,13 @@ callOnErr(const relpSess_t *__restrict__ const pThis, | |
| } | ||
| } | ||
|
|
||
| static int | ||
| relpSessCltCanGracefullyDisconnect(const relpSess_t *const pThis) | ||
| { | ||
| return pThis->sessState == eRelpSessState_READY_TO_SEND | ||
| || pThis->sessState == eRelpSessState_WINDOW_FULL; | ||
| } | ||
|
|
||
|
|
||
| /* helper to free permittedPeer structure */ | ||
| static inline void | ||
|
|
@@ -180,8 +187,7 @@ relpSessDestruct(relpSess_t **ppThis) | |
| } | ||
| } else { | ||
| /* we are at the client side of the connection */ | ||
| if( pThis->sessState != eRelpSessState_DISCONNECTED | ||
| && pThis->sessState != eRelpSessState_BROKEN) { | ||
| if(relpSessCltCanGracefullyDisconnect(pThis)) { | ||
| relpSessCltDoDisconnect(pThis); | ||
| } | ||
| } | ||
|
|
@@ -613,15 +619,15 @@ relpSessWaitState(relpSess_t *const pThis, const relpSessState_t stateExpected, | |
| clock_gettime(CLOCK_REALTIME, &tCurr); | ||
| } | ||
|
|
||
| finalize_it: | ||
| finalize_it: | ||
| pThis->pEngine->dbgprint((char*)"relpSessWaitState returns %d\n", iRet); | ||
| if( iRet == RELP_RET_TIMED_OUT || | ||
| iRet == RELP_RET_SESSION_BROKEN || | ||
| relpEngineShouldStop(pThis->pEngine)) { | ||
| if(iRet == RELP_RET_TIMED_OUT || relpEngineShouldStop(pThis->pEngine)) { | ||
| /* the session is broken! */ | ||
| callOnErr(pThis, (char*) "error waiting on required session state, session broken", | ||
| RELP_RET_SESSION_BROKEN); | ||
| pThis->sessState = eRelpSessState_BROKEN; | ||
| } else if(iRet == RELP_RET_SESSION_BROKEN) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||
| pThis->sessState = eRelpSessState_BROKEN; | ||
| } | ||
|
Comment on lines
+624
to
631
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current logic still allows for redundant if(pThis->sessState != eRelpSessState_BROKEN) {
if(iRet == RELP_RET_TIMED_OUT || iRet == RELP_RET_SESSION_BROKEN || relpEngineShouldStop(pThis->pEngine)) {
/* the session is broken! */
callOnErr(pThis, (char*) "error waiting on required session state, session broken",
RELP_RET_SESSION_BROKEN);
pThis->sessState = eRelpSessState_BROKEN;
}
} |
||
|
|
||
| LEAVE_RELPFUNC; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| import os | ||
| import socket | ||
| import time | ||
|
|
||
|
|
||
| def main() -> None: | ||
| port = int(os.environ["TESTPORT"]) | ||
| frame = b"1 open 12 relp_version\n" | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| sock.settimeout(2) | ||
| sock.connect(("127.0.0.1", port)) | ||
| sock.sendall(frame) | ||
| try: | ||
| sock.recv(1024) | ||
| except socket.timeout: | ||
| pass | ||
| time.sleep(0.1) | ||
| sock.close() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Guard
onSessOpenFailso it only fires for sessions that never reached the open state; otherwise a duplicateopenrequest reports a spurious open failure on an already-open session.Prompt for AI agents