LSP-server-example
189 строк · 4.3 Кб
1package server
2
3import (
4"bufio"
5"encoding/json"
6"fmt"
7"io"
8"log/slog"
9"net"
10"net/textproto"
11"strconv"
12"sync"
13
14"lsp-server/internal/server/handlers"
15)
16
17type Mux struct {
18concurrencyLimit int64
19requestHandlers map[string]handlers.Request
20notificationHandlers map[string]handlers.Notification
21writeLock *sync.Mutex
22conn *net.Conn
23}
24
25const concurrencyLimit = 4
26
27func NewMux() *Mux {
28return &Mux{
29concurrencyLimit: concurrencyLimit,
30requestHandlers: map[string]handlers.Request{},
31notificationHandlers: map[string]handlers.Notification{},
32writeLock: &sync.Mutex{},
33}
34}
35
36func (m *Mux) Process() error {
37for {
38req, err := Read(bufio.NewReader(*m.conn))
39if err != nil {
40return err
41}
42if req.IsNotification() {
43if req.Method != "exit" {
44// Drop notifications sent before initialization.
45slog.Warn("dropping notification sent before initialization", slog.Any("req", req))
46continue
47}
48m.handleMessage(req)
49continue
50}
51if req.Method != "initialize" {
52// Return an error if methods used before initialization.
53slog.Warn("the client sent a method before initialization", slog.Any("req", req))
54if err = m.write(NewResponseError(req.ID, ErrServerNotInitialized)); err != nil {
55return err
56}
57continue
58}
59m.handleMessage(req)
60break
61}
62slog.Info("initialization complete")
63
64// Handle standard flow.
65sem := make(chan struct{}, m.concurrencyLimit)
66for {
67sem <- struct{}{}
68req, err := Read(bufio.NewReader(*m.conn))
69if err != nil {
70return err
71}
72go func(req Request) {
73m.handleMessage(req)
74<-sem
75}(req)
76}
77}
78
79func (m *Mux) handleMessage(req Request) {
80if req.IsNotification() {
81m.handleNotification(req)
82return
83}
84m.handleRequest(req)
85}
86
87func (m *Mux) handleNotification(req Request) {
88log := slog.With(slog.String("method", req.Method))
89nh, ok := m.notificationHandlers[req.Method]
90if !ok {
91log.Warn("notification not handled")
92return
93}
94// We don't need to notify clients if the notification results in an error.
95if err := nh.Call(req.Params); err != nil {
96log.Error("failed to handle notification", slog.Any("error", err))
97}
98}
99
100func (m *Mux) handleRequest(req Request) {
101log := slog.With(slog.Any("id", req.ID), slog.String("method", req.Method))
102mh, ok := m.requestHandlers[req.Method]
103if !ok {
104log.Error("method not found")
105if err := m.write(NewResponseError(req.ID, ErrMethodNotFound)); err != nil {
106log.Error("failed to respond", slog.Any("error", err))
107}
108return
109}
110var res Response
111result, err := mh.Call(req.Params)
112if err != nil {
113log.Error("failed to handle", slog.Any("error", err))
114res = NewResponseError(req.ID, err)
115} else {
116res = NewResponse(req.ID, result)
117}
118if err = m.write(res); err != nil {
119log.Error("failed to respond", slog.Any("error", err))
120}
121}
122
123func (m *Mux) write(msg Message) error {
124m.writeLock.Lock()
125defer m.writeLock.Unlock()
126return Write(bufio.NewWriter(*m.conn), msg)
127}
128
129func Read(r *bufio.Reader) (Request, error) {
130// Read header.
131header, err := textproto.NewReader(r).ReadMIMEHeader()
132if err != nil {
133return Request{}, err
134}
135var req Request
136contentLength, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64)
137if err != nil {
138return Request{}, ErrInvalidContentLengthHeader
139}
140// Read body.
141err = json.NewDecoder(io.LimitReader(r, contentLength)).Decode(&req)
142if err != nil {
143return Request{}, err
144}
145if !req.IsJSONRPC() {
146return req, ErrInvalidRequest
147}
148return req, nil
149}
150
151func Write(w *bufio.Writer, msg Message) error {
152// Calculate body size.
153body, err := json.Marshal(msg)
154if err != nil {
155return err
156}
157// Write the header.
158_, err = w.WriteString(fmt.Sprintf("Content-Length: %d\r\n\r\n", len(body)))
159if err != nil {
160return err
161}
162// Write the body.
163_, err = w.Write(body)
164if err != nil {
165return err
166}
167// Flush.
168err = w.Flush()
169return err
170}
171func (m *Mux) Notify(method string, params any) error {
172n := Notification{
173ProtocolVersion: protocolVersion,
174Method: method,
175Params: params,
176}
177return m.write(n)
178}
179func (m *Mux) HandleRequest(name string, request handlers.Request) {
180m.requestHandlers[name] = request
181}
182
183func (m *Mux) HandleNotification(name string, notification handlers.Notification) {
184m.notificationHandlers[name] = notification
185}
186
187func (m *Mux) NewConn(conn *net.Conn) {
188m.conn = conn
189}
190