File Coverage

XSHeaders.xs
Criterion Covered Total %
statement 205 223 91.9
branch 113 172 65.7
condition n/a
subroutine n/a
pod n/a
total 318 395 80.5


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