Skip to content

Commit 8ce32dc

Browse files
author
Yang Lyu
authored
Merge pull request #32 from aspnet/yalyu/ETag
Yalyu/e tag
2 parents 0178347 + 2154760 commit 8ce32dc

7 files changed

Lines changed: 684 additions & 19 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading.Tasks;
66
using Microsoft.AspNetCore.Http;
77
using Microsoft.AspNetCore.Http.Headers;
8+
using Microsoft.AspNetCore.Http.Features;
89

910
namespace Microsoft.VisualStudio.Web.BrowserLink
1011
{
@@ -99,6 +100,9 @@ private async Task ExecuteWithFilter(IHttpSocketAdapter injectScriptSocket, stri
99100
return StaticTaskResult.True;
100101
});
101102

103+
IHttpSendFileFeature originalSendFile = httpContext.Features.Get<IHttpSendFileFeature>();
104+
httpContext.Features.Set<IHttpSendFileFeature>(new SendFilesWrapper(originalSendFile, httpContext.Response));
105+
102106
using (AddPageExecutionListenerFeatureTo(httpContext, requestId))
103107
{
104108
await _next(httpContext);

src/Microsoft.VisualStudio.Web.BrowserLink/Common/ArteryConstants.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System;
2-
31
namespace Microsoft.VisualStudio.Web.BrowserLink
42
{
53
/// <summary>
@@ -50,6 +48,22 @@ internal static class BrowserLinkConstants
5048
/// </summary>
5149
public const string RequestScheme = "Scheme";
5250

51+
/// <summary>
52+
/// Suffix added to the end of the instance file name to identify a
53+
/// Version 2 instance file
54+
/// </summary>
55+
public const string Version2Suffix = ".v2";
56+
57+
// Keys for data in the instance file (v2)
58+
public const string HostNameKey = "host-name";
59+
public const string FetchScriptVerbKey = "verb-fetch-script";
60+
public const string InjectScriptVerbKey = "verb-inject-script";
61+
public const string MappingDataVerbKey = "verb-mapping-data";
62+
public const string HttpPortKey = "http-port";
63+
public const string HttpsPortKey = "https-port";
64+
public const string ServerDataVerbKey = "verb-server-data";
65+
public const string ProjectDataKey = "project";
66+
5367
/// <summary>
5468
/// Constants representing the type of mapping data to follow
5569
/// </summary>

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,23 @@ namespace Microsoft.VisualStudio.Web.BrowserLink
1010
/// </summary>
1111
internal class HostConnectionData
1212
{
13-
internal HostConnectionData(string connectionString, string sslConnectionString, string requestSignalName, string readySignalName, IEnumerable<string> projectPaths)
13+
internal HostConnectionData(
14+
string connectionString,
15+
string sslConnectionString,
16+
string requestSignalName,
17+
string readySignalName,
18+
string injectScriptVerb,
19+
string mappingDataVerb,
20+
string serverDataVerb,
21+
IEnumerable<string> projectPaths)
1422
{
1523
ConnectionString = connectionString;
1624
SslConnectionString = sslConnectionString;
1725
RequestSignalName = requestSignalName;
1826
ReadySignalName = readySignalName;
27+
InjectScriptVerb = injectScriptVerb;
28+
MappingDataVerb = mappingDataVerb;
29+
ServerDataVerb = serverDataVerb;
1930
ProjectPaths = projectPaths;
2031
}
2132

@@ -39,6 +50,21 @@ internal HostConnectionData(string connectionString, string sslConnectionString,
3950
/// </summary>
4051
public string SslConnectionString { get; private set; }
4152

53+
/// <summary>
54+
/// API verb for injecting the Browser Link script into the page
55+
/// </summary>
56+
public string InjectScriptVerb { get; private set; }
57+
58+
/// <summary>
59+
/// API verb for posting mapping data
60+
/// </summary>
61+
public string MappingDataVerb { get; private set; }
62+
63+
/// <summary>
64+
/// API verb for posting data about the server
65+
/// </summary>
66+
public string ServerDataVerb { get; private set; }
67+
4268
/// <summary>
4369
/// The physical paths of projects loaded in this instance of the design tool.
4470
/// </summary>

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

Lines changed: 251 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ internal static class HostConnectionUtil
1717
// The actual time it takes should be significantly less than this.
1818
private static readonly TimeSpan HostStartupTimeout = TimeSpan.FromMilliseconds(1500);
1919

20+
// These were well-known values in V1 instance files. V2 instance files
21+
// allow these to be overridden.
22+
private static readonly IReadOnlyDictionary<string, string> V1DefaultProperties = new Dictionary<string, string>()
23+
{
24+
{ BrowserLinkConstants.HostNameKey, "localhost" },
25+
{ BrowserLinkConstants.FetchScriptVerbKey, "browserLink" },
26+
{ BrowserLinkConstants.InjectScriptVerbKey, "injectScriptLink" },
27+
{ BrowserLinkConstants.MappingDataVerbKey, "sendMappingData" }
28+
};
29+
2030
/// <summary>
2131
/// Find the host connection for an ASP.NET application.
2232
/// </summary>
@@ -29,9 +39,7 @@ internal static bool FindHostConnection(string applicationPhysicalPath, out Host
2939

3040
foreach (string instanceFileName in GetAllInstanceFileNames())
3141
{
32-
HostConnectionData connectionCandidate;
33-
34-
if (ReadConnectionData(instanceFileName, out connectionCandidate))
42+
foreach (HostConnectionData connectionCandidate in ReadConnectionData(instanceFileName))
3543
{
3644
if (ConnectionContainsApplication(connectionCandidate, applicationPhysicalPath))
3745
{
@@ -164,31 +172,259 @@ private static IEnumerable<string> GetAllInstanceFileNames()
164172
return fileNames;
165173
}
166174

167-
private static bool ReadConnectionData(string instanceFileName, out HostConnectionData connection)
175+
private static IEnumerable<HostConnectionData> ReadConnectionData(string instanceFileName)
168176
{
169-
List<string> lines = ReadAllLinesFrom(instanceFileName);
170-
171-
if (lines.Count > 2)
177+
string version2FileName = instanceFileName + BrowserLinkConstants.Version2Suffix;
178+
if (MappedFileExists(version2FileName))
172179
{
173-
string connectionString = lines[0];
174-
string sslConnectionString = lines[1];
175-
string requestSignalName = instanceFileName + BrowserLinkConstants.RequestSignalSuffix;
176-
string readySignalName = instanceFileName + BrowserLinkConstants.ReadySignalSuffix;
177-
List<string> projects = new List<string>();
180+
return ReadV2ConnectionData(version2FileName);
181+
}
182+
else
183+
{
184+
HostConnectionData connectionData;
178185

179-
for (int i = 2; i < lines.Count; i++)
186+
if (ReadV1ConnectionData(instanceFileName, out connectionData))
187+
{
188+
return new HostConnectionData[] { connectionData };
189+
}
190+
else
180191
{
181-
projects.Add(PathUtil.NormalizeDirectoryPath(lines[i]));
192+
return new HostConnectionData[0];
182193
}
194+
}
195+
}
196+
197+
private static bool ReadV1ConnectionData(string instanceFileName, out HostConnectionData connection)
198+
{
199+
List<string> lines = ReadAllLinesFrom(instanceFileName);
200+
201+
return ParseV1ConnectionData(instanceFileName, lines, out connection);
202+
}
203+
204+
internal static bool ParseV1ConnectionData(string instanceFileName, List<string> lines, out HostConnectionData connection)
205+
{
206+
if (lines.Count > 2)
207+
{
208+
string connectionString;
209+
string sslConnectionString;
210+
string requestSignalName;
211+
string readySignalName;
212+
string injectScriptVerb;
213+
string mappingDataVerb;
214+
string serverDataVerb;
215+
IEnumerable<string> projectPaths;
216+
217+
CreateConnectionStringsAndProjectPaths(
218+
lines,
219+
out connectionString,
220+
out sslConnectionString,
221+
out projectPaths);
222+
223+
CreateSignalNames(
224+
instanceFileName,
225+
out requestSignalName,
226+
out readySignalName);
227+
228+
CreateVerbUrls(
229+
V1DefaultProperties,
230+
connectionString,
231+
out injectScriptVerb,
232+
out mappingDataVerb,
233+
out serverDataVerb);
234+
235+
connection = new HostConnectionData(
236+
connectionString,
237+
sslConnectionString,
238+
requestSignalName,
239+
readySignalName,
240+
injectScriptVerb,
241+
mappingDataVerb,
242+
serverDataVerb,
243+
projectPaths);
183244

184-
connection = new HostConnectionData(connectionString, sslConnectionString, requestSignalName, readySignalName, projects);
185245
return true;
186246
}
187247

188248
connection = null;
189249
return false;
190250
}
191251

252+
private static void CreateConnectionStringsAndProjectPaths(List<string> lines, out string connectionString, out string sslConnectionString, out IEnumerable<string> projectPaths)
253+
{
254+
connectionString = lines[0];
255+
sslConnectionString = lines[1];
256+
257+
List<string> projectPathsList = new List<string>();
258+
259+
for (int i = 2; i < lines.Count; i++)
260+
{
261+
projectPathsList.Add(PathUtil.NormalizeDirectoryPath(lines[i]));
262+
}
263+
264+
projectPaths = projectPathsList;
265+
}
266+
267+
private static IEnumerable<HostConnectionData> ReadV2ConnectionData(string instanceFileName)
268+
{
269+
List<string> instanceFileLines = ReadAllLinesFrom(instanceFileName);
270+
271+
return ParseV2ConnectionData(instanceFileName, instanceFileLines);
272+
}
273+
274+
internal static IEnumerable<HostConnectionData> ParseV2ConnectionData(string instanceFileName, List<string> instanceFileLines)
275+
{
276+
string connectionString;
277+
string sslConnectionString;
278+
string requestSignalName;
279+
string readySignalName;
280+
string injectScriptVerb;
281+
string mappingDataVerb;
282+
string serverDataVerb;
283+
IEnumerable<string> projectPaths;
284+
285+
string instanceFileNameBase = instanceFileName.Substring(0, instanceFileName.Length - BrowserLinkConstants.Version2Suffix.Length);
286+
CreateSignalNames(
287+
instanceFileNameBase,
288+
out requestSignalName,
289+
out readySignalName);
290+
291+
Dictionary<string, string> properties = new Dictionary<string, string>();
292+
Dictionary<string, string> projects = new Dictionary<string, string>();
293+
294+
ParseValuesFromLines(instanceFileLines, properties, projects);
295+
296+
List<HostConnectionData> connections = new List<HostConnectionData>();
297+
298+
foreach (KeyValuePair<string, string> project in projects)
299+
{
300+
CreateConnectionStrings(
301+
properties,
302+
project.Key,
303+
out connectionString,
304+
out sslConnectionString);
305+
306+
CreateVerbUrls(
307+
properties,
308+
connectionString,
309+
out injectScriptVerb,
310+
out mappingDataVerb,
311+
out serverDataVerb);
312+
313+
projectPaths = new string[] { PathUtil.NormalizeDirectoryPath(project.Value) };
314+
315+
connections.Add(new HostConnectionData(
316+
connectionString,
317+
sslConnectionString,
318+
requestSignalName,
319+
readySignalName,
320+
injectScriptVerb,
321+
mappingDataVerb,
322+
serverDataVerb,
323+
projectPaths));
324+
}
325+
326+
return connections;
327+
}
328+
329+
private static void CreateConnectionStrings(Dictionary<string, string> properties, string projectKey, out string connectionString, out string sslConnectionString)
330+
{
331+
connectionString = String.Empty;
332+
sslConnectionString = String.Empty;
333+
334+
string hostName;
335+
string fetchScriptVerb;
336+
if (properties.TryGetValue(BrowserLinkConstants.HostNameKey, out hostName) &&
337+
properties.TryGetValue(BrowserLinkConstants.FetchScriptVerbKey, out fetchScriptVerb))
338+
{
339+
string httpPort;
340+
if (properties.TryGetValue(BrowserLinkConstants.HttpPortKey, out httpPort))
341+
{
342+
connectionString = String.Format("http://{0}:{1}/{2}/{3}",
343+
hostName,
344+
httpPort,
345+
projectKey,
346+
fetchScriptVerb);
347+
}
348+
349+
string httpsPort;
350+
if (properties.TryGetValue(BrowserLinkConstants.HttpsPortKey, out httpsPort))
351+
{
352+
sslConnectionString = String.Format("https://{0}:{1}/{2}/{3}",
353+
hostName,
354+
httpsPort,
355+
projectKey,
356+
fetchScriptVerb);
357+
}
358+
}
359+
}
360+
361+
private static void CreateSignalNames(string instanceFileName, out string requestSignalName, out string readySignalName)
362+
{
363+
requestSignalName = instanceFileName + BrowserLinkConstants.RequestSignalSuffix;
364+
readySignalName = instanceFileName + BrowserLinkConstants.ReadySignalSuffix;
365+
}
366+
367+
private static void CreateVerbUrls(IReadOnlyDictionary<string, string> properties, string httpConnectionString, out string injectScriptVerb, out string mappingDataVerb, out string serverDataVerb)
368+
{
369+
injectScriptVerb = null;
370+
mappingDataVerb = null;
371+
serverDataVerb = null;
372+
373+
if (httpConnectionString != null)
374+
{
375+
Uri connectionUri;
376+
if (Uri.TryCreate(httpConnectionString, UriKind.Absolute, out connectionUri))
377+
{
378+
string verbName;
379+
380+
if (properties.TryGetValue(BrowserLinkConstants.InjectScriptVerbKey, out verbName))
381+
{
382+
injectScriptVerb = new Uri(connectionUri, verbName).ToString();
383+
}
384+
385+
if (properties.TryGetValue(BrowserLinkConstants.MappingDataVerbKey, out verbName))
386+
{
387+
mappingDataVerb = new Uri(connectionUri, verbName).ToString();
388+
}
389+
390+
if (properties.TryGetValue(BrowserLinkConstants.ServerDataVerbKey, out verbName))
391+
{
392+
serverDataVerb = new Uri(connectionUri, verbName).ToString();
393+
}
394+
}
395+
}
396+
}
397+
398+
private static void ParseValuesFromLines(List<string> lines, Dictionary<string, string> properties, Dictionary<string, string> projects)
399+
{
400+
foreach (string line in lines)
401+
{
402+
int colonIndex = line.IndexOf(':');
403+
if (colonIndex < 0)
404+
{
405+
continue;
406+
}
407+
408+
string key = line.Substring(0, colonIndex);
409+
string value = line.Substring(colonIndex + 1);
410+
411+
if (String.Equals(key, BrowserLinkConstants.ProjectDataKey, StringComparison.Ordinal))
412+
{
413+
string[] parts = value.Split(';');
414+
if (parts.Length < 2)
415+
{
416+
continue;
417+
}
418+
419+
projects[parts[1]] = parts[0];
420+
}
421+
else
422+
{
423+
properties[key] = value;
424+
}
425+
}
426+
}
427+
192428
private static List<string> ReadAllLinesFrom(string fileName)
193429
{
194430
MemoryMappedFile file = null;

0 commit comments

Comments
 (0)