OWIN — Open Web Interface for .NET, v1.0 Draft 2

Contents

Overview

This document defines a standard interface between .NET web servers and web applications. The goal of the OWIN interface is to decouple server and application, encourage the development of simple "middleware" modules for .NET web development, and, by being an open standard, stimulate the open source ecosystem of .NET web development tools.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Definition

OWIN comprises three core interfaces: IApplication, IRequest, and IResponse. In this document, an OWIN-compatible web server is referred to as a "host", and an object implementing IApplication is referred to as an "application". Broadly speaking, hosts provide application objects with request objects, and application objects provide response objects back to the host. How an application is provided to a host is outside the scope of this specification and must be documented by the host implementor.

IApplication

public interface IApplication
{
    IAsyncResult BeginInvoke(IRequest request, AsyncCallback callback, object state);
    IResponse EndInvoke(IAsyncResult result);
}

An application generates a response to a request received by a host by implementing the IApplication interface. This interface defines a single asynchronous operation returning IResponse. The asynchronous operation uses the IAsyncResult pattern (see: MSDN Asynchronous Programming Overview).

The host must call the BeginInvoke method with a non-null IRequest object and a non-null AsyncCallback. The application must either throw an exception from BeginInvoke, or invoke the AsyncCallback in response to the call to BeginInvoke. The host must call EndInvoke in response to the AsyncCallback. The EndInvoke method may return null or throw an exception.

IRequest

public interface IRequest
{
    string Method { get; }
    string Uri { get; }
    IDictionary<string, IEnumerable<string>> Headers { get; }

    IAsyncResult BeginReadBody(byte[] buffer, int offset, int count, AsyncCallback callback, object state);
    int EndReadBody(IAsyncResult result);

    IDictionary<string, object> Items { get; }
}

The Method property is the HTTP request method string of the request (e.g., "GET", "POST").

The Uri property is the HTTP request URI string of the request, relative to the application object. See Paths. The value of the Uri property includes the query string of the request URI (e.g., "/path/and?query=string").

The Headers property is a dictionary whose items correspond to HTTP headers in the request. Keys must be header names without ':' or whitespace, and the keys must be case-insensitive. Values are IEnumerable<string> sequences containing the corresponding header value strings, without newlines. If a header appears in a request multiple times, the sequence value for that key must contain a number of elements corresponding to the number of times the header appears in the request, with each element being a value of a single header.

The BeginReadBody and EndReadBody methods define an asynchronous operation which reads body data of the request into a destination buffer. Applications must provide a valid destination buffer and a non-null AsyncCallback to the BeginReadBody method. Hosts must either throw an exception from BeginReadBody, or invoke the AsyncCallback in response to the call to BeginReadBody. The application must call EndReadBody in response to the AsyncCallback. The EndReadBody method must return the number of bytes copied into the destination buffer as a result of the read operation, or return 0 to signal the end of the request body, or throw an exception. If either BeginReadBody or EndReadBody throw an exception, the application must propagate the error back to the host. See Error Handling.

The Items property is a bag in which the host, application, or user can store arbitrary data associated with the request. Hosts should provide the following keys in Items:

IResponse

public interface IResponse
{
    string Status { get; }
    IDictionary<string, IEnumerable<string>> Headers { get; }
    IEnumerable<object> GetBody();
}

The Status property is a string containing the integer status of the response followed by a space and a reason phrase without a newline (e.g., "200 OK"). All characters in the status string provided by an application should be within the ASCII codepage.

The Headers property is a dictionary representing the headers to be sent with the request. Keys must be header names without ':' or whitespace. Values must be IEnumerable<string> sequences containing the corresponding header value strings, without newlines. If the sequence value for a header name contains multiple elements, the host must write a header name-value line with that name once for each value in the sequence. All characters in header name and value strings should be within the ASCII codepage.

The GetBody method may return an enumerable which represents the body data, or null. Each element in the enumerable must be of one of the following types:

Hosts must write both byte[] and ArraySegment<byte> to the underlying transport as raw data. FileInfo must cause the host to write the named file to the underlying transport. How relative file paths are resolved is outside the scope of this specification and must be documented by the host implementor. Hosts must enumerate the enumerable to completion (i.e., until MoveNext returns false) or until an exception is thrown by MoveNext() or the Current property. After all of the items have been enumerated or if an exception occurs during enumeration, the host must call Dispose on the enumerator.

[TODO] Discuss Task<T>.

Paths

Hosts may have the ability to map application objects to some base path. For example, a host might have an application object configured to respond to requests beginning with "/my-app", in which case it must set the value of "owin.BaseUri" in IRequest.Items to "/my-app". If this host receives a request for "/my-app/foo", the Uri property of the IRequest object provided to the application at "/my-app" must be "/foo". The value of "owin.BaseUri" may be an empty string and must not end with a trailing slash; the value of the URI property must not be an empty string and must start with a slash.

Error Handling

Application Errors

An application might throw an exception in the following places:

An application should make every attempt to trap its own internal errors and generate an appropriate (possibly 500-level) response rather than throwing an exception up to the host. Before writing the response headers to the underlying transport, the host must call GetBody and if the return value is a non-null enumerable containing at least one item, the host must enumerate at least one item from the enumerable. This allows the application to guarantee that it has caught as many of its internal errors as possible; that the host can begin the response without further buffering. If an exception is thrown by the application while enumerating subsequent items from the response body enumerable, the host may write a textual description of the error to the underlying transport, and/or close the connection.

Host Errors

Hosts may throw exceptions in the following places:

Hosts must not throw exceptions from the AsyncCallback provided to the application through the BeginInvoke method.

An exception from either of these methods may indicate that the client has closed or dropped the connection, or another transport-layer error has occurred. The application should perform any post-mortem logic it needs to, and must propagate the exception back to the host through one of the calls described in Application Errors.

Example

public class HelloApp : IApplication
{
    public IAsyncResult BeginInvoke(IRequest request, AsyncCallback callback, object state)
    {
        callback(null);
        return null;
    }

    IResponse EndInvoke(IAsyncResult result)
    {
        return new HelloResponse();
    }
}

class HelloResponse : IResponse
{
    public string Status
    {
        get { return "200 OK"; }
    }

    public IDictionary<string, IEnumerable<string>> Headers
    {
        get
        {
            return new Dictionary<string, IEnumerable<string>>() {
                { "Content-Type", new string[] { "text/html" } },
                { "Content-Length", new string[] { "13" } }
            };
        }
    }

    public IEnumerable<object> GetBody()
    {
        yield return Encoding.UTF8.GetBytes("Hello, world!");
    }
}