Merge pull request #92 from jholzer/master

Stability improvement:
This commit is contained in:
Jonas Dellinger 2016-07-31 15:56:17 +02:00 committed by GitHub
commit 409a444d53
6 changed files with 184 additions and 59 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
[Bb]in/
[Oo]bj/

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Image = System.Drawing.Image;
@ -26,14 +27,7 @@ namespace SpotifyAPI.Example
InitializeComponent();
_savedTracks = new List<FullTrack>();
_auth = new ImplicitGrantAuth
{
RedirectUri = "http://localhost:8000",
ClientId = "26d287105e31491889f3cd293d85bfea",
Scope = Scope.UserReadPrivate | Scope.UserReadEmail | Scope.PlaylistReadPrivate | Scope.UserLibraryRead | Scope.UserReadPrivate | Scope.UserFollowRead | Scope.UserReadBirthdate | Scope.UserTopRead,
State = "XSS"
};
_auth.OnResponseReceivedEvent += _auth_OnResponseReceivedEvent;
}
private void _auth_OnResponseReceivedEvent(Token token, string state)
@ -57,7 +51,7 @@ namespace SpotifyAPI.Example
AccessToken = token.AccessToken,
TokenType = token.TokenType
};
InitialSetup();
}
private async void InitialSetup()
@ -129,8 +123,32 @@ namespace SpotifyAPI.Example
private void authButton_Click(object sender, EventArgs e)
{
_auth.StartHttpServer(8000);
_auth.DoAuth();
Task.Run(() => RunAuthentication());
}
private async void RunAuthentication()
{
WebAPIFactory webApiFactory = new WebAPIFactory(
"http://localhost",
8000,
"26d287105e31491889f3cd293d85bfea",
Scope.UserReadPrivate | Scope.UserReadEmail | Scope.PlaylistReadPrivate | Scope.UserLibraryRead |
Scope.UserReadPrivate | Scope.UserFollowRead | Scope.UserReadBirthdate | Scope.UserTopRead,
TimeSpan.FromSeconds(20));
try
{
_spotify = await webApiFactory.GetWebApi();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
if (_spotify == null)
return;
InitialSetup();
}
}
}

View File

@ -8,7 +8,7 @@ using System.Timers;
namespace SpotifyAPI.Local
{
public class SpotifyLocalAPI
public class SpotifyLocalAPI : IDisposable
{
[DllImport("user32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
@ -49,7 +49,7 @@ namespace SpotifyAPI.Local
private const int KeyeventfKeyup = 0x2;
private readonly RemoteHandler _rh;
private readonly Timer _eventTimer;
private Timer _eventTimer;
private StatusResponse _eventStatusResponse;
public event EventHandler<TrackChangeEventArgs> OnTrackChange;
@ -60,13 +60,17 @@ namespace SpotifyAPI.Local
public event EventHandler<TrackTimeChangeEventArgs> OnTrackTimeChange;
public SpotifyLocalAPI()
public SpotifyLocalAPI(int timerIntervall = 50)
{
_rh = new RemoteHandler();
AttachTimer(timerIntervall);
}
private void AttachTimer(int intervall)
{
_eventTimer = new Timer
{
Interval = 50,
Interval = intervall,
AutoReset = false,
Enabled = false
};
@ -333,5 +337,13 @@ namespace SpotifyAPI.Local
Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\spotifywebhelper.exe"));
}
}
public void Dispose()
{
if (_eventTimer == null)
return;
_eventTimer.Enabled = false;
_eventTimer.Elapsed -= ElapsedTick;
}
}
}

View File

@ -58,6 +58,7 @@
</Compile>
<Compile Include="Local\Models\SpotifyUri.cs" />
<Compile Include="Local\VolumeMixerControl.cs" />
<Compile Include="Web\Auth\WebAPIFactory.cs" />
<Compile Include="Web\Enums\TimeRangeType.cs" />
<Compile Include="Web\IClient.cs" />
<Compile Include="Local\Models\CFID.cs" />

View File

@ -0,0 +1,87 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using SpotifyAPI.Web.Enums;
using SpotifyAPI.Web.Models;
namespace SpotifyAPI.Web.Auth
{
public class WebAPIFactory
{
private readonly string _redirectUrl;
private readonly int _listeningPort;
private readonly string _clientId;
private readonly TimeSpan _timeout;
private readonly Scope _scope;
public WebAPIFactory(string redirectUrl, int listeningPort, string clientId, Scope scope, TimeSpan timeout)
{
_redirectUrl = redirectUrl;
_listeningPort = listeningPort;
_clientId = clientId;
_scope = scope;
_timeout = timeout;
}
public Task<SpotifyWebAPI> GetWebApi()
{
var authentication = new ImplicitGrantAuth
{
RedirectUri = $"{_redirectUrl}:{_listeningPort}",
ClientId = _clientId,
Scope = _scope,
State = "XSS"
};
AutoResetEvent authenticationWaitFlag = new AutoResetEvent(false);
SpotifyWebAPI spotifyWebApi = null;
authentication.OnResponseReceivedEvent += (token, state) =>
{
spotifyWebApi = HandleSpotifyResponse(state, token);
authenticationWaitFlag.Set();
};
try
{
authentication.StartHttpServer(_listeningPort);
authentication.DoAuth();
authenticationWaitFlag.WaitOne(_timeout);
if (spotifyWebApi == null)
throw new TimeoutException($"No valid response received for the last {_timeout.TotalSeconds} seconds");
}
finally
{
authentication.StopHttpServer();
}
return Task.FromResult(spotifyWebApi);
}
private static SpotifyWebAPI HandleSpotifyResponse(string state, Token token)
{
if (state != "XSS")
throw new SpotifyWebApiException($"Wrong state '{state}' received.");
if (token.Error != null)
throw new SpotifyWebApiException($"Error: {token.Error}");
var spotifyWebApi = new SpotifyWebAPI
{
UseAuth = true,
AccessToken = token.AccessToken,
TokenType = token.TokenType
};
return spotifyWebApi;
}
}
[Serializable]
public class SpotifyWebApiException : Exception
{
public SpotifyWebApiException(string message) : base(message)
{ }
}
}

View File

@ -1,10 +1,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
// offered to the public domain for any use with no restriction
@ -33,34 +37,17 @@ namespace SpotifyAPI.Web
_srv = srv;
}
private string StreamReadLine(Stream inputStream)
private string[] GetIncomingRequest(Stream inputStream)
{
string data = "";
while (_isActive)
{
var nextChar = inputStream.ReadByte();
if (nextChar == '\n')
{
break;
}
if (nextChar == '\r')
{
continue;
}
if (nextChar == -1)
{
Thread.Sleep(1);
continue;
}
data += Convert.ToChar(nextChar);
}
return data;
var buffer = new byte[4096];
var read = inputStream.Read(buffer, 0, buffer.Length);
var inputData = Encoding.ASCII.GetString(buffer.Take(read).ToArray());
return inputData.Split('\n').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
}
public void Process(object tcpClient)
public void Process(TcpClient socket)
{
TcpClient socket = tcpClient as TcpClient;
// we can't use a StreamReader for input, because it buffers up extra data on us inside it's
// "processed" view of the world, and we want the data raw after the headers
_inputStream = new BufferedStream(socket.GetStream());
@ -69,8 +56,11 @@ namespace SpotifyAPI.Web
OutputStream = new StreamWriter(new BufferedStream(socket.GetStream()));
try
{
ParseRequest();
ReadHeaders();
var requestLines = GetIncomingRequest(_inputStream);
ParseRequest(requestLines.First());
ReadHeaders(requestLines.Skip(1));
if (HttpMethod.Equals("GET"))
{
HandleGetRequest();
@ -80,19 +70,17 @@ namespace SpotifyAPI.Web
HandlePostRequest();
}
}
catch
catch (Exception ex)
{
WriteFailure();
}
OutputStream.Flush();
_inputStream = null;
OutputStream = null;
socket.Close();
}
public void ParseRequest()
public void ParseRequest(string request)
{
string request = StreamReadLine(_inputStream);
string[] tokens = request.Split(' ');
if (tokens.Length < 2)
{
@ -102,10 +90,9 @@ namespace SpotifyAPI.Web
HttpUrl = tokens[1];
}
public void ReadHeaders()
public void ReadHeaders(IEnumerable<string> requestLines)
{
string line;
while ((line = StreamReadLine(_inputStream)) != null)
foreach(var line in requestLines)
{
if (string.IsNullOrEmpty(line))
{
@ -219,16 +206,8 @@ namespace SpotifyAPI.Web
_listener = new TcpListener(IPAddress.Any, Port);
_listener.Start();
using (HttpProcessor processor = new HttpProcessor(this))
{
while (IsActive)
{
TcpClient s = _listener.AcceptTcpClient();
Thread thread = new Thread(processor.Process);
thread.Start(s);
Thread.Sleep(1);
}
}
_listener.BeginAcceptTcpClient(AcceptTcpConnection, _listener);
}
catch (SocketException e)
{
@ -237,6 +216,28 @@ namespace SpotifyAPI.Web
}
}
private void AcceptTcpConnection(IAsyncResult ar)
{
TcpListener listener = (TcpListener)ar.AsyncState;
try
{
var tcpCLient = listener.EndAcceptTcpClient(ar);
using (HttpProcessor processor = new HttpProcessor(this))
{
processor.Process(tcpCLient);
}
}
catch (ObjectDisposedException)
{
// Ignore
}
if (!IsActive)
return;
//listener.Start();
listener.BeginAcceptTcpClient(AcceptTcpConnection, listener);
}
public abstract void HandleGetRequest(HttpProcessor p);
public abstract void HandlePostRequest(HttpProcessor p, StreamReader inputData);
@ -315,6 +316,8 @@ namespace SpotifyAPI.Web
"window.location = hashes" +
"</script>" +
"<h1>Spotify Auth successful!<br>Please copy the URL and paste it into the application</h1></body></html>");
p.OutputStream.Flush();
p.OutputStream.Close();
return;
}
string url = p.HttpUrl;
@ -345,8 +348,12 @@ namespace SpotifyAPI.Web
State = col.Get(3)
});
});
p.OutputStream.Flush();
p.OutputStream.Close();
}
}
t.Start();
}