/** Convenience functions for working with web forms. Copyright: © 2012-2015 Sönke Ludwig License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Sönke Ludwig, Jan Krüger */ module vibe.http.form; public import vibe.inet.webform; import vibe.http.client : HTTPClientRequest; // for writeFormBody import vibe.http.server; import std.array; import std.conv; import std.range; import std.string; import std.typecons : isTuple; /** Encodes the given dictionary as URL encoded form data. */ void writeFormData(R)(R dst, in string[string] data) if (isOutputRange!(R, char)) { import vibe.textfilter.urlencode; bool first = true; foreach (k, v; data) { if (first) first = false; else dst.put("&"); filterURLEncode(dst, k); dst.put("="); filterURLEncode(dst, v); } } /// unittest { import std.array; import vibe.core.log; import vibe.http.form; void test() { auto dst = appender!string(); dst.writeFormData(["field1": "value1", "field2": "value2"]); logInfo("Form data: %s", dst.data); } } /** Encodes the given ranges of `Tuple!(string, string)` as URL encoded form data */ void writeFormData(R, PairRange)(R dst, PairRange pr) if (isOutputRange!(R, char) && isTuple!(ElementType!PairRange) && ElementType!PairRange.length == 2) { import vibe.textfilter.urlencode; if(pr.empty) return; auto fst = pr.front; pr.popFront(); filterURLEncode(dst, fst[0]); dst.put("="); filterURLEncode(dst, fst[1]); foreach (pair; pr) { dst.put("&"); filterURLEncode(dst, pair[0]); dst.put("="); filterURLEncode(dst, pair[1]); } } /** Writes a `vibe.http.client.HTTPClientRequest` body as URL encoded form data. */ void writeFormBody(HTTPClientRequest req, in string[string] form) { import vibe.http.form; import vibe.stream.wrapper; StringLengthCountingRange len; writeFormData(&len, form); req.contentType = "application/x-www-form-urlencoded"; req.contentLength = len.count; auto rng = streamOutputRange(req.bodyWriter); writeFormData(&rng, form); } /// unittest { import vibe.core.log; import vibe.http.client; import vibe.http.form; import vibe.stream.operations; void sendForm() { requestHTTP("http://example.com/form", (scope req) { req.method = HTTPMethod.POST; req.writeFormBody(["field1": "value1", "field2": "value2"]); }, (scope res) { logInfo("Response: %s", res.bodyReader.readAllUTF8()); }); } } /** Writes a `vibe.http.client.HTTPClientRequest` body as URL encoded form data. Params: req = Request object to write to. form = range of `t = Tuple!(string, string)`, where `t[0]` is the name and `t[1]` the value of a form entry. */ void writeFormBody(PairRange)(HTTPClientRequest req, PairRange form) if(isTuple!(ElementType!PairRange) && ElementType!PairRange.length == 2) { import vibe.http.form; import vibe.stream.wrapper; StringLengthCountingRange len; writeFormData(&len, form.save); req.contentType = "application/x-www-form-urlencoded"; req.contentLength = len.count; auto rng = streamOutputRange(req.bodyWriter); writeFormData(&rng, form); } /// unittest { import vibe.core.log; import vibe.http.client; import vibe.http.form; import vibe.stream.operations; import std.range; void sendForm() { string[] names = ["foo", "bar", "baz"]; string[] values = ["1", "2", "3"]; auto form = zip(names, values); requestHTTP("http://example.com/form", (scope req) { req.method = HTTPMethod.POST; req.writeFormBody(form); }, (scope res) { logInfo("Response: %s", res.bodyReader.readAllUTF8()); }); } } /// private struct StringLengthCountingRange { import std.utf; size_t count = 0; void put(string str) { count += str.length; } void put(dchar ch) { count += codeLength!char(ch); } }