diff --git a/.gitignore b/.gitignore
index 7c280441..b306bf88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,27 +1,68 @@
-# See https://www.dartlang.org/tools/private-files.html
+### VisualStudioCode template
+.settings
-# Files and directories created by pub
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
+
+*.iml
+
+## Directory-based project format:
+.idea/
+/Test Results - Run_All_Tests.html
+# if you remove the above rule, at least ignore the following:
+
+# User-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/dictionaries
+
+# Sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+# .idea/uiDesigner.xml
+
+# Gradle:
+# .idea/gradle.xml
+# .idea/libraries
+
+# Mongo Explorer plugin:
+# .idea/mongoSettings.xml
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+### Dart template
+# Don’t commit the following directories created by pub.
.buildlog
-.packages
-.project
.pub/
build/
-**/packages/
+packages
+.packages
-# Files created by dart2js
-# (Most Dart developers will use pub build to compile Dart, use/modify these
-# rules if you intend to use dart2js directly
-# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
-# differentiate from explicit Javascript files)
+# Or the files created by dart2js.
*.dart.js
-*.part.js
+*.js_
*.js.deps
*.js.map
-*.info.json
-# Directory created by dartdoc
-doc/api/
-
-# Don't commit pubspec lock file
-# (Library packages only! Remove pattern if developing an application package)
+# Include when developing application packages.
pubspec.lock
+
diff --git a/Test Results - Run_All_Tests.html b/Test Results - Run_All_Tests.html
new file mode 100644
index 00000000..5eb5a1aa
--- /dev/null
+++ b/Test Results - Run_All_Tests.html
@@ -0,0 +1,636 @@
+
+
+
+
+Test Results — Run All Tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+-
+
+
922 ms
+Test server support
+
+-
+
+
922 ms
+JSON
+
+-
+
+
528 ms
+passedPost Simple JSON
+
+-
+Test server listening on http://localhost:60667
+
+
+
+-
+
+
394 ms
+failedPost Complex JSON
+
+-
+Test server listening on http://localhost:60669
+
+-
+package:test expect
test\all_tests.dart 56:9 main.<fn>.<fn>.<fn>.<async>
===== asynchronous gap ===========================
dart:async _Completer.completeError
test\all_tests.dart 57:8 main.<fn>.<fn>.<fn>.<async>
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
test\all_tests.dart main.<fn>.<fn>.<fn>
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/body_parser.dart b/lib/body_parser.dart
new file mode 100644
index 00000000..cdb02558
--- /dev/null
+++ b/lib/body_parser.dart
@@ -0,0 +1,67 @@
+// A library for parsing HTTP request bodies and queries.
+
+library body_parser;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:json_god/json_god.dart';
+
+/// A representation of data from an incoming request.
+class BodyParseResult {
+ /// The parsed body.
+ Map body = {};
+
+ /// The parsed query string.
+ Map query = {};
+}
+
+/// Grabs data from an incoming request.
+///
+/// Supports urlencoded and JSON.
+Future parseBody(HttpRequest request) async {
+ BodyParseResult result = new BodyParseResult();
+ ContentType contentType = request.headers.contentType;
+
+ // Parse body
+ if (contentType != null) {
+ if (contentType.mimeType == 'application/json')
+ result.body = JSON.decode(await request.transform(UTF8.decoder).join());
+ else if (contentType.mimeType == 'application/x-www-form-urlencoded') {
+ String body = await request.transform(UTF8.decoder).join();
+ buildMapFromUri(result.body, body);
+ }
+ }
+
+ // Parse query
+ RegExp queryRgx = new RegExp(r'\?(.+)$');
+ String uriString = request.requestedUri.toString();
+ if (queryRgx.hasMatch(uriString)) {
+ Match queryMatch = queryRgx.firstMatch(uriString);
+ buildMapFromUri(result.query, queryMatch.group(1));
+ }
+
+ return result;
+}
+
+/// Parses a URI-encoded string into real data! **Wow!**
+buildMapFromUri(Map map, String body) {
+ God god = new God();
+ for (String keyValuePair in body.split('&')) {
+ if (keyValuePair.contains('=')) {
+ List split = keyValuePair.split('=');
+ String key = Uri.decodeQueryComponent(split[0]);
+ String value = Uri.decodeQueryComponent(split[1]);
+ num numValue = num.parse(value, (_) => double.NAN);
+ if (!numValue.isNaN)
+ map[key] = numValue;
+ else if (value.startsWith('[') && value.endsWith(']'))
+ map[key] = god.deserialize(value);
+ else if (value.startsWith('{') && value.endsWith('}'))
+ map[key] = god.deserialize(value);
+ else if (value.trim().toLowerCase() == 'null')
+ map[key] = null;
+ else map[key] = value;
+ } else map[Uri.decodeQueryComponent(keyValuePair)] = true;
+ }
+}
\ No newline at end of file
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 00000000..1bdc9254
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,9 @@
+name: body_parser
+author: Tobe O
+version: 1.0.0-dev
+description: Parse request bodies and query strings in Dart.
+dependencies:
+ json_god: any
+dev_dependencies:
+ http: any
+ test: any
\ No newline at end of file
diff --git a/test/all_tests.dart b/test/all_tests.dart
new file mode 100644
index 00000000..de173cd8
--- /dev/null
+++ b/test/all_tests.dart
@@ -0,0 +1,60 @@
+import 'dart:io';
+
+import 'package:body_parser/body_parser.dart';
+import 'package:http/http.dart' as http;
+import 'package:json_god/json_god.dart';
+import 'package:test/test.dart';
+
+main() {
+ group('Test server support', () {
+ HttpServer server;
+ String url;
+ http.Client client;
+ God god;
+
+ setUp(() async {
+ server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 0);
+ server.listen((HttpRequest request) async {
+ //Server will simply return a JSON representation of the parsed body
+ request.response.write(god.serialize(await parseBody(request)));
+ await request.response.close();
+ });
+ url = 'http://localhost:${server.port}';
+ print('Test server listening on $url');
+ client = new http.Client();
+ god = new God();
+ });
+ tearDown(() async {
+ await server.close(force: true);
+ client.close();
+ server = null;
+ url = null;
+ client = null;
+ god = null;
+ });
+
+ group('JSON', () {
+ test('Post Simple JSON', () async {
+ var response = await client.post(url, body: {
+ 'hello': 'world'
+ });
+ expect(response.body, equals('{"body":{"hello":"world"},"query":{}}'));
+ });
+
+ test('Post Complex JSON', () async {
+ var postData = god.serialize({
+ 'hello': 'world',
+ 'nums': [1, 2.0, 3 - 1],
+ 'map': {
+ 'foo': {
+ 'bar': 'baz'
+ }
+ }
+ });
+ var response = await client.post(url, body: postData);
+ var body = god.deserialize(response.body)['body'];
+ expect(body['hello'], equals('world'));
+ });
+ });
+ });
+}
\ No newline at end of file