From 0b3bc057ca6c68214614f257704cb5a332747b75 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Thu, 02 Dec 2004 21:35:29 +0000 Subject: setting proper ignores. adding an empty cache files trying to fix >16kb writes. --- diff --git a/include/memcache.h b/include/memcache.h new file mode 100644 index 0000000..b11bf4c --- /dev/null +++ b/include/memcache.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2004 Sean Chittenden + * + * All rights reserved until such time as this code is released with + * an official license. Use of this code for commerical, + * non-commercial, and personal purposes is encouraged. Public forks + * of this code is permitted so long as the fork and its decendents + * use this copyright/license. Use of this software in programs + * released under the GPL programs is expressly prohibited by the + * author (ie, BSD, closed source, or artistic license is okay, but + * GPL is not). */ + +#ifndef MEMCACHE_H +#define MEMCACHE_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Our initial read(2) buffer has to be long enough to read the + * first line of the response. ie: + * + * "VALUE #{'k' * 250} #{2 ** 15} #{2 ** 32}\r\n.length => 275 + * + * However, since we want to avoid the number of system calls + * necessary, include trailing part of the protocol in our estimate: + * + * "\r\nEND\r\n".length => 7 + * + * Which yields a manditory limit of 282 bytes for a successful + * response. If we wish to try and get lucky with our first read(2) + * call and be able to read(2) in small values without making a second + * read(2) call, pad this number with a sufficiently large byte value. + * If most of your keys are 512B, then a GET_INIT_BUF_SIZE of 794 + * would be prudent (512 + 282). + * + * The default value of 1024 means that values less than 724 bytes + * will always be read(2) via the first read(2) call. Increasing this + * value to large values is not beneficial. If a second read(2) call + * is necessary, the read(2) will be made with a sufficiently large + * buffer already allocated. */ +#define GET_INIT_BUF_SIZE ((size_t)1024) + +/* Enables extra protocol checking. It's not strickly necesary if the + * server's sending the right data. This should be on by default, + * but, for those that don't want every check done to make sure things + * are right, undef this. 99% of the universe should leave this as + * is. If you think you're than 1% who doesn't need it, you're on + * crack and should definately leave PEDANTIC on. %*/ +#define PEDANTIC + +#define MAX_KEY_LEN 250 + +#define HAVE_SELECT 1 + +#define USE_CRC32_HASH 1 +/* #define USE_ELF_HASH 1 */ +/* #define USE_PERL_HASH 1 */ + +/* Various values for _flags */ +#define MC_RES_FREE_ON_DELETE 0x01 +#define MC_RES_NO_FREE_ON_DELETE 0x02 +#define MC_RES_FOUND 0x04 +struct memcache_res { + const char *key; /* key */ + size_t len; /* length of key */ + u_int32_t hash; /* hash of the key */ + void *val; /* the value */ + size_t bytes; /* length of val */ + + /* If size is zero (default), the memory for val is automatically + * allocated using mcMalloc(3). If size is zero, _flags has its + * MC_RES_FREE_ON_DELETE bit set. + * + * If size is non-zero, libmemcache(3) assumes that the caller has + * set val to an available portion of memory that is size bytes + * long. libmemcache(3) will only populate val with as many bytes + * as are specified by size (ie, it will trim the value in order to + * fit into val). If size is non-zero, _flags has its + * MC_RES_NO_FREE_ON_DELETE bit set by default. */ + size_t size; + TAILQ_ENTRY(memcache_res) entries; + + /* Note: this flags is very different than _flags. */ + u_int16_t flags; + + /* If _flags has 0x01 set, val will be free(3)'ed on when this + * struct is cleaned up via mc_res_free() or the request is cleaned + * up via mc_req_free(). + * + * If _flags has is 0x02 set, val will not be free(3)'ed when this + * response or its parent request are cleaned up. + * + * Note: Use mc_res_free_on_delete() to set the "free on delete" + * bits. */ + char _flags; +}; + +struct memcache_req { + TAILQ_HEAD(memcache_res_list, memcache_res) query; + u_int16_t num_keys; +}; + +struct memcache_server { + /* The hostname of the server. */ + char *hostname; + + /* Port number of the host we're connecting to. */ + char *port; + + /* The file descriptor for this server */ + int fd; + + /* The file descriptor flags */ + int flags; + + /* The timeout for this server */ + struct timeval tv; + + /* Is this particular server active or not? + * + * 'd' == Down Last request was unsuccessful + * 'n' == No host The hostname doesn't exist + * 't' == Try Haven't connected to it yet, will attempt + * 'u' == Up Has been connected to successfully + */ + char active; + + /* A cached copy of the looked up host. */ + struct addrinfo *hostinfo; + + /* The number of addresses in the cached copy. If there is more + * than one per DNS entry (discouraged), we establish a connection + * to them all. */ + u_int32_t num_addrs; + +#ifdef HAVE_SELECT + /* Reduces the amount of user time required when reading data. */ + fd_set fds; + struct timeval select_tv; +#endif + + /* Misc list bits */ + TAILQ_ENTRY(memcache_server) entries; +}; + + +struct memcache_server_stats { + pid_t pid; + time_t uptime; + time_t time; + char *version; + struct timeval rusage_user; + struct timeval rusage_system; + u_int32_t curr_items; + u_int64_t total_items; + u_int64_t bytes; + u_int32_t curr_connections; + u_int64_t total_connections; + u_int32_t connection_structures; + u_int64_t cmd_get; + u_int64_t cmd_refresh; + u_int64_t cmd_set; + u_int64_t get_hits; + u_int64_t get_misses; + u_int64_t refresh_hits; + u_int64_t refresh_misses; + u_int64_t bytes_read; + u_int64_t bytes_written; + u_int64_t limit_maxbytes; +}; + + +struct memcache { + /* The default timeout for all servers */ + struct timeval tv; + + /* The default read(2) size when reading a response. */ + size_t read_size; + + /* The complete list of servers */ + TAILQ_HEAD(memcache_server_list, memcache_server) server_list; + + /* A buffer for data */ + char *buf; + + /* A cursor for where we are in the buffer */ + char *cur; + + /* A pointer to where data should be appended with future read(2) + * calls. */ + char *read_cur; + + /* A pointer to the start of the current line in the buffer. */ + char *start; + + /* The allocated size of the buffer */ + size_t size; + + /* The number of servers in live_servers */ + u_int32_t num_live_servers; + + /* An array of usable memcache_servers */ + struct memcache_server **live_servers; +}; + + +/* Adds a given key to the cache */ +int mc_add(struct memcache *mc, + const char *key, const size_t key_len, + const void *val, const size_t bytes, + const time_t expire, const u_int16_t flags); + +/* Gets the value from memcache and allocates the data for the caller. + * It is the caller's responsibility to free the returned value. + * mc_get() is the preferred interface, however. */ +void *mc_aget(struct memcache *mc, const char *key, const size_t len); + +/* Gets the value from memcache and allocates the data for the caller. + * It is the caller's responsibility to free the returned value. + * mc_refresh() is the preferred interface, however. */ +void *mc_arefresh(struct memcache *mc, const char *key, const size_t len); + +/* Disconnects from a given server and marks it as down. */ +void mc_deactivate_server(struct memcache *mc, struct memcache_server *ms); + +/* Decrements a given key */ +u_int32_t mc_decr(struct memcache *mc, const char *key, const size_t key_len, const u_int32_t val); + +/* Deletes a given key */ +int mc_delete(struct memcache *mc, const char *key, const size_t key_len, const time_t hold); + +/* When given a hash value, this function returns the appropriate + * server to connect to in order to find the key. */ +struct memcache_server *mc_find_server(struct memcache *mc, const u_int32_t hash); + +/* Flushes all keys */ +int mc_flush_all(struct memcache *mc, const char *key, const size_t key_len); + +/* cleans up a memcache object. */ +void mc_free(struct memcache *mc); + +/* Tells the response object to free the allocated memory when it gets + * cleaned up or to let the caller manage the memory. */ +void mc_res_free_on_delete(struct memcache_res *res, int free_on_delete); + +/* mc_get() is the preferred method of accessing memcache. It + * accepts multiple keys and lets a user (should they so choose) + * perform memory caching to reduce the number of mcMalloc(3) calls + * mades. */ +void mc_get(struct memcache *mc, struct memcache_req *req); + +/* Generates a hash value from a given key */ +u_int32_t mc_hash_key(const char *key, const size_t len); + +/* Increments a given key */ +u_int32_t mc_incr(struct memcache *mc, const char *key, const size_t key_len, const u_int32_t val); + +/* Allocates a new memcache object */ +struct memcache *mc_new(void); + +/* mc_refresh() is the preferred method of accessing memcache. It + * accepts multiple keys and lets a user (should they so choose) + * perform memory caching to reduce the number of mcMalloc(3) calls + * mades. mc_refresh() differs from mc_get() in that mc_refresh + * updates the expiration time to be now + whatever the expiration for + * the item was set to. Sessions should use this as a way of noting + * sessions expiring. */ +void mc_refresh(struct memcache *mc, struct memcache_req *req); + +/* Replaces a given key to the cache */ +int mc_replace(struct memcache *mc, + const char *key, const size_t key_len, + const void *val, const size_t bytes, + const time_t expire, const u_int16_t flags); + +/* Adds a key to a given request */ +struct memcache_res *mc_req_add(struct memcache_req *req, const char *key, size_t len); + +/* Cleans up a given request and its subsequent responses. If _flags + * has the MC_RES_FREE_ON_DELETE bit set (default), it will clean up + * the value too. If _flags has MC_RES_NO_FREE_ON_DELETE set, + * however, it is the caller's responsibility to free the value. To + * prevent double free(3) errors, if a value is free(3)'ed before + * mc_req_free() is called, set val to NULL. */ +void mc_req_free(struct memcache_req *req); + +/* Allocates a new memcache request object. */ +struct memcache_req *mc_req_new(void); + +/* Cleans up an individual response object. Normally this is not + * necessary as a call to mc_req_free() will clean up its response + * objects. */ +void mc_res_free(struct memcache_req *req, struct memcache_res *res); + +/* Adds a server to the list of available servers. By default, + * servers are assumed to be available. Return codes: + * + * 0: success + * -1: Unable to allocate a new server instance + * -2: Unable to strdup hostname + * -3: Unable to strdup port + * -4: Unable to Unable to resolve the host, server deactivated, but added to list + * -5: Unable to realloc(3) the server list, server list unchanged */ +int mc_server_add(struct memcache *mc, const char *hostname, const char *port); +int mc_server_add2(struct memcache *mc, + const char *hostname, const size_t hostname_len, + const char *port, const size_t port_len); + +/* Cleans up a given stat's object */ +void mc_server_stats_free(struct memcache_server_stats *s); + +/* Gets a stats object from the given server. It is the caller's + * responsibility to cleanup the resulting object via + * mc_server_stats_free(). */ +struct memcache_server_stats *mc_server_stats(struct memcache *mc, struct memcache_server *ms); + +/* Sets a given key */ +int mc_set(struct memcache *mc, + const char *key, const size_t key_len, + const void *val, const size_t bytes, + const time_t expire, const u_int16_t flags); + +/* Creates a stats object for all available servers and returns the + * cumulative stats. Per host-specific data is generally the same as + * the last server querried. */ +struct memcache_server_stats *mc_stats(struct memcache *mc); + +/* Sets the default timeout for new servers. */ +void mc_timeout(struct memcache *mc, const int sec, const int usec); + + + +/* BEGIN memory management API functions */ + +/* The memcache API allows callers to provide their own memory + * allocation routines to aid in embedability with existing programs, + * libraries, programming languages, and environments that have their + * own memory handling routines. This interface was inspired by + * libxml. */ +typedef void (*mcFreeFunc)(void *mem); +typedef void *(*mcMallocFunc)(const size_t size); +typedef void *(*mcReallocFunc)(void *mem, const size_t size); +typedef char *(*mcStrdupFunc)(const char *str); + +extern mcFreeFunc mcFree; +extern mcMallocFunc mcMalloc; +extern mcMallocFunc mcMallocAtomic; +extern mcReallocFunc mcRealloc; +extern mcStrdupFunc mcStrdup; + +int mcMemSetup(mcFreeFunc freeFunc, mcMallocFunc mallocFunc, + mcReallocFunc reallocFunc, mcStrdupFunc strdupFunc); +int mcMemGet(mcFreeFunc *freeFunc, mcMallocFunc *mallocFunc, + mcReallocFunc *reallocFunc, mcStrdupFunc *strdupFunc); +/* END memory management API functions */ + + +/* APIs that should be implemented: */ + +/* Resets all hosts that are down to try */ +void mc_server_reset_all_active(struct memcache *mc); + +/* Resets a given host back to a try state */ +void mc_server_reset_active(struct memcache *mc, const char *hostname, const int port); + +/* Resets all dns entries */ +void mc_server_reset_all_dns(struct memcache *mc); + +/* Resets only one host's DNS cache */ +void mc_server_reset_dns(struct memcache *mc, const char *hostname, const int port); + +/* Disconnects from all memcache servers */ +void mc_server_disconnect_all(struct memcache *mc); + +/* Disconnects from one memcache server */ +void mc_server_disconnect(struct memcache *mc, const char *hostname, const int port); + +#ifdef TCP_NODELAY +/* Enable/disable TCP_NODELAY */ +void mc_nodelay_enable(struct memcache *mc, const int enable); + +/* Enable/disable TCP_NODELAY for a given server */ +void mc_server_nodelay_enable(struct memcache_server *ms, const int enable); +#endif + +/* Set the timeout on a per server basis */ +void mc_server_timeout(struct memcache_server *ms, const int sec, const int usec); + +/* Set the number of seconds you're willing to wait in total for a + * response. ??? */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/gnutls_cache.c b/src/gnutls_cache.c new file mode 100644 index 0000000..f2fb803 --- /dev/null +++ b/src/gnutls_cache.c @@ -0,0 +1,25 @@ +/* ==================================================================== + * Copyright 2004 Paul Querna + * + * 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. + * + */ + +#include "mod_gnutls.h" + +/** + * GnuTLS Session Cache using libmemcached + * + */ + +#include "libmemcache/memcache.h" diff --git a/src/gnutls_io.c b/src/gnutls_io.c index ec2d08c..856b6a3 100644 --- a/src/gnutls_io.c +++ b/src/gnutls_io.c @@ -372,7 +372,7 @@ static void gnutls_do_handshake(mod_gnutls_handle_t * ctxt) ctxt->status = -1; return; #else -ret = gnutls_handshake(ctxt->session); + ret = gnutls_handshake(ctxt->session); if (ret < 0) { if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) { @@ -419,7 +419,7 @@ apr_status_t mod_gnutls_filter_input(ap_filter_t * f, } if (ctxt->status < 0) { -// return ap_get_brigade(f->next, bb, mode, block, readbytes); + return ap_get_brigade(f->next, bb, mode, block, readbytes); } /* XXX: we don't currently support anything other than these modes. */ @@ -485,22 +485,35 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *bucket = APR_BRIGADE_FIRST(bb); - if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) { - /** TODO: GnuTLS doesn't have a special flush method? **/ + if (APR_BUCKET_IS_EOS(bucket)) { + + /* gnutls_bye(ctxt->session, GNUTLS_SHUT_RDWR); */ + + if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { + return status; + } + break; + + } else if (APR_BUCKET_IS_FLUSH(bucket)) { + if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { return status; } break; + } else if (AP_BUCKET_IS_EOC(bucket)) { - gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); + gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); + gnutls_deinit(ctxt->session); if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { return status; } break; + } else { + /* filter output */ const char *data; apr_size_t len; @@ -518,7 +531,10 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, break; } - ret = gnutls_record_send(ctxt->session, data, len); + do { + ret = gnutls_record_send(ctxt->session, data, len); + } + while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); if (ret < 0) { /* error sending output */ @@ -531,6 +547,8 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, } } else if ((apr_size_t) ret != len) { + //apr_bucket_split(bucket, ret); + //APR_BUCKET_REMOVE(bucket); /* not all of the data was sent. */ /* mod_ssl basicly errors out here.. this doesn't seem right? */ ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->output_rc, @@ -538,6 +556,7 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, "GnuTLS: failed to write %" APR_SSIZE_T_FMT " of %" APR_SIZE_T_FMT " bytes.", len - (apr_size_t) ret, len); + //continue; if (ctxt->output_rc == APR_SUCCESS) { ctxt->output_rc = APR_EGENERAL; } -- cgit v0.9.2