1 /** 2 Convenience functions for working with web forms. 3 4 Copyright: © 2012-2015 Sönke Ludwig 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig, Jan Krüger 7 */ 8 module vibe.http.form; 9 10 public import vibe.inet.webform; 11 12 import vibe.http.client : HTTPClientRequest; // for writeFormBody 13 import vibe.http.server; 14 15 import std.array; 16 import std.conv; 17 import std.range; 18 import std.string; 19 import std.typecons : isTuple; 20 21 22 /** 23 Encodes the given dictionary as URL encoded form data. 24 */ 25 void writeFormData(R)(R dst, in string[string] data) 26 if (isOutputRange!(R, char)) 27 { 28 import vibe.textfilter.urlencode; 29 30 bool first = true; 31 foreach (k, v; data) { 32 if (first) first = false; 33 else dst.put("&"); 34 filterURLEncode(dst, k); 35 dst.put("="); 36 filterURLEncode(dst, v); 37 } 38 } 39 40 /// 41 unittest { 42 import std.array; 43 import vibe.core.log; 44 import vibe.http.form; 45 46 void test() 47 { 48 auto dst = appender!string(); 49 dst.writeFormData(["field1": "value1", "field2": "value2"]); 50 logInfo("Form data: %s", dst.data); 51 } 52 } 53 54 /** 55 Encodes the given ranges of `Tuple!(string, string)` as URL encoded form data 56 */ 57 void writeFormData(R, PairRange)(R dst, PairRange pr) 58 if (isOutputRange!(R, char) && isTuple!(ElementType!PairRange) && ElementType!PairRange.length == 2) 59 { 60 import vibe.textfilter.urlencode; 61 62 if(pr.empty) return; 63 64 auto fst = pr.front; 65 pr.popFront(); 66 67 filterURLEncode(dst, fst[0]); 68 dst.put("="); 69 filterURLEncode(dst, fst[1]); 70 71 foreach (pair; pr) { 72 dst.put("&"); 73 filterURLEncode(dst, pair[0]); 74 dst.put("="); 75 filterURLEncode(dst, pair[1]); 76 } 77 } 78 79 /** 80 Writes a `vibe.http.client.HTTPClientRequest` body as URL encoded form data. 81 */ 82 void writeFormBody(HTTPClientRequest req, in string[string] form) 83 { 84 import vibe.http.form; 85 import vibe.stream.wrapper; 86 87 StringLengthCountingRange len; 88 writeFormData(&len, form); 89 req.contentType = "application/x-www-form-urlencoded"; 90 req.contentLength = len.count; 91 auto rng = streamOutputRange(req.bodyWriter); 92 writeFormData(&rng, form); 93 } 94 95 /// 96 unittest { 97 import vibe.core.log; 98 import vibe.http.client; 99 import vibe.http.form; 100 import vibe.stream.operations; 101 102 void sendForm() 103 { 104 requestHTTP("http://example.com/form", 105 (scope req) { 106 req.method = HTTPMethod.POST; 107 req.writeFormBody(["field1": "value1", "field2": "value2"]); 108 }, 109 (scope res) { 110 logInfo("Response: %s", res.bodyReader.readAllUTF8()); 111 }); 112 } 113 } 114 115 /** 116 Writes a `vibe.http.client.HTTPClientRequest` body as URL encoded form data. 117 118 Params: 119 req = Request object to write to. 120 form = range of `t = Tuple!(string, string)`, 121 where `t[0]` is the name and `t[1]` the 122 value of a form entry. 123 */ 124 void writeFormBody(PairRange)(HTTPClientRequest req, PairRange form) 125 if(isTuple!(ElementType!PairRange) && ElementType!PairRange.length == 2) 126 { 127 import vibe.http.form; 128 import vibe.stream.wrapper; 129 130 StringLengthCountingRange len; 131 writeFormData(&len, form.save); 132 req.contentType = "application/x-www-form-urlencoded"; 133 req.contentLength = len.count; 134 auto rng = streamOutputRange(req.bodyWriter); 135 writeFormData(&rng, form); 136 } 137 138 /// 139 unittest { 140 import vibe.core.log; 141 import vibe.http.client; 142 import vibe.http.form; 143 import vibe.stream.operations; 144 import std.range; 145 146 void sendForm() 147 { 148 string[] names = ["foo", "bar", "baz"]; 149 string[] values = ["1", "2", "3"]; 150 auto form = zip(names, values); 151 requestHTTP("http://example.com/form", 152 (scope req) { 153 req.method = HTTPMethod.POST; 154 req.writeFormBody(form); 155 }, 156 (scope res) { 157 logInfo("Response: %s", res.bodyReader.readAllUTF8()); 158 }); 159 } 160 } 161 162 163 /// private 164 struct StringLengthCountingRange { 165 import std.utf; 166 size_t count = 0; 167 void put(string str) { count += str.length; } 168 void put(dchar ch) { count += codeLength!char(ch); } 169 }