Skip to content

Commit

Permalink
Compression [fixes #232]
Browse files Browse the repository at this point in the history
  • Loading branch information
vfarcic committed May 5, 2017
1 parent a5cfc5a commit e74f646
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 3 deletions.
2 changes: 2 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The following environment variables can be used to configure the *Docker Flow Pr
|BIND_PORTS |Ports to bind in addition to `80` and `443`. Multiple values can be separated with comma. If a port is specified with the `srcPort` reconfigure parameter, it is not required to specify it in this environment variable. Those values will be used as default ports used for services that do not specify `srcPort`.<br>Example: 8085, 8086|No| |
|CHECK_RESOLVERS |Enable `docker` as a resolver. Provides higher reliability at the cost of backend initialization time. If enabled, it might take a few seconds until a backend is resolved and operational| |false|
|CERTS |This parameter is **deprecated** as of February 2017. All the certificates from the `/cets/` directory are now loaded automatically| | |
|COMPRESSION_ALGO |Enable HTTP compression. The currently supported algorithms are:<br>**identity**: this is mostly for debugging.<br>**gzip**: applies gzip compression. This setting is only available when support for zlib or libslz was built in.<br>**deflate** same as *gzip*, but with deflate algorithm and zlib format. Note that this algorithm has ambiguous support on many browsers and no support at all from recent ones. It is strongly recommended not to use it for anything else than experimentation. This setting is only available when support for zlib or libslz was built in.<br>**raw-deflate**: same as *deflate* without the zlib wrapper, and used as an alternative when the browser wants "deflate". All major browsers understand it and despite violating the standards, it is known to work better than *deflate*, at least on MSIE and some versions of Safari. This setting is only available when support for zlib or libslz was built in.<br>Compression will be activated depending on the Accept-Encoding request header. With identity, it does not take care of that header. If backend servers support HTTP compression, these directives will be no-op: haproxy will see the compressed response and will not compress again. If backend servers do not support HTTP compression and there is Accept-Encoding header in request, haproxy will compress the matching response.<br>Compression is disabled when:<br>* the request does not advertise a supported compression algorithm in the "Accept-Encoding" header<br>* the response message is not HTTP/1.1<br>* HTTP status code is not 200<br>* response header "Transfer-Encoding" contains "chunked" (Temporary Workaround)<br>* response contain neither a "Content-Length" header nor a "Transfer-Encoding" whose last value is "chunked"<br>* response contains a "Content-Type" header whose first value starts with "multipart"<br>* the response contains the "no-transform" value in the "Cache-control" header<br>* User-Agent matches "Mozilla/4" unless it is MSIE 6 with XP SP2, or MSIE 7 and later<br>* The response contains a "Content-Encoding" header, indicating that the response is already compressed (see compression offload)<br>Example: gzip|No| |
|COMPRESSION_TYPE |The type of files that will be compressed.<br>Example: text/css text/html text/javascript application/javascript text/plain text/xml application/json|No| |
|CONNECTION_MODE |HAProxy supports 5 connection modes.<br><br>`http-keep-alive`: all requests and responses are processed.<br>`http-tunnel`: only the first request and response are processed, everything else is forwarded with no analysis.<br>`httpclose`: tunnel with "Connection: close" added in both directions.<br>`http-server-close`: the server-facing connection is closed after the response.<br>`forceclose`: the connection is actively closed after end of response.<br><br>In general, it is preferred to use `http-server-close` with application servers, and some static servers might benefit from `http-keep-alive`.<br>Example: http-keep-alive|No|http-keep-alive|
|DEBUG |Enables logging of each request sent through the proxy. Please consult [Debug Format](#debug-format) for info about the log entries. This feature should be used with caution. **Do not enable debugging in production unless necessary.**<br>Example: true|No|false|
|DEBUG_ERRORS_ONLY |If set to `true`, only requests that resulted in an error, timeout, retry, and redispatch will be logged. If a request is HTTP, responses with a status 5xx will be logged too. This variable will take effect only if `DEBUG` is set to `true`.<br>Example: true|No|false|
Expand Down
37 changes: 34 additions & 3 deletions integration_tests/integration_swarm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"testing"
"time"
"log"
)

// Setup
Expand Down Expand Up @@ -54,7 +55,10 @@ func TestGeneralIntegrationSwarmTestSuite(t *testing.T) {
-e STATS_PASS=none \
%s/docker-flow-proxy:beta`,
s.dockerHubUser)
s.createService(cmd)
_, err := s.createService(cmd)
if err != nil {
log.Fatal(err)
}

s.createService(`docker service create --name go-demo-db \
--network go-demo \
Expand Down Expand Up @@ -82,6 +86,33 @@ func (s IntegrationSwarmTestSuite) Test_Reconfigure() {
}
}

func (s IntegrationSwarmTestSuite) Test_Compression() {
defer func() {
exec.Command("/bin/sh", "-c", `docker service update --env-rm "COMPRESSION_ALGO" proxy`).Output()
s.waitForContainers(1, "proxy")
}()
_, err := exec.Command(
"/bin/sh",
"-c",
`docker service update --env-add "COMPRESSION_ALGO=gzip" --env-add "COMPRESSION_TYPE=text/css text/html text/javascript application/javascript text/plain text/xml application/json" proxy`,
).Output()
s.NoError(err)
s.waitForContainers(1, "proxy")
s.reconfigureGoDemo("")

client := new(http.Client)
url := fmt.Sprintf("http://%s/demo/hello", s.hostIP)
req, err := http.NewRequest("GET", url, nil)
req.Header.Add("Accept-Encoding", "gzip")
resp, err := client.Do(req)

s.NoError(err)
if resp != nil {
s.Equal(200, resp.StatusCode, s.getProxyConf())
s.Contains(resp.Header["Content-Encoding"], "gzip", s.getProxyConf())
}
}

func (s IntegrationSwarmTestSuite) Test_Stats() {
url := fmt.Sprintf("http://%s/admin?stats", s.hostIP)

Expand Down Expand Up @@ -324,8 +355,8 @@ func (s *IntegrationSwarmTestSuite) areContainersRunning(expected int, name stri
return len(lines) == (expected + 1) //+1 because there is new line at the end of ps output
}

func (s *IntegrationSwarmTestSuite) createService(command string) {
exec.Command("/bin/sh", "-c", command).Output()
func (s *IntegrationSwarmTestSuite) createService(command string) ([]byte, error) {
return exec.Command("/bin/sh", "-c", command).Output()
}

func (s *IntegrationSwarmTestSuite) removeServices(service ...string) {
Expand Down
12 changes: 12 additions & 0 deletions proxy/ha_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,18 @@ func (m HaProxy) getConfigData() ConfigData {
d.ExtraDefaults += `
default-server init-addr last,libc,none`
}
if len(os.Getenv("COMPRESSION_ALGO")) > 0 {
d.ExtraDefaults += fmt.Sprintf(`
compression algo %s`,
os.Getenv("COMPRESSION_ALGO"),
)
if len(os.Getenv("COMPRESSION_TYPE")) > 0 {
d.ExtraDefaults += fmt.Sprintf(`
compression type %s`,
os.Getenv("COMPRESSION_TYPE"),
)
}
}
if strings.EqualFold(GetSecretOrEnvVar("DEBUG", ""), "true") {
d.ExtraGlobal += `
log 127.0.0.1:1514 local0`
Expand Down
60 changes: 60 additions & 0 deletions proxy/ha_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,66 @@ func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_AddsDefaultServer_WhenC
s.Equal(expectedData, actualData)
}

func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_AddsCompressionAlgo_WhenSet() {
compressionAlgoOrig := os.Getenv("COMPRESSION_ALGO")
defer func() { os.Setenv("COMPRESSION_ALGO", compressionAlgoOrig) }()
os.Setenv("COMPRESSION_ALGO", "gzip")
var actualData string
tmpl := strings.Replace(
s.TemplateContent,
"balance roundrobin\n",
"balance roundrobin\n\n compression algo gzip",
-1,
)
expectedData := fmt.Sprintf(
"%s%s",
tmpl,
s.ServicesContent,
)
writeFile = func(filename string, data []byte, perm os.FileMode) error {
actualData = string(data)
return nil
}

NewHaProxy(s.TemplatesPath, s.ConfigsPath).CreateConfigFromTemplates()

s.Equal(expectedData, actualData)
}

func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_AddsCompressionType_WhenCompressionAlgoAndTypeAreSet() {
compressionAlgoOrig := os.Getenv("COMPRESSION_ALGO")
compressionTypeOrig := os.Getenv("COMPRESSION_TYPE")
defer func() {
os.Setenv("COMPRESSION_ALGO", compressionAlgoOrig)
os.Setenv("COMPRESSION_TYPE", compressionTypeOrig)
}()
os.Setenv("COMPRESSION_ALGO", "gzip")
os.Setenv("COMPRESSION_TYPE", "text/css text/html text/javascript application/javascript text/plain text/xml application/json")
var actualData string
tmpl := strings.Replace(
s.TemplateContent,
"balance roundrobin\n",
`balance roundrobin
compression algo gzip
compression type text/css text/html text/javascript application/javascript text/plain text/xml application/json`,
-1,
)
expectedData := fmt.Sprintf(
"%s%s",
tmpl,
s.ServicesContent,
)
writeFile = func(filename string, data []byte, perm os.FileMode) error {
actualData = string(data)
return nil
}

NewHaProxy(s.TemplatesPath, s.ConfigsPath).CreateConfigFromTemplates()

s.Equal(expectedData, actualData)
}

func (s HaProxyTestSuite) Test_CreateConfigFromTemplates_AddsStats_WhenStatsUserAndPassArePresent() {
var actualData string
statUserOrig := os.Getenv("STATS_USER")
Expand Down

0 comments on commit e74f646

Please sign in to comment.