Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Copyright (c) 2014-2016, Brandon White
All rights reserved.

Contributors:
Brandon White - jennexproject+webserver@gmail.com
Tyler Thompson - tyler@tylerthompson.me

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
116 changes: 116 additions & 0 deletions bin/web_server.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import "dart:io";
import "dart:async";
import "package:web_server/web_server.dart" as webServer;

/**
* Accepted command line arguments:
* > --port=<port-number>
*/
Future<Null> main(final List<String> args) async {
const Map<String, String> SHORTHAND_TO_FULL_CMD_LINE_ARG_KEYS = const <String, String>{
"h": "help"
};
final Map<String, dynamic> cmdLineArgsMap = _parseCmdLineArgs(args, SHORTHAND_TO_FULL_CMD_LINE_ARG_KEYS);
InternetAddress hostAddr = InternetAddress.ANY_IP_V4;
int portNumber = 8080; // Default value.

if (cmdLineArgsMap.containsKey('help')) {
_outputHelpDetails();
exit(0);
}

// Interpret the command line arguments if needed.
if (cmdLineArgsMap.containsKey('host') && cmdLineArgsMap['host'] is String) {
final RegExp _ipv4AddrRegExp = new RegExp(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$');

if (_ipv4AddrRegExp.hasMatch(cmdLineArgsMap['host'])) {
hostAddr = new InternetAddress(cmdLineArgsMap['host']);
} else {
stderr.writeln('The specified (--host=${cmdLineArgsMap['host']}) argument is invalid; only IPV4 addresses are accepted right now (e.g. --host=127.0.0.1).');
exit(1);
}
}

if (cmdLineArgsMap.containsKey('port')) {
if (cmdLineArgsMap['port'] is int) {
portNumber = cmdLineArgsMap['port'];
} else {
stderr.writeln('The specified (--port=${cmdLineArgsMap['port']}) argument is invalid; must be an integer value (e.g. --port=8080).');
exit(1);
}
}

final webServer.WebServer localWebServer = new webServer.WebServer(hostAddr, portNumber, hasHttpServer: true);

stdout.writeln('WebServer started and listening for HTTP requests at the address: ${localWebServer.isSecure ? 'https' : 'http'}://${localWebServer.address.host}:$portNumber');

await localWebServer.httpServerHandler.serveStaticVirtualDirectory(Directory.current.path, shouldPreCache: false);

// Handle errors
localWebServer.httpServerHandler
..onErrorDocument(HttpStatus.NOT_FOUND, (final HttpRequest httpRequest) {
// Use the helper method from this WebServer package
webServer.HttpServerRequestHandler.sendPageNotFoundResponse(httpRequest,
'<h1>${HttpStatus.NOT_FOUND} - Page not found</h1>');
})
..onErrorDocument(HttpStatus.INTERNAL_SERVER_ERROR, (final HttpRequest httpRequest) {
// Use the helper method from this WebServer package
webServer.HttpServerRequestHandler.sendInternalServerErrorResponse(httpRequest,
'<h1>${HttpStatus.INTERNAL_SERVER_ERROR} - Internal Server Error</h1>');
});
}

Map<String, dynamic> _parseCmdLineArgs(final List<String> cmdLineArgsList, [final Map<String, String> argKeyMappingIndex = null]) {
final Map<String, dynamic> cmdLineArgsMap = <String, dynamic>{};
final RegExp leadingDashesRegExp = new RegExp(r'^\-{1,2}');
final RegExp keyValArgRegExp = new RegExp(r'^\-{1,2}[A-z]+\=\S+');
final RegExp intValRegExp = new RegExp(r'^\-?\d+$');

cmdLineArgsList.forEach((final String cmdLineArg) {
if (cmdLineArg.startsWith(new RegExp('^\-{1,2}'))) {
if (cmdLineArg.startsWith(keyValArgRegExp)) {
final List<String> _keyValPieces = cmdLineArg.split('=');
String _argKey = _keyValPieces[0].replaceFirst(leadingDashesRegExp, '');
dynamic _argVal = _keyValPieces[1];

if (intValRegExp.hasMatch(_argVal)) {
_argVal = int.parse(_argVal);
}

// Map the keyname, if needed.
if (argKeyMappingIndex != null && argKeyMappingIndex.containsKey(_argKey)) {
_argKey = argKeyMappingIndex[_argKey];
}

cmdLineArgsMap[_argKey] = _argVal;
} else {
String _argKey = cmdLineArg.replaceFirst(leadingDashesRegExp, '');

// Map the keyname, if needed.
if (argKeyMappingIndex != null && argKeyMappingIndex.containsKey(_argKey)) {
_argKey = argKeyMappingIndex[_argKey];
}

cmdLineArgsMap[_argKey] = true;
}
}
});

return cmdLineArgsMap;
}

void _outputHelpDetails() {
final String outputHelpDetails = '''
WebServer is a Dart package for serving files from a directory.

Usage: web_server [arguments]

Global options:
-h, --help Prints this usage information.
--host=<address> Bind the web server to the specified host address; the default is 0.0.0.0 (any available addresses).
--port=<port-number> Uses the provided port number to bind the web server to; the default is 8080.

See https://github.com/bwhite000/web-server for package details.''';

stdout.writeln(outputHelpDetails);
}
213 changes: 211 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,213 @@
WebServer
=========
WebServer Changelog
===================

v2.0.0+3 (3.5.2016)
-------------------

### Tool Changes

* `web_server` - pub global
* The developer can now pass a `--port=<port-number>` argument to the executable in the command line to
specify a specific port, not just the default 8080, for the web server to bind itself to; e.g.
`web_server --port=80`
* Added an interpretation of the `--help` or `-h` argument to output details into the terminal about the
executable and its functionality.
* Minor understandability improvement to the output terminal log when the server is started.
* Added another optional developer command line argument for `--host=<address>` to allow binding to a
specific address, such as only `127.0.0.1`; the default value is `0.0.0.0`.

### Documentation/Example Changes
* ReadMe
* Changed the filename to uppercase to be more inline with README file naming formats.
* Updated the examples to reflect the new parameters in the web_server tool.
* Added the very important details about reminding developers to run `pub global activate` on the package
every once and a while to get the latest updates; also included details about easily checking for breaking
changes before updating; hopefully the details will make it not-so-scary to update.
* ChangeLog
* Changed the filename to uppercase to be more inline with CHANGELOG file naming formats.
* Code Example Files
* `virtual_directory.dart` - (Issue #5) Updated the InternetAddress.LOOPBACK_IP_V4 to be ANY_IP_V4 in the
example to help out developers new to the package with avoiding the frustration of not catching that they
were binding only to a local host address when they try out their code on a remote machine and try to access
their web server.

v2.0.0+2 (1.13.2016)
--------------------

### Tool Changes

* `web_server` - pub global
* `shouldPreCache` has been changed to false since the Dart language seems to not be closing out filesystem
connections, even when explicitly coded to, and will error when setting up to serve directories with more
files than the computer's maximum file connection limit; will now cache as the files are requested instead
of in advance.

v2.0.0+1 (12.22.2015)
---------------------

### Library Changes

* `HttpServerRequestHandler`
* Added better comments to some methods for the DartDocs generator to make better use of.
* Removed the need for the '.' in the supportedFileExtensions extension list in `serveStaticVirtualDirectory`
that was unintentionally introduced in the previous release; functionality still behaved like before,
but the dot was needed; not anymore now.

### Documentation/Example Changes

* ReadMe
* Updated some code examples and section information to be easier to understand.
* Added a code example of preprocessing using the `html` Dart package to show that a developer can modify
web pages as richly and complexly as they could on the client side, but before even serving the page; this
way, the developer can have code templates in their web page's HTML, use it on the server like client side
DOM to fill it with data and clone it, if wanted, then return the built page to the client.

* Code Example Files
* Cleaned-up and fixed the code to be efficiently and aesthetically better; e.g. added the `final` keyword
in a few missing spots.

* Changelog
* Made the markdown data easier to read by dividing common change areas into sections; inspired by the
Changelog pattern in the main Dart SDK GitHub.

v2.0.0 (12.20.2015)
-------------------

### Library Changes

* `WebServer`
* Added a new constructor for binding a secure server and switched the syntax to using the new Dart 1.13
BoringSSL format instead of the old NSS method; use the `new WebServer.secure()` constructor to support this.
* Removed the 'GET' and 'POST' allowedMethods default value so that any type of request header will be
allowed by default; the developer will no longer have to specify every type of allowed request method.
* Bumped the default wait time for a response to generate from the server code (response deadline) from
20 seconds to 30 seconds to allow for more complex response generation to not catch the developer off
guard as quickly in case the developer isn't coding with this deadline in mind.

* `HttpServerRequestHandler`
* Added content types for .mp3, .ogg, .oga, .ogv.
* Changed the \_fileExtensions content types over to using the ContentType Object instead of a List<String>
and having to build the ContentType Object for every request; also, removed the declaration of it being a
'const' so that a developer using the server would be able to add their own new content types
programatically.
* Some code clean-up and optimizations for the occasional variable reuse optimization or the similar.
* Created a new format for listening for webpage path requests to make it easier to understand and more
intuitive by making the event format similar to binding DOM events on a webpage, like, for example, the
format of `querySelector('...').onClick.listen((_) {})`.
* Added a method and structures for opening the ability for developers using this package to add their own
custom error code handlers using `.onErrorDocument()`.
* Deprecated `serveVirtualDirectory()` in favor of the clearer and having more features, like being dynamic,
`serveStaticVirtualDirectory()` and `serveDynamicVirtualDirectory()` (arriving eventually; a.k.a. coming
soon).
* Renamed the `UrlData` Class to `UrlPath` to make it easier to understand what the Object is representing.
* `serveStaticVirtualDirectory`
* Made the requirement of providing a whitelist of supported file extensions
an optional parameter to allow for serving an entire directory, or a directory with flexible file types,
simple and not requiring a server restart; also, this makes it possible for the pub global `web_server`
command to operate with any directory's files.
* Removed a loop that was checking file extensions on every file entity for a match in the
`serveStaticVirtualDirectory` and is now doing a `.contains()` on the List to be much faster and loop
less.
* Removed the redundant `isRelativePath` parameter.
* Now, there is a parameter to enable pre-caching for files in this static Directory to make reads pull
from memory instead of the FileSystem, which will be much faster and economical.
* Improved the helper methods for sending 404 and 500 errors easily with `sendPageNotFoundResponse()` and
`sendInternalServerErrorResponse()`; will now use the supplied custom error response HTML from the developer,
if provided.
* `serveStaticFile:` Automatically detects relative file paths and more efficiently resolves the relative path
to access the static file; removed the redundant `isRelativePath` parameter.

* Improved some of the verbose error logging by including the name of the Class and method in the error text to
make debugging easier (especially if manually working with a forked version of this repo and learning how it
works).

### Tool Changes

* `web_server`
* Added an `bin/` directory and an executable rule to the Pubspec to enable using this package with `pub global
activate` and running a server right from a directory using the command line without even having to write any
code! It's as simple as the `web_server` command from the terminal, and it will serve that directory as a
`serveStaticVirtualDirectory()` command.

### Documentation/Example Changes

* ReadMe
* Updates for the new methods and features; clarified and demonstrated features that might not have been as
well-known or exemplified before; added a testimonial for my work using this package in many side projects
and work-requested projects at Ebates.
* Added a section asking other developers to let me know if they are making something exciting using my Dart
package.
* Clarification to some of the code examples and section titles; added details about SocialFlare to the
"Who is using this package?" section.

* Code Example Files
* Updated the code examples directory files to reflect the new API changes and additions from this release.

* LICENSE
* Added a LICENSE file to allow other developers to use this code and for compatibility with the Dart Pub
requirements.

v1.1.4 (5.14.2015)
------------------
* Found that HttpRequest paths were not matching serveVirtualDirectory() generated paths on
Windows machines because the Url would be something like: '/main.dart' and Windows would
provide and store a path segment with the opposite separator at '\main.dart' resulting in
the String comparison to fail; this has been resolved.

v1.1.3 (5.9.2015)
-----------------
* Added a handleRequestsStartingWith() method for intercepting requests starting with a specified
string; this is useful for handling everything in API patterns such as starting with '/api/';
added an example to the examples folder in "example/web_server.dart".

v1.1.2 (5.5.2015)
-----------------
* Removed a single inefficient .runtimeType use.
* Changed a mimetype definition and added more.
* Images and some binary sources were loading incorrectly, so switching to directly piping the
file contents into the HttpResponse, instead of reading, then adding.
* Fixed issue with the file extension not matching in serveVirtualDirectory if the extension
was not all lowercase.
* Solved issue with file and directory path building that would assemble incorrectly on Windows
machines.
* Changed the serveVirtualDirectory() parameter for "includeDirNameInPath" to
"includeContainerDirNameInPath" for parameter meaning clarity.
* Fixed a broken try/catch when loading a virtual file for a request.
* Made _VirtualDirectoryFileData easier to use by adding getters with clearer meaning such as
.absoluteFileSystemPath and .httpRequestPath.
* Greatly improved the efficiency of serveStandardFile for certain binary file formats and nicely
improved speed and memory for all binary file formats.
* Removed UTF-8 from being the default charset for non-matched mimetypes in binary files.
* Removed diffent handling in serveStandardFile based on the mimetype and now all use the same
piping to the HttpResponse.
* Included another log into shouldBeVerbose guide.
* Nicely clarified some confusing parts of the code.

v1.1.1 (4.26.2015)
-----------------
* Removing the default UTF-8 charset requirement in the response header to allow for different
file encodings; will re-add the charset soon when encoding detection (appears to be difficult
at the moment) is implemented.
* Handling for non-UTF8 file encoding by piping the file bytes directly into the response without
passing through a string decoder first; last release, I understood this differently and the
behavior was not what I wanted it to behave as; stinkin' byte encoding detection.
* Will re-add the byte encoding to the content-type header soon; I really don't like leaving this
out in requests, but don't want to prevent clients from serving non-UTF8 files until this can
be determined.

v1.1.0 (4.25.2015)
------------------
* Renamed the WebServer.webServer library to just WebServer.
* Renamed the WebServer.webSocketConnectionManager library to just WebSocketConnectionManager.
* Added tons more docs, comments, and inline code examples; published Docs online using Jennex
as the host and placed the link in the Pubspec.
* Implemented the URI Object to make relative file path resolution more accurate.
* Possibly solved issue that appears on Windows when resolving relative paths in
serveVirtualDirectory() and serverStaticFile().
* Added better honoring of the shouldBeVerbose parameter and changed to static property
[breaking API change].
* UTF8 encoding was required for files to be read before, but now it will work with any
encoding and convert it to UTF8 during file read.

v1.0.9 (4.16.2015)
--------------------
Expand All @@ -13,6 +221,7 @@ v1.0.9 (4.16.2015)
potential confusion for future developers using this Dart package; also added this to
`serveStaticFile()`.
* Improved some of the comments and code in the example files.
* Added an option to switch off recursive indexing in `serveVirtualDirectory()`.

v1.0.8 (4.15.2015)
------------------
Expand Down
15 changes: 8 additions & 7 deletions example/virtual_directory.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import "dart:io";
import "package:web_server/web_server.dart";
import "dart:async";
import "package:web_server/web_server.dart" as webServer;

void main() {
Future<Null> main() async {
// Initialize the WebServer
final WebServer localWebServer = new WebServer(InternetAddress.LOOPBACK_IP_V4, 8080,
hasHttpServer: true, hasWebSocketServer: true);
final webServer.WebServer localWebServer = new webServer.WebServer(InternetAddress.ANY_IP_V4, 8080,
hasHttpServer: true);

// Log out some of the connection information
print('Local web server started at: (http://${localWebServer.address.address}:${localWebServer.port})'); // http://127.0.0.1:8080
// Log out some of the connection information.
stdout.writeln('Local web server started at: (http://${localWebServer.address.address}:${localWebServer.port})'); // http://127.0.0.1:8080

// Automatically parse for indexing and serve all recursive items in this directory matching the accepted file extensions.
localWebServer.httpServerHandler.serveVirtualDirectory('test_dir', const <String>['html', 'css', 'dart', 'js']);
await localWebServer.httpServerHandler.serveStaticVirtualDirectory('test_dir', shouldPreCache: true);
}
Loading