Skip to content

Commit

Permalink
Integrate linenoise package with web server
Browse files Browse the repository at this point in the history
In the previous version of console implementation, we tried to integrate
tiny-web-server to enable the ability of processing commands from web
requests. As the result, the package linenoise which is responsible for
command-line auto-complete needs to be disabled during the running time
of tiny-web-server. Because the `line_edit()` function in linenoise.c
doesn't have the ability to handle web requests correctly.

When we start the web server, we use `cmd_select` in console.c and use
`select` function to monitor web socket file descriptor and stdin_fd at
the same time. I re-design the control flow of web request and
command-line input by implement the function `web_select()`, and
register it as a hook function of type `line_select_callback_t` inside
linenoise package. As the result, we can utilize function inside the
main loop of linenoise which is located inside the function
`line_edit()`.

`web_select()` is a function which use the function `select()` to
monitor both input file descriptor and web file descriptor and modify
`line_edit()` to use `web_select()` so we can process command-line
input as normal.

One may wonder why don't we simply modify the function `line_edt()`, the
reason is that linenoise is an upstream package so we only want to do
the miminal changes to this package.
  • Loading branch information
vax-r committed Mar 1, 2024
1 parent 267cca7 commit ed02a34
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 46 deletions.
54 changes: 11 additions & 43 deletions console.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ static bool do_web(int argc, char *argv[])
web_fd = web_open(port);
if (web_fd > 0) {
printf("listen on port %d, fd is %d\n", port, web_fd);
line_set_select_callback(web_select);
use_linenoise = false;
} else {
perror("ERROR");
Expand Down Expand Up @@ -560,13 +561,13 @@ static int cmd_select(int nfds,
fd_set *exceptfds,
struct timeval *timeout)
{
int infd;
fd_set local_readset;

if (cmd_done())
return 0;

if (!block_flag) {
int infd;
/* Process any commands in input buffer */
if (!readfds)
readfds = &local_readset;
Expand All @@ -581,51 +582,18 @@ static int cmd_select(int nfds,
FD_SET(web_fd, readfds);

if (infd == STDIN_FILENO && prompt_flag) {
printf("%s", prompt);
char *cmdline = linenoise(prompt);
if (cmdline)
interpret_cmd(cmdline);
fflush(stdout);
prompt_flag = true;
} else if (infd != STDIN_FILENO) {
char *cmdline = readline();
if (cmdline)
interpret_cmd(cmdline);
}

if (infd >= nfds)
nfds = infd + 1;
if (web_fd >= nfds)
nfds = web_fd + 1;
}
if (nfds == 0)
return 0;

int result = select(nfds, readfds, writefds, exceptfds, timeout);
if (result <= 0)
return result;

infd = buf_stack->fd;
if (readfds && FD_ISSET(infd, readfds)) {
/* Commandline input available */
FD_CLR(infd, readfds);
result--;

set_echo(0);
char *cmdline = readline();
if (cmdline)
interpret_cmd(cmdline);
} else if (readfds && FD_ISSET(web_fd, readfds)) {
FD_CLR(web_fd, readfds);
result--;
struct sockaddr_in clientaddr;
socklen_t clientlen = sizeof(clientaddr);
web_connfd =
accept(web_fd, (struct sockaddr *) &clientaddr, &clientlen);

char *p = web_recv(web_connfd, &clientaddr);
char *buffer = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
web_send(web_connfd, buffer);

if (p)
interpret_cmd(p);
free(p);
close(web_connfd);
}
return result;
return 0;
}

bool finish_cmd()
Expand Down Expand Up @@ -706,4 +674,4 @@ bool run_console(char *infile_name)
}

return err_cnt == 0;
}
}
17 changes: 16 additions & 1 deletion linenoise.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ static char *unsupported_term[] = {"dumb", "cons25", "emacs", NULL};
static line_completion_callback_t *completion_callback = NULL;
static line_hints_callback_t *hints_callback = NULL;
static line_free_hints_callback_t *free_hints_callback = NULL;
static line_select_callback_t *select_callback = NULL;

static struct termios orig_termios; /* In order to restore at exit.*/
static bool maskmode = false; /* Show "***" instead of input. For passwords. */
Expand Down Expand Up @@ -472,6 +473,14 @@ void line_set_free_hints_callback(line_free_hints_callback_t *fn)
free_hints_callback = fn;
}

/* Register the select function to monitor multiple file descriptor from
* different input at the same time.
*/
void line_set_select_callback(line_select_callback_t *fn)
{
select_callback = fn;
}

/* This function is used by the callback function registered by the user
* in order to add completion options given the input string when the
* user typed <tab>.
Expand Down Expand Up @@ -932,6 +941,12 @@ static int line_edit(int stdin_fd,
int nread;
char seq[5];

if (select_callback != NULL) {
int result = select_callback(l.buf);
if (result != 0)
return result;
}

nread = read(l.ifd, &c, 1);
if (nread <= 0)
return l.len;
Expand Down Expand Up @@ -1362,4 +1377,4 @@ int line_history_load(const char *filename)
}
fclose(fp);
return 0;
}
}
4 changes: 3 additions & 1 deletion linenoise.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ typedef struct {
typedef void(line_completion_callback_t)(const char *, line_completions_t *);
typedef char *(line_hints_callback_t)(const char *, int *color, int *bold);
typedef void(line_free_hints_callback_t)(void *);
typedef int(line_select_callback_t)(char *);
void line_set_completion_callback(line_completion_callback_t *);
void line_set_hints_callback(line_hints_callback_t *);
void line_set_free_hints_callback(line_free_hints_callback_t *);
void line_set_select_callback(line_select_callback_t *);
void line_add_completion(line_completions_t *, const char *);
/* clang-format on */

Expand All @@ -74,4 +76,4 @@ void line_mask_mode_disable(void);
}
#endif

#endif /* __LINENOISE_H */
#endif /* __LINENOISE_H */
40 changes: 40 additions & 0 deletions web.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#define TCP_CORK TCP_NOPUSH
#endif

int server_fd;

typedef struct {
int fd; /* descriptor for this buf */
int count; /* unread byte in this buf */
Expand Down Expand Up @@ -153,6 +155,9 @@ int web_open(int port)
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, LISTENQ) < 0)
return -1;

server_fd = listenfd;

return listenfd;
}

Expand Down Expand Up @@ -228,3 +233,38 @@ char *web_recv(int fd, struct sockaddr_in *clientaddr)

return ret;
}

int web_select(char *buf)
{
fd_set listenset;

FD_ZERO(&listenset);
FD_SET(STDIN_FILENO, &listenset);
int max_fd = STDIN_FILENO;
if (server_fd > 0) {
FD_SET(server_fd, &listenset);
max_fd = max_fd > server_fd ? max_fd : server_fd;
}
int result = select(max_fd + 1, &listenset, NULL, NULL, NULL);
if (result < 0)
return -1;

if (server_fd > 0 && FD_ISSET(server_fd, &listenset)) {
FD_CLR(server_fd, &listenset);
struct sockaddr_in clientaddr;
socklen_t clientlen = sizeof(clientaddr);
int web_connfd =
accept(server_fd, (struct sockaddr *) &clientaddr, &clientlen);

char *p = web_recv(web_connfd, &clientaddr);
char *buffer = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
web_send(web_connfd, buffer);
strncpy(buf, p, strlen(p) + 1);
free(p);
close(web_connfd);
return strlen(buf);
}

FD_CLR(STDIN_FILENO, &listenset);
return 0;
}
4 changes: 3 additions & 1 deletion web.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ char *web_recv(int fd, struct sockaddr_in *clientaddr);

void web_send(int out_fd, char *buffer);

#endif
int web_select(char *buf);

#endif

0 comments on commit ed02a34

Please sign in to comment.