Home | History | Annotate | Download | only in test
      1 /* Copyright (c) 2014, Google Inc.
      2  *
      3  * Permission to use, copy, modify, and/or distribute this software for any
      4  * purpose with or without fee is hereby granted, provided that the above
      5  * copyright notice and this permission notice appear in all copies.
      6  *
      7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
     12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
     14 
     15 #include "async_bio.h"
     16 
     17 #include <errno.h>
     18 #include <string.h>
     19 
     20 #include <openssl/mem.h>
     21 
     22 
     23 namespace {
     24 
     25 extern const BIO_METHOD g_async_bio_method;
     26 
     27 struct AsyncBio {
     28   bool datagram;
     29   bool enforce_write_quota;
     30   size_t read_quota;
     31   size_t write_quota;
     32 };
     33 
     34 AsyncBio *GetData(BIO *bio) {
     35   if (bio->method != &g_async_bio_method) {
     36     return NULL;
     37   }
     38   return (AsyncBio *)bio->ptr;
     39 }
     40 
     41 static int AsyncWrite(BIO *bio, const char *in, int inl) {
     42   AsyncBio *a = GetData(bio);
     43   if (a == NULL || bio->next_bio == NULL) {
     44     return 0;
     45   }
     46 
     47   if (!a->enforce_write_quota) {
     48     return BIO_write(bio->next_bio, in, inl);
     49   }
     50 
     51   BIO_clear_retry_flags(bio);
     52 
     53   if (a->write_quota == 0) {
     54     BIO_set_retry_write(bio);
     55     errno = EAGAIN;
     56     return -1;
     57   }
     58 
     59   if (!a->datagram && (size_t)inl > a->write_quota) {
     60     inl = a->write_quota;
     61   }
     62   int ret = BIO_write(bio->next_bio, in, inl);
     63   if (ret <= 0) {
     64     BIO_copy_next_retry(bio);
     65   } else {
     66     a->write_quota -= (a->datagram ? 1 : ret);
     67   }
     68   return ret;
     69 }
     70 
     71 static int AsyncRead(BIO *bio, char *out, int outl) {
     72   AsyncBio *a = GetData(bio);
     73   if (a == NULL || bio->next_bio == NULL) {
     74     return 0;
     75   }
     76 
     77   BIO_clear_retry_flags(bio);
     78 
     79   if (a->read_quota == 0) {
     80     BIO_set_retry_read(bio);
     81     errno = EAGAIN;
     82     return -1;
     83   }
     84 
     85   if (!a->datagram && (size_t)outl > a->read_quota) {
     86     outl = a->read_quota;
     87   }
     88   int ret = BIO_read(bio->next_bio, out, outl);
     89   if (ret <= 0) {
     90     BIO_copy_next_retry(bio);
     91   } else {
     92     a->read_quota -= (a->datagram ? 1 : ret);
     93   }
     94   return ret;
     95 }
     96 
     97 static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
     98   if (bio->next_bio == NULL) {
     99     return 0;
    100   }
    101   BIO_clear_retry_flags(bio);
    102   int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
    103   BIO_copy_next_retry(bio);
    104   return ret;
    105 }
    106 
    107 static int AsyncNew(BIO *bio) {
    108   AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a));
    109   if (a == NULL) {
    110     return 0;
    111   }
    112   memset(a, 0, sizeof(*a));
    113   a->enforce_write_quota = true;
    114   bio->init = 1;
    115   bio->ptr = (char *)a;
    116   return 1;
    117 }
    118 
    119 static int AsyncFree(BIO *bio) {
    120   if (bio == NULL) {
    121     return 0;
    122   }
    123 
    124   OPENSSL_free(bio->ptr);
    125   bio->ptr = NULL;
    126   bio->init = 0;
    127   bio->flags = 0;
    128   return 1;
    129 }
    130 
    131 static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
    132   if (bio->next_bio == NULL) {
    133     return 0;
    134   }
    135   return BIO_callback_ctrl(bio->next_bio, cmd, fp);
    136 }
    137 
    138 const BIO_METHOD g_async_bio_method = {
    139   BIO_TYPE_FILTER,
    140   "async bio",
    141   AsyncWrite,
    142   AsyncRead,
    143   NULL /* puts */,
    144   NULL /* gets */,
    145   AsyncCtrl,
    146   AsyncNew,
    147   AsyncFree,
    148   AsyncCallbackCtrl,
    149 };
    150 
    151 }  // namespace
    152 
    153 ScopedBIO AsyncBioCreate() {
    154   return ScopedBIO(BIO_new(&g_async_bio_method));
    155 }
    156 
    157 ScopedBIO AsyncBioCreateDatagram() {
    158   ScopedBIO ret(BIO_new(&g_async_bio_method));
    159   if (!ret) {
    160     return nullptr;
    161   }
    162   GetData(ret.get())->datagram = true;
    163   return ret;
    164 }
    165 
    166 void AsyncBioAllowRead(BIO *bio, size_t count) {
    167   AsyncBio *a = GetData(bio);
    168   if (a == NULL) {
    169     return;
    170   }
    171   a->read_quota += count;
    172 }
    173 
    174 void AsyncBioAllowWrite(BIO *bio, size_t count) {
    175   AsyncBio *a = GetData(bio);
    176   if (a == NULL) {
    177     return;
    178   }
    179   a->write_quota += count;
    180 }
    181 
    182 void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) {
    183   AsyncBio *a = GetData(bio);
    184   if (a == NULL) {
    185     return;
    186   }
    187   a->enforce_write_quota = enforce;
    188 }
    189