Skip to content
This repository was archived by the owner on Sep 3, 2024. It is now read-only.
Merged
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
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ env:
- secure: fKxyki9JzY95G17hNHSy4dgDmH+zXIk50BR6honAz7LmsA9jGv2gg+l1AIUERH3eckdJwxze4K0e5hX88Xfn4j5nN8TnEw7Wwn7c1vjR9FxvRr5cjaJC0SVsOTZj4VC1R9Sqjzrkqv3jJDtDdz9B8aZMdEzJ+BkHwUMFxmuko2p2qabKnAonaGk5VDSTtDyvDMa0aFjKDYuB6MrnrxhfCzLk1ciyGL61SNfz0/u7Hj3xC6v4U0f0BQ9n65l5dIVn+mpuCdQ7GO0HGj0ySo7ffif3qqXXTx7ZWIl3wvf5RCp0PouA8q2dRHYYyGqhhCwuRNU7dfhS/eFtUtcITclZ/DJE86NBQGJ9tpUUhL7BJ3sNJZzbajh1F28paJgi595SKpScp4VGx3iBuDSKH4eb3cRj5TJqCUoscue8/uIZGgR4PGkGF4lbad3b7xexIk1YY/2Rwun1J0N7tqS0rej3ZRZBtipMF64NrVnt9dZzgsIlssZ0W2NvG0vcLX6DUdeZaR0diz8bUl1DXrMFxAUQ/QYxilvaESmCslRElzlb+5eaqQTG5lSP5l6f3ZcKAhvcUGggu9t3vjzpEq3zFa5dorCyK+es1ASNlwZtZr8in9oYkvam9aiDkZD8cEc+9u+qxvnX9w3MsA0P047YCMQGgwRl5dpizoySC3wr52voK7I=
- secure: HvSSWjsLR6qFi9/iHMKKoVUT4BDN7h4QjKHLvcrznwpfojrA1+nIlIwgcaIkxi5WClsoPmwW8YInPlyIyV+37+SaNK4sxB2puzwaC3iHBi6Ql38q+olsLdCE8u+DcfEcnzQo8pinNoyBLRjBgHjTAbrwIkLETNOZchss4A4UVhvPSznuNz5imlZxp4LqZ2pW/YcaLeCMQzuJHt5iRM6O4CMu7O4kMP/SPQXIXcqqYWI+o4um1JFO5juXBXtrhcTkOHfvGjqZghwma1F3v7zLHtqrbGBxaTLGNj0EIWPQhQEE5wDpvAqLEZcySl0ZjsPQUJd2Uxi9b8mWM1x6WaZTu9uqIy5t9lkCwUkEAwc/LUXZxwL0oi8HncgJvu7Q5KwVn375sJ0CER0Vn1FO8jphnMbpfkqUmbZmkwoIAUENNNYyGEp6zoNnRvaFQTjEW4bL2huPS0g3BgC2xyh8d5ZsPykIgQXlM8EEA2BZkd+ep2rKjOPT94rs5V5IPuVi9FYzFIsc2kpEkKZO/38j6pEAdCVIZp1m0+KRlLNQ9FeaI+xtil95V5ALIEXpc44o5OuUSoxt5ZirYaGSJtUtxo8E+HqpNL5WrTUeE8+aZos6Tu7jF63U2ehwg5xR6EubvrBjWvSHsQKcwDfdeHK2m15S/mVlahpwyyoCASAJtV7mRJI=
matrix:
- LIBRARY_VERSION=1.2.0-beta.3.21 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=alpine3.9 RUNTIME=alpine-x64
- LIBRARY_VERSION=1.2.0-beta.3.21 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=alpine3.8 RUNTIME=alpine-x64
- LIBRARY_VERSION=1.2.0-beta.3.21 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=bionic RUNTIME=ubuntu.18.04-x64
- LIBRARY_VERSION=1.2.0-beta.3.21 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=stretch-slim RUNTIME=debian.9-x64
- LIBRARY_VERSION=1.2.0-beta.3.22 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=alpine3.9 RUNTIME=alpine-x64
- LIBRARY_VERSION=1.2.0-beta.3.22 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=alpine3.8 RUNTIME=alpine-x64
- LIBRARY_VERSION=1.2.0-beta.3.22 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=bionic RUNTIME=ubuntu.18.04-x64
- LIBRARY_VERSION=1.2.0-beta.3.22 CONTAINER_RUNTIME_VERSION=2.2.4 CONTAINER_RUNTIME=stretch-slim RUNTIME=debian.9-x64
git:
depth: false
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ARG CONTAINER_RUNTIME=alpine3.8

FROM node:10.12.0-alpine AS build-javascript
ARG CLIENT_PACKAGE=@sqlstreamstore/browser
ARG CLIENT_VERSION=0.9.2
ARG CLIENT_VERSION=0.9.3
ARG NPM_REGISTRY=https://www.myget.org/F/sqlstreamstore/npm/

ENV REACT_APP_CLIENT_VERSION=${CLIENT_VERSION}
Expand Down
4 changes: 2 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
set -e

CONTAINER_RUNTIME=${CONTAINER_RUNTIME:-alpine3.9}
LIBRARY_VERSION=${LIBRARY_VERSION:-1.2.0-beta.3.19}
CLIENT_VERSION=${CLIENT_VERSION:-0.9.2}
LIBRARY_VERSION=${LIBRARY_VERSION:-1.2.0-beta.3.22}
CLIENT_VERSION=${CLIENT_VERSION:-0.9.3}

LOCAL_IMAGE="sql-stream-store-server"
LOCAL="${LOCAL_IMAGE}:latest"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ namespace SqlStreamStore.Server.Browser
internal static class SqlStreamStoreBrowserMiddleware
{
public static IApplicationBuilder UseSqlStreamStoreBrowser(
this IApplicationBuilder builder)
this IApplicationBuilder builder,
Type rootType = default)
{
rootType = rootType ?? typeof(SqlStreamStoreBrowserMiddleware);
var sqlStreamStoreBrowserFileProvider = new EmbeddedFileProvider(
typeof(SqlStreamStoreBrowserMiddleware).Assembly,
typeof(SqlStreamStoreBrowserMiddleware).Namespace);
rootType.Assembly,
rootType.Namespace);

var staticFiles = typeof(SqlStreamStoreBrowserMiddleware).Assembly.GetManifestResourceNames()
.Where(name => name.StartsWith(typeof(SqlStreamStoreBrowserMiddleware).Namespace));
var staticFiles = rootType.Assembly.GetManifestResourceNames()
.Where(name => name.StartsWith(rootType.Namespace));

Log.Debug(
"The following embedded resources were found and will be served as static content: {staticFiles}",
"The following embedded resources were found and will be served as static content: {staticFiles}",
string.Join(", ", staticFiles));

return builder.Use(IndexPage).UseStaticFiles(new StaticFileOptions
Expand All @@ -32,11 +34,12 @@ public static IApplicationBuilder UseSqlStreamStoreBrowser(

Task IndexPage(HttpContext context, Func<Task> next)
{
if(GetAcceptHeaders(context.Request).Contains("text/html"))
if (!GetAcceptHeaders(context.Request).Contains("text/html"))
{
context.Request.Path = new PathString("/index.html");
return TryRedirectStaticContent(context, next);
}

context.Request.Path = new PathString("/index.html");
return next();
}
}
Expand All @@ -47,5 +50,36 @@ private static string[] GetAcceptHeaders(HttpRequest contextRequest)
value => MediaTypeWithQualityHeaderValue.TryParse(value, out var header)
? header.MediaType
: null);

private static Task TryRedirectStaticContent(HttpContext context, Func<Task> next)
{
if (!context.Request.Path.HasValue)
{
return next();
}

var requestPieces = context.Request.Path.Value.Split('/');

for (var i = 2; i < requestPieces.Length; i++)
{
if (requestPieces[i] != "static")
{
continue;
}

var staticFilePath = new string[requestPieces.Length - i];
for (var j = i; j < requestPieces.Length; j++)
{
staticFilePath[j - i] = requestPieces[j];
}

context.Response.StatusCode = 308;
context.Response.Headers["Location"] =
$"{string.Join("/", Enumerable.Repeat("..", i + 1))}/{string.Join("/", staticFilePath)}";
return Task.CompletedTask;
}

return next();
}
}
}
2 changes: 1 addition & 1 deletion src/SqlStreamStore.Server/SqlStreamStore.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<LangVersion>latest</LangVersion>
<CrossGenDuringPublish>false</CrossGenDuringPublish>
<LibraryVersion Condition="$(LibraryVersion) == ''">1.2.0-beta.3.21</LibraryVersion>
<LibraryVersion Condition="$(LibraryVersion) == ''">1.2.0-beta.3.22</LibraryVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ILLink.Tasks" Version="0.1.5-preview-1841731" />
Expand Down
1 change: 0 additions & 1 deletion src/SqlStreamStore.Server/SqlStreamStoreServerStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using SqlStreamStore.HAL;
using SqlStreamStore.Server.Browser;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using SqlStreamStore.Server.Browser;
using Xunit;

namespace SqlStreamStore.Server.Tests.Browser
{
public class SqlStreamStoreBrowserTests : IDisposable
{
private readonly TestServer _server;
private readonly HttpClient _httpClient;

public SqlStreamStoreBrowserTests()
{
_server = new TestServer(
new WebHostBuilder()
.Configure(app => app.UseSqlStreamStoreBrowser(typeof(SqlStreamStoreBrowserTests))));

_httpClient = new HttpClient(_server.CreateHandler())
{
BaseAddress = new UriBuilder().Uri
};
}

public static IEnumerable<object[]> IndexPageCases()
{
yield return new object[] {"/"};
yield return new object[] {"/stream"};
yield return new object[] {"/streams/a-stream"};
yield return new object[] {"/streams/a-stream/metadata"};
}

[Theory, MemberData(nameof(IndexPageCases))]
public async Task RequestsForHtmlReturnTheIndexPage(string path)
{
using (var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, path)
{
Headers = {Accept = {new MediaTypeWithQualityHeaderValue("text/html")}}
}))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(await GetStaticEmbeddedResource("index.html"), await response.Content.ReadAsStringAsync());
}
}

[Fact]
public async Task RequestsForStaticFilesFromRootAreReturned()
{
using (var response = await _httpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Get, "/static/js/ws.js")
{
Headers = {Accept = {new MediaTypeWithQualityHeaderValue("*/*")}}
}))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(
await GetStaticEmbeddedResource("static.js.ws.js"),
await response.Content.ReadAsStringAsync());
}
}

public static IEnumerable<object[]> StaticContentCases()
{
yield return new object[] {"/stream/", "../../../"};
yield return new object[] {"/streams/a-stream/", "../../../../"};
yield return new object[] {"/streams/a-stream/metadata/", "../../../../../"};
}

[Theory, MemberData(nameof(StaticContentCases))]
public async Task RequestsForStaticAreRedirectedIfNotAtRoot(string path, string parent)
{
using (var response = await _httpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Get, $"{path}static/js/ws.js")
{
Headers = {Accept = {new MediaTypeWithQualityHeaderValue("*/*")}}
}))
{
Assert.Equal(HttpStatusCode.PermanentRedirect, response.StatusCode);
Assert.Equal($"{parent}static/js/ws.js", response.Headers.Location?.ToString());
}
}

private static async Task<string> GetStaticEmbeddedResource(string resource)
{
using (var stream = typeof(SqlStreamStoreBrowserTests)
.Assembly
.GetManifestResourceStream(typeof(SqlStreamStoreBrowserTests), resource))
using (var reader = new StreamReader(stream))
{
return await reader.ReadToEndAsync();
}
}

public void Dispose()
{
_httpClient?.Dispose();
_server?.Dispose();
}
}
}
10 changes: 10 additions & 0 deletions tests/SqlStreamStore.Server.Tests/Browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

</body>
</html>
2 changes: 2 additions & 0 deletions tests/SqlStreamStore.Server.Tests/Browser/static/js/ws.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(function () {
})();
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,12 @@
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Browser\**" Exclude="Browser\**\*.cs">
<Link>Browser\%(RecursiveDir)%(Filename)%(Extension)</Link>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Include="Browser\static\js\ws.js" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using SqlStreamStore;
using SqlStreamStore.HAL;
using SqlStreamStore.Server;
Expand All @@ -15,8 +14,7 @@ namespace SQLStreamStore.Server.Tests
public class SqlStreamStoreServerStartupTests : IDisposable
{
private readonly InMemoryStreamStore _streamStore;
private readonly IWebHost _host;
private TestServer _server;
private readonly TestServer _server;
private readonly HttpClient _httpClient;

public SqlStreamStoreServerStartupTests()
Expand Down Expand Up @@ -52,7 +50,7 @@ public async Task StartsUp()
public void Dispose()
{
_streamStore?.Dispose();
_host?.Dispose();
_server?.Dispose();
_httpClient?.Dispose();
}
}
Expand Down