Skip to content

Commit b6abc14

Browse files
author
Yang Lyu
committed
Bug 226152:Browser Link K: Static files don't connect after restarting Browser Link
1 parent 2e68974 commit b6abc14

5 files changed

Lines changed: 245 additions & 1 deletion

File tree

src/Microsoft.VisualStudio.Web.BrowserLink/BrowserLinkMiddleWare.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ internal class BrowserLinkMiddleware
2323
private RequestDelegate _next;
2424
private string _applicationPath;
2525

26+
private const string headerIfNoneMatch = "If-None-Match";
27+
2628
internal BrowserLinkMiddleware(string applicationPath, RequestDelegate next)
2729
{
2830
_applicationPath = applicationPath;
@@ -44,6 +46,11 @@ internal Task Invoke(HttpContext context)
4446
}
4547
else
4648
{
49+
if (context.Request.Headers.ContainsKey(headerIfNoneMatch) && BrowserLinkMiddleWareUtil.GetRequestPort(context.Request.Headers) != -1)
50+
{
51+
BrowserLinkMiddleWareUtil.RemoveETagAndTimeStamp(context.Request.Headers);
52+
}
53+
4754
return ExecuteWithoutFilter(context);
4855
}
4956
}
@@ -67,13 +74,22 @@ private PageExecutionListenerFeature AddPageExecutionListenerFeatureTo(HttpConte
6774
private async Task ExecuteWithFilter(IHttpSocketAdapter injectScriptSocket, string requestId, HttpContext httpContext)
6875
{
6976
ScriptInjectionFilterContext filterContext = new ScriptInjectionFilterContext(httpContext);
77+
int currentPort = -1;
78+
79+
PreprocessRequestHeader(httpContext, ref currentPort);
80+
81+
if (currentPort == -1)
82+
{
83+
BrowserLinkMiddleWareUtil.RemoveETagAndTimeStamp(httpContext.Request.Headers);
84+
}
7085

7186
using (ScriptInjectionFilterStream filter = new ScriptInjectionFilterStream(injectScriptSocket, filterContext))
7287
{
7388
httpContext.Response.Body = filter;
7489
httpContext.Response.OnStarting(delegate ()
7590
{
7691
httpContext.Response.ContentLength = null;
92+
BrowserLinkMiddleWareUtil.AddToETag(httpContext.Response.Headers, currentPort);
7793

7894
return StaticTaskResult.True;
7995
});
@@ -207,5 +223,18 @@ private static bool EnsureHostServerStarted(string applicationPath, ref HostConn
207223

208224
return true;
209225
}
226+
227+
private void PreprocessRequestHeader(HttpContext httpContext, ref int currentPort)
228+
{
229+
if (httpContext.Request.Headers.ContainsKey(headerIfNoneMatch))
230+
{
231+
HostConnectionData connectionData;
232+
233+
if (GetHostConnectionData(_applicationPath, out connectionData))
234+
{
235+
currentPort = BrowserLinkMiddleWareUtil.FilterRequestHeader(httpContext.Request.Headers, connectionData.ConnectionString);
236+
}
237+
}
238+
}
210239
}
211240
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using Microsoft.AspNetCore.Http;
2+
using System;
3+
using Microsoft.AspNetCore.Http.Headers;
4+
using Microsoft.Net.Http.Headers;
5+
using Microsoft.Extensions.Primitives;
6+
7+
namespace Microsoft.VisualStudio.Web.BrowserLink
8+
{
9+
internal static class BrowserLinkMiddleWareUtil
10+
{
11+
private const string headerIfNoneMatch = "If-None-Match";
12+
13+
internal static int GetRequestPort(IHeaderDictionary headers)
14+
{
15+
RequestHeaders requestHeader = new RequestHeaders(headers);
16+
17+
foreach (EntityTagHeaderValue value in requestHeader.IfNoneMatch)
18+
{
19+
string[] strings = value.ToString().Split(':');
20+
21+
if (strings.Length >= 2)
22+
{
23+
return Int32.Parse(strings[1].Substring(0, strings[1].Length - 1));
24+
}
25+
}
26+
27+
return -1;
28+
}
29+
30+
internal static int GetCurrentPort(string connectionString)
31+
{
32+
string[] strings1 = connectionString.Split(':');
33+
34+
if (strings1.Length >= 3)
35+
{
36+
string[] strings2 = strings1[2].Split('/');
37+
38+
return Int32.Parse(strings2[0]);
39+
}
40+
41+
return -1;
42+
}
43+
44+
internal static void RemoveETagAndTimeStamp(IHeaderDictionary headers)
45+
{
46+
RequestHeaders requestHeader = new RequestHeaders(headers);
47+
48+
requestHeader.IfNoneMatch = null;
49+
requestHeader.IfModifiedSince = null;
50+
}
51+
52+
internal static void DeletePortFromETag(IHeaderDictionary headers)
53+
{
54+
RequestHeaders requestHeader = new RequestHeaders(headers);
55+
string newEtag = "";
56+
57+
foreach(EntityTagHeaderValue value in requestHeader.IfNoneMatch)
58+
{
59+
String[] strings = value.ToString().Split(':');
60+
61+
if (strings.Length >= 2)
62+
{
63+
newEtag = strings[0] + "\"";
64+
break;
65+
}
66+
}
67+
68+
if (newEtag.Length > 0)
69+
{
70+
headers.Remove(headerIfNoneMatch);
71+
headers.Add(headerIfNoneMatch, new StringValues(newEtag));
72+
}
73+
}
74+
75+
internal static void AddToETag(IHeaderDictionary headers, int port)
76+
{
77+
ResponseHeaders responseHeader = new ResponseHeaders(headers);
78+
79+
if (responseHeader.ETag != null)
80+
{
81+
string temp = responseHeader.ETag.ToString().Substring(0, responseHeader.ETag.ToString().Length - 1) + ":" + port + "\"";
82+
responseHeader.ETag = new EntityTagHeaderValue(temp);
83+
}
84+
}
85+
86+
internal static int FilterRequestHeader(IHeaderDictionary headers, string connectionString)
87+
{
88+
int requestPort = GetRequestPort(headers);
89+
int currentPort = GetCurrentPort(connectionString);
90+
91+
if (requestPort != currentPort)
92+
{
93+
RemoveETagAndTimeStamp(headers);
94+
}
95+
else
96+
{
97+
DeletePortFromETag(headers);
98+
}
99+
100+
return currentPort;
101+
}
102+
}
103+
}

src/Microsoft.VisualStudio.Web.BrowserLink/Project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"dependencies": {
1515
"Microsoft.AspNetCore.Hosting.Abstractions": "1.0.0",
1616
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0",
17+
"Microsoft.AspNetCore.Http.Extensions": "1.0.0",
1718
"Microsoft.Extensions.FileProviders.Physical": "1.0.0",
1819
"Microsoft.Extensions.TaskCache.Sources": {
1920
"version": "1.1.0-*",
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Http.Headers;
3+
using Microsoft.Extensions.Primitives;
4+
using Xunit;
5+
6+
namespace Microsoft.VisualStudio.Web.BrowserLink.Test
7+
{
8+
public class BrowserLinkMiddleWareUtilTest
9+
{
10+
private string headerIfNoneMatch = "If-None-Match";
11+
private string ETag = "ETag";
12+
13+
[Fact]
14+
public void GetCurrentPort_ContainsPortNumber()
15+
{
16+
// Arrange
17+
string connectionString = "http://localhost:3082/acskakdjaksa";
18+
19+
// Act
20+
int currentPort = BrowserLinkMiddleWareUtil.GetCurrentPort(connectionString);
21+
22+
// Assert
23+
Assert.Equal(currentPort, 3082);
24+
}
25+
26+
[Fact]
27+
public void GetCurrentPort_EmptyConnectionString()
28+
{
29+
// Arrange
30+
string connectionString = "";
31+
32+
// Act
33+
int currentPort = BrowserLinkMiddleWareUtil.GetCurrentPort(connectionString);
34+
35+
// Assert
36+
Assert.Equal(currentPort, -1);
37+
}
38+
39+
[Fact]
40+
public void GetRequestPort_ContainsPortNumber()
41+
{
42+
// Arrange
43+
IHeaderDictionary dict = new HeaderDictionary
44+
{
45+
{ headerIfNoneMatch, new StringValues("\"1d20ac81ccb7b87:7576\"") }
46+
};
47+
48+
// Act
49+
int port = BrowserLinkMiddleWareUtil.GetRequestPort(dict);
50+
51+
// Assert
52+
Assert.Equal(port, 7576);
53+
}
54+
55+
[Fact]
56+
public void GetRequestPort_EmptyIfNoneMatch()
57+
{
58+
// Arrange
59+
IHeaderDictionary dict = new HeaderDictionary
60+
{
61+
{ headerIfNoneMatch, new StringValues("\"\"") }
62+
};
63+
64+
// Act
65+
int port = BrowserLinkMiddleWareUtil.GetRequestPort(dict);
66+
67+
// Assert
68+
Assert.Equal(port, -1);
69+
}
70+
71+
[Fact]
72+
public void DeletePortFromEtag_ContainsPortNumber()
73+
{
74+
// Arrange
75+
IHeaderDictionary dict = new HeaderDictionary
76+
{
77+
{ headerIfNoneMatch, new StringValues("\"1d20ac81ccb7b87:7576\"") }
78+
};
79+
string expectedOutput = "\"1d20ac81ccb7b87\"";
80+
81+
// Act
82+
BrowserLinkMiddleWareUtil.DeletePortFromETag(dict);
83+
RequestHeaders requestHeader = new RequestHeaders(dict);
84+
string actualOutput = requestHeader.IfNoneMatch[0].ToString();
85+
86+
// Assert
87+
Assert.Equal(actualOutput, expectedOutput);
88+
}
89+
90+
[Fact]
91+
public void AddToETag_NotNullETag()
92+
{
93+
// Arrange
94+
int port = 7576;
95+
IHeaderDictionary dict = new HeaderDictionary
96+
{
97+
{ ETag, new StringValues("\"1d20ac81ccb7b87\"") }
98+
};
99+
string expectedOutput = "\"1d20ac81ccb7b87:7576\"";
100+
101+
// Act
102+
BrowserLinkMiddleWareUtil.AddToETag(dict, port);
103+
ResponseHeaders responseHeader = new ResponseHeaders(dict);
104+
string actualOutput = responseHeader.ETag.ToString();
105+
106+
// Assert
107+
Assert.Equal(actualOutput, expectedOutput);
108+
}
109+
}
110+
}

test/Microsoft.VisualStudio.Web.BrowserLink.Test/project.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"dependencies": {
88
"Microsoft.VisualStudio.Web.BrowserLink": "1.0.0",
99
"xunit": "2.2.0-*",
10-
"dotnet-test-xunit": "2.2.0-*"
10+
"dotnet-test-xunit": "2.2.0-*",
11+
"Microsoft.AspNetCore.Http": "1.0.0"
1112
},
1213
"testRunner": "xunit",
1314
"frameworks": {

0 commit comments

Comments
 (0)