kinda working
diff --git a/lib/shelf_proxy.dart b/lib/shelf_proxy.dart
index 3c7f8bf..6adc1a1 100644
--- a/lib/shelf_proxy.dart
+++ b/lib/shelf_proxy.dart
@@ -1 +1,48 @@
library shelf_proxy;
+
+import 'dart:io';
+
+import 'package:shelf/shelf.dart';
+
+///
+///
+/// Imagine that rootUri is specified as `http://example.com/files`
+///
+/// A request for `/test/sample.html` would result is a request for
+/// `http://example.com/files/test/sample.html`.
+Handler createProxyHandler(Uri rootUri) {
+ assert(rootUri.scheme == 'http' || rootUri.scheme == 'https');
+ assert(rootUri.query == '');
+ assert(rootUri.isAbsolute);
+
+ return (Request request) {
+ // TODO: really need to tear down the client when this is done...
+ var client = new HttpClient();
+
+ var url = _getProxyUrl(rootUri, request.url);
+
+ return client.openUrl(request.method, url).then((ioRequest) {
+ return ioRequest.close();
+ }).then((ioResponse) {
+
+ return new Response(ioResponse.statusCode, body: ioResponse);
+ });
+ };
+}
+
+Uri _getProxyUrl(Uri proxyRoot, Uri requestUrl) {
+ assert(proxyRoot.scheme == 'http' || proxyRoot.scheme == 'https');
+ assert(proxyRoot.query == '');
+ assert(proxyRoot.isAbsolute);
+ assert(!requestUrl.isAbsolute);
+
+ var updatedPath = proxyRoot.pathSegments.toList()
+ ..addAll(requestUrl.pathSegments);
+
+ return new Uri(scheme: proxyRoot.scheme,
+ userInfo: proxyRoot.userInfo,
+ host: proxyRoot.host,
+ port: proxyRoot.port,
+ pathSegments: updatedPath,
+ query: requestUrl.query);
+}
diff --git a/lib/src/util.dart b/lib/src/util.dart
new file mode 100644
index 0000000..6085013
--- /dev/null
+++ b/lib/src/util.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library shelf_proxy.util;
+
+import 'dart:async';
+
+import 'package:stack_trace/stack_trace.dart';
+
+/// Like [Future.sync], but wraps the Future in [Chain.track] as well.
+Future syncFuture(callback()) => Chain.track(new Future.sync(callback));
diff --git a/pubspec.yaml b/pubspec.yaml
index 8e6eb12..58f62fa 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -13,3 +13,4 @@
hop_unittest: '>=0.1.0 <0.2.0'
path: '>=1.1.0 <2.0.0'
scheduled_test: '>=0.11.0 <0.12.0'
+ shelf_static: any
diff --git a/test/harness_console.dart b/test/harness_console.dart
index d04e002..bb1c3c3 100644
--- a/test/harness_console.dart
+++ b/test/harness_console.dart
@@ -2,6 +2,12 @@
import 'package:scheduled_test/scheduled_test.dart';
+import 'proxy_test.dart' as proxy;
+import 'static_file_test.dart' as static_file;
+
void main() {
groupSep = ' - ';
+
+ proxy.main();
+ static_file.main();
}
diff --git a/test/proxy_test.dart b/test/proxy_test.dart
new file mode 100644
index 0000000..1ad5baf
--- /dev/null
+++ b/test/proxy_test.dart
@@ -0,0 +1,93 @@
+library shelf_proxy.proxy_test;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:shelf/shelf.dart';
+import 'package:shelf/shelf_io.dart' as shelf_io;
+import 'package:shelf_proxy/shelf_proxy.dart';
+
+import 'test_util.dart';
+
+void main() {
+ test('root', () {
+ _scheduleServer(_handler);
+
+ schedule(() {
+ var url = new Uri.http('localhost:$_serverPort', '');
+ var handler = createProxyHandler(url);
+
+ return makeRequest(handler, '/').then((response) {
+ expect(response.statusCode, HttpStatus.OK);
+ expect(response.readAsString(), completion('root with slash'));
+ });
+ });
+ });
+
+ test('bar', () {
+ _scheduleServer(_handler);
+
+ schedule(() {
+ var url = new Uri.http('localhost:$_serverPort', '');
+ var handler = createProxyHandler(url);
+
+ return makeRequest(handler, '/bar').then((response) {
+ expect(response.statusCode, HttpStatus.OK);
+ expect(response.readAsString(), completion('bar'));
+ });
+ });
+ });
+
+ test('bar/', () {
+ _scheduleServer(_handler);
+
+ schedule(() {
+ var url = new Uri.http('localhost:$_serverPort', '');
+ var handler = createProxyHandler(url);
+
+ return makeRequest(handler, '/bar/').then((response) {
+ expect(response.statusCode, HttpStatus.OK);
+ expect(response.readAsString(), completion('bar with slash'));
+ });
+ });
+ });
+}
+
+Response _handler(Request request) {
+ if (request.method != 'GET') {
+ return new Response.forbidden("I don't like method ${request.method}.");
+ }
+
+ String content;
+ switch (request.url.path) {
+ case '':
+ content = 'root';
+ break;
+ case '/':
+ content = 'root with slash';
+ break;
+ case '/bar':
+ content = 'bar';
+ break;
+ case '/bar/':
+ content = 'bar with slash';
+ break;
+ default:
+ return new Response.notFound("I don't like '${request.url.path}'.");
+ }
+ return new Response.ok(content);
+}
+
+int _serverPort;
+
+Future _scheduleServer(Handler handler) {
+ return schedule(() => shelf_io.serve(handler, 'localhost', 0).then((server) {
+ currentSchedule.onComplete.schedule(() {
+ _serverPort = null;
+ return server.close(force: true);
+ });
+
+ _serverPort = server.port;
+ }));
+}
diff --git a/test/static_file_test.dart b/test/static_file_test.dart
new file mode 100644
index 0000000..2705f23
--- /dev/null
+++ b/test/static_file_test.dart
@@ -0,0 +1,46 @@
+library shelf_proxy.static_file_test;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:shelf/shelf.dart';
+import 'package:shelf/shelf_io.dart' as shelf_io;
+import 'package:shelf_static/shelf_static.dart' as shelf_static;
+import 'package:shelf_proxy/shelf_proxy.dart';
+
+import 'test_util.dart';
+
+void main() {
+ test('foo', () {
+ _scheduleServer(_handler);
+
+ schedule(() {
+ var url = new Uri.http('localhost:$_serverPort', '');
+ var handler = createProxyHandler(url);
+
+ return makeRequest(handler, '/').then((response) {
+ expect(response.statusCode, HttpStatus.OK);
+ expect(response.readAsString(),
+ completion(contains('<title>shelf_static</title>')));
+ expect(response.contentLength, isNotNull);
+ });
+ });
+ });
+}
+
+final _handler = shelf_static.createStaticHandler('test/test_files',
+ defaultDocument: 'index.html');
+
+int _serverPort;
+
+Future _scheduleServer(Handler handler) {
+ return schedule(() => shelf_io.serve(handler, 'localhost', 0).then((server) {
+ currentSchedule.onComplete.schedule(() {
+ _serverPort = null;
+ return server.close(force: true);
+ });
+
+ _serverPort = server.port;
+ }));
+}
diff --git a/test/test_files/dart.png b/test/test_files/dart.png
new file mode 100644
index 0000000..fd6de2a
--- /dev/null
+++ b/test/test_files/dart.png
Binary files differ
diff --git a/test/test_files/favicon.ico b/test/test_files/favicon.ico
new file mode 100644
index 0000000..e605972
--- /dev/null
+++ b/test/test_files/favicon.ico
Binary files differ
diff --git a/test/test_files/index.html b/test/test_files/index.html
new file mode 100644
index 0000000..7bf28d1
--- /dev/null
+++ b/test/test_files/index.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>shelf_static</title>
+ </head>
+ <body>
+ <h1>Hello, shelf_static!</h1>
+ <img src='dart.png' alt='Dart logo' width='150' height='151'>
+ </body>
+</html>
diff --git a/test/test_util.dart b/test/test_util.dart
new file mode 100644
index 0000000..eedd672
--- /dev/null
+++ b/test/test_util.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library shelf_proxy.test_util;
+
+import 'dart:async';
+
+import 'package:path/path.dart' as p;
+import 'package:shelf/shelf.dart';
+import 'package:shelf_proxy/src/util.dart';
+
+final p.Context _ctx = p.url;
+
+/// Makes a simple GET request to [handler] and returns the result.
+Future<Response> makeRequest(Handler handler, String path,
+ {String scriptName, Map<String, String> headers}) {
+ var rootedHandler = _rootHandler(scriptName, handler);
+ return syncFuture(() => rootedHandler(_fromPath(path, headers)));
+}
+
+Request _fromPath(String path, Map<String, String> headers) =>
+ new Request('GET', Uri.parse('http://localhost' + path), headers: headers);
+
+Handler _rootHandler(String scriptName, Handler handler) {
+ if (scriptName == null || scriptName.isEmpty) {
+ return handler;
+ }
+
+ if (!scriptName.startsWith('/')) {
+ throw new ArgumentError('scriptName must start with "/" or be empty');
+ }
+
+ return (Request request) {
+ if (!_ctx.isWithin(scriptName, request.requestedUri.path)) {
+ return new Response.notFound('not found');
+ }
+ assert(request.scriptName.isEmpty);
+
+ var relativeRequest = request.change(scriptName: scriptName);
+
+ return handler(relativeRequest);
+ };
+}