using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using CsharpHttpHelper; using Work.Store.Api; // Compile against Work.dll, AllBase.dll and Newtonsoft.Json.dll; run beside these assemblies. internal static class ApiTransportSmoke { private static void Check(bool passed) { if (!passed) throw new Exception("ApiTransport assertion failed."); } private static async Task Respond(int status, string json, Func> request) { var listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); var server = Task.Run(() => { using (var client = listener.AcceptTcpClient()) using (var stream = client.GetStream()) using (var reader = new StreamReader(stream, Encoding.ASCII, false, 1024, true)) { while (!string.IsNullOrEmpty(reader.ReadLine())) { } var body = Encoding.UTF8.GetBytes(json); var header = Encoding.ASCII.GetBytes("HTTP/1.1 " + status + " Response\r\nContent-Type: application/json\r\nContent-Length: " + body.Length + "\r\nConnection: close\r\n\r\n"); stream.Write(header, 0, header.Length); stream.Write(body, 0, body.Length); } }); try { var port = ((IPEndPoint)listener.LocalEndpoint).Port; return await request(new ApiTransport("http://127.0.0.1:" + port)); } finally { listener.Stop(); await server; } } private static Task Send(ApiTransport transport) { return transport.SendAsync("Test", MethodType.Get, authenticated: false, timeoutMilliseconds: 5000); } public static async Task Main() { Check(await Respond(200, "{\"code\":0,\"success\":true,\"data\":\"ok\"}", Send) == "ok"); Check(await Respond(200, "{\"code\":0,\"success\":true,\"data\":null}", Send) == null); Check(await Respond(200, "{\"success\":true,\"data\":null}", transport => transport.SendAsync("Test", MethodType.Get, authenticated: false)) == 0); Check((await Respond(200, "{\"success\":true,\"data\":[1,2]}", transport => transport.SendAsync("Test", MethodType.Get, authenticated: false)))[1] == 2); foreach (var status in new[] { 200, 500 }) { await Respond(status, "{\"data\":null,\"code\":5000,\"msg\":\"semaphore limit\",\"traceId\":\"trace-1\",\"success\":false}", async transport => { try { await Send(transport); throw new Exception("Expected ApiException."); } catch (ApiException ex) { Check(ex.Code == 5000 && ex.TraceId == "trace-1" && ex.Message.Contains("semaphore limit")); } return true; }); } await Respond(200, "{\"code\":200,\"success\":false,\"data\":{}}", async transport => { try { await Send(transport); throw new Exception("Expected ApiException."); } catch (ApiException ex) { Check(ex.Code == 200 && ex.ResponseData is Newtonsoft.Json.Linq.JObject); } return true; }); foreach (var json in new[] { "not json", "[]", "null", "{\"code\":\"invalid\",\"success\":true}", "{\"code\":200,\"data\":\"legacy\"}", "{\"code\":200,\"success\":null}", "{}" }) { await Respond(200, json, async transport => { try { await Send(transport); throw new Exception("Expected response format error."); } catch (HttpException ex) { Check(ex.Message.Contains("Result")); } return true; }); } await Respond(401, "{\"code\":401,\"success\":false,\"msg\":\"expired\"}", async transport => { try { await Send(transport); throw new Exception("Expected authentication error."); } catch (HttpException ex) { Check(!ex.IsOnline && ex.Message == "expired"); } return true; }); Check(await Respond(200, "{\"success\":true,\"data\":\"ok\"}", transport => Task.Run(() => transport.GetRequired("Test", MethodType.Get, authenticated: false))) == "ok"); var stalled = new TcpListener(IPAddress.Loopback, 0); stalled.Start(); try { var transport = new ApiTransport("http://" + stalled.LocalEndpoint); try { await transport.SendAsync("Test", MethodType.Get, authenticated: false, timeoutMilliseconds: 250); throw new Exception("Expected timeout."); } catch (HttpException ex) { Check(ex.Message.Contains("超时")); } } finally { stalled.Stop(); } Console.WriteLine("ApiTransport smoke checks passed."); } }