diff options
Diffstat (limited to 'src/microspdy/applicationlayer.c')
-rw-r--r-- | src/microspdy/applicationlayer.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/src/microspdy/applicationlayer.c b/src/microspdy/applicationlayer.c new file mode 100644 index 00000000..cbe484bc --- /dev/null +++ b/src/microspdy/applicationlayer.c | |||
@@ -0,0 +1,679 @@ | |||
1 | /* | ||
2 | This file is part of libmicrospdy | ||
3 | Copyright (C) 2012 Andrey Uzunov | ||
4 | |||
5 | This program is free software: you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation, either version 3 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | /** | ||
20 | * @file applicationlayer.c | ||
21 | * @brief SPDY application or HTTP layer | ||
22 | * @author Andrey Uzunov | ||
23 | */ | ||
24 | |||
25 | #include "platform.h" | ||
26 | #include "applicationlayer.h" | ||
27 | #include "alstructures.h" | ||
28 | #include "structures.h" | ||
29 | #include "internal.h" | ||
30 | #include "daemon.h" | ||
31 | #include "session.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Callback called when new stream is created. It extracts the info from | ||
36 | * the stream to create (HTTP) request object and pass it to the client. | ||
37 | * | ||
38 | * @param cls | ||
39 | * @param stream the new SPDY stream | ||
40 | * @return SPDY_YES on success, SPDY_NO on memomry error | ||
41 | */ | ||
42 | static int | ||
43 | spdy_handler_new_stream (void *cls, | ||
44 | struct SPDYF_Stream * stream) | ||
45 | { | ||
46 | (void)cls; | ||
47 | uint i; | ||
48 | char *method = NULL; | ||
49 | char *path = NULL; | ||
50 | char *version = NULL; | ||
51 | char *host = NULL; | ||
52 | char *scheme = NULL; | ||
53 | struct SPDY_Request * request = NULL; | ||
54 | struct SPDY_NameValue * headers = NULL; | ||
55 | struct SPDY_NameValue * iterator = stream->headers; | ||
56 | struct SPDY_Daemon *daemon; | ||
57 | |||
58 | daemon = stream->session->daemon; | ||
59 | |||
60 | //if the user doesn't care, ignore it | ||
61 | if(NULL == daemon->new_request_cb) | ||
62 | return SPDY_YES; | ||
63 | |||
64 | if(NULL == (headers=SPDY_name_value_create())) | ||
65 | goto free_and_fail; | ||
66 | |||
67 | if(NULL==(request = malloc(sizeof(struct SPDY_Request)))) | ||
68 | goto free_and_fail; | ||
69 | |||
70 | memset(request, 0, sizeof(struct SPDY_Request)); | ||
71 | request->stream = stream; | ||
72 | |||
73 | /* extract the mandatory fields from stream->headers' structure | ||
74 | * to pass them to the client */ | ||
75 | while(iterator != NULL) | ||
76 | { | ||
77 | if(strcmp(":method",iterator->name) == 0) | ||
78 | { | ||
79 | if(1 != iterator->num_values) | ||
80 | break; | ||
81 | method = iterator->value[0]; | ||
82 | } | ||
83 | else if(strcmp(":path",iterator->name) == 0) | ||
84 | { | ||
85 | if(1 != iterator->num_values) | ||
86 | break; | ||
87 | path = iterator->value[0]; | ||
88 | } | ||
89 | else if(strcmp(":version",iterator->name) == 0) | ||
90 | { | ||
91 | if(1 != iterator->num_values) | ||
92 | break; | ||
93 | version = iterator->value[0]; | ||
94 | } | ||
95 | else if(strcmp(":host",iterator->name) == 0) | ||
96 | { | ||
97 | //TODO can it have more values? | ||
98 | if(1 != iterator->num_values) | ||
99 | break; | ||
100 | host = iterator->value[0]; | ||
101 | } | ||
102 | else if(strcmp(":scheme",iterator->name) == 0) | ||
103 | { | ||
104 | if(1 != iterator->num_values) | ||
105 | break; | ||
106 | scheme = iterator->value[0]; | ||
107 | } | ||
108 | else | ||
109 | for(i=0; i<iterator->num_values; ++i) | ||
110 | if (SPDY_YES != SPDY_name_value_add(headers,iterator->name,iterator->value[i])) | ||
111 | goto free_and_fail; | ||
112 | |||
113 | iterator = iterator->next; | ||
114 | } | ||
115 | |||
116 | request->method=method; | ||
117 | request->path=path; | ||
118 | request->version=version; | ||
119 | request->host=host; | ||
120 | request->scheme=scheme; | ||
121 | request->headers=headers; | ||
122 | |||
123 | //check request validity, all these fields are mandatory for a request | ||
124 | if(NULL == method || strlen(method) == 0 | ||
125 | || NULL == path || strlen(path) == 0 | ||
126 | || NULL == version || strlen(version) == 0 | ||
127 | || NULL == host || strlen(host) == 0 | ||
128 | || NULL == scheme || strlen(scheme) == 0 | ||
129 | ) | ||
130 | { | ||
131 | //TODO HTTP 400 Bad Request must be answered | ||
132 | |||
133 | SPDYF_DEBUG("Bad request"); | ||
134 | |||
135 | SPDY_destroy_request(request); | ||
136 | |||
137 | return SPDY_YES; | ||
138 | } | ||
139 | |||
140 | //call client's callback function to notify | ||
141 | daemon->new_request_cb(daemon->cls, | ||
142 | request, | ||
143 | stream->priority, | ||
144 | method, | ||
145 | path, | ||
146 | version, | ||
147 | host, | ||
148 | scheme, | ||
149 | headers); | ||
150 | |||
151 | return SPDY_YES; | ||
152 | |||
153 | //for GOTO | ||
154 | free_and_fail: | ||
155 | |||
156 | SPDY_name_value_destroy(headers); | ||
157 | return SPDY_NO; | ||
158 | } | ||
159 | |||
160 | |||
161 | /** | ||
162 | * Callback to be called when the response queue object was handled and | ||
163 | * the data was already sent or discarded. | ||
164 | * | ||
165 | * @param cls | ||
166 | * @param response_queue the object which is being handled | ||
167 | * @param status shows if actually the response was sent or it was | ||
168 | * discarded by the lib for any reason (e.g., closing session, | ||
169 | * closing stream, stopping daemon, etc.). It is possible that | ||
170 | * status indicates an error but parts of the response headers | ||
171 | * and/or body (in one | ||
172 | * or several frames) were already sent to the client. | ||
173 | */ | ||
174 | static void | ||
175 | spdy_handler_response_queue_result(void * cls, | ||
176 | struct SPDYF_Response_Queue *response_queue, | ||
177 | enum SPDY_RESPONSE_RESULT status) | ||
178 | { | ||
179 | int streamopened; | ||
180 | struct SPDY_Request *request = (struct SPDY_Request *)cls; | ||
181 | |||
182 | SPDYF_ASSERT(NULL == response_queue->data_frame | ||
183 | && NULL != response_queue->control_frame | ||
184 | || NULL != response_queue->data_frame | ||
185 | && NULL == response_queue->control_frame, | ||
186 | "response queue must have either control frame or data frame"); | ||
187 | |||
188 | streamopened = !response_queue->stream->is_out_closed; | ||
189 | |||
190 | response_queue->rrcb(response_queue->rrcb_cls, response_queue->response, request, status, streamopened); | ||
191 | } | ||
192 | |||
193 | |||
194 | int | ||
195 | SPDY_init () | ||
196 | { | ||
197 | SPDYF_ASSERT(SPDYF_BUFFER_SIZE >= SPDY_MAX_SUPPORTED_FRAME_SIZE, | ||
198 | "Buffer size is less than max supported frame size!"); | ||
199 | SPDYF_ASSERT(SPDY_MAX_SUPPORTED_FRAME_SIZE >= 32, | ||
200 | "Max supported frame size must be bigger than the minimal value!"); | ||
201 | SPDYF_tls_global_init(); | ||
202 | return SPDY_YES; | ||
203 | } | ||
204 | |||
205 | |||
206 | void | ||
207 | SPDY_deinit () | ||
208 | { | ||
209 | //currently nothing to be freed/deinited | ||
210 | //SPDYF_tls_global_deinit doesn't do anything now | ||
211 | //SPDYF_tls_global_deinit(); | ||
212 | } | ||
213 | |||
214 | |||
215 | void | ||
216 | SPDY_run (struct SPDY_Daemon *daemon) | ||
217 | { | ||
218 | if(NULL == daemon) | ||
219 | { | ||
220 | SPDYF_DEBUG("daemon is NULL"); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | SPDYF_run(daemon); | ||
225 | } | ||
226 | |||
227 | |||
228 | int | ||
229 | SPDY_get_timeout (struct SPDY_Daemon *daemon, | ||
230 | unsigned long long *timeout) | ||
231 | { | ||
232 | if(NULL == daemon) | ||
233 | { | ||
234 | SPDYF_DEBUG("daemon is NULL"); | ||
235 | return SPDY_INPUT_ERROR; | ||
236 | } | ||
237 | |||
238 | return SPDYF_get_timeout(daemon,timeout); | ||
239 | } | ||
240 | |||
241 | |||
242 | int | ||
243 | SPDY_get_fdset (struct SPDY_Daemon *daemon, | ||
244 | fd_set *read_fd_set, | ||
245 | fd_set *write_fd_set, | ||
246 | fd_set *except_fd_set) | ||
247 | { | ||
248 | if(NULL == daemon | ||
249 | || NULL == read_fd_set | ||
250 | || NULL == write_fd_set | ||
251 | || NULL == except_fd_set) | ||
252 | { | ||
253 | SPDYF_DEBUG("a parameter is NULL"); | ||
254 | return SPDY_INPUT_ERROR; | ||
255 | } | ||
256 | |||
257 | return SPDYF_get_fdset(daemon, | ||
258 | read_fd_set, | ||
259 | write_fd_set, | ||
260 | except_fd_set, | ||
261 | false); | ||
262 | } | ||
263 | |||
264 | |||
265 | struct SPDY_Daemon * | ||
266 | SPDY_start_daemon (uint16_t port, | ||
267 | const char *certfile, | ||
268 | const char *keyfile, | ||
269 | SPDY_NewSessionCallback nscb, | ||
270 | SPDY_SessionClosedCallback sccb, | ||
271 | SPDY_NewRequestCallback nrcb, | ||
272 | SPDY_NewPOSTDataCallback npdcb, | ||
273 | void * cls, | ||
274 | ...) | ||
275 | { | ||
276 | struct SPDY_Daemon *daemon; | ||
277 | va_list valist; | ||
278 | |||
279 | if(NULL == certfile) | ||
280 | { | ||
281 | SPDYF_DEBUG("certfile is NULL"); | ||
282 | return NULL; | ||
283 | } | ||
284 | if(NULL == keyfile) | ||
285 | { | ||
286 | SPDYF_DEBUG("keyfile is NULL"); | ||
287 | return NULL; | ||
288 | } | ||
289 | |||
290 | va_start(valist, cls); | ||
291 | daemon = SPDYF_start_daemon_va ( port, | ||
292 | certfile, | ||
293 | keyfile, | ||
294 | nscb, | ||
295 | sccb, | ||
296 | nrcb, | ||
297 | npdcb, | ||
298 | &spdy_handler_new_stream, | ||
299 | cls, | ||
300 | NULL, | ||
301 | valist | ||
302 | ); | ||
303 | va_end(valist); | ||
304 | |||
305 | return daemon; | ||
306 | } | ||
307 | |||
308 | |||
309 | void | ||
310 | SPDY_stop_daemon (struct SPDY_Daemon *daemon) | ||
311 | { | ||
312 | if(NULL == daemon) | ||
313 | { | ||
314 | SPDYF_DEBUG("daemon is NULL"); | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | SPDYF_stop_daemon(daemon); | ||
319 | } | ||
320 | |||
321 | |||
322 | struct SPDY_Response * | ||
323 | SPDY_build_response(int status, | ||
324 | const char * statustext, | ||
325 | const char * version, | ||
326 | struct SPDY_NameValue * headers, | ||
327 | const void * data, | ||
328 | size_t size) | ||
329 | { | ||
330 | struct SPDY_Response *response = NULL; | ||
331 | struct SPDY_NameValue ** all_headers = NULL; | ||
332 | char *fullstatus = NULL; | ||
333 | int ret; | ||
334 | int num_hdr_containers = 1; | ||
335 | |||
336 | if(NULL == version) | ||
337 | { | ||
338 | SPDYF_DEBUG("version is NULL"); | ||
339 | return NULL; | ||
340 | } | ||
341 | |||
342 | if(NULL == (response = malloc(sizeof(struct SPDY_Response)))) | ||
343 | goto free_and_fail; | ||
344 | memset(response, 0, sizeof(struct SPDY_Response)); | ||
345 | |||
346 | if(NULL != headers) | ||
347 | num_hdr_containers = 2; | ||
348 | |||
349 | if(NULL == (all_headers = malloc(num_hdr_containers * sizeof(struct SPDY_NameValue *)))) | ||
350 | goto free_and_fail; | ||
351 | memset(all_headers, 0, num_hdr_containers * sizeof(struct SPDY_NameValue *)); | ||
352 | |||
353 | if(2 == num_hdr_containers) | ||
354 | all_headers[1] = headers; | ||
355 | |||
356 | if(NULL == (all_headers[0] = SPDY_name_value_create())) | ||
357 | goto free_and_fail; | ||
358 | |||
359 | if(NULL == statustext) | ||
360 | ret = asprintf(&fullstatus, "%i", status); | ||
361 | else | ||
362 | ret = asprintf(&fullstatus, "%i %s", status, statustext); | ||
363 | if(-1 == ret) | ||
364 | goto free_and_fail; | ||
365 | |||
366 | if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":status", fullstatus)) | ||
367 | goto free_and_fail; | ||
368 | |||
369 | free(fullstatus); | ||
370 | fullstatus = NULL; | ||
371 | |||
372 | if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":version", version)) | ||
373 | goto free_and_fail; | ||
374 | |||
375 | if(0 >= (response->headers_size = SPDYF_name_value_to_stream(all_headers, | ||
376 | num_hdr_containers, | ||
377 | &(response->headers)))) | ||
378 | goto free_and_fail; | ||
379 | |||
380 | SPDY_name_value_destroy(all_headers[0]); | ||
381 | free(all_headers); | ||
382 | |||
383 | if(size > 0) | ||
384 | { | ||
385 | //copy the data to the response object | ||
386 | if(NULL == (response->data = malloc(size))) | ||
387 | { | ||
388 | free(response->headers); | ||
389 | goto free_and_fail; | ||
390 | } | ||
391 | memcpy(response->data, data, size); | ||
392 | response->data_size = size; | ||
393 | } | ||
394 | |||
395 | return response; | ||
396 | |||
397 | //for GOTO | ||
398 | free_and_fail: | ||
399 | |||
400 | free(fullstatus); | ||
401 | if(NULL != all_headers) | ||
402 | SPDY_name_value_destroy(all_headers[0]); | ||
403 | free(all_headers); | ||
404 | free(response); | ||
405 | |||
406 | return NULL; | ||
407 | } | ||
408 | |||
409 | |||
410 | struct SPDY_Response * | ||
411 | SPDY_build_response_with_callback(int status, | ||
412 | const char * statustext, | ||
413 | const char * version, | ||
414 | struct SPDY_NameValue * headers, | ||
415 | SPDY_ResponseCallback rcb, | ||
416 | void *rcb_cls, | ||
417 | uint32_t block_size) | ||
418 | { | ||
419 | struct SPDY_Response *response; | ||
420 | |||
421 | if(NULL == rcb) | ||
422 | { | ||
423 | SPDYF_DEBUG("rcb is NULL"); | ||
424 | return NULL; | ||
425 | } | ||
426 | if(block_size > SPDY_MAX_SUPPORTED_FRAME_SIZE) | ||
427 | { | ||
428 | SPDYF_DEBUG("block_size is wrong"); | ||
429 | return NULL; | ||
430 | } | ||
431 | |||
432 | if(0 == block_size) | ||
433 | block_size = SPDY_MAX_SUPPORTED_FRAME_SIZE; | ||
434 | |||
435 | response = SPDY_build_response(status, | ||
436 | statustext, | ||
437 | version, | ||
438 | headers, | ||
439 | NULL, | ||
440 | 0); | ||
441 | |||
442 | if(NULL == response) | ||
443 | { | ||
444 | return NULL; | ||
445 | } | ||
446 | |||
447 | response->rcb = rcb; | ||
448 | response->rcb_cls = rcb_cls; | ||
449 | response->rcb_block_size = block_size; | ||
450 | |||
451 | return response; | ||
452 | } | ||
453 | |||
454 | |||
455 | int | ||
456 | SPDY_queue_response (struct SPDY_Request * request, | ||
457 | struct SPDY_Response *response, | ||
458 | bool closestream, | ||
459 | bool consider_priority, | ||
460 | SPDY_ResponseResultCallback rrcb, | ||
461 | void * rrcb_cls) | ||
462 | { | ||
463 | struct SPDYF_Response_Queue *headers_to_queue; | ||
464 | struct SPDYF_Response_Queue *body_to_queue; | ||
465 | SPDYF_ResponseQueueResultCallback frqcb = NULL; | ||
466 | void *frqcb_cls = NULL; | ||
467 | int int_consider_priority = consider_priority ? SPDY_YES : SPDY_NO; | ||
468 | |||
469 | if(NULL == request) | ||
470 | { | ||
471 | SPDYF_DEBUG("request is NULL"); | ||
472 | return SPDY_INPUT_ERROR; | ||
473 | } | ||
474 | if(NULL == response) | ||
475 | { | ||
476 | SPDYF_DEBUG("request is NULL"); | ||
477 | return SPDY_INPUT_ERROR; | ||
478 | } | ||
479 | |||
480 | if(request->stream->is_out_closed | ||
481 | || SPDY_SESSION_STATUS_CLOSING == request->stream->session->status) | ||
482 | return SPDY_NO; | ||
483 | |||
484 | if(NULL != rrcb) | ||
485 | { | ||
486 | frqcb_cls = request; | ||
487 | frqcb = &spdy_handler_response_queue_result; | ||
488 | } | ||
489 | |||
490 | if(response->data_size > 0) | ||
491 | { | ||
492 | //SYN_REPLY and DATA will be queued | ||
493 | |||
494 | if(NULL == (headers_to_queue = SPDYF_response_queue_create(false, | ||
495 | response->headers, | ||
496 | response->headers_size, | ||
497 | response, | ||
498 | request->stream, | ||
499 | false, | ||
500 | NULL, | ||
501 | NULL, | ||
502 | NULL, | ||
503 | NULL))) | ||
504 | { | ||
505 | return SPDY_NO; | ||
506 | } | ||
507 | |||
508 | if(NULL == (body_to_queue = SPDYF_response_queue_create(true, | ||
509 | response->data, | ||
510 | response->data_size, | ||
511 | response, | ||
512 | request->stream, | ||
513 | closestream, | ||
514 | frqcb, | ||
515 | frqcb_cls, | ||
516 | rrcb, | ||
517 | rrcb_cls))) | ||
518 | { | ||
519 | SPDYF_response_queue_destroy(headers_to_queue); | ||
520 | return SPDY_NO; | ||
521 | } | ||
522 | |||
523 | SPDYF_queue_response (headers_to_queue, | ||
524 | request->stream->session, | ||
525 | int_consider_priority); | ||
526 | |||
527 | SPDYF_queue_response (body_to_queue, | ||
528 | request->stream->session, | ||
529 | int_consider_priority); | ||
530 | } | ||
531 | else if(NULL == response->rcb) | ||
532 | { | ||
533 | //no "body" will be queued, e.g. HTTP 404 without body | ||
534 | |||
535 | if(NULL == (headers_to_queue = SPDYF_response_queue_create(false, | ||
536 | response->headers, | ||
537 | response->headers_size, | ||
538 | response, | ||
539 | request->stream, | ||
540 | closestream, | ||
541 | frqcb, | ||
542 | frqcb_cls, | ||
543 | rrcb, | ||
544 | rrcb_cls))) | ||
545 | { | ||
546 | return SPDY_NO; | ||
547 | } | ||
548 | |||
549 | SPDYF_queue_response (headers_to_queue, | ||
550 | request->stream->session, | ||
551 | int_consider_priority); | ||
552 | } | ||
553 | else | ||
554 | { | ||
555 | //response with callbacks | ||
556 | |||
557 | if(NULL == (headers_to_queue = SPDYF_response_queue_create(false, | ||
558 | response->headers, | ||
559 | response->headers_size, | ||
560 | response, | ||
561 | request->stream, | ||
562 | false, | ||
563 | NULL, | ||
564 | NULL, | ||
565 | NULL, | ||
566 | NULL))) | ||
567 | { | ||
568 | return SPDY_NO; | ||
569 | } | ||
570 | |||
571 | if(NULL == (body_to_queue = SPDYF_response_queue_create(true, | ||
572 | response->data, | ||
573 | response->data_size, | ||
574 | response, | ||
575 | request->stream, | ||
576 | closestream, | ||
577 | frqcb, | ||
578 | frqcb_cls, | ||
579 | rrcb, | ||
580 | rrcb_cls))) | ||
581 | { | ||
582 | SPDYF_response_queue_destroy(headers_to_queue); | ||
583 | return SPDY_NO; | ||
584 | } | ||
585 | |||
586 | SPDYF_queue_response (headers_to_queue, | ||
587 | request->stream->session, | ||
588 | int_consider_priority); | ||
589 | |||
590 | SPDYF_queue_response (body_to_queue, | ||
591 | request->stream->session, | ||
592 | int_consider_priority); | ||
593 | } | ||
594 | |||
595 | return SPDY_YES; | ||
596 | } | ||
597 | |||
598 | |||
599 | socklen_t | ||
600 | SPDY_get_remote_addr(struct SPDY_Session * session, | ||
601 | struct sockaddr ** addr) | ||
602 | { | ||
603 | if(NULL == session) | ||
604 | { | ||
605 | SPDYF_DEBUG("session is NULL"); | ||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | *addr = session->addr; | ||
610 | |||
611 | return session->addr_len; | ||
612 | } | ||
613 | |||
614 | |||
615 | struct SPDY_Session * | ||
616 | SPDY_get_session_for_request(const struct SPDY_Request * request) | ||
617 | { | ||
618 | if(NULL == request) | ||
619 | { | ||
620 | SPDYF_DEBUG("request is NULL"); | ||
621 | return NULL; | ||
622 | } | ||
623 | |||
624 | return request->stream->session; | ||
625 | } | ||
626 | |||
627 | |||
628 | void * | ||
629 | SPDY_get_cls_from_session(struct SPDY_Session * session) | ||
630 | { | ||
631 | if(NULL == session) | ||
632 | { | ||
633 | SPDYF_DEBUG("session is NULL"); | ||
634 | return NULL; | ||
635 | } | ||
636 | |||
637 | return session->user_cls; | ||
638 | } | ||
639 | |||
640 | |||
641 | void | ||
642 | SPDY_set_cls_to_session(struct SPDY_Session * session, | ||
643 | void * cls) | ||
644 | { | ||
645 | if(NULL == session) | ||
646 | { | ||
647 | SPDYF_DEBUG("session is NULL"); | ||
648 | return; | ||
649 | } | ||
650 | |||
651 | session->user_cls = cls; | ||
652 | } | ||
653 | |||
654 | |||
655 | void * | ||
656 | SPDY_get_cls_from_request(struct SPDY_Request * request) | ||
657 | { | ||
658 | if(NULL == request) | ||
659 | { | ||
660 | SPDYF_DEBUG("request is NULL"); | ||
661 | return NULL; | ||
662 | } | ||
663 | |||
664 | return request->user_cls; | ||
665 | } | ||
666 | |||
667 | |||
668 | void | ||
669 | SPDY_set_cls_to_request(struct SPDY_Request * request, | ||
670 | void * cls) | ||
671 | { | ||
672 | if(NULL == request) | ||
673 | { | ||
674 | SPDYF_DEBUG("request is NULL"); | ||
675 | return; | ||
676 | } | ||
677 | |||
678 | request->user_cls = cls; | ||
679 | } | ||