agoo 2.6.1 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

@@ -231,6 +231,15 @@ extern gqlArg gql_field_arg(agooErr err,
231
231
  struct _gqlValue *def_value,
232
232
  bool required);
233
233
 
234
+ extern gqlArg gql_input_arg(agooErr err,
235
+ gqlType input,
236
+ const char *name,
237
+ gqlType type,
238
+ const char *desc,
239
+ size_t dlen,
240
+ struct _gqlValue *def_value,
241
+ bool required);
242
+
234
243
  extern gqlType gql_scalar_create(agooErr err, const char *name, const char *desc, size_t dlen);
235
244
 
236
245
  extern gqlDir gql_directive_create(agooErr err, const char *name, const char *desc, size_t dlen);
@@ -17,10 +17,16 @@ agoo_hook_create(agooMethod method, const char *pattern, void *handler, agooHook
17
17
 
18
18
  if (NULL == pattern) {
19
19
  if (AGOO_NONE != method) {
20
- pat = AGOO_STRDUP("");
20
+ if (NULL == (pat = AGOO_STRDUP(""))) {
21
+ AGOO_FREE(hook);
22
+ return NULL;
23
+ }
21
24
  }
22
25
  } else {
23
- pat = AGOO_STRDUP(pattern);
26
+ if (NULL == (pat = AGOO_STRDUP(pattern))) {
27
+ AGOO_FREE(hook);
28
+ return NULL;
29
+ }
24
30
  }
25
31
  hook->pattern = pat;
26
32
  hook->next = NULL;
@@ -42,10 +48,16 @@ agoo_hook_func_create(agooMethod method, const char *pattern, void (*func)(agooR
42
48
 
43
49
  if (NULL == pattern) {
44
50
  if (AGOO_NONE != method) {
45
- pat = AGOO_STRDUP("");
51
+ if (NULL == (pat = AGOO_STRDUP(""))) {
52
+ AGOO_FREE(hook);
53
+ return NULL;
54
+ }
46
55
  }
47
56
  } else {
48
- pat = AGOO_STRDUP(pattern);
57
+ if (NULL == (pat = AGOO_STRDUP(pattern))) {
58
+ AGOO_FREE(hook);
59
+ return NULL;
60
+ }
49
61
  }
50
62
  hook->pattern = pat;
51
63
  hook->next = NULL;
@@ -60,9 +72,7 @@ agoo_hook_func_create(agooMethod method, const char *pattern, void (*func)(agooR
60
72
 
61
73
  void
62
74
  agoo_hook_destroy(agooHook hook) {
63
- if (NULL != hook->pattern) {
64
- AGOO_FREE(hook->pattern);
65
- }
75
+ AGOO_FREE(hook->pattern);
66
76
  AGOO_FREE(hook);
67
77
  }
68
78
 
@@ -327,12 +327,8 @@ loop(void *ctx) {
327
327
  agoo_log_rotate();
328
328
  }
329
329
  }
330
- if (NULL != e->whatp) {
331
- AGOO_FREE(e->whatp);
332
- }
333
- if (NULL != e->tidp) {
334
- AGOO_FREE(e->tidp);
335
- }
330
+ AGOO_FREE(e->whatp);
331
+ AGOO_FREE(e->tidp);
336
332
  e->ready = false;
337
333
  }
338
334
  }
@@ -590,8 +586,8 @@ agoo_log_start(agooErr err, bool with_pid) {
590
586
  return AGOO_ERR_OK;
591
587
  }
592
588
 
593
- void
594
- agoo_log_init(const char *app) {
589
+ int
590
+ agoo_log_init(agooErr err, const char *app) {
595
591
  time_t t = time(NULL);
596
592
  struct tm *tm = localtime(&t);
597
593
  int qsize = 1024;
@@ -615,9 +611,10 @@ agoo_log_init(const char *app) {
615
611
  *agoo_log.day_buf = '\0';
616
612
  agoo_log.thread = 0;
617
613
 
618
- agoo_log.q = (agooLogEntry)AGOO_MALLOC(sizeof(struct _agooLogEntry) * qsize);
614
+ if (NULL == (agoo_log.q = (agooLogEntry)AGOO_CALLOC(qsize, sizeof(struct _agooLogEntry)))) {
615
+ return AGOO_ERR_MEM(err, "Log Queue");
616
+ }
619
617
  agoo_log.end = agoo_log.q + qsize;
620
- memset(agoo_log.q, 0, sizeof(struct _agooLogEntry) * qsize);
621
618
  atomic_init(&agoo_log.head, agoo_log.q);
622
619
  atomic_init(&agoo_log.tail, agoo_log.q + 1);
623
620
 
@@ -639,4 +636,5 @@ agoo_log_init(const char *app) {
639
636
  agoo_log_cat_reg(&agoo_push_cat, "push", AGOO_INFO, AGOO_DARK_CYAN, false);
640
637
 
641
638
  //agoo_log_start(false);
639
+ return AGOO_ERR_OK;
642
640
  }
@@ -105,7 +105,7 @@ extern struct _agooLogCat agoo_resp_cat;
105
105
  extern struct _agooLogCat agoo_eval_cat;
106
106
  extern struct _agooLogCat agoo_push_cat;
107
107
 
108
- extern void agoo_log_init(const char *app);
108
+ extern int agoo_log_init(agooErr err, const char *app);
109
109
  extern void agoo_log_open_file();
110
110
 
111
111
  extern void agoo_log_close();
@@ -29,6 +29,15 @@ typedef struct _slot {
29
29
  int klen;
30
30
  } *Slot;
31
31
 
32
+ typedef struct _headRule {
33
+ struct _headRule *next;
34
+ char *path;
35
+ char *mime;
36
+ char *key;
37
+ char *value;
38
+ int len; // length of key + value + ': ' + '\r\n'
39
+ } *HeadRule;
40
+
32
41
  typedef struct _mimeSlot {
33
42
  struct _mimeSlot *next;
34
43
  char key[MAX_MIME_KEY_LEN + 1];
@@ -42,6 +51,7 @@ typedef struct _cache {
42
51
  MimeSlot muckets[MIME_BUCKET_SIZE];
43
52
  char *root;
44
53
  agooGroup groups;
54
+ HeadRule head_rules;
45
55
  } *Cache;
46
56
 
47
57
  typedef struct _mime {
@@ -98,12 +108,14 @@ static struct _mime mime_map[] = {
98
108
  };
99
109
 
100
110
  static const char page_fmt[] = "HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %ld\r\n\r\n";
111
+ static const char page_min_fmt[] = "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n";
101
112
 
102
113
  static struct _cache cache = {
103
114
  .buckets = {0},
104
115
  .muckets = {0},
105
116
  .root = NULL,
106
117
  .groups = NULL,
118
+ .head_rules = NULL,
107
119
  };
108
120
 
109
121
  static uint64_t
@@ -196,12 +208,14 @@ mime_set(agooErr err, const char *key, const char *value) {
196
208
  ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, len))) {
197
209
 
198
210
  AGOO_FREE(s->value);
199
- s->value = AGOO_STRDUP(value);
211
+ if (NULL == (s->value = AGOO_STRDUP(value))) {
212
+ return agoo_err_set(err, AGOO_ERR_ARG, "out of memory adding %s", key);
213
+ }
200
214
  return AGOO_ERR_OK;
201
215
  }
202
216
  }
203
217
  if (NULL == (s = (MimeSlot)AGOO_MALLOC(sizeof(struct _mimeSlot)))) {
204
- return agoo_err_set(err, AGOO_ERR_ARG, "out of memory adding %s", key);
218
+ return AGOO_ERR_MEM(err, "mime key and value");
205
219
  }
206
220
  s->hash = h;
207
221
  s->klen = len;
@@ -210,7 +224,10 @@ mime_set(agooErr err, const char *key, const char *value) {
210
224
  } else {
211
225
  strcpy(s->key, key);
212
226
  }
213
- s->value = AGOO_STRDUP(value);
227
+ if (NULL == (s->value = AGOO_STRDUP(value))) {
228
+ AGOO_FREE(s);
229
+ return AGOO_ERR_MEM(err, "mime key and value");
230
+ }
214
231
  s->next = *bucket;
215
232
  *bucket = s;
216
233
 
@@ -257,26 +274,31 @@ cache_set(const char *key, int klen, agooPage value) {
257
274
  return old;
258
275
  }
259
276
 
260
- void
261
- agoo_pages_init() {
277
+ int
278
+ agoo_pages_init(agooErr err) {
262
279
  Mime m;
263
- struct _agooErr err = AGOO_ERR_INIT;
264
280
 
265
281
  memset(&cache, 0, sizeof(struct _cache));
266
- cache.root = AGOO_STRDUP(".");
282
+ if (NULL == (cache.root = AGOO_STRDUP("."))) {
283
+ return agoo_err_set(err, AGOO_ERR_ARG, "out of memory allocating root path");
284
+ }
267
285
  for (m = mime_map; NULL != m->suffix; m++) {
268
- mime_set(&err, m->suffix, m->type);
286
+ mime_set(err, m->suffix, m->type);
269
287
  }
288
+ return err->code;
270
289
  }
271
290
 
272
- void
273
- agoo_pages_set_root(const char *root) {
291
+ int
292
+ agoo_pages_set_root(agooErr err, const char *root) {
274
293
  AGOO_FREE(cache.root);
275
294
  if (NULL == root) {
276
295
  cache.root = NULL;
277
296
  } else {
278
- cache.root = AGOO_STRDUP(root);
297
+ if (NULL == (cache.root = AGOO_STRDUP(root))) {
298
+ return agoo_err_set(err, AGOO_ERR_ARG, "out of memory allocating root path");
299
+ }
279
300
  }
301
+ return AGOO_ERR_OK;
280
302
  }
281
303
 
282
304
  static void
@@ -297,6 +319,7 @@ agoo_pages_cleanup() {
297
319
  MimeSlot *mp = cache.muckets;
298
320
  MimeSlot sm;
299
321
  MimeSlot m;
322
+ HeadRule hr;
300
323
  int i;
301
324
 
302
325
  for (i = PAGE_BUCKET_SIZE; 0 < i; i--, sp++) {
@@ -315,6 +338,14 @@ agoo_pages_cleanup() {
315
338
  }
316
339
  *mp = NULL;
317
340
  }
341
+ while (NULL != (hr = cache.head_rules)) {
342
+ cache.head_rules = hr->next;
343
+ AGOO_FREE(hr->path);
344
+ AGOO_FREE(hr->mime);
345
+ AGOO_FREE(hr->key);
346
+ AGOO_FREE(hr->value);
347
+ AGOO_FREE(hr);
348
+ }
318
349
  AGOO_FREE(cache.root);
319
350
  }
320
351
 
@@ -339,6 +370,64 @@ path_mime(const char *path) {
339
370
  return mime;
340
371
  }
341
372
 
373
+ static const char*
374
+ path_suffix(const char *path) {
375
+ const char *suffix = path + strlen(path) - 1;
376
+
377
+ for (; '.' != *suffix; suffix--) {
378
+ if (suffix <= path) {
379
+ suffix = NULL;
380
+ break;
381
+ }
382
+ }
383
+ if (suffix <= path) {
384
+ suffix = NULL;
385
+ }
386
+ if (NULL != suffix) {
387
+ suffix++;
388
+ }
389
+ return suffix;
390
+ }
391
+
392
+ static bool
393
+ path_match(const char *pat, const char *path) {
394
+ if (NULL == pat) {
395
+ return true;
396
+ }
397
+ if (NULL == path) {
398
+ return '*' == *pat && '*' == pat[1] && '\0' == pat[2];
399
+ }
400
+ if ('/' == *path) {
401
+ path++;
402
+ }
403
+ for (; '\0' != *pat && '\0' != *path; pat++) {
404
+ if (*path == *pat) {
405
+ path++;
406
+ } else if ('*' == *pat) {
407
+ if ('*' == *(pat + 1)) {
408
+ return true;
409
+ }
410
+ for (; '\0' != *path && '/' != *path; path++) {
411
+ }
412
+ } else {
413
+ break;
414
+ }
415
+ }
416
+ return '\0' == *pat && '\0' == *path;
417
+ }
418
+
419
+ static bool
420
+ head_rule_match(HeadRule rule, const char *path, const char *mime) {
421
+ const char *suffix = NULL;
422
+
423
+ if (NULL != path) {
424
+ suffix = path_suffix(path);
425
+ }
426
+ return path_match(rule->path, path) &&
427
+ (NULL == rule->mime || 0 == strcmp(rule->mime, mime) ||
428
+ (NULL != suffix && 0 == strcmp(rule->mime, suffix)));
429
+ }
430
+
342
431
  // The page resp points to the page resp msg to save memory and reduce
343
432
  // allocations.
344
433
  agooPage
@@ -350,7 +439,10 @@ agoo_page_create(const char *path) {
350
439
  if (NULL == path) {
351
440
  p->path = NULL;
352
441
  } else {
353
- p->path = AGOO_STRDUP(path);
442
+ if (NULL == (p->path = AGOO_STRDUP(path))) {
443
+ AGOO_FREE(p);
444
+ return NULL;
445
+ }
354
446
  }
355
447
  p->mtime = 0;
356
448
  p->last_check = 0.0;
@@ -363,19 +455,34 @@ agooPage
363
455
  agoo_page_immutable(agooErr err, const char *path, const char *content, int clen) {
364
456
  agooPage p = (agooPage)AGOO_MALLOC(sizeof(struct _agooPage));
365
457
  const char *mime = path_mime(path);
458
+ char *rel_path = NULL;
366
459
  long msize;
367
460
  int cnt;
368
461
  int plen = 0;
462
+ long hlen = 0;
463
+ HeadRule hr;
369
464
 
370
465
  if (NULL == p) {
371
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for page.");
466
+ AGOO_ERR_MEM(err, "Page");
372
467
  return NULL;
373
468
  }
374
469
  if (NULL == path) {
375
470
  p->path = NULL;
376
471
  } else {
377
- p->path = AGOO_STRDUP(path);
472
+ if (NULL == (p->path = AGOO_STRDUP(path))) {
473
+ AGOO_FREE(p);
474
+ return NULL;
475
+ }
378
476
  plen = (int)strlen(path);
477
+ if (NULL != cache.root) {
478
+ int rlen = strlen(cache.root);
479
+
480
+ if (0 == strncmp(cache.root, p->path, rlen) && '/' == p->path[rlen]) {
481
+ rel_path = p->path + rlen + 1;
482
+ } else {
483
+ rel_path = NULL;
484
+ }
485
+ }
379
486
  }
380
487
  p->mtime = 0;
381
488
  p->last_check = 0.0;
@@ -387,15 +494,40 @@ agoo_page_immutable(agooErr err, const char *path, const char *content, int clen
387
494
  if (0 == clen) {
388
495
  clen = (int)strlen(content);
389
496
  }
497
+ for (hr = cache.head_rules; NULL != hr; hr = hr->next) {
498
+ if (head_rule_match(hr, rel_path, mime)) {
499
+ hlen += hr->len;
500
+ }
501
+ }
390
502
  // Format size plus space for the length, the mime type, and some
391
503
  // padding. Then add the content length.
392
504
  msize = sizeof(page_fmt) + 60 + clen;
393
505
  if (NULL == (p->resp = agoo_text_allocate((int)msize))) {
506
+ AGOO_ERR_MEM(err, "Page content");
394
507
  AGOO_FREE(p);
395
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for page content.");
396
508
  return NULL;
397
509
  }
398
- cnt = sprintf(p->resp->text, page_fmt, mime, (long)clen);
510
+ if (0 < hlen) {
511
+ bool has_ct;
512
+
513
+ cnt = sprintf(p->resp->text, page_min_fmt, (long)clen); // HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n
514
+ for (hr = cache.head_rules; NULL != hr; hr = hr->next) {
515
+ if (head_rule_match(hr, rel_path, mime)) {
516
+ cnt += sprintf(p->resp->text + cnt, "%s: %s\r\n", hr->key, hr->value);
517
+ if (0 == strcasecmp("Content-Type", hr->key)) {
518
+ has_ct = true;
519
+ }
520
+ }
521
+ }
522
+ if (!has_ct) {
523
+ cnt += sprintf(p->resp->text + cnt, "Content-Type: %s\r\n\r\n", mime);
524
+ } else {
525
+ strcpy(p->resp->text + cnt, "\r\n");
526
+ cnt += 2;
527
+ }
528
+ } else {
529
+ cnt = sprintf(p->resp->text, page_fmt, mime, (long)clen);
530
+ }
399
531
  msize = cnt + clen;
400
532
  memcpy(p->resp->text + cnt, content, clen);
401
533
  p->resp->text[msize] = '\0';
@@ -416,15 +548,21 @@ close_return_false(FILE *f) {
416
548
  static bool
417
549
  update_contents(agooPage p) {
418
550
  const char *mime = path_mime(p->path);
551
+ char path[1024];
552
+ char *rel_path = NULL;
419
553
  int plen = (int)strlen(p->path);
420
554
  long size;
421
555
  struct stat fattr;
422
556
  long msize;
557
+ long hlen = 0;
423
558
  int cnt;
424
559
  struct stat fs;
425
560
  agooText t;
426
561
  FILE *f = fopen(p->path, "rb");
427
-
562
+ HeadRule hr;
563
+
564
+ strncpy(path, p->path, sizeof(path));
565
+ path[sizeof(path) - 1] = '\0';
428
566
  // On linux a directory is opened by fopen (sometimes? all the time?) so
429
567
  // fstat is called to get the file mode and verify it is a regular file or
430
568
  // a symlink.
@@ -441,7 +579,6 @@ update_contents(agooPage p) {
441
579
  if (NULL == f) {
442
580
  // If not found how about with a /index.html added?
443
581
  if (NULL == mime) {
444
- char path[1024];
445
582
  int cnt;
446
583
 
447
584
  if ('/' == p->path[plen - 1]) {
@@ -471,13 +608,45 @@ update_contents(agooPage p) {
471
608
  }
472
609
  rewind(f);
473
610
 
611
+ if (NULL != cache.root) {
612
+ int rlen = strlen(cache.root);
613
+
614
+ if (0 == strncmp(cache.root, path, rlen) && '/' == path[rlen]) {
615
+ rel_path = path + rlen + 1;
616
+ }
617
+ }
618
+ for (hr = cache.head_rules; NULL != hr; hr = hr->next) {
619
+ if (head_rule_match(hr, rel_path, mime)) {
620
+ hlen += hr->len;
621
+ }
622
+ }
474
623
  // Format size plus space for the length, the mime type, and some
475
- // padding. Then add the content length.
476
- msize = sizeof(page_fmt) + 60 + size;
624
+ // padding. Then add the header rule and content length.
625
+ msize = sizeof(page_fmt) + 60 + size + hlen;
477
626
  if (NULL == (t = agoo_text_allocate((int)msize))) {
478
627
  return close_return_false(f);
479
628
  }
480
- cnt = sprintf(t->text, page_fmt, mime, size);
629
+ if (0 < hlen) {
630
+ bool has_ct = false;
631
+
632
+ cnt = sprintf(t->text, page_min_fmt, size); // HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n
633
+ for (hr = cache.head_rules; NULL != hr; hr = hr->next) {
634
+ if (head_rule_match(hr, rel_path, mime)) {
635
+ cnt += sprintf(t->text + cnt, "%s: %s\r\n", hr->key, hr->value);
636
+ if (0 == strcasecmp("Content-Type", hr->key)) {
637
+ has_ct = true;
638
+ }
639
+ }
640
+ }
641
+ if (!has_ct) {
642
+ cnt += sprintf(t->text + cnt, "Content-Type: %s\r\n\r\n", mime);
643
+ } else {
644
+ strcpy(t->text + cnt, "\r\n");
645
+ cnt += 2;
646
+ }
647
+ } else {
648
+ cnt = sprintf(t->text, page_fmt, mime, size);
649
+ }
481
650
  msize = cnt + size;
482
651
  if (0 < size) {
483
652
  if (size != (long)fread(t->text + cnt, 1, size, f)) {
@@ -569,13 +738,13 @@ agoo_page_get(agooErr err, const char *path, int plen) {
569
738
  *s++ = '/';
570
739
  }
571
740
  if ((int)sizeof(full_path) <= plen + (s - full_path)) {
572
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for page path.");
741
+ AGOO_ERR_MEM(err, "Page path");
573
742
  return NULL;
574
743
  }
575
744
  strncpy(s, path, plen);
576
745
  s[plen] = '\0';
577
746
  if (NULL == (page = agoo_page_create(full_path))) {
578
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for agooPage.");
747
+ AGOO_ERR_MEM(err, "Page");
579
748
  return NULL;
580
749
  }
581
750
  if (!update_contents(page) || NULL == page->resp) {
@@ -646,7 +815,7 @@ group_get(agooErr err, const char *path, int plen) {
646
815
  agooPage old;
647
816
 
648
817
  if (NULL == (page = agoo_page_create(path))) {
649
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for agooPage.");
818
+ AGOO_ERR_MEM(err, "Page");
650
819
  return NULL;
651
820
  }
652
821
  if (!update_contents(page) || NULL == page->resp) {
@@ -668,23 +837,65 @@ group_create(const char *path) {
668
837
  agooGroup g = (agooGroup)AGOO_MALLOC(sizeof(struct _agooGroup));
669
838
 
670
839
  if (NULL != g) {
671
- g->next = cache.groups;
672
- cache.groups = g;
673
- g->path = AGOO_STRDUP(path);
840
+ if (NULL == (g->path = AGOO_STRDUP(path))) {
841
+ AGOO_FREE(g);
842
+ return NULL;
843
+ }
674
844
  g->plen = (int)strlen(path);
675
845
  g->dirs = NULL;
846
+ g->next = cache.groups;
847
+ cache.groups = g;
676
848
  }
677
849
  return g;
678
850
  }
679
851
 
680
- void
681
- group_add(agooGroup g, const char *dir) {
852
+ agooDir
853
+ group_add(agooErr err, agooGroup g, const char *dir) {
682
854
  agooDir d = (agooDir)AGOO_MALLOC(sizeof(struct _agooDir));
683
855
 
684
856
  if (NULL != d) {
857
+ if (NULL == (d->path = AGOO_STRDUP(dir))) {
858
+ AGOO_ERR_MEM(err, "Group");
859
+ return NULL;
860
+ }
861
+ d->plen = (int)strlen(dir);
685
862
  d->next = g->dirs;
686
863
  g->dirs = d;
687
- d->path = AGOO_STRDUP(dir);
688
- d->plen = (int)strlen(dir);
689
864
  }
865
+ return d;
866
+ }
867
+
868
+ int
869
+ agoo_header_rule(agooErr err, const char *path, const char *mime, const char *key, const char *value) {
870
+ HeadRule hr;
871
+
872
+ if (0 == strcasecmp("Content-Length", key)) {
873
+ return agoo_err_set(err, AGOO_ERR_ARG, "Can not mask Content-Length with a header rule.");
874
+ }
875
+ if (NULL == (hr = (HeadRule)AGOO_CALLOC(1, sizeof(struct _headRule)))) {
876
+ return AGOO_ERR_MEM(err, "Header Rule");
877
+ }
878
+ if (NULL == (hr->path = AGOO_STRDUP(path)) ||
879
+ NULL == (hr->key = AGOO_STRDUP(key)) ||
880
+ NULL == (hr->value = AGOO_STRDUP(value))) {
881
+ goto ERROR;
882
+ }
883
+ if ('*' == *mime && '\0' == mime[1]) {
884
+ hr->mime = NULL;
885
+ } else if (NULL == (hr->mime = AGOO_STRDUP(mime))) {
886
+ goto ERROR;
887
+ }
888
+ hr->len = strlen(hr->key) + strlen(hr->value) + 4;
889
+ hr->next = cache.head_rules;
890
+ cache.head_rules = hr;
891
+
892
+ return AGOO_ERR_OK;
893
+
894
+ ERROR:
895
+ AGOO_FREE(hr->path);
896
+ AGOO_FREE(hr->key);
897
+ AGOO_FREE(hr->value);
898
+ AGOO_FREE(hr);
899
+
900
+ return AGOO_ERR_MEM(err, "Header Rule");
690
901
  }