Skip to content

Commit 84cd802

Browse files
authored
Merge pull request #19 from antarr/perf/session-storage-optimization-170410250325887014
⚡ Improve thread safety and scalability by using ISession
2 parents 60e6700 + 4cb5a5e commit 84cd802

2 files changed

Lines changed: 97 additions & 65 deletions

File tree

Withings.Example/Program.cs

Lines changed: 94 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,47 @@
33
using System.Text.Json;
44
using Microsoft.AspNetCore.Builder;
55
using Microsoft.AspNetCore.Http;
6-
using Microsoft.Extensions.Caching.Memory;
76
using Microsoft.Extensions.DependencyInjection;
87
using Withings.NET.Client;
98
using Withings.NET.Models;
109

1110
var builder = WebApplication.CreateBuilder(args);
12-
builder.Services.AddMemoryCache();
13-
var app = builder.Build();
11+
12+
// Add services to the container.
13+
builder.Services.AddDistributedMemoryCache();
14+
builder.Services.AddSession(options =>
15+
{
16+
options.IdleTimeout = TimeSpan.FromMinutes(30);
17+
options.Cookie.HttpOnly = true;
18+
options.Cookie.IsEssential = true;
19+
});
1420

1521
var credentials = new WithingsCredentials();
1622
credentials.SetCallbackUrl(Environment.GetEnvironmentVariable("WithingsCallbackUrl"));
1723
credentials.SetConsumerProperties(
1824
Environment.GetEnvironmentVariable("WithingsConsumerKey"),
1925
Environment.GetEnvironmentVariable("WithingsConsumerSecret"));
2026

21-
var authenticator = new Authenticator(credentials);
22-
var session = new Dictionary<string, string>();
27+
builder.Services.AddSingleton(credentials);
28+
builder.Services.AddSingleton<Authenticator>();
29+
builder.Services.AddSingleton<WithingsClient>();
30+
31+
var app = builder.Build();
2332

24-
var activityStartDate = new DateTime(2017, 1, 1);
25-
var activityEndDate = new DateTime(2017, 3, 30);
26-
var bodyStartDate = new DateTime(2017, 5, 8);
27-
var bodyEndDate = new DateTime(2017, 5, 10);
33+
app.UseSession();
2834

2935
app.MapGet("/", () => Results.Redirect("/api/oauth/authorize", permanent: true));
3036

31-
app.MapGet("/api/oauth/authorize", () =>
37+
app.MapGet("/api/oauth/authorize", (HttpContext context, Authenticator authenticator) =>
3238
{
3339
var state = Guid.NewGuid().ToString();
34-
session["State"] = state;
40+
context.Session.SetString("State", state);
3541
var scope = "user.info,user.metrics,user.activity";
3642
var url = authenticator.GetAuthCodeUrl(scope, state);
3743
return Results.Redirect(url);
3844
});
3945

40-
app.MapGet("/api/oauth/callback", async (HttpContext context) =>
46+
app.MapGet("/api/oauth/callback", async (HttpContext context, Authenticator authenticator) =>
4147
{
4248
var query = context.Request.Query;
4349

@@ -49,111 +55,136 @@
4955
if (string.IsNullOrWhiteSpace(state))
5056
return Results.BadRequest("Missing required query parameter 'state'.");
5157

52-
if (!session.TryGetValue("State", out var storedState) || storedState != state)
58+
var storedState = context.Session.GetString("State");
59+
if (string.IsNullOrEmpty(storedState) || storedState != state)
5360
return Results.BadRequest("Invalid state.");
5461

5562
var token = await authenticator.GetAccessToken(code);
5663

57-
session["AccessToken"] = token.AccessToken;
58-
session["RefreshToken"] = token.RefreshToken;
59-
session["UserId"] = token.UserId;
64+
context.Session.SetString("AccessToken", token.AccessToken);
65+
context.Session.SetString("RefreshToken", token.RefreshToken);
66+
context.Session.SetString("UserId", token.UserId);
6067

6168
return Results.Json(token);
6269
});
6370

64-
app.MapGet("/api/withings/activity", async (IMemoryCache cache) =>
71+
app.MapGet("/api/withings/activity", async (HttpContext context, WithingsClient client) =>
6572
{
66-
var start = DateTime.Parse("2017-01-01");
67-
var end = DateTime.Parse("2017-03-30");
68-
var userId = session["UserId"];
69-
var accessToken = session["AccessToken"];
70-
71-
var key = $"activity_{userId}_{start:yyyyMMdd}_{end:yyyyMMdd}";
72-
73-
var activity = await cache.GetOrCreateAsync(key, async entry =>
74-
{
75-
var client = new WithingsClient(credentials);
76-
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
77-
return await client.GetActivityMeasures(
78-
start,
79-
end,
80-
userId,
81-
accessToken);
82-
});
73+
var userId = context.Session.GetString("UserId");
74+
var accessToken = context.Session.GetString("AccessToken");
75+
76+
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(accessToken))
77+
return Results.Unauthorized();
8378

79+
var activity = await client.GetActivityMeasures(
80+
DateTime.Parse("2017-01-01"),
81+
DateTime.Parse("2017-03-30"),
82+
userId,
83+
accessToken);
8484
return Results.Json(activity);
8585
});
8686

87-
app.MapGet("/api/withings/dailyactivity", async () =>
87+
app.MapGet("/api/withings/dailyactivity", async (HttpContext context, WithingsClient client) =>
8888
{
89-
var client = new WithingsClient(credentials);
89+
var userId = context.Session.GetString("UserId");
90+
var accessToken = context.Session.GetString("AccessToken");
91+
92+
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(accessToken))
93+
return Results.Unauthorized();
94+
9095
var activity = await client.GetActivityMeasures(
9196
DateTime.Today.AddDays(-30),
92-
session["UserId"],
93-
session["AccessToken"]);
97+
userId,
98+
accessToken);
9499
return Results.Json(activity);
95100
});
96101

97-
app.MapGet("/api/withings/sleepsummary", async () =>
102+
app.MapGet("/api/withings/sleepsummary", async (HttpContext context, WithingsClient client) =>
98103
{
99-
var client = new WithingsClient(credentials);
104+
var accessToken = context.Session.GetString("AccessToken");
105+
if (string.IsNullOrEmpty(accessToken))
106+
return Results.Unauthorized();
107+
100108
var activity = await client.GetSleepSummary(
101109
"2017-01-01",
102110
"2017-03-30",
103-
session["AccessToken"]);
111+
accessToken);
104112
return Results.Json(activity);
105113
});
106114

107-
app.MapGet("/api/withings/workouts", async () =>
115+
app.MapGet("/api/withings/workouts", async (HttpContext context, WithingsClient client) =>
108116
{
109-
var client = new WithingsClient(credentials);
117+
var accessToken = context.Session.GetString("AccessToken");
118+
if (string.IsNullOrEmpty(accessToken))
119+
return Results.Unauthorized();
120+
110121
var activity = await client.GetWorkouts(
111122
"2017-06-01",
112123
"2017-06-05",
113-
session["AccessToken"]);
124+
accessToken);
114125
return Results.Json(activity);
115126
});
116127

117-
app.MapGet("/api/withings/sleepmeasures", async () =>
128+
app.MapGet("/api/withings/sleepmeasures", async (HttpContext context, WithingsClient client) =>
118129
{
119-
var client = new WithingsClient(credentials);
130+
var userId = context.Session.GetString("UserId");
131+
var accessToken = context.Session.GetString("AccessToken");
132+
133+
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(accessToken))
134+
return Results.Unauthorized();
135+
120136
var activity = await client.GetSleepMeasures(
121-
session["UserId"],
137+
userId,
122138
DateTime.Now.AddDays(-90),
123139
DateTime.Now.AddDays(-1),
124-
session["AccessToken"]);
140+
accessToken);
125141
return Results.Json(activity);
126142
});
127143

128-
app.MapGet("/api/withings/body", async () =>
144+
app.MapGet("/api/withings/body", async (HttpContext context, WithingsClient client) =>
129145
{
130-
var client = new WithingsClient(credentials);
146+
var userId = context.Session.GetString("UserId");
147+
var accessToken = context.Session.GetString("AccessToken");
148+
149+
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(accessToken))
150+
return Results.Unauthorized();
151+
131152
var activity = await client.GetBodyMeasures(
132-
session["UserId"],
133-
bodyStartDate,
134-
bodyEndDate,
135-
session["AccessToken"]);
153+
userId,
154+
DateTime.Parse("2017-05-08"),
155+
DateTime.Parse("2017-05-10"),
156+
accessToken);
136157
return Results.Json(activity);
137158
});
138159

139-
app.MapGet("/api/withings/bodysince", async () =>
160+
app.MapGet("/api/withings/bodysince", async (HttpContext context, WithingsClient client) =>
140161
{
141-
var client = new WithingsClient(credentials);
162+
var userId = context.Session.GetString("UserId");
163+
var accessToken = context.Session.GetString("AccessToken");
164+
165+
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(accessToken))
166+
return Results.Unauthorized();
167+
142168
var activity = await client.GetBodyMeasures(
143-
session["UserId"],
144-
bodyStartDate,
145-
session["AccessToken"]);
169+
userId,
170+
DateTime.Parse("2017-05-08"),
171+
accessToken);
146172
return Results.Json(activity);
147173
});
148174

149-
app.MapGet("/api/withings/intraday", async () =>
175+
app.MapGet("/api/withings/intraday", async (HttpContext context, WithingsClient client) =>
150176
{
151-
var client = new WithingsClient(credentials);
177+
var userId = context.Session.GetString("UserId");
178+
var accessToken = context.Session.GetString("AccessToken");
179+
180+
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(accessToken))
181+
return Results.Unauthorized();
182+
152183
var activity = await client.GetIntraDayActivity(
153-
session["UserId"],
184+
userId,
154185
DateTime.Now.AddDays(-90),
155186
DateTime.Now.AddDays(-1),
156-
session["AccessToken"]);
187+
accessToken);
157188
return Results.Json(activity);
158189
});
159190

Withings.NET/Client/Authenticator.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,10 @@ public T Deserialize<T>(string s)
124124

125125
public T Deserialize<T>(Stream stream)
126126
{
127-
using (stream)
127+
using (var reader = new StreamReader(stream))
128128
{
129-
return JsonSerializer.Deserialize<T>(stream, _options);
129+
var text = reader.ReadToEnd();
130+
return JsonSerializer.Deserialize<T>(text, _options);
130131
}
131132
}
132133

0 commit comments

Comments
 (0)