Skip to content

Commit

Permalink
Middleware to decode content encoding
Browse files Browse the repository at this point in the history
Provide tests and documentation.
  • Loading branch information
arteymix committed Apr 20, 2016
1 parent a4bac1b commit 2c4834c
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/middlewares/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ Middlewares

.. toctree::
basepath
decode
server-sent-events
subdomain
2 changes: 2 additions & 0 deletions examples/app/app.vala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public async void respond_async (VSGI.Request req, VSGI.Response res) throws Err

var app = new Router ();

app.use (decode ());

app.use ((req, res, next) => {
res.headers.append ("Server", "Valum/1.0");
HashTable<string, string>? @params = new HashTable<string, string> (str_hash, str_equal);
Expand Down
86 changes: 86 additions & 0 deletions src/valum/valum-decode.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* This file is part of Valum.
*
* Valum is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Valum is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Valum. If not, see <http://www.gnu.org/licenses/>.
*/

using GLib;
using VSGI;

namespace Valum {

public enum DecodeFlags {
/**
* @since 0.3
*/
NONE,
/**
* Forward with the remaining content encodings if they are expected to
* be processed later.
*
* @since 0.3
*/
FORWARD_REMAINING_ENCODINGS
}

/**
* Decode any applied 'Content-Encoding'.
*
* Supports 'gzip', 'deflate' and 'identity', otherwise raise a
* {@link Valum.ServerError.NOT_IMPLEMENTED}.
*
* @since 0.3
*/
public HandlerCallback decode (DecodeFlags flags = DecodeFlags.NONE) {
return (req, res, next, ctx) => {
var encodings = Soup.header_parse_list (req.headers.get_list ("Content-Encoding") ?? "");

// decode is in the opposite order of application
encodings.reverse ();

req.headers.remove ("Content-Encoding");

for (unowned SList<string> encoding = encodings; encoding != null; encoding = encoding.next) {
switch (encoding.data.down ()) {
case "gzip":
case "x-gzip":
req.headers.set_encoding (Soup.Encoding.EOF);
req.convert (new ZlibDecompressor (ZlibCompressorFormat.GZIP));
break;
case "deflate":
req.headers.set_encoding (Soup.Encoding.EOF);
req.convert (new ZlibDecompressor (ZlibCompressorFormat.RAW));
break;
case "identity":
// nothing to do, let's take a break ;)
break;
default:
// reapply remaining encodings
encoding.reverse ();
foreach (var remaining in encoding) {
req.headers.append ("Content-Encoding", remaining);
}
if (DecodeFlags.FORWARD_REMAINING_ENCODINGS in flags) {
return next ();
} else {
throw new ServerError.NOT_IMPLEMENTED ("The '%s' encoding is not supported.",
encoding.data);
}
}
}

return next ();
};
}
}
144 changes: 144 additions & 0 deletions tests/decode-test.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* This file is part of Valum.
*
* Valum is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Valum is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Valum. If not, see <http://www.gnu.org/licenses/>.
*/

using Valum;
using VSGI.Mock;

/**
* @since 0.3
*/
public void test_decode_gzip () {
var req = new Request.with_method ("POST", new Soup.URI ("http://127.0.0.1/"));
var res = new Response (req);

req.headers.append ("Content-Encoding", "gzip");

assert ("gzip" == req.headers.get_list ("Content-Encoding"));

try {
decode () (req, res, () => {
assert (null == req.headers.get_list ("Content-Encoding"));
return true;
}, new Context ());
} catch (Error err) {
assert_not_reached ();
}
}

/**
* @since 0.3
*/
public void test_decode_xgzip () {
var req = new Request.with_method ("POST", new Soup.URI ("http://127.0.0.1/"));
var res = new Response (req);

req.headers.append ("Content-Encoding", "x-gzip");

assert ("x-gzip" == req.headers.get_list ("Content-Encoding"));

try {
decode () (req, res, () => {
assert (null == req.headers.get_list ("Content-Encoding"));
return true;
}, new Context ());
} catch (Error err) {
assert_not_reached ();
}
}

/**
* @since 0.3
*/
public void test_decode_deflate () {
var req = new Request.with_method ("POST", new Soup.URI ("http://127.0.0.1/"));
var res = new Response (req);

req.headers.append ("Content-Encoding", "deflate");

assert ("deflate" == req.headers.get_list ("Content-Encoding"));

try {
decode () (req, res, () => {
assert (null == req.headers.get_list ("Content-Encoding"));
return true;
}, new Context ());
} catch (Error err) {
assert_not_reached ();
}
}

/**
* @since 0.3
*/
public void test_decode_identity () {
var req = new Request.with_method ("POST", new Soup.URI ("http://127.0.0.1/"));
var res = new Response (req);

req.headers.append ("Content-Encoding", "identity");

assert ("identity" == req.headers.get_list ("Content-Encoding"));

try {
decode () (req, res, () => {
assert (null == req.headers.get_list ("Content-Encoding"));
return true;
}, new Context ());
} catch (Error err) {
assert_not_reached ();
}
}

/**
* @since 0.3
*/
public void test_decode_unknown_encoding () {
var req = new Request.with_method ("POST", new Soup.URI ("http://127.0.0.1/"));
var res = new Response (req);

req.headers.append ("Content-Encoding", "br, gzip");

try {
decode () (req, res, () => {
assert_not_reached ();
}, new Context ());
assert_not_reached ();
} catch (ServerError.NOT_IMPLEMENTED err) {
assert ("br" == req.headers.get_list ("Content-Encoding"));
} catch (Error err) {
assert_not_reached ();
}
}

/**
* @since 0.3
*/
public void test_decode_forward_remaining_encodings () {
var req = new Request.with_method ("POST", new Soup.URI ("http://127.0.0.1/"));
var res = new Response (req);

req.headers.append ("Content-Encoding", "gzip, br, deflate"); // brotli is not handled

try {
decode (DecodeFlags.FORWARD_REMAINING_ENCODINGS) (req, res, () => {
assert ("gzip, br" == req.headers.get_list ("Content-Encoding"));
return true;
}, new Context ());
} catch (Error err) {
assert_not_reached ();
}
}

5 changes: 5 additions & 0 deletions tests/tests.vala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ public int main (string[] args) {
Test.add_func ("/route/match/not_matching", test_route_match_not_matching);
Test.add_func ("/route/fire", test_route_match_not_matching);

Test.add_func ("/decode/gzip", test_decode_gzip);
Test.add_func ("/decode/deflate", test_decode_deflate);
Test.add_func ("/decode/unknown_encoding", test_decode_unknown_encoding);
Test.add_func ("/decode/forward_remaining_encodings", test_decode_forward_remaining_encodings);

Test.add_func ("/subdomain", test_subdomain);
Test.add_func ("/subdomain/joker", test_subdomain_joker);
Test.add_func ("/subdomain/strict", test_subdomain_strict);
Expand Down

0 comments on commit 2c4834c

Please sign in to comment.