windowcapture
исходный код / Helpers/TipBridge.cs

TipBridge.cs

154 строк · 5,937 байт · модуль Helpers
  1using System;
  2using System.IO;
  3using System.Net;
  4using System.Net.Sockets;
  5using System.Text;
  6using System.Threading;
  7
  8namespace WindowCapture.Helpers
  9{
 10    /// <summary>
 11    /// Localhost bridge for the native TSF TIP (Tip/WCTip.cpp). The TIP, loaded into other apps, reads
 12    /// the just-typed word + its left context via ITfRange and POSTs them here; we run the SAME brain
 13    /// as live typing (TextProcessor.CorrectWordForTip) and return the corrected word, which the TIP
 14    /// writes back IN PLACE. Uses a plain TcpListener on 127.0.0.1 (no HttpListener urlacl/admin needed)
 15    /// and a tiny hand-rolled HTTP/1.1 reply. Off unless Settings.TsfBridge is enabled. Never throws out.
 16    ///
 17    /// Request  (POST /correctword): body = "word\nleftContext" (UTF-8).
 18    /// Response (200): body = corrected word (UTF-8); equals the input word when no correction applies.
 19    /// </summary>
 20    public static class TipBridge
 21    {
 22        public const int Port = 8766; // MUST match BRAIN_PORT in Tip/WCTip.cpp
 23
 24        private static readonly object gate = new object();
 25        private static TcpListener listener;
 26        private static Thread thread;
 27        private static volatile bool running;
 28
 29        public static bool IsRunning { get { return running; } }
 30
 31        public static void Start()
 32        {
 33            lock (gate)
 34            {
 35                if (running) return;
 36                try
 37                {
 38                    listener = new TcpListener(IPAddress.Loopback, Port);
 39                    listener.Start();
 40                    running = true;
 41                    thread = new Thread(Loop) { IsBackground = true };
 42                    thread.Start();
 43                }
 44                catch { running = false; try { if (listener != null) listener.Stop(); } catch { } listener = null; }
 45            }
 46        }
 47
 48        public static void Stop()
 49        {
 50            lock (gate)
 51            {
 52                running = false;
 53                try { if (listener != null) listener.Stop(); } catch { }
 54                listener = null;
 55            }
 56        }
 57
 58        private static void Loop()
 59        {
 60            while (running)
 61            {
 62                TcpClient client = null;
 63                try { client = listener.AcceptTcpClient(); }
 64                catch { if (!running) break; else continue; }
 65                try { Handle(client); }
 66                catch { }
 67                finally { try { if (client != null) client.Close(); } catch { } }
 68            }
 69        }
 70
 71        private static void Handle(TcpClient client)
 72        {
 73            client.ReceiveTimeout = 2000;
 74            client.SendTimeout = 2000;
 75            using (var ns = client.GetStream())
 76            {
 77                var buf = new byte[8192];
 78                var ms = new MemoryStream();
 79                int headerEnd = -1;
 80                int contentLen = -1;
 81                while (true)
 82                {
 83                    int n = ns.Read(buf, 0, buf.Length);
 84                    if (n <= 0) break;
 85                    ms.Write(buf, 0, n);
 86                    if (headerEnd < 0)
 87                    {
 88                        byte[] cur = ms.ToArray();
 89                        headerEnd = IndexOfDoubleCrlf(cur);
 90                        if (headerEnd >= 0)
 91                            contentLen = ParseContentLength(Encoding.ASCII.GetString(cur, 0, headerEnd));
 92                    }
 93                    if (headerEnd >= 0)
 94                    {
 95                        long bodyHave = ms.Length - (headerEnd + 4);
 96                        if (contentLen < 0 || bodyHave >= contentLen) break;
 97                    }
 98                    if (ms.Length > 65536) break; // sanity cap
 99                }
100
101                byte[] all = ms.ToArray();
102                string body = "";
103                if (headerEnd >= 0)
104                {
105                    int start = headerEnd + 4;
106                    int len = (contentLen >= 0) ? Math.Min(contentLen, all.Length - start) : all.Length - start;
107                    if (len > 0) body = Encoding.UTF8.GetString(all, start, len);
108                }
109
110                string wordPart = body, ctxPart = "";
111                int nl = body.IndexOf('\n');
112                if (nl >= 0) { wordPart = body.Substring(0, nl); ctxPart = body.Substring(nl + 1); }
113                wordPart = wordPart.Trim('\r', '\n', ' ');
114
115                string corrected = wordPart;
116                try { corrected = TextProcessor.CorrectWordForTip(wordPart, ctxPart); }
117                catch { corrected = wordPart; }
118                if (corrected == null) corrected = wordPart;
119
120                byte[] respBody = Encoding.UTF8.GetBytes(corrected);
121                var sb = new StringBuilder();
122                sb.Append("HTTP/1.1 200 OK\r\n");
123                sb.Append("Content-Type: text/plain; charset=utf-8\r\n");
124                sb.Append("Content-Length: ").Append(respBody.Length).Append("\r\n");
125                sb.Append("Connection: close\r\n\r\n");
126                byte[] head = Encoding.ASCII.GetBytes(sb.ToString());
127                ns.Write(head, 0, head.Length);
128                ns.Write(respBody, 0, respBody.Length);
129                ns.Flush();
130            }
131        }
132
133        private static int IndexOfDoubleCrlf(byte[] b)
134        {
135            for (int i = 0; i + 3 < b.Length; i++)
136                if (b[i] == 13 && b[i + 1] == 10 && b[i + 2] == 13 && b[i + 3] == 10) return i;
137            return -1;
138        }
139
140        private static int ParseContentLength(string headers)
141        {
142            foreach (var line in headers.Split('\n'))
143            {
144                string t = line.Trim();
145                if (t.ToLower().StartsWith("content-length:"))
146                {
147                    int v;
148                    if (int.TryParse(t.Substring("content-length:".Length).Trim(), out v)) return v;
149                }
150            }
151            return -1;
152        }
153    }
154}