File: jsonrpc.cxx

package info (click to toggle)
systemtap 5.1-5
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 47,964 kB
  • sloc: cpp: 80,838; ansic: 54,757; xml: 49,725; exp: 43,665; sh: 11,527; python: 5,003; perl: 2,252; tcl: 1,312; makefile: 1,006; javascript: 149; lisp: 105; awk: 101; asm: 91; java: 70; sed: 16
file content (142 lines) | stat: -rw-r--r-- 5,717 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Jsonrpc (client) implementation
// Copyright (C) 2022 Red Hat Inc.
//
// This file is part of systemtap, and is free software.  You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.
#include "jsonrpc.h"

#if ! HAVE_JSON_C
    json_object* json_tokener_parse(const char*) { return NULL; }
    json_object* json_object_new_object() { return NULL; }
    json_object* json_object_new_string(const char*) { return NULL; }
    json_object* json_object_new_int(int32_t) { return NULL; }
    json_object* json_object_new_boolean(bool) { return NULL; }
    json_object* json_object_new_double(double) { return NULL; }
    const char*  json_object_get_string(json_object*) { return ""; }
    int32_t      json_object_get_int(json_object*) { return 0; }
    bool         json_object_get_boolean(json_object*) { return 0; }
    uint64_t     json_object_get_uint64(json_object*) { return 0; }
    double       json_object_get_double(json_object*) { return 0; }
    bool         json_object_object_get_ex (json_object*,const char*, json_object**) { return 0; }
    json_type    json_object_get_type (json_object*) { return json_type_null; }
    int          json_object_object_add(json_object*, const char*, json_object*) { return 0; }
    size_t       json_object_array_length(json_object*) { return 0; }
    json_object* json_object_array_get_idx(json_object*, size_t) { return NULL; }
    const char*  json_object_to_json_string_length(json_object*, int, size_t*) { return ""; }
    const char*  json_object_to_json_string(json_object*) { return ""; }
    json_object* json_object_new_array() { return NULL; }
    void         json_object_array_add(json_object*, json_object*) { return; }
#endif

#include <assert.h>
#include <sys/select.h>
#include <iostream>
#include <string>
#include <unistd.h>
#include <string.h>

using namespace std;

#define MAX_HEADER_LINELENGTH 256
/* Store a header line into s_line, and return true if the line is NOT the final line ('\r\n')
 * The header consists of lines of the form  'Field: Value\r\n'
 * See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#headerPart
 */
bool jsonrpc_connection::_read_header_line(string &s_line)
{
    char line[MAX_HEADER_LINELENGTH];
    int c;
    for (c = 0;
         c < MAX_HEADER_LINELENGTH && ((c < 2) || (line[c - 2] != '\r' && line[c - 1] != '\n'));
         c++)
    {
        if (1 != read(IN_FILNO, line + c, 1))
            throw jsonrpc_error(LSPErrCode.InternalError, "In file descriptor closed unexpectedly");
    }
    line[c - 1] = '\0'; // Don't bother returning the "\r\n";
    s_line = line;

    return c > 2 && c < MAX_HEADER_LINELENGTH;
}

void jsonrpc_connection::_read_header(jsonrpc_header &h)
{
    string line;
    while (_read_header_line(line))
    {
        // These are the only 2 supported header fields
        const char *field1 = "Content-Length: ";
        const char *field2 = "Content-Type: ";

        if (line.substr(0, strlen(field1)) == field1)
        {
            h.content_length = stoi(line.substr(strlen(field1)));
        }
        else if (line.substr(0, strlen(field2)) == field2)
        {
            // There is only one supported LSP content type: "application/vscode-jsonrpc; utf-8"
            // FIXME: Just ignore it or be strict?
        }
    };
}

/* Block until a request arrives
 */
void jsonrpc_connection::wait_for_request()
{
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(IN_FILNO, &rfds);
    select(1, &rfds, NULL, NULL, NULL);
}

jsonrpc_request *jsonrpc_connection::get_request()
{
    jsonrpc_header h;
    _read_header(h);

    char *jsonrpc_payload = (char *)malloc(h.content_length + 1);
    for(int bytes_read = 0, n; bytes_read < (ssize_t)h.content_length; bytes_read += n){
        n = read(IN_FILNO, jsonrpc_payload + bytes_read, h.content_length - bytes_read);
        if(n <= 0) throw jsonrpc_error(LSPErrCode.InternalError, "The was an issue reading the request payload");
    }
    jsonrpc_payload[h.content_length] = '\0';

    if (verbose > 2)
    {
        cerr << "Content-Length: " << to_string(h.content_length) << endl;
        cerr << "Content-Type: " << h.content_type << endl;
        cerr << jsonrpc_payload << endl;
    }

    jsonrpc_request *req = new jsonrpc_request(jsonrpc_payload);
    free(jsonrpc_payload);

    return req;
}

void jsonrpc_connection::_write_header_line(string field, string value, bool final_line)
{
    string hline = field + ": " + value + "\r\n" + (final_line ? "\r\n" : "");
    for(int bytes_written = 0, n; bytes_written < (ssize_t)hline.size(); bytes_written += n){
        n = write(OUT_FILENO, hline.c_str() + bytes_written, hline.size() - bytes_written);
        if(n <= 0) throw jsonrpc_error(LSPErrCode.InternalError, "The was an issue writing the response header");
    }
}

void jsonrpc_connection::send_response(jsonrpc_request *request, jsonrpc_response *response)
{
    jsonrpc_header h;
    json_object *body = response->to_json(request);
    assert(response->result_or_error_set);

    const char *body_str = json_object_to_json_string_length(body, JSON_C_TO_STRING_SPACED, &(h.content_length));
    _write_header_line("Content-Length", to_string(h.content_length));
    _write_header_line("Content-Type", h.content_type, /*final_line = */ true);
    for(int bytes_written = 0, n; bytes_written < (ssize_t)h.content_length; bytes_written += n){
        n = write(OUT_FILENO, body_str + bytes_written, h.content_length - bytes_written);
        if(n <= 0) throw jsonrpc_error(LSPErrCode.InternalError, "The was an issue writing the response payload");
    }
}