File Coverage

XSHeaders.xs
Criterion Covered Total %
statement 235 253 92.8
branch 106 140 75.7
condition n/a
subroutine n/a
pod n/a
total 341 393 86.7


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT /* we want efficiency */
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "ppport.h"
6             #include "glog.h"
7             #include "gmem.h"
8             #include "util.h"
9             #include "header.h"
10              
11             #if defined(USE_ITHREADS) && !defined(sv_dup_inc)
12             # define sv_dup_inc(sv, param) SvREFCNT_inc(sv_dup(sv, param))
13             #endif
14              
15             #ifndef PERL_UNUSED_ARG
16             # define PERL_UNUSED_ARG(x) ((void)x)
17             #endif
18              
19 262           static MAGIC* THX_mg_find(pTHX_ SV* sv, const MGVTBL* const vtbl) {
20             MAGIC* mg;
21              
22 262 50         if (SvTYPE(sv) < SVt_PVMG)
23 0           return NULL;
24              
25 262 50         for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
26 262 50         if(mg->mg_virtual == vtbl)
27 262           return mg;
28             }
29 0           return NULL;
30             }
31              
32 41           static int THX_mg_free(pTHX_ SV* const sv, MAGIC* const mg) {
33 41           HList* const hl = (HList*)mg->mg_ptr;
34             int j, k;
35              
36             GLOG(("=X= @@@ mg_free(%p|%d)", hl, hlist_size(hl)));
37              
38 125 100         for (j = 0; j < hl->ulen; ++j) {
39 84           HNode* hn = &hl->data[j];
40 84           PList* pl = hn->values;
41 187 100         for (k = 0; k < pl->ulen; ++k) {
42 103           PNode* pn = &pl->data[k];
43 103           SvREFCNT_dec((SV*)pn->ptr);
44             }
45             }
46              
47 41           hlist_destroy(hl);
48 41           return 0;
49             }
50              
51 0           static int THX_mg_dup(pTHX_ MAGIC* const mg, CLONE_PARAMS* const param) {
52             #ifdef USE_ITHREADS
53             HList* const hl = (HList*)mg->mg_ptr;
54             HList* clone;
55             int j, k;
56              
57             GLOG(("=X= @@@ mg_dup(%p|%d)", hl, hlist_size(hl)));
58              
59             if (!(clone = hlist_clone(hl)))
60             croak("Could not clone HList object");
61              
62             for (j = 0; j < clone->ulen; ++j) {
63             HNode* hnode = &clone->data[j];
64             PList* plist = hnode->values;
65             for (k = 0; k < plist->ulen; ++k) {
66             PNode* pnode = &plist->data[k];
67             pnode->ptr = sv_dup_inc((SV*)pnode->ptr, param);
68             }
69             }
70             mg->mg_ptr = (char *)clone;
71             #else
72             PERL_UNUSED_ARG(mg);
73             PERL_UNUSED_ARG(param);
74             #endif
75 0           return 0;
76             }
77              
78             static MGVTBL const hlist_mgvtbl = {
79             NULL, /* get */
80             NULL, /* set */
81             NULL, /* len */
82             NULL, /* clear */
83             THX_mg_free, /* free */
84             NULL, /* copy */
85             THX_mg_dup, /* dup */
86             #ifdef MGf_LOCAL
87             NULL, /* local */
88             #endif
89             };
90              
91 41           static SV * THX_newSV_HList(pTHX_ HList* const hl, HV * const stash) {
92             MAGIC *mg;
93             HV *hv;
94             SV *rv;
95              
96             GLOG(("=X= Will bless new object"));
97              
98 41           hv = newHV();
99 41           rv = newRV_noinc((SV*)hv);
100 41           mg = sv_magicext((SV*)hv, NULL, PERL_MAGIC_ext, &hlist_mgvtbl, (char *)hl, 0);
101 41           mg->mg_flags |= MGf_DUP;
102 41           sv_bless(rv, stash);
103 41           sv_2mortal(rv);
104 41           return rv;
105             }
106              
107 275           static HList * THX_sv_2HList(pTHX_ SV* const sv, const char *name) {
108 275           MAGIC* mg = NULL;
109              
110 275           SvGETMAGIC(sv);
111 275 100         if (SvROK(sv))
112 254           mg = THX_mg_find(aTHX_ SvRV(sv), &hlist_mgvtbl);
113              
114 275 100         if (!mg)
115 21           croak("%s is not an instance of HTTP::XSHeaders", name);
116              
117 254           return (HList*)mg->mg_ptr;
118             }
119              
120             #define newSV_HList(hl, stash) \
121             THX_newSV_HList(aTHX_ hl, stash)
122              
123             #define sv_2HList(sv, name) \
124             THX_sv_2HList(aTHX_ sv, name)
125              
126             MODULE = HTTP::XSHeaders PACKAGE = HTTP::XSHeaders
127             PROTOTYPES: DISABLE
128              
129              
130             #################################################################
131              
132             int
133             _is_xsheaders(SV* sv)
134             PREINIT:
135 8 50         MAGIC* mg = NULL;
136             CODE:
137 8           SvGETMAGIC(sv);
138 8 50         if (SvROK(sv)) {
139 8           mg = THX_mg_find(aTHX_ SvRV(sv), &hlist_mgvtbl);
140             }
141 8 100         RETVAL = mg ? 1 : 0;
142             OUTPUT: RETVAL
143              
144              
145             void
146             new( SV* klass, ... )
147             PREINIT:
148 24           int argc = 0;
149 24 50         HList* hl = 0;
150             int j;
151             SV* pkey;
152             SV* pval;
153             char* ckey;
154              
155             CODE:
156 24 50         if (!SvOK(klass) || !SvPOK(klass)) {
    50          
157 0           XSRETURN_EMPTY;
158             }
159              
160 24           argc = items - 1;
161 24 50         if ( argc % 2 ) {
162 0           croak("Expecting a hash as input to constructor");
163             }
164              
165             GLOG(("=X= @@@ new()"));
166 24 50         if (!(hl = hlist_create()))
167 0           croak("Could not create new HList object");
168              
169 24           ST(0) = newSV_HList(hl, gv_stashpv(SvPV_nolen(klass), 0));
170              
171             /* create the initial list */
172 66 100         for (j = 1; j <= argc; ) {
173 42           pkey = ST(j++);
174              
175             /* did we reach the end by any chance? */
176 42 50         if (j > argc) {
177 0           break;
178             }
179              
180 42           pval = ST(j++);
181 42           ckey = SvPV_nolen(pkey);
182             GLOG(("=X= Will set [%s] to [%s]", ckey, SvPV_nolen(pval)));
183 42           set_value(aTHX_ hl, ckey, pval);
184             }
185 24           XSRETURN(1);
186              
187              
188             void
189             clone(HList* hl)
190             PREINIT:
191             HList* clone;
192             int j;
193             int k;
194             CODE:
195             GLOG(("=X= @@@ clone(%p|%d)", hl, hlist_size(hl)));
196              
197 13 50         if (!(clone = hlist_clone(hl)))
198 0           croak("Could not clone HList object");
199              
200 13           ST(0) = newSV_HList(clone, SvSTASH(SvRV(ST(0))));
201              
202             /* Clone the SVs into new ones */
203 59 100         for (j = 0; j < clone->ulen; ++j) {
204 46           HNode* hnode = &clone->data[j];
205 46           PList* plist = hnode->values;
206 104 100         for (k = 0; k < plist->ulen; ++k) {
207 58           PNode* pnode = &plist->data[k];
208 58           pnode->ptr = newSVsv( (SV*)pnode->ptr );
209             }
210             }
211              
212 13           XSRETURN(1);
213              
214              
215             #
216             # Clear object, leaving it as freshly created.
217             #
218             void
219             clear(HList* hl, ...)
220             CODE:
221             GLOG(("=X= @@@ clear(%p|%d)", hl, hlist_size(hl)));
222 6           hlist_clear(hl);
223              
224              
225             #
226             # Get all the keys in an existing HList.
227             #
228             void
229             header_field_names(HList* hl)
230             PPCODE:
231             GLOG(("=X= @@@ header_field_names(%p|%d), want %d",
232             hl, hlist_size(hl), GIMME_V));
233 9           hlist_sort(hl);
234 9           PUTBACK;
235 9           return_hlist(aTHX_ hl, "header_field_names", GIMME_V);
236 9           SPAGAIN;
237              
238              
239             #
240             # init_header
241             #
242             void
243             init_header(HList* hl, ...)
244             PREINIT:
245 12           int argc = 0;
246             SV* pkey;
247             SV* pval;
248             STRLEN len;
249             char* ckey;
250              
251             CODE:
252             GLOG(("=X= @@@ init_header(%p|%d), %d params, want %d",
253             hl, hlist_size(hl), argc, GIMME_V));
254 10           argc = items - 1;
255 10 100         if (argc != 2) {
256 3           croak("init_header needs two arguments");
257             }
258              
259             /* TODO: apply this check everywhere! */
260 7           pkey = ST(1);
261 7 100         if (!SvOK(pkey) || !SvPOK(pkey)) {
    50          
262 1           croak("init_header not called with a first string argument");
263             }
264 6           ckey = SvPV(pkey, len);
265 6           pval = ST(2);
266              
267 6 100         if (!hlist_get(hl, ckey)) {
268 4           set_value(aTHX_ hl, ckey, pval);
269             }
270              
271             #
272             # push_header
273             #
274             void
275             push_header(HList* hl, ...)
276             PREINIT:
277 13           int argc = 0;
278             int j;
279             SV* pkey;
280             SV* pval;
281             STRLEN len;
282             char* ckey;
283              
284             CODE:
285             GLOG(("=X= @@@ push_header(%p|%d), %d params, want %d",
286             hl, hlist_size(hl), argc, GIMME_V));
287              
288 11           argc = items - 1;
289 11 50         if (argc % 2 != 0) {
290 0           croak("push_header needs an even number of arguments");
291             }
292              
293 22 100         for (j = 1; j <= argc; ) {
294 11 50         if (j > argc) {
295 0           break;
296             }
297 11           pkey = ST(j++);
298              
299 11 50         if (j > argc) {
300 0           break;
301             }
302 11           pval = ST(j++);
303              
304 11           ckey = SvPV(pkey, len);
305 11           set_value(aTHX_ hl, ckey, pval);
306             }
307              
308              
309             #
310             # header
311             #
312             void
313             header(HList* hl, ...)
314             PREINIT:
315 142           int argc = 0;
316             int j;
317 142           SV* pkey = 0;
318 142           SV* pval = 0;
319             STRLEN len;
320 142           char* ckey = 0;
321 142           HNode* n = 0;
322 142           HList* seen = 0; /* TODO: make this more efficient; use Perl hash? */
323              
324             PPCODE:
325             GLOG(("=X= @@@ header(%p|%d), %d params, want %d",
326             hl, hlist_size(hl), argc, GIMME_V));
327              
328 140           argc = items - 1;
329             do {
330 140 100         if (argc == 0) {
331 1           croak("header called with no arguments");
332             }
333              
334 139 100         if (argc == 1) {
335 87           pkey = ST(1);
336 87           ckey = SvPV(pkey, len);
337 87           n = hlist_get(hl, ckey);
338 87 100         if (n && plist_size(n->values) > 0) {
    50          
339 65           PUTBACK;
340 65           return_plist(aTHX_ n->values, "header1", GIMME_V);
341 65           SPAGAIN;
342             }
343 87           break;
344             }
345              
346 52 50         if (argc % 2 != 0) {
347 0           croak("init_header needs one or an even number of arguments");
348             }
349              
350 52           seen = hlist_create();
351 109 100         for (j = 1; j <= argc; ) {
352 57 50         if (j > argc) {
353 0           break;
354             }
355 57           pkey = ST(j++);
356              
357 57 50         if (j > argc) {
358 0           break;
359             }
360 57           pval = ST(j++);
361              
362 57           ckey = SvPV(pkey, len);
363 57           int clear = 0;
364 57 100         if (! hlist_get(seen, ckey)) {
365 56           clear = 1;
366 56           hlist_add(seen, ckey, 0);
367             }
368              
369 57           n = hlist_get(hl, ckey);
370 57 100         if (n) {
371 17 100         if (j > argc && plist_size(n->values) > 0) {
    50          
372             /* Last value, return its current contents */
373 15           PUTBACK;
374 15           return_plist(aTHX_ n->values, "header2", GIMME_V);
375 15           SPAGAIN;
376             }
377 17 100         if (clear) {
378 16           plist_clear(n->values);
379             }
380             }
381              
382 57           set_value(aTHX_ hl, ckey, pval);
383             }
384 52           hlist_destroy(seen);
385 52           break;
386             } while (0);
387              
388              
389             #
390             # _header
391             #
392             # Yes, this is an internal function, but it is used by some modules!
393             # So far, I am aware of HTTP::Cookies as one of the culprits.
394             # Luckily, they only use it with a single arg, which will be the
395             # ONLY usecase supported, at least for now.
396             #
397             void
398             _header(HList* hl, ...)
399             PREINIT:
400 5           int argc = 0;
401 5           SV* pkey = 0;
402             STRLEN len;
403 5           char* ckey = 0;
404 5           HNode* n = 0;
405              
406             PPCODE:
407             GLOG(("=X= @@@ header(%p|%d), %d params, want %d",
408             hl, hlist_size(hl), argc, GIMME_V));
409              
410 4           argc = items - 1;
411 4 100         if (argc != 1) {
412 1           croak("_header not called with one argument");
413             }
414              
415 3           pkey = ST(1);
416 3 100         if (!SvOK(pkey) || !SvPOK(pkey)) {
    50          
417 1           croak("_header not called with one string argument");
418             }
419 2           ckey = SvPV(pkey, len);
420 2           n = hlist_get(hl, ckey);
421 2 50         if (n && plist_size(n->values) > 0) {
    50          
422 2           PUTBACK;
423 2           return_plist(aTHX_ n->values, "_header", GIMME_V);
424 2           SPAGAIN;
425             }
426              
427              
428             #
429             # remove_header
430             #
431             void
432             remove_header(HList* hl, ...)
433             PREINIT:
434 17           int argc = 0;
435             int j;
436             SV* pkey;
437             STRLEN len;
438             char* ckey;
439 17           int size = 0;
440 17           int total = 0;
441              
442             PPCODE:
443             GLOG(("=X= @@@ remove_header(%p|%d), %d params, want %d",
444             hl, hlist_size(hl), argc, GIMME_V));
445              
446 15           argc = items - 1;
447 37 100         for (j = 1; j <= argc; ++j) {
448 22           pkey = ST(j);
449 22           ckey = SvPV(pkey, len);
450              
451 22           HNode* n = hlist_get(hl, ckey);
452 22 100         if (!n) {
453 5           continue;
454             }
455              
456 17           size = plist_size(n->values);
457 17 50         if (size > 0) {
458 17           total += size;
459 17 100         if (GIMME_V == G_ARRAY) {
460 7           PUTBACK;
461 7           return_plist(aTHX_ n->values, "remove_header", G_ARRAY);
462 7           SPAGAIN;
463             }
464             }
465              
466 17           hlist_del(hl, ckey);
467             GLOG(("=X= remove_header: deleted key [%s]", ckey));
468             }
469              
470 15 100         if (GIMME_V == G_SCALAR) {
471             GLOG(("=X= remove_header: returning count %d", total));
472 5 50         EXTEND(SP, 1);
473 5           PUSHs(sv_2mortal(newSViv(total)));
474             }
475              
476              
477             #
478             # remove_content_headers
479             #
480             void
481             remove_content_headers(HList* hl, ...)
482             PREINIT:
483 6           HList* to = 0;
484 6           HNode* n = 0;
485             int j;
486              
487             CODE:
488             GLOG(("=X= @@@ remove_content_headers(%p|%d)",
489             hl, hlist_size(hl)));
490              
491 4 50         if (!(to = hlist_create()))
492 0           croak("Could not create new HList object");
493              
494 4           ST(0) = newSV_HList(to, SvSTASH(SvRV(ST(0))));
495              
496 38 100         for (j = 0; j < hl->ulen; ) {
497 34           n = &hl->data[j];
498 34 100         if (! header_is_entity(n->header)) {
499 14           ++j;
500 14           continue;
501             }
502 20           hlist_transfer_header(hl, j, to);
503             }
504              
505 4           XSRETURN(1);
506              
507              
508             const char*
509             as_string(HList* hl, ...)
510             PREINIT:
511 32           char* str = 0;
512 32 50         int size = 0;
513              
514             CODE:
515             GLOG(("=X= @@@ as_string(%p|%d) %d", hl, hlist_size(hl), items));
516              
517 30           const char* cendl = "\n";
518 30 100         if ( items > 1 ) {
519 3           SV* pendl = ST(1);
520 3           cendl = SvPV_nolen(pendl);
521             }
522              
523 30           str = format_all(aTHX_ hl, 1, cendl, &size);
524 30           RETVAL = str;
525              
526             OUTPUT: RETVAL
527              
528             CLEANUP:
529 30           GMEM_DEL(str, char*, size);
530              
531              
532             const char*
533             as_string_without_sort(HList* hl, ...)
534             PREINIT:
535 4           char* str = 0;
536 4 50         int size = 0;
537              
538             CODE:
539             GLOG(("=X= @@@ as_string_without_sort(%p|%d) %d", hl, hlist_size(hl), items));
540              
541 2           const char* cendl = "\n";
542 2 50         if ( items > 1 ) {
543 0           SV* pendl = ST(1);
544 0           cendl = SvPV_nolen(pendl);
545             }
546              
547 2           str = format_all(aTHX_ hl, 0, cendl, &size);
548 2           RETVAL = str;
549              
550             OUTPUT: RETVAL
551              
552             CLEANUP:
553 2           GMEM_DEL(str, char*, size);
554              
555              
556             SV*
557             psgi_flatten(HList* hl)
558             PREINIT:
559 2           AV* av = 0;
560             int j;
561             int k;
562             CODE:
563             GLOG(("=X= @@@ psgi_flatten(%p|%d)", hl, hlist_size(hl)));
564 2           hlist_sort(hl);
565 2           av = newAV();
566 5 100         for (j = 0; j < hl->ulen; ++j) {
567 3           HNode* hn = &hl->data[j];
568 3           const char* header = hn->header->name;
569 3           PList* pl = hn->values;
570 7 100         for (k = 0; k < pl->ulen; ++k) {
571 4           PNode* pn = &pl->data[k];
572 4           SV* value = (SV*) pn->ptr;
573 4           av_push(av, newSVpv(header, 0));
574 4           av_push(av, newSVsv(value));
575             }
576             }
577 2           RETVAL = newRV_noinc((SV*) av);
578             OUTPUT: RETVAL
579              
580              
581             SV*
582             psgi_flatten_without_sort(HList* hl)
583             PREINIT:
584 2           AV* av = 0;
585             int j;
586             int k;
587             CODE:
588             GLOG(("=X= @@@ psgi_flatten_without_sort(%p|%d)", hl, hlist_size(hl)));
589 2           av = newAV();
590 5 100         for (j = 0; j < hl->ulen; ++j) {
591 3           HNode* hn = &hl->data[j];
592 3           const char* header = hn->header->name;
593 3           PList* pl = hn->values;
594 7 100         for (k = 0; k < pl->ulen; ++k) {
595 4           PNode* pn = &pl->data[k];
596 4           SV* value = (SV*) pn->ptr;
597 4           av_push(av, newSVpv(header, 0));
598 4           av_push(av, newSVsv(value));
599             }
600             }
601 2           RETVAL = newRV_noinc((SV*) av);
602             OUTPUT: RETVAL
603              
604              
605             void
606             scan(HList* hl, SV* sub)
607             PREINIT:
608             int j;
609             int k;
610              
611             CODE:
612             GLOG(("=X= @@@ scan(%p|%d)", hl, hlist_size(hl)));
613              
614 6 100         if (!SvOK(sub) || !SvRV(sub) || SvTYPE( SvRV(sub) ) != SVt_PVCV ) {
    50          
    50          
615 1           croak("Second argument must be a CODE reference");
616             }
617              
618 5           hlist_sort(hl);
619 19 100         for (j = 0; j < hl->ulen; ++j) {
620 15           HNode* hn = &hl->data[j];
621 15           const char* header = hn->header->name;
622 15           SV* pheader = sv_2mortal(newSVpv(header, 0));
623 15           PList* pl = hn->values;
624 33 100         for (k = 0; k < pl->ulen; ++k) {
625 19           PNode* pn = &pl->data[k];
626 19           SV* value = (SV*) pn->ptr;
627              
628 19           ENTER;
629 19           SAVETMPS;
630              
631 19 50         PUSHMARK(SP);
632 19           PUSHs( pheader );
633 19           PUSHs( value );
634 19           PUTBACK;
635 19           call_sv( (SV *)SvRV(sub), G_DISCARD );
636              
637 18 50         FREETMPS;
638 18           LEAVE;
639             }
640             }