Skip to content

Releases: scottoffen/grapevine-legacy

4.2.2

26 Jan 05:03
Compare
Choose a tag to compare

Fix for #216

PathInfo was being derived from trimming any query string off of RawUrl. The rest was being used as the path, and that works for almost all cases. However, there are some situation (specifically when requests are proxied to the server) where the entire url (including protocol, host and port) ends up in the RawUrl, which prevent routes from being matched because route matching is based on the part of the url that is between the protocol://host:port and the query string.

PathInfo is now being pulled from HttpRequest.Url.AbsolutedPath, which does not contain the entire url, even when requests are proxied.

4.2.1

13 Jan 23:49
Compare
Choose a tag to compare
  • Fixes #219 WebExceptionHandler not firing on ConnectionFailure
  • Moves repository to original owner
  • Updates logo for new branding

4.2.0

01 Jan 04:55
Compare
Choose a tag to compare

Cleaned up commented out code, removed unused references and references that were causing Mono to crash. Removed obsoleted items.

Grapevine 4 is in maintenance mode only. Please migrate to Grapevine 5

4.1.2

09 Aug 18:26
Compare
Choose a tag to compare

Defects

#171 Uncaught exception in HttpResponse Can Cause Application Crash

Fixed logic to catch and send a 500 Internal Server Error on crash.

4.1.1.0

21 Aug 19:34
Compare
Choose a tag to compare

Enhancements

#179 Append Server Header on Response

On outgoing responses, the Server http header will now be populated with the name of the server software (Grapevine) and the version number in use. This can be suppressed if desired by setting the static property HttpResponse.SuppressServerHeader to true.

#180 Add Ability To Match On Header Values

You can now match routes on any key/value pair found in the request headers by passing the name of the expected header and a regular expression to match values on.

// Only match on routes where the Accept header contains a "text" value
route.MatchOn("Accept", "text/.+");

In addition to matching the http verb and the path info spec, it will now also look for matching headers. If the specified header is not found, it will attempt to match on an empty string.

This feature is only available on manual route registration or post automatic route discovery. There is not yet an attribute value that corresponds to this method.

Defects

#171 Change Default Regex Pattern To Avoid Including SubPath

Changed the default regex pattern from (.+) to ([^/]+), in order to avoid including sub path.

Given this attribute on a method:

[RestRoute(HttpMethod = HttpMethod.POST, PathInfo = "/controllers/[id]")]

It would not expect to match on a request sent to /controller/2/requests, but it was matching with an id value of 2/requests. The change in the regular expression prevents this matching in the first place.

#183 PR Setting Router Logger Correctly

Much thanks goes out the Jack Gardner for not just catching the bug, but also fixing it and submitting a pull request!

When constructing a RestServer object using ServerSettings, the server was not properly setting the logger from the settings.

E.g.

ServerSettings settings = new ServerSettings();
setting.Logger = new FileLogger("output.log");
//where FileLogger is a custom logger which directs logs to file, for example

using (var server = new RestServer(setting))
{
    server.Start();
    Console.ReadLine();
    server.Stop();
}

Instead of using the custom logger specified, the router was using the default NullLogger. It will now correctly use the custom logger.

#187 SendResponse in BeforeRouting Does Not Stop Processing

Attempting to respond early by sending a response to the request during the execution of the BeforeRouting event handlers was failing, and could cause some interesting errors to occur.

In addition to resolving the issue such that you can respond to a request during the handler execution (this was, after all, one of the main use cases for introducing this feature), error handling was modified so that the exception message - while still logged - is not sent in the 500 Internal Server Error response by default. You can turn this behavior back on by setting the property Router.SendExceptionMessages to true, and this may still be beneficial in testing environments.

4.1.0.0

19 May 22:19
Compare
Choose a tag to compare

Enhancements

#169 Automatically Determine ContentType Based On File Extension

When sending a response that is a static asset from a public folder, the content-type header will now be set based on the file extension. If there is no extension or no matching content type is found for the extension, then the default will be used.

Defects

#157 Mono lacks support for ExtendedProtectionSelector

Removed the ExtendedProtectiongSelector property from the API.

I know this violates semver here. Because I am removing part of the API and therefore introducing a breaking change, I should do a major version change (from 4 to 5). But I'm not going to do that - opting instead to go to version 4.1 - for two reasons.

  1. This API feature was actually making the library unusable for some people. And to the best of my knowledge, this isn't a feature anyone is using. I honestly don't get much feedback from my users base unless there is a problem, so it might just be a matter of not enough of the right kind of feedback, but if you really want to use this feature of the API, just stay on version 4.0.

  2. Version 5 is already underway, and it not only includes many, many other breaking changes, it also provides a spectacularly better way of managing these kinds of API/dependency feature implementation thingys (i.e., exposing features of the underlying http implementation to the user). In fact, changes in version 5 will make it possible to provide future minor versions (e.g. 5.1, 5.2, etc.) that don't rely on the HttpListener class at all. I may never do this, but at least if I don't it won't be because my library is too tightly coupled to the existing HttpListener implementation.

#175 Memory Leak in Route Caching

I implemented route caching in a previous release with the intention of speeding things up, then later realized my hasty implementation could cause a memory leak. This doesn't change the API at all, and I may enable the feature at some future point with a better implementation (and a feature toggle), but for now I just removed the offending code.

#177 Managing HttpListenerException On Closed Connections

If the client closed the connection before the server could send the response, then when the server finally started sending the response an error was thrown, and the block that caught the exception was try to send an Internal Server Error (500) response, which would cause another exception to be thrown, and that would cause the server to crash.

Fixed it so that the exceptions get properly handled, and the response get closed so that no other route can try to write to the closed output stream.

4.1.0-alpha.1

17 Mar 20:16
Compare
Choose a tag to compare
4.1.0-alpha.1 Pre-release
Pre-release

Removing code related to the ExtendedProtectionSelector as a stop gap for using Grapevine on Mono and Xamarin.

4.0.0.252

16 Feb 21:54
Compare
Choose a tag to compare

The Performance Update

I've been referring to this release in my head as The Performance Update for several months now. Almost every enhancement included a performance update component, so in addition to new and better functionality, things should also run faster.

Enhancements

#79 Support For Multiple Public Folders

I've used Apache for years, and I've always liked the ability it has to configure virtual directories. I wanted Grapevine to have that same functionality - mapping requests not just to specific methods in code, but to disparate file system locations - and now it does. This ended up being a complete refactor of the PublicFolder class to include some performance and security enhancements as well. If you want details, feel free to look over the associated pull request - we had quite the discussion on this one!

Public Folders Are Now Optional
In short, IRestServer now has a property named PublicFolders, which is a list of all configured public folders. If you don't add any to the list, none are created - not even the default public folder! When a request comes in, each public folder in turn (in the order they appear in the list) is checked for whether there is such a file or whether there should be a file (if a prefix was specified and if it appears as the first element in the path info of the request). The file is returned if it exists or an error is thrown if it should exist and no further routing is done. Otherwise, routing continues as usual.

Ensuring Backwards Compatibility
The PublicFolder property is still available on IRestServer, but the implementation on RestServer is as an alias for PublicFolders[0]. It is important to note that if you are counting on having a public folder automatically created by RestServer (which is the original implementation), it will still do that if you try to access the PublicFolder property and there is no value in PublicFolders[0].

Performance: Caching File Locations
It's much faster to scan an in-memory data structure than to use I/O to scan the file system. So now, when a PublicFolder object is created, a deep scan is done and paths to all files (including those in sub-folders) are cached. In order to keep that cache from getting stale, a file watcher is added. Whenever files are added, removed or renamed, the cache updates automatically.

This also resolves #127 PublicFolder.GetFilePath() may be vulnerable to directory traversal attack. While a vulnerability was never found, that method has not only been completely removed, but the approach it took has been replaced with the introduction of the cache.

#111 Add Before and After Start and Stop Server Event Handlers

Having only a single generic delegate to do everything you want to do before and after your server starts and stops can lead to unnecessarily tight coupling. In an attempt to reduce that, event handlers have been introduced. The old OnBefore* and OnAfter* properties still exist, but are deprecated and scheduled to be removed in a future release.

The delegate for the event handler takes the instance of the RestServer as a parameter, allowing the developer to modify or monitor the server at each stage of starting and stopping.

server.AfterStarting += restServer =>
{
    Console.WriteLine("started");
};

#112 Add Before and After Routing Event Handlers

In a similar fashion to adding the event handlers to the starting and stopping of the IRestServer, event handlers have been added for before and after routing. This is to facilitate those things that should always happen before routing and always happen after routing - typically meaning after a response has been sent, but not necessarily. An example use case would be to create and dispose of sessions and connections.

It is important to know that the AfterRouting handlers will be executed in the reverse order that they were added in.

The general expectation is that they can be added in pairs by extensions. Hence the following:

// First extension manages the database connection
server.Router.BeforeRouting += context => { Console.WriteLine("Connect to the database"); };
server.Router.AfterRouting += context => { Console.WriteLine("Disconnect from the database"); };

// Second extension does session management, may assume access to the database
server.Router.BeforeRouting += context => { Console.WriteLine("Fetch the session"); };
server.Router.AfterRouting += context => { Console.WriteLine("Store the session"); };

will produce the console output:

Connect to the database
Fetch the session
Store the session
Disconnect from the database

#143 Implement Route Caching

The first time a request is received, the http method and exact path info are cached along with the supplied list of routes that match. Subsequent requests that match the http method and exact path info will use the cached list of routes instead of scanning the routing table again. Any changes to the routing table will cause the cache to be cleared.

In the future, a toggle will be added to disable this caching, as it might be quicker to scan the routing table every time in certain scenarios.

#145 Implement Thread Pooling for Server Threading

When I initially wrote Grapevine, I was misinformed about how to properly manage thread resources. While the use cases I had for Grapevine never exposed these weaknesses, it was brought to my attention through several channels that a better, more reliable and more performant method existed. I finally bit the bullet and implemented it in this release.

#151 Change RestServer.ThreadSafeStop to Extension Method on IRestServer

This simplifies the interface and allows all instances of IRestServer to have the same implementation of the method without using an abstract class.

#153 Remove Routing Logic From RestServer class

The routing logic really didn't belong in the server, so I moved it. The logic itself didn't really change, except where noted by other issues.

#164 Refactor SendResponse()

An design flaw limited the number of ways in which responses could be sent (e.g. FileStream was the only type of Stream objects that could be sent). This change opens the door for responses to be sent in a manner that best suits your needs. The only method required on the interface is void SendResponse(byte[] contents). All previously existing methods have been turned into extensions methods on IHttpResponse, and are used to set properties and transform different objects into byte arrays before calling the interface implementation.

Defects

#161 Route Fails to Parse More Than One Parameter

This was a great catch by Meik Tranel. See the issue for more details.

4.0.0.195

16 Nov 21:32
Compare
Choose a tag to compare

Enhancements

#146 Add Delegates For Multiple WebExceptionStatus Values

We previously added the ability to provide a generic delegate to handle WebException exceptions thrown in the RestClient when the status of the exception was WebExceptionStatus.Timeout. While this worked great, it didn't properly take into consideration other status values, and as a result, when other exceptions were throw, the cause of the exception was being masked by a NullReferenceException in the RestResponse constructor.

So, in addition to fixing that so that the original WebException will get thrown (and not masked), we also extended the ability so that a generic delegate can be provided for all status values. This functionality obsoletes the previous functionality introduced, but the syntax remains for now for compatibility.

#147 Call Dispose() on IDisposable Objects Created By Routes

When routes are generated using MethodInfo for non-static methods, if those classes implement IDisposable then the objects need to be disposed of properly. Failure to dispose of them properly could lead to memory leaks.

The objects generated in the routes are now checked after route execution to see if they implement IDisposable, and then calls Dispose() on the object if they do.

#149 Change Properties Implementation To Use ConcurrentDictionary

As we start taking a greater interest in performance, we've decided to move away from the ExpandoObject implementation of the DynamicProperties abstract class. I've noticed that it's always easier to treat the Properties feature like a dictionary anyway. Couple that with the performance hit from using the dynamic, and the concurrency needed anyway, this is a no-brainer.

4.0.0.187

31 Oct 22:03
Compare
Choose a tag to compare

Enhancements

#102 Add Route(s) to Router.RoutingTable at Specified Index

Currently, routes are added to the routing table in the order that they are registered, and there is no mechanism to circumvent this. Hence, methods have been added to Router to:

  • Splice a route in at specific index
  • Splice a range of routes in at a given index
  • Splice a route either before or after a given Route object
  • Splice a range of routes either before or after a given Route object
  • Add a route or range of routes to top of routing table

#108 Provide Generic Delegate for WebExceptionStatus.Timeout

Currently if the RestClient.Execute() gets a WebException with a status of WebExceptionStatus.Timeout when attempting to send the request, the exception is just re-thrown.

This adds the ability to provide a generic delegate on the RestClient that would execute when the timeout occurs. This will simplify the end user implementation so that the code to handle this situation only needs to get written once; e.g. in situations where you might want to try again.

#122 Formatting Logic for PublicFolder.Prefix

Rather than always pre-pending a leading slash ('/') to the Prefix property of the PublicFolder class - which would be problematic if it already starts with a slash - logic was added to the setter to trim white space from the string and remove any leading and trailing slashes before adding a leading slash to the resulting value.

#123 Propagate Logger Assignment

When the Logger property is set on the RestServer class, it should set the value on the Router as well. Similarly, when the Logger property is set on the Router class (including in the previous scenario), it should set the value on the RouteScanner.

As the inverse of this is not true, in order to set different loggers for each class, they should be set in this order:

  1. RestServer
  2. Router
  3. RouteScanner

#133 Add Missing ContentType Values

Some additional values have been added to the ContentType enum to support Google Protocol Buffers and additional multipart formats.

Value Type Header Value
GoogleProtoBuf Binary application/vnd.google.protobuf
MultipartAlternative Text multipart/alternative
MultipartEncrypted Text multipart/encrypted
MultipartMixed Text multipart/mixed
MultipartRelated Text multipart/related
MultipartSigned Text multipart/signed
XProtoBuf Binary application/x-protobuf

Important This changes does not add support for Google Protocol Buffers, it just adds the content types for those who want to use them.

#134 ContentType Parsing Performance Enhancements

After a round of testing various approaches, we discovered that the implementation of ContentType.FromString() was embarrassingly slow. So, we changed it to the (verifiably) fastest algorithm submitted by our open source community. If you know how to speed this up even more, let us know!

We went ahead and applied this same change to the HttpMethod.FromString() method.

#136 Parsing Multiple Values or Malformed Content Type Http Headers

According to the specification, it's completely valid to send multiple values - as well as parameters - in the content type HTTP header. And if the first value doesn't match with anything in the ContentType enum, we should still check the other values provided. So, now we are. This incidentally also happens to resolve an issue that might occur if a poorly implemented client sends a malformed value, as long as somewhere in that value there is a string that matches tree/subtree.

So as not to kill our performance gains, we also added caching, so values we've seen before don't have to be parsed all over again.

Bug Fixes

#129 ContentType Detection Failures Resolved

In scenarios where multiple values were provided in the Content-type HTTP header or where the value included parameters, a matching type would not be found in the ContentType enum, and the default value would be used.

Example:

Content-type: text/html; charset=UTF-8,text/html; charset=windows-1251

With this fix, parameters are ignored and the first MIME type value provided is used to find a matching enum. In conjunction with this, the order of ContentType.HTML and ContentType.HTM were swapped such that text/html would return ContentType.HTML.

See: