Skip to content

Instantly share code, notes, and snippets.

@avsej
Created January 24, 2013 12:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save avsej/4621066 to your computer and use it in GitHub Desktop.
Save avsej/4621066 to your computer and use it in GitHub Desktop.
Super minimal couchbase client in C
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* GistID: 4621066
*/
/*
* Copyright 2013 Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#define PROTOCOL_BINARY_REQ 0x80
#define PROTOCOL_BINARY_RAW_BYTES 0x00
#define PROTOCOL_BINARY_CMD_GET 0x00
#define PROTOCOL_BINARY_RESPONSE_SUCCESS 0x00
typedef union {
struct {
uint8_t magic;
uint8_t opcode;
uint16_t keylen;
uint8_t extlen;
uint8_t datatype;
uint16_t vbucket;
uint32_t bodylen;
uint32_t opaque;
uint64_t cas;
} request;
uint8_t bytes[24];
} protocol_binary_request_header;
typedef union {
struct {
uint8_t magic;
uint8_t opcode;
uint16_t keylen;
uint8_t extlen;
uint8_t datatype;
uint16_t status;
uint32_t bodylen;
uint32_t opaque;
uint64_t cas;
} response;
uint8_t bytes[24];
} protocol_binary_response_header;
int main(int argc, char *argv[])
{
int fd;
struct addrinfo hints, *addr, *p;
int rv;
if (argc != 3) {
fprintf(stderr, "usage: %s hostname port\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &addr)) != 0) {
fprintf(stderr, "getaddrinfo: %d, %s\n", rv, gai_strerror(rv));
return 1;
}
/* loop through all the results and connect to the first we can */
for (p = addr; p != NULL; p = p->ai_next) {
if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(fd, p->ai_addr, p->ai_addrlen) == -1) {
close(fd);
perror("client: connect");
continue;
}
break;
}
freeaddrinfo(addr); /* all done with this structure */
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
exit(EXIT_FAILURE);
}
{
protocol_binary_request_header req;
protocol_binary_response_header res;
const char *key = "foo";
size_t nkey = strlen(key);
char *body;
size_t nbody;
uint16_t status;
memset(&req, 0, sizeof(req));
memset(&res, 0, sizeof(req));
req.request.magic = PROTOCOL_BINARY_REQ;
req.request.opcode = PROTOCOL_BINARY_CMD_GET;
req.request.keylen = ntohs(nkey);
req.request.extlen = 0;
req.request.datatype = PROTOCOL_BINARY_RAW_BYTES;
/* for key "foo" in single-node cluster with default vbucket
* count, vbucket value will be 115. When in doubt run this
* command:
*
* $ cbc hash foo
* "foo" Server:"127.0.0.1:11210" vBucket:115
*/
req.request.vbucket = htons(115);
req.request.bodylen = htonl(nkey);
req.request.opaque = 0xdeadbeef;
req.request.cas = 0;
if (send(fd, req.bytes, sizeof(req.bytes), 0) == -1) {
perror("send header");
exit(EXIT_FAILURE);
}
if (send(fd, key, nkey, 0) == -1) {
perror("send key");
exit(EXIT_FAILURE);
}
if (recv(fd, res.bytes, sizeof(res.bytes), 0) == -1) {
perror("recv header");
exit(EXIT_FAILURE);
}
nbody = ntohl(res.response.bodylen);
body = (char *)calloc(nbody, sizeof(char));
if (body == NULL) {
fprintf(stderr, "client: cannot allocate reponse buffer\n");
exit(EXIT_FAILURE);
}
if (recv(fd, body, nbody, 0) == -1) {
perror("recv header");
exit(EXIT_FAILURE);
}
status = ntohs(res.response.status);
fprintf(stderr, "client: received status 0x%02x\n", (int)status);
if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
fprintf(stderr, "client: received body of %d bytes\n", (int)nbody);
fprintf(stderr, "------->8--------\n");
fwrite(body, sizeof(char), nbody, stdout);
fprintf(stderr, "-------8<--------\n");
}
free(body);
}
close(fd);
exit(EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment