Optionally compress large responses, if the browser accepts deflate compression. This mainly improves start up time.

git-svn-id: https://shellinabox.googlecode.com/svn/trunk@142 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
zodiac 2009-07-08 08:33:36 +00:00
parent ca18a5346f
commit 8920606f6f
9 changed files with 2764 additions and 2148 deletions

View file

@ -1,3 +1,8 @@
2009-07-08 Markus Gutschke <markus@shellinabox.com>
* Optionally compress large responses, if the browser accepts
deflate compression. This mainly improves start up time.
2009-07-06 Markus Gutschke <markus@shellinabox.com> 2009-07-06 Markus Gutschke <markus@shellinabox.com>
* Making it easier to host the terminal on non-root URLs by always * Making it easier to host the terminal on non-root URLs by always

View file

@ -106,6 +106,9 @@
/* Define to 1 if you have the <utmp.h> header file. */ /* Define to 1 if you have the <utmp.h> header file. */
#define HAVE_UTMP_H 1 #define HAVE_UTMP_H 1
/* Define to 1 if zlib development files are installed */
#define HAVE_ZLIB 1
/* Define to the sub-directory in which libtool stores uninstalled libraries. /* Define to the sub-directory in which libtool stores uninstalled libraries.
*/ */
#define LT_OBJDIR ".libs/" #define LT_OBJDIR ".libs/"
@ -132,7 +135,7 @@
#define STDC_HEADERS 1 #define STDC_HEADERS 1
/* Most recent revision number in the version control system */ /* Most recent revision number in the version control system */
#define VCS_REVISION "141" #define VCS_REVISION "142"
/* Version number of package */ /* Version number of package */
#define VERSION "2.9" #define VERSION "2.9"

View file

@ -105,6 +105,9 @@
/* Define to 1 if you have the <utmp.h> header file. */ /* Define to 1 if you have the <utmp.h> header file. */
#undef HAVE_UTMP_H #undef HAVE_UTMP_H
/* Define to 1 if zlib development files are installed */
#undef HAVE_ZLIB
/* Define to the sub-directory in which libtool stores uninstalled libraries. /* Define to the sub-directory in which libtool stores uninstalled libraries.
*/ */
#undef LT_OBJDIR #undef LT_OBJDIR

4677
configure vendored

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@ AC_PREREQ(2.57)
dnl This is the one location where the authoritative version number is stored dnl This is the one location where the authoritative version number is stored
AC_INIT(shellinabox, 2.9, markus@shellinabox.com) AC_INIT(shellinabox, 2.9, markus@shellinabox.com)
VCS_REVISION=141 VCS_REVISION=142
AC_SUBST(VCS_REVISION) AC_SUBST(VCS_REVISION)
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
[Most recent revision number in the version control system]) [Most recent revision number in the version control system])
@ -122,6 +122,12 @@ if test "x$enable_runtime_loading" == xno; then
fi fi
fi fi
AC_CHECK_LIB(z, deflate, [
AC_CHECK_HEADER(zlib.h, [LIBS="-lz $LIBS"
AC_DEFINE(HAVE_ZLIB, 1,
Define to 1 if zlib development files are installed)
])])
dnl Generate output files dnl Generate output files
AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([Makefile])
AC_OUTPUT AC_OUTPUT

View file

@ -1693,7 +1693,7 @@ VT100.prototype.toggleBell = function() {
}; };
VT100.prototype.about = function() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.9 (revision 141)" + alert("VT100 Terminal Emulator " + "2.9 (revision 142)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" + "\nCopyright 2008-2009 by Markus Gutschke\n" +
"For more information check http://shellinabox.com"); "For more information check http://shellinabox.com");
}; };

View file

@ -48,6 +48,7 @@
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <math.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdio.h> #include <stdio.h>
@ -58,6 +59,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#ifdef HAVE_STRLCAT #ifdef HAVE_STRLCAT
#define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; }) #define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; })
#endif #endif
@ -414,15 +419,87 @@ void deleteHttpConnection(struct HttpConnection *http) {
free(http); free(http);
} }
#ifdef HAVE_ZLIB
static int httpAcceptsEncoding(struct HttpConnection *http,
const char *encoding) {
int encodingLength = strlen(encoding);
const char *accepts = getFromHashMap(&http->header, "accept-encoding");
if (!accepts) {
return 0;
}
double all = -1.0;
double match = -1.0;
while (*accepts) {
while (*accepts == ' ' || *accepts == '\t' ||
*accepts == '\r' || *accepts == '\n') {
accepts++;
}
const char *ptr = accepts;
while (*ptr && *ptr != ',' && *ptr != ';' &&
*ptr != ' ' && *ptr != '\t' &&
*ptr != '\r' && *ptr != '\n') {
ptr++;
}
int isAll = ptr - accepts == 1 && *accepts == '*';
int isMatch = ptr - accepts == encodingLength &&
!strncasecmp(accepts, encoding, encodingLength);
while (*ptr && *ptr != ';' && *ptr != ',') {
ptr++;
}
double val = 1.0;
if (*ptr == ';') {
ptr++;
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') {
ptr++;
}
if ((*ptr | 0x20) == 'q') {
ptr++;
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') {
ptr++;
}
if (*ptr == '=') {
val = strtod(ptr + 1, (char **)&ptr);
}
}
}
if (isnan(val) || val == -HUGE_VAL || val < 0) {
val = 0;
} else if (val == HUGE_VAL || val > 1.0) {
val = 1.0;
}
if (isAll) {
all = val;
} else if (isMatch) {
match = val;
}
while (*ptr && *ptr != ',') {
ptr++;
}
while (*ptr == ',') {
ptr++;
}
accepts = ptr;
}
if (match >= 0.0) {
return match > 0.0;
} else {
return all > 0.0;
}
}
#endif
void httpTransfer(struct HttpConnection *http, char *msg, int len) { void httpTransfer(struct HttpConnection *http, char *msg, int len) {
check(msg); check(msg);
check(len >= 0); check(len >= 0);
int compress = 0;
char *contentLength = NULL;
if (!http->totalWritten) { if (!http->totalWritten) {
// Perform some basic sanity checks. This does not necessarily catch all // Perform some basic sanity checks. This does not necessarily catch all
// possible problems, though. // possible problems, though.
int l = len; int l = len;
for (char *eol, *lastLine = NULL, *line = msg; char *line = msg;
for (char *eol, *lastLine = NULL;
l > 0 && (eol = memchr(line, '\n', l)) != NULL; ) { l > 0 && (eol = memchr(line, '\n', l)) != NULL; ) {
// All lines end in CR LF // All lines end in CR LF
check(eol[-1] == '\r'); check(eol[-1] == '\r');
@ -432,9 +509,9 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
check(!memcmp(line, "HTTP/1.", 7)); check(!memcmp(line, "HTTP/1.", 7));
check(line[7] >= '0' && line[7] <= '9' && check(line[7] >= '0' && line[7] <= '9' &&
(line[8] == ' ' || line[8] == '\t')); (line[8] == ' ' || line[8] == '\t'));
int i = eol - line - 9; int i = eol - line - 9;
for (char *ptr = line + 9; i-- > 0; ) { for (char *ptr = line + 9; i-- > 0; ) {
char ch = *ptr++; char ch = *ptr++;
if (ch < '0' || ch > '9') { if (ch < '0' || ch > '9') {
check(ptr > line + 10); check(ptr > line + 10);
check(ch == ' ' || ch == '\t'); check(ch == ' ' || ch == '\t');
@ -443,49 +520,122 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
} }
check(i > 1); check(i > 1);
} else if (line + 1 == eol) { } else if (line + 1 == eol) {
// Don't send any data with HEAD requests // Found the end of the headers.
check(l == 2 || strcmp(http->method, "HEAD"));
// Check that we don't send any data with HEAD requests
int isHead = !strcmp(http->method, "HEAD");
check(l == 2 || !isHead);
#ifdef HAVE_ZLIB
// Compress replies that might exceed the size of a single IP packet
compress = !isHead &&
!http->isPartialReply &&
len > 1400 &&
httpAcceptsEncoding(http, "deflate");
#endif
break; break;
} else { } else {
// Header lines either contain a colon, or they are continuation // Header lines either contain a colon, or they are continuation
// lines // lines
if (*line != ' ' && *line != '\t') { if (*line != ' ' && *line != '\t') {
check(memchr(line, ':', eol - line)); check(memchr(line, ':', eol - line));
// Remember where we saw the Content-Length header. We need to edit
// it, if we end up sending compressed data
if (!strncasecmp(line, "Content-Length:", 15)) {
contentLength = line;
}
} }
} }
lastLine = line; lastLine = line;
l -= eol - line + 1; l -= eol - line + 1;
line = eol + 1; line = eol + 1;
}
if (compress) {
#ifdef HAVE_ZLIB
// Create a copy of msg, and edit existing Content-Length line
// out of header.
char *compressed, *ptr;
check(compressed = malloc(len + 100));
if (contentLength) {
memcpy(compressed, msg, contentLength - msg);
char *part2 = strchr(contentLength, '\n');
check(part2++);
memcpy(compressed + (contentLength - msg), part2, line - part2);
ptr = compressed + (contentLength - msg) +
(line - part2);
} else {
memcpy(compressed, msg, line - msg);
ptr = compressed + (line - msg);
}
// Add new Content-Encoding and new Content-Length lines. Leave enough
// space to later edit the actual length back in.
memcpy(ptr,
"Content-Encoding: deflate\r\n"
"Content-Length: \r\n\r\n",
66);
contentLength = ptr + 42;
ptr += 66;
// Compress the message
z_stream strm = { .zalloc = Z_NULL,
.zfree = Z_NULL,
.opaque = Z_NULL,
.avail_in = l - 2,
.next_in = (unsigned char *)line + 2,
.avail_out = len - (ptr - compressed),
.next_out = (unsigned char *)ptr
};
if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) == Z_OK) {
if (deflate(&strm, Z_FINISH) == Z_STREAM_END) {
// Compression was successful and resulted in reduction in size
debug("Compressed response from %d to %d", len, len-strm.avail_out);
free(msg);
msg = compressed;
len -= strm.avail_out;
snprintf(contentLength, 21, "%20d", len - (ptr - compressed));
contentLength[20] = '\r';
} else {
free(compressed);
}
deflateEnd(&strm);
} else {
free(compressed);
}
#endif
} }
} }
http->totalWritten += len;
http->totalWritten += len;
if (!len) { if (!len) {
free(msg); free(msg);
} else if (http->msg) { } else if (http->msg) {
check(http->msg = realloc(http->msg, check(http->msg = realloc(http->msg,
http->msgLength - http->msgOffset + len)); http->msgLength - http->msgOffset + len));
if (http->msgOffset) { if (http->msgOffset) {
memmove(http->msg, http->msg + http->msgOffset, memmove(http->msg, http->msg + http->msgOffset,
http->msgLength - http->msgOffset); http->msgLength - http->msgOffset);
http->msgLength -= http->msgOffset; http->msgLength -= http->msgOffset;
http->msgOffset = 0; http->msgOffset = 0;
} }
memcpy(http->msg + http->msgLength, msg, len); memcpy(http->msg + http->msgLength, msg, len);
http->msgLength += len; http->msgLength += len;
free(msg); free(msg);
} else { } else {
check(!http->msgOffset); check(!http->msgOffset);
http->msg = msg; http->msg = msg;
http->msgLength = len; http->msgLength = len;
} }
// Internet Explorer prior to version 7 has a bug when send XMLHttpRequests // Internet Explorer prior to version 7 has a bug when sending
// over HTTPS that go through a proxy. It won't see the reply until we // XMLHttpRequests over HTTPS that go through a proxy. It won't see the
// close the connection. // reply until we close the connection.
int ieBug = 0; int ieBug = 0;
if (http->sslHndl) { if (http->sslHndl) {
const char *userAgent = getFromHashMap(&http->header, "user-agent"); const char *userAgent = getFromHashMap(&http->header, "user-agent");
const char *msie = userAgent ? strstr(userAgent, "MSIE ") : NULL; const char *msie = userAgent ? strstr(userAgent, "MSIE ") : NULL;
if (msie && msie[5] >= '4' && msie[5] <= '6') { if (msie && msie[5] >= '4' && msie[5] <= '6') {
ieBug++; ieBug++;
} }
@ -499,20 +649,20 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
// return additional data in subsequent calls to the callback handler. // return additional data in subsequent calls to the callback handler.
if (http->isSuspended || http->isPartialReply) { if (http->isSuspended || http->isPartialReply) {
if (http->msg && http->msgLength > 0) { if (http->msg && http->msgLength > 0) {
int wrote = httpWrite(http, http->msg, http->msgLength); int wrote = httpWrite(http, http->msg, http->msgLength);
if (wrote < 0 && errno != EAGAIN) { if (wrote < 0 && errno != EAGAIN) {
httpCloseRead(http); httpCloseRead(http);
free(http->msg); free(http->msg);
http->msgLength = 0; http->msgLength = 0;
http->msg = NULL; http->msg = NULL;
} else if (wrote > 0) { } else if (wrote > 0) {
if (wrote == http->msgLength) { if (wrote == http->msgLength) {
free(http->msg); free(http->msg);
http->msgLength = 0; http->msgLength = 0;
http->msg = NULL; http->msg = NULL;
} else { } else {
memmove(http->msg, http->msg + wrote, http->msgLength - wrote); memmove(http->msg, http->msg + wrote, http->msgLength - wrote);
http->msgLength -= wrote; http->msgLength -= wrote;
} }
} }
} }

View file

@ -355,7 +355,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
}; };
ShellInABox.prototype.about = function() { ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + "2.9 (revision 141)" + alert("Shell In A Box version " + "2.9 (revision 142)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" + "\nCopyright 2008-2009 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" + "For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?

View file

@ -1693,7 +1693,7 @@ VT100.prototype.toggleBell = function() {
}; };
VT100.prototype.about = function() { VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.9 (revision 141)" + alert("VT100 Terminal Emulator " + "2.9 (revision 142)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" + "\nCopyright 2008-2009 by Markus Gutschke\n" +
"For more information check http://shellinabox.com"); "For more information check http://shellinabox.com");
}; };