Permalink
Browse files

Implement turbocache varying by cookie value

  • Loading branch information...
1 parent a6a16c8 commit a760649cd79fde4363ace40dd0e353c445449d04 @FooBarWidget FooBarWidget committed Dec 23, 2014
View
@@ -123,6 +123,10 @@
ext/common/ServerKit/HttpRequest.h
ext/common/ServerKit/HttpHeaderParser.h
ext/common/ServerKit/HttpChunkedBodyParser.h),
+ 'test/cxx/ServerKit/CookieUtilsTest.o' => %w(
+ test/cxx/ServerKit/CookieUtilsTest.cpp
+ ext/common/ServerKit/CookieUtils.h
+ ext/common/DataStructures/LString.h),
'test/cxx/DataStructures/LStringTest.o' => %w(
test/cxx/DataStructures/LStringTest.cpp
ext/common/DataStructures/LString.h),
@@ -118,6 +118,16 @@ psg_lstr_append_part(LString *str, LString::Part *part) {
}
inline void
+psg_lstr_append_part_from_another_lstr(LString *str, psg_pool_t *pool, const LString::Part *part) {
+ LString::Part *copy = (LString::Part *) psg_palloc(pool, sizeof(LString::Part));
+ *copy = *part;
+ if (part->mbuf_block != NULL) {
+ mbuf_block_ref(part->mbuf_block);
+ }
+ psg_lstr_append_part(str, copy);
+}
+
+inline void
psg_lstr_append(LString *str, psg_pool_t *pool, const MemoryKit::mbuf &buffer,
const char *data, unsigned int size)
{
@@ -0,0 +1,246 @@
+/*
+ * Phusion Passenger - https://www.phusionpassenger.com/
+ * Copyright (c) 2014 Phusion
+ *
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_
+#define _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_
+
+#include <cstring>
+#include <cassert>
+#include <MemoryKit/palloc.h>
+#include <DataStructures/LString.h>
+
+namespace Passenger {
+namespace ServerKit {
+
+
+inline bool findCookieNameValueSeparator(const LString::Part *part, size_t index,
+ const LString::Part **separatorPart, size_t *separatorIndex);
+inline bool findCookieEnd(const LString::Part *separatorPart, size_t separatorIndex,
+ const LString::Part **endPart, size_t *endIndex);
+inline bool matchCookieName(psg_pool_t *pool, const LString::Part *part, size_t index,
+ const LString::Part *separatorPart, size_t separatorIndex,
+ const LString *name);
+inline LString *extractCookieValue(psg_pool_t *pool,
+ const LString::Part *separatorPart, size_t separatorIndex,
+ const LString::Part *endPart, size_t endIndex);
+
+
+/**
+ * Given the value of an HTTP cookie header, returns the value of the cookie
+ * of the given name, or NULL if not found.
+ */
+inline LString *
+findCookie(psg_pool_t *pool, const LString *cookieHeaderValue, const LString *name) {
+ const LString::Part *part = cookieHeaderValue->start;
+ const LString::Part *separatorPart, *endPart;
+ size_t index = 0, separatorIndex, endIndex;
+ bool done = part == NULL;
+ LString *result = NULL;
+
+ while (!done) {
+ if (findCookieNameValueSeparator(part, index, &separatorPart, &separatorIndex)) {
+ if (!findCookieEnd(separatorPart, separatorIndex, &endPart, &endIndex)) {
+ done = true;
+ } else if (matchCookieName(pool, part, index, separatorPart, separatorIndex, name)) {
+ result = extractCookieValue(pool, separatorPart, separatorIndex, endPart, endIndex);
+ done = true;
+ } else {
+ part = endPart;
+ index = endIndex;
+ done = endIndex >= endPart->size;
+ }
+ } else {
+ done = true;
+ }
+ }
+
+ return result;
+}
+
+inline bool
+findCookieNameValueSeparator(const LString::Part *part, size_t index,
+ const LString::Part **separatorPart, size_t *separatorIndex)
+{
+ const char *pos;
+ bool result = false;
+ bool done = part == NULL;
+
+ while (!done) {
+ pos = (const char *) memchr(part->data + index, '=', part->size - index);
+ if (pos == NULL) {
+ part = part->next;
+ index = 0;
+ done = part == NULL;
+ } else {
+ *separatorPart = part;
+ *separatorIndex = pos - part->data;
+ result = true;
+ done = true;
+ }
+ }
+
+ return result;
+}
+
+inline bool
+findCookieEnd(const LString::Part *separatorPart, size_t separatorIndex,
+ const LString::Part **endPart, size_t *endIndex)
+{
+ const LString::Part *part = separatorPart;
+ size_t index = separatorIndex;
+ const char *pos;
+ bool result = false;
+ bool done = part == NULL;
+
+ while (!done) {
+ pos = (const char *) memchr(part->data + index, ';', part->size - index);
+ if (pos == NULL) {
+ if (part->next == NULL) {
+ // Semicolon not found in entire LString. Return end-of-LString
+ // as cookie end.
+ *endPart = part;
+ *endIndex = part->size;
+ result = true;
+ done = true;
+ } else {
+ part = part->next;
+ index = 0;
+ done = part == NULL;
+ }
+ } else {
+ // Semicolon found.
+ *endPart = part;
+ *endIndex = pos - part->data;
+ result = true;
+ done = true;
+ }
+ }
+
+ return result;
+}
+
+inline void
+_matchCookieName_skipWhitespace(LString *str) {
+ LString::Part *part = str->start;
+ size_t pos = 0;
+ bool done = false;
+
+ while (!done) {
+ while (part->data[pos] == ' ' || part->data[pos] == ';') {
+ pos++;
+ }
+
+ if (pos == part->size) {
+ str->start = part->next;
+ str->size -= part->size;
+ part = part->next;
+ if (part == NULL) {
+ assert(str->size == 0);
+ done = true;
+ str->end = NULL;
+ }
+ } else {
+ part->data += pos;
+ part->size -= pos;
+ str->size -= pos;
+ done = true;
+ }
+ }
+}
+
+inline bool
+matchCookieName(psg_pool_t *pool, const LString::Part *part, size_t index,
+ const LString::Part *separatorPart, size_t separatorIndex,
+ const LString *name)
+{
+ LString *str = (LString *) psg_palloc(pool, sizeof(LString));
+ psg_lstr_init(str);
+
+ if (part == separatorPart) {
+ assert(index < separatorIndex);
+ psg_lstr_append(str, pool,
+ part->data + index,
+ separatorIndex - index);
+ } else {
+ psg_lstr_append(str, pool,
+ part->data + index,
+ part->size - index);
+
+ part = part->next;
+ while (part != separatorPart) {
+ psg_lstr_append(str, pool, part->data, part->size);
+ part = part->next;
+ }
+
+ if (separatorIndex != 0) {
+ psg_lstr_append(str, pool, separatorPart->data, separatorIndex);
+ }
+ }
+
+ _matchCookieName_skipWhitespace(str);
+
+ bool result = psg_lstr_cmp(str, name);
+ psg_lstr_deinit(str);
+ return result;
+}
+
+inline LString *
+extractCookieValue(psg_pool_t *pool,
+ const LString::Part *separatorPart, size_t separatorIndex,
+ const LString::Part *endPart, size_t endIndex)
+{
+ LString *str = (LString *) psg_palloc(pool, sizeof(LString));
+ psg_lstr_init(str);
+
+ if (separatorPart == endPart) {
+ assert(separatorIndex < endIndex);
+ psg_lstr_append(str, pool,
+ separatorPart->data + separatorIndex + 1,
+ endIndex - separatorIndex - 1);
+ } else {
+ if (separatorIndex < separatorPart->size - 1) {
+ psg_lstr_append(str, pool,
+ separatorPart->data + separatorIndex + 1,
+ separatorPart->size - separatorIndex - 1);
+ }
+
+ const LString::Part *part = separatorPart->next;
+ while (part != endPart) {
+ psg_lstr_append(str, pool, part->data, part->size);
+ part = part->next;
+ }
+
+ if (endIndex != 0) {
+ psg_lstr_append(str, pool, endPart->data, endIndex);
+ }
+ }
+
+ return str;
+}
+
+
+} // namespace ServerKit
+} // namespace Passenger
+
+#endif /* _PASSENGER_SERVER_KIT_COOKIE_UTILS_H_ */
@@ -975,6 +975,7 @@ setAgentsOptionsDefaults() {
options.setDefaultInt("stat_throttle_rate", DEFAULT_STAT_THROTTLE_RATE);
options.setDefault("server_software", SERVER_TOKEN_NAME "/" PASSENGER_VERSION);
options.setDefaultBool("show_version_in_header", true);
+ options.setDefault("sticky_sessions_cookie_name", DEFAULT_STICKY_SESSIONS_COOKIE_NAME);
options.setDefaultBool("turbocaching", true);
options.setDefault("data_buffer_dir", getSystemTempDir());
options.setDefaultBool("selfchecks", false);
@@ -108,8 +108,6 @@ serverUsage() {
printf(" Force friendly error pages to be always on\n");
printf(" --disable-friendly-error-pages\n");
printf(" Force friendly error pages to be always off\n");
- printf(" --disable-turbocaching\n");
- printf(" Disable turbocaching\n");
printf("\n");
printf(" --ruby PATH Default Ruby interpreter to use.\n");
printf("\n");
@@ -123,6 +121,14 @@ serverUsage() {
printf(" may be idle. Default: %d\n", DEFAULT_POOL_IDLE_TIME);
printf(" --min-instances N Minimum number of application processes. Default: 1\n");
printf("\n");
+ printf("Request handling options (optional):\n");
+ printf(" --sticky-sessions-cookie-name NAME\n");
+ printf(" Cookie name to use for sticky sessions\n");
+ printf(" --vary-turbocache-by-cookie NAME\n");
+ printf(" Vary the turbocache by the cookie of the given name\n");
+ printf(" --disable-turbocaching\n");
+ printf(" Disable turbocaching\n");
+ printf("\n");
printf("Other options (optional):\n");
printf(" --log-file PATH Log to the given file.\n");
printf(" --log-level LEVEL Logging level. Default: %d\n", DEFAULT_LOG_LEVEL);
@@ -262,6 +268,12 @@ parseServerOption(int argc, const char *argv[], int &i, VariantMap &options) {
} else if (p.isFlag(argv[i], '\0', "--disable-friendly-error-pages")) {
options.set("friendly_error_pages", "false");
i++;
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--sticky-sessions-cookie-name")) {
+ options.set("sticky_sessions_cookie_name", argv[i + 1]);
+ i += 2;
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--vary-turbocache-by-cookie")) {
+ options.set("vary_turbocache_by_cookie", argv[i + 1]);
+ i += 2;
} else if (p.isFlag(argv[i], '\0', "--disable-turbocaching")) {
options.setBool("turbocaching", false);
i++;
@@ -206,6 +206,8 @@ class RequestHandler: public ServerKit::HttpServer<RequestHandler, Client> {
StaticString defaultServerName;
StaticString defaultServerPort;
StaticString serverSoftware;
+ StaticString defaultStickySessionsCookieName;
+ StaticString defaultVaryTurbocacheByCookie;
HashedStaticString PASSENGER_APP_GROUP_NAME;
HashedStaticString PASSENGER_MAX_REQUESTS;
@@ -231,6 +233,7 @@ class RequestHandler: public ServerKit::HttpServer<RequestHandler, Client> {
StaticString serverLogName;
friend class TurboCaching<Request>;
+ friend class ResponseCache<Request>;
struct ev_check checkWatcher;
TurboCaching<Request> turboCaching;
@@ -307,6 +310,13 @@ class RequestHandler: public ServerKit::HttpServer<RequestHandler, Client> {
agentsOptions->get("default_server_port"));
serverSoftware = psg_pstrdup(stringPool,
agentsOptions->get("server_software"));
+ defaultStickySessionsCookieName = psg_pstrdup(stringPool,
+ agentsOptions->get("sticky_sessions_cookie_name"));
+
+ if (agentsOptions->has("vary_turbocache_by_cookie")) {
+ defaultVaryTurbocacheByCookie = psg_pstrdup(stringPool,
+ agentsOptions->get("vary_turbocache_by_cookie"));
+ }
generateServerLogName(_threadNumber);
@@ -90,6 +90,7 @@ virtual void reinitializeRequest(Client *client, Request *req) {
req->bodyBytesBuffered = 0;
req->cacheKey = HashedStaticString();
req->cacheControl = NULL;
+ req->varyCookie = NULL;
#ifdef DEBUG_RH_EVENT_LOOP_BLOCKING
req->timedAppPoolGet = false;
Oops, something went wrong.

0 comments on commit a760649

Please sign in to comment.