Improved code session and URL handling

* URL dependency was removed from session handling code. URL object
  was only needed to get session key from client request. This was
  moved somewhere else to achive better code reusability.
* Added URL parsing functionality that can be used without URL object.
This commit is contained in:
KLuka 2015-05-26 22:36:56 +02:00
parent fad30cd57c
commit 6402688084
5 changed files with 37 additions and 39 deletions

View file

@ -113,7 +113,7 @@ static char *urlMakeString(const char *buf, int len) {
} }
} }
static void urlParseQueryString(struct URL *url, const char *query, int len) { static void urlParseQueryString(struct HashMap *hashmap, const char *query, int len) {
const char *key = query; const char *key = query;
const char *value = NULL; const char *value = NULL;
for (const char *ampersand = query; len-- >= 0; ampersand++) { for (const char *ampersand = query; len-- >= 0; ampersand++) {
@ -131,7 +131,7 @@ static void urlParseQueryString(struct URL *url, const char *query, int len) {
v = urlMakeString(value, vl); v = urlMakeString(value, vl);
urlUnescape(v); urlUnescape(v);
} }
addToHashMap(&url->args, k, v); addToHashMap(hashmap, k, v);
} }
key = ampersand + 1; key = ampersand + 1;
value = NULL; value = NULL;
@ -275,7 +275,7 @@ static void urlParsePostBody(struct URL *url,
const char *ctHeader = getFromHashMap(&http->header, "content-type"); const char *ctHeader = getFromHashMap(&http->header, "content-type");
urlParseHeaderLine(&contentType, ctHeader, ctHeader ? strlen(ctHeader) : 0); urlParseHeaderLine(&contentType, ctHeader, ctHeader ? strlen(ctHeader) : 0);
if (getRefFromHashMap(&contentType, "application/x-www-form-urlencoded")) { if (getRefFromHashMap(&contentType, "application/x-www-form-urlencoded")) {
urlParseQueryString(url, buf, len); urlParseQueryString(&url->args, buf, len);
} else if (getRefFromHashMap(&contentType, "multipart/form-data")) { } else if (getRefFromHashMap(&contentType, "multipart/form-data")) {
const char *boundary = getFromHashMap(&contentType, "boundary"); const char *boundary = getFromHashMap(&contentType, "boundary");
if (boundary && *boundary) { if (boundary && *boundary) {
@ -346,7 +346,7 @@ void initURL(struct URL *url, const struct HttpConnection *http,
url->url = NULL; url->url = NULL;
initHashMap(&url->args, urlDestroyHashMapEntry, NULL); initHashMap(&url->args, urlDestroyHashMapEntry, NULL);
if (!strcmp(http->method, "GET")) { if (!strcmp(http->method, "GET")) {
urlParseQueryString(url, url->query, strlen(url->query)); urlParseQueryString(&url->args, url->query, strlen(url->query));
} else if (!strcmp(http->method, "POST")) { } else if (!strcmp(http->method, "POST")) {
urlParsePostBody(url, http, buf, len); urlParsePostBody(url, http, buf, len);
} }
@ -428,3 +428,9 @@ const char *urlGetURL(struct URL *url) {
const struct HashMap *urlGetArgs(struct URL *url) { const struct HashMap *urlGetArgs(struct URL *url) {
return &url->args; return &url->args;
} }
struct HashMap *urlParseQuery(const char *buf, int len) {
struct HashMap *hashmap = newHashMap(urlDestroyHashMapEntry, NULL);
urlParseQueryString(hashmap, buf, len);
return hashmap;
}

View file

@ -83,4 +83,6 @@ const char *urlGetAnchor(struct URL *url);
const char *urlGetURL(struct URL *url); const char *urlGetURL(struct URL *url);
const struct HashMap *urlGetArgs(struct URL *url); const struct HashMap *urlGetArgs(struct URL *url);
struct HashMap *urlParseQuery(const char *buf, int len);
#endif /* URL_H__ */ #endif /* URL_H__ */

View file

@ -108,13 +108,12 @@ void checkGraveyard(void) {
} }
void initSession(struct Session *session, const char *sessionKey, void initSession(struct Session *session, const char *sessionKey,
Server *server, URL *url, const char *peerName) { Server *server, const char *peerName) {
session->sessionKey = sessionKey; session->sessionKey = sessionKey;
session->server = server; session->server = server;
check(session->peerName = strdup(peerName)); check(session->peerName = strdup(peerName));
session->connection = NULL; session->connection = NULL;
session->http = NULL; session->http = NULL;
session->url = url;
session->done = 0; session->done = 0;
session->pty = -1; session->pty = -1;
session->width = 0; session->width = 0;
@ -125,11 +124,11 @@ void initSession(struct Session *session, const char *sessionKey,
session->cleanup = 0; session->cleanup = 0;
} }
struct Session *newSession(const char *sessionKey, Server *server, URL *url, struct Session *newSession(const char *sessionKey, Server *server,
const char *peerName) { const char *peerName) {
struct Session *session; struct Session *session;
check(session = malloc(sizeof(struct Session))); check(session = malloc(sizeof(struct Session)));
initSession(session, sessionKey, server, url, peerName); initSession(session, sessionKey, server, peerName);
return session; return session;
} }
@ -137,7 +136,6 @@ void destroySession(struct Session *session) {
if (session) { if (session) {
free((char *)session->peerName); free((char *)session->peerName);
free((char *)session->sessionKey); free((char *)session->sessionKey);
deleteURL(session->url);
if (session->pty >= 0) { if (session->pty >= 0) {
NOINTR(close(session->pty)); NOINTR(close(session->pty));
} }
@ -205,38 +203,33 @@ char *newSessionKey(void) {
return sessionKey; return sessionKey;
} }
struct Session *findCGISession(int *isNew, HttpConnection *http, URL *url, struct Session *findSession(const char *sessionKey, const char *cgiSessionKey,
const char *cgiSessionKey) { int *sessionIsNew, HttpConnection *http) {
*isNew = 1; *sessionIsNew = 1;
if (!sessions) { if (!sessions) {
sessions = newHashMap(destroySessionHashEntry, NULL); sessions = newHashMap(destroySessionHashEntry, NULL);
} }
const HashMap *args = urlGetArgs(url);
const char *sessionKey = getFromHashMap(args, "session");
struct Session *session= NULL; struct Session *session= NULL;
if (cgiSessionKey && if (cgiSessionKey &&
(!sessionKey || strcmp(cgiSessionKey, sessionKey))) { (!sessionKey || strcmp(cgiSessionKey, sessionKey))) {
// In CGI mode, we only ever allow exactly one session with a // In CGI mode, we only ever allow exactly one session with a
// pre-negotiated key. // pre-negotiated key.
deleteURL(url);
} else { } else {
if (sessionKey && *sessionKey) { if (sessionKey && *sessionKey) {
session = (struct Session *)getFromHashMap(sessions, session = (struct Session *)getFromHashMap(sessions,
sessionKey); sessionKey);
} }
if (session) { if (session) {
*isNew = 0; *sessionIsNew = 0;
deleteURL(session->url);
session->url = url;
} else if (!cgiSessionKey && sessionKey && *sessionKey) { } else if (!cgiSessionKey && sessionKey && *sessionKey) {
*isNew = 0; *sessionIsNew = 0;
debug("Failed to find session: %s", sessionKey); debug("Failed to find session: %s", sessionKey);
deleteURL(url);
} else { } else {
// First contact. Create session, now. // First contact. Create session, now.
check(sessionKey = cgiSessionKey ? strdup(cgiSessionKey) check(sessionKey = cgiSessionKey ? strdup(cgiSessionKey)
: newSessionKey()); : newSessionKey());
session = newSession(sessionKey, httpGetServer(http), url, session = newSession(sessionKey, httpGetServer(http),
httpGetPeerName(http)); httpGetPeerName(http));
addToHashMap(sessions, sessionKey, (const char *)session); addToHashMap(sessions, sessionKey, (const char *)session);
debug("Creating a new session: %s", sessionKey); debug("Creating a new session: %s", sessionKey);
@ -245,10 +238,6 @@ struct Session *findCGISession(int *isNew, HttpConnection *http, URL *url,
return session; return session;
} }
struct Session *findSession(int *isNew, HttpConnection *http, URL *url) {
return findCGISession(isNew, http, url, NULL);
}
void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg){ void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg){
iterateOverHashMap(sessions, fnc, arg); iterateOverHashMap(sessions, fnc, arg);
} }

View file

@ -56,7 +56,6 @@ struct Session {
ServerConnection *connection; ServerConnection *connection;
const char *peerName; const char *peerName;
HttpConnection *http; HttpConnection *http;
URL *url;
int done; int done;
int pty; int pty;
int width; int width;
@ -70,8 +69,8 @@ struct Session {
void addToGraveyard(struct Session *session); void addToGraveyard(struct Session *session);
void checkGraveyard(void); void checkGraveyard(void);
void initSession(struct Session *session, const char *sessionKey, void initSession(struct Session *session, const char *sessionKey,
Server *server, URL *url, const char *peerName); Server *server, const char *peerName);
struct Session *newSession(const char *sessionKey, Server *server, URL *url, struct Session *newSession(const char *sessionKey, Server *server,
const char *peerName); const char *peerName);
void destroySession(struct Session *session); void destroySession(struct Session *session);
void deleteSession(struct Session *session); void deleteSession(struct Session *session);
@ -79,9 +78,8 @@ void abandonSession(struct Session *session);
char *newSessionKey(void); char *newSessionKey(void);
void finishSession(struct Session *session); void finishSession(struct Session *session);
void finishAllSessions(void); void finishAllSessions(void);
struct Session *findCGISession(int *isNew, HttpConnection *http, URL *url, struct Session *findSession(const char *sessionKey, const char *cgiSessionKey,
const char *cgiSessionKey); int *sessionIsNew, HttpConnection *http);
struct Session *findSession(int *isNew, HttpConnection *http, URL *url);
void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg); void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg);
int numSessions(void); int numSessions(void);

View file

@ -345,28 +345,29 @@ static int dataHandler(HttpConnection *http, struct Service *service,
if (!buf) { if (!buf) {
// Somebody unexpectedly closed our http connection (e.g. because of a // Somebody unexpectedly closed our http connection (e.g. because of a
// timeout). This is the last notification that we will get. // timeout). This is the last notification that we will get.
deleteURL(url);
iterateOverSessions(invalidatePendingHttpSession, http); iterateOverSessions(invalidatePendingHttpSession, http);
return HTTP_DONE; return HTTP_DONE;
} }
// Find an existing session, or create the record for a new one // Find an existing session, or create the record for a new one
int isNew; const HashMap *args = urlGetArgs(url);
struct Session *session = findCGISession(&isNew, http, url, cgiSessionKey); const char *sessionKey = getFromHashMap(args, "session");
int sessionIsNew;
struct Session *session = findSession(sessionKey, cgiSessionKey, &sessionIsNew, http);
if (session == NULL) { if (session == NULL) {
httpSendReply(http, 400, "Bad Request", NO_MSG); httpSendReply(http, 400, "Bad Request", NO_MSG);
return HTTP_DONE; return HTTP_DONE;
} }
// Sanity check // Sanity check
if (!isNew && strcmp(session->peerName, httpGetPeerName(http))) { if (!sessionIsNew && strcmp(session->peerName, httpGetPeerName(http))) {
error("Peername changed from %s to %s", error("Peername changed from %s to %s",
session->peerName, httpGetPeerName(http)); session->peerName, httpGetPeerName(http));
httpSendReply(http, 400, "Bad Request", NO_MSG); httpSendReply(http, 400, "Bad Request", NO_MSG);
return HTTP_DONE; return HTTP_DONE;
} }
const HashMap *args = urlGetArgs(session->url);
int oldWidth = session->width; int oldWidth = session->width;
int oldHeight = session->height; int oldHeight = session->height;
const char *width = getFromHashMap(args, "width"); const char *width = getFromHashMap(args, "width");
@ -381,7 +382,7 @@ static int dataHandler(HttpConnection *http, struct Service *service,
} }
// Create a new session, if the client did not provide an existing one // Create a new session, if the client did not provide an existing one
if (isNew) { if (sessionIsNew) {
if (keys) { if (keys) {
bad_new_session: bad_new_session:
abandonSession(session); abandonSession(session);
@ -456,7 +457,7 @@ static int dataHandler(HttpConnection *http, struct Service *service,
session->connection = serverGetConnection(session->server, session->connection = serverGetConnection(session->server,
session->connection, session->connection,
session->pty); session->pty);
if (session->buffered || isNew) { if (session->buffered || sessionIsNew) {
if (completePendingRequest(session, "", 0, MAX_RESPONSE) && if (completePendingRequest(session, "", 0, MAX_RESPONSE) &&
session->connection) { session->connection) {
// Reset the timeout, as we just received a new request. // Reset the timeout, as we just received a new request.
@ -636,7 +637,9 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
!strncasecmp(contentType, "application/x-www-form-urlencoded", 33)) { !strncasecmp(contentType, "application/x-www-form-urlencoded", 33)) {
// XMLHttpRequest carrying data between the AJAX application and the // XMLHttpRequest carrying data between the AJAX application and the
// client session. // client session.
return dataHandler(http, arg, buf, len, url); int status = dataHandler(http, arg, buf, len, url);
deleteURL(url);
return status;
} }
UNUSED(rootPageSize); UNUSED(rootPageSize);
char *html = stringPrintf(NULL, rootPageStart, char *html = stringPrintf(NULL, rootPageStart,