From e00b22e090c68434b9f44986b2ac9fa9b8d96896 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 21 Feb 2019 23:13:08 +0100 Subject: [PATCH] Gopher: initial request handling. --- src/Makefile | 2 +- src/gopher.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ src/networking.c | 8 ++++++ src/server.h | 1 + 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/gopher.c diff --git a/src/Makefile b/src/Makefile index adf32d557..d4874f7cf 100644 --- a/src/Makefile +++ b/src/Makefile @@ -164,7 +164,7 @@ endif REDIS_SERVER_NAME=redis-server REDIS_SENTINEL_NAME=redis-sentinel -REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o acl.o +REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o acl.o gopher.o REDIS_CLI_NAME=redis-cli REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o REDIS_BENCHMARK_NAME=redis-benchmark diff --git a/src/gopher.c b/src/gopher.c new file mode 100644 index 000000000..a1b04ed1c --- /dev/null +++ b/src/gopher.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "server.h" + +/* Emit an item in Gopher directory listing format: + * + * If descr or selector are NULL, then the "(NULL)" string is used instead. */ +void addReplyGopherItem(client *c, const char *type, const char *descr, + const char *selector, const char *hostname, int port) +{ + sds item = sdscatfmt(sdsempty(),"%s%s\t%s\t%s\t%i\r\n", + type, descr, + selector ? selector : "(NULL)", + hostname ? hostname : "(NULL)", + port); + addReplyProto(c,item,sdslen(item)); + sdsfree(item); +} + +/* This is called by processInputBuffer() when an inline request is processed + * with Gopher mode enabled, and the request happens to have zero or just one + * argument. In such case we get the relevant key and reply using the Gopher + * protocol. */ +void processGopherRequest(client *c) { + robj *keyname = c->argc == 0 ? createStringObject("/",1) : c->argv[1]; + robj *o = lookupKeyRead(c->db,keyname); + + /* If there is no such key, return with a Gopher error. */ + if (o == NULL || o->type != OBJ_STRING) { + char *errstr; + if (o == NULL) + errstr = "Error: no content at the specified key"; + else + errstr = "Error: selected key type is invalid " + "for Gopher output"; + addReplyGopherItem(c,"i",errstr,NULL,NULL,0); + addReplyGopherItem(c,"i","Redis Gopher server",NULL,NULL,0); + } else { + } + + /* Cleanup, also make sure to emit the final ".CRLF" line. Note that + * the connection will be closed immediately after this because the client + * will be flagged with CLIENT_CLOSE_AFTER_REPLY, in accordance with the + * Gopher protocol. */ + if (c->argc == 0) decrRefCount(keyname); + addReplyProto(c,".\r\n",3); +} diff --git a/src/networking.c b/src/networking.c index 23bc97eec..17f46bb2c 100644 --- a/src/networking.c +++ b/src/networking.c @@ -1564,6 +1564,14 @@ void processInputBuffer(client *c) { if (c->reqtype == PROTO_REQ_INLINE) { if (processInlineBuffer(c) != C_OK) break; + /* If the Gopher mode and we got zero or one argument, process + * the request in Gopher mode. */ + if (server.gopher_enabled && (c->argc == 1 || c->argc == 0)) { + processGopherRequest(c); + resetClient(c); + c->flags |= CLIENT_CLOSE_AFTER_REPLY; + break; + } } else if (c->reqtype == PROTO_REQ_MULTIBULK) { if (processMultibulkBuffer(c) != C_OK) break; } else { diff --git a/src/server.h b/src/server.h index daa0b0047..8b2e524e5 100644 --- a/src/server.h +++ b/src/server.h @@ -1514,6 +1514,7 @@ void setDeferredAttributeLen(client *c, void *node, long length); void setDeferredPushLen(client *c, void *node, long length); void processInputBuffer(client *c); void processInputBufferAndReplicate(client *c); +void processGopherRequest(client *c); void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask); void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask); void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);