Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(243)

Side by Side Diff: net/base/nss_memio.c

Issue 4049: Port SSLClientSocket to Linux (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/nss_memio.h ('k') | net/base/ssl_client_socket_nss.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 // Written in NSPR style to also be suitable for adding to the NSS demo suite
5
6 /* memio is a simple NSPR I/O layer that lets you decouple NSS from
7 * the real network. It's rather like openssl's memory bio,
8 * and is useful when your app absolutely, positively doesn't
9 * want to let NSS do its own networking.
10 */
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include <prerror.h>
17 #include <prinit.h>
18 #include <prlog.h>
19
20 #include "nss_memio.h"
21
22 /*--------------- private memio types -----------------------*/
23
24 /*----------------------------------------------------------------------
25 Simple private circular buffer class. Size cannot be changed once allocated.
26 ----------------------------------------------------------------------*/
27
28 struct memio_buffer {
29 int head; /* where to take next byte out of buf */
30 int tail; /* where to put next byte into buf */
31 int bufsize; /* number of bytes allocated to buf */
32 /* TODO(port): error handling is pessimistic right now.
33 * Once an error is set, the socket is considered broken
34 * (PR_WOULD_BLOCK_ERROR not included).
35 */
36 PRErrorCode last_err;
37 char *buf;
38 };
39
40
41 /* The 'secret' field of a PRFileDesc created by memio_CreateIOLayer points
42 * to one of these.
43 * In the public header, we use struct memio_Private as a typesafe alias
44 * for this. This causes a few ugly typecasts in the private file, but
45 * seems safer.
46 */
47 struct PRFilePrivate {
48 /* read requests are satisfied from this buffer */
49 struct memio_buffer readbuf;
50
51 /* write requests are satisfied from this buffer */
52 struct memio_buffer writebuf;
53
54 /* SSL needs to know socket peer's name */
55 PRNetAddr peername;
56
57 /* if set, empty I/O returns EOF instead of EWOULDBLOCK */
58 int eof;
59 };
60
61 /*--------------- private memio_buffer functions ---------------------*/
62
63 /* Forward declarations. */
64
65 /* Allocate a memio_buffer of given size. */
66 static void memio_buffer_new(struct memio_buffer *mb, int size);
67
68 /* Deallocate a memio_buffer allocated by memio_buffer_new. */
69 static void memio_buffer_destroy(struct memio_buffer *mb);
70
71 /* How many bytes have been put into the buffer */
72 static int memio_buffer_used(const struct memio_buffer *mb);
73
74 /* How many bytes can be read out of the buffer without wrapping */
75 static int memio_buffer_used_contiguous(const struct memio_buffer *mb);
76
77 /* How many bytes can still be put into the buffer */
78 static int memio_buffer_unused(const struct memio_buffer *mb);
79
80 /* How many bytes can be written into the buffer without wrapping */
81 static int memio_buffer_unused_contiguous(const struct memio_buffer *mb);
82
83 /* Is buffer completely empty? */
84 static int memio_buffer_empty(const struct memio_buffer *mb);
85
86 /* Is buffer completely full? */
87 static int memio_buffer_full(const struct memio_buffer *mb);
88
89 /* Write n bytes into the buffer. Returns number of bytes written. */
90 static int memio_buffer_put(struct memio_buffer *mb, const char *buf, int n);
91
92 /* Read n bytes from the buffer. Returns number of bytes read. */
93 static int memio_buffer_get(struct memio_buffer *mb, char *buf, int n);
94
95 /* Allocate a memio_buffer of given size. */
96 static void memio_buffer_new(struct memio_buffer *mb, int size)
97 {
98 mb->head = 0;
99 mb->tail = 0;
100 mb->bufsize = size;
101 mb->buf = malloc(size);
102 }
103
104 /* Deallocate a memio_buffer allocated by memio_buffer_new. */
105 static void memio_buffer_destroy(struct memio_buffer *mb)
106 {
107 free(mb->buf);
108 mb->buf = NULL;
109 mb->head = 0;
110 mb->tail = 0;
111 }
112
113 /* How many bytes have been put into the buffer */
114 static int memio_buffer_used(const struct memio_buffer *mb)
115 {
116 int n = mb->tail - mb->head;
117 if (n < 0) n += mb->bufsize;
118 return n;
119 }
120
121 /* How many bytes can be read out of the buffer without wrapping */
122 static int memio_buffer_used_contiguous(const struct memio_buffer *mb)
123 {
124 return (((mb->tail >= mb->head) ? mb->tail : mb->bufsize) - mb->head);
125 }
126
127 /* How many bytes can still be put into the buffer */
128 static int memio_buffer_unused(const struct memio_buffer *mb)
129 {
130 return mb->bufsize - memio_buffer_used(mb) - 1;
131 }
132
133 /* How many bytes can be written into the buffer without wrapping */
134 static int memio_buffer_unused_contiguous(const struct memio_buffer *mb)
135 {
136 if (mb->head > mb->tail) return mb->head - mb->tail - 1;
137 return mb->bufsize - mb->tail - (mb->head == 0);
138 }
139
140 /* Is buffer completely empty? */
141 static int memio_buffer_empty(const struct memio_buffer *mb)
142 {
143 return mb->head == mb->tail;
144 }
145
146 /* Is buffer completely full? */
147 static int memio_buffer_full(const struct memio_buffer *mb)
148 {
149 return memio_buffer_unused(mb) == 0;
150 }
151
152 /* Write n bytes into the buffer. Returns number of bytes written. */
153 static int memio_buffer_put(struct memio_buffer *mb, const char *buf, int n)
154 {
155 int len;
156 int transferred = 0;
157
158 /* Handle part before wrap */
159 len = PR_MIN(n, memio_buffer_unused_contiguous(mb));
160 if (len > 0) {
161 /* Buffer not full */
162 memcpy(&mb->buf[mb->tail], buf, len);
163 mb->tail += len;
164 if (mb->tail == mb->bufsize)
165 mb->tail = 0;
166 n -= len;
167 buf += len;
168 transferred += len;
169
170 /* Handle part after wrap */
171 len = PR_MIN(n, memio_buffer_unused_contiguous(mb));
172 if (len > 0) {
173 /* Output buffer still not full, input buffer still not empty */
174 memcpy(&mb->buf[mb->tail], buf, len);
175 mb->tail += len;
176 if (mb->tail == mb->bufsize)
177 mb->tail = 0;
178 transferred += len;
179 }
180 }
181
182 return transferred;
183 }
184
185
186 /* Read n bytes from the buffer. Returns number of bytes read. */
187 static int memio_buffer_get(struct memio_buffer *mb, char *buf, int n)
188 {
189 int len;
190 int transferred = 0;
191
192 /* Handle part before wrap */
193 len = PR_MIN(n, memio_buffer_used_contiguous(mb));
194 if (len) {
195 memcpy(buf, &mb->buf[mb->head], len);
196 mb->head += len;
197 if (mb->head == mb->bufsize)
198 mb->head = 0;
199 n -= len;
200 buf += len;
201 transferred += len;
202
203 /* Handle part after wrap */
204 len = PR_MIN(n, memio_buffer_used_contiguous(mb));
205 if (len) {
206 memcpy(buf, &mb->buf[mb->head], len);
207 mb->head += len;
208 if (mb->head == mb->bufsize)
209 mb->head = 0;
210 transferred += len;
211 }
212 }
213
214 return transferred;
215 }
216
217 /*--------------- private memio functions -----------------------*/
218
219 static PRStatus PR_CALLBACK memio_Close(PRFileDesc *fd)
220 {
221 struct PRFilePrivate *secret = fd->secret;
222 memio_buffer_destroy(&secret->readbuf);
223 memio_buffer_destroy(&secret->writebuf);
224 free(secret);
225 fd->dtor(fd);
226 return PR_SUCCESS;
227 }
228
229 static PRStatus PR_CALLBACK memio_Shutdown(PRFileDesc *fd, PRIntn how)
230 {
231 /* TODO: pass shutdown status to app somehow */
232 return PR_SUCCESS;
233 }
234
235 /* If there was a network error in the past taking bytes
236 * out of the buffer, return it to the next call that
237 * tries to read from an empty buffer.
238 */
239 static int PR_CALLBACK memio_Recv(PRFileDesc *fd, void *buf, PRInt32 len,
240 PRIntn flags, PRIntervalTime timeout)
241 {
242 struct PRFilePrivate *secret;
243 struct memio_buffer *mb;
244 int rv;
245
246 if (flags) {
247 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
248 return -1;
249 }
250
251 secret = fd->secret;
252 mb = &secret->readbuf;
253 PR_ASSERT(mb->bufsize);
254 rv = memio_buffer_get(mb, buf, len);
255 if (rv == 0 && !secret->eof) {
256 if (mb->last_err)
257 PR_SetError(mb->last_err, 0);
258 else
259 PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
260 return -1;
261 }
262
263 return rv;
264 }
265
266 static int PR_CALLBACK memio_Read(PRFileDesc *fd, void *buf, PRInt32 len)
267 {
268 /* pull bytes from buffer */
269 return memio_Recv(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT);
270 }
271
272 static int PR_CALLBACK memio_Send(PRFileDesc *fd, const void *buf, PRInt32 len,
273 PRIntn flags, PRIntervalTime timeout)
274 {
275 struct PRFilePrivate *secret;
276 struct memio_buffer *mb;
277 int rv;
278
279 secret = fd->secret;
280 mb = &secret->writebuf;
281 PR_ASSERT(mb->bufsize);
282
283 if (mb->last_err) {
284 PR_SetError(mb->last_err, 0);
285 return -1;
286 }
287 rv = memio_buffer_put(mb, buf, len);
288 if (rv == 0) {
289 PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
290 return -1;
291 }
292 return rv;
293 }
294
295 static int PR_CALLBACK memio_Write(PRFileDesc *fd, const void *buf, PRInt32 len)
296 {
297 /* append bytes to buffer */
298 return memio_Send(fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT);
299 }
300
301 static PRStatus PR_CALLBACK memio_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
302 {
303 /* TODO: fail if memio_SetPeerName has not been called */
304 struct PRFilePrivate *secret = fd->secret;
305 *addr = secret->peername;
306 return PR_SUCCESS;
307 }
308
309 static PRStatus memio_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
310 {
311 /*
312 * Even in the original version for real tcp sockets,
313 * PR_SockOpt_Nonblocking is a special case that does not
314 * translate to a getsockopt() call
315 */
316 if (PR_SockOpt_Nonblocking == data->option) {
317 data->value.non_blocking = PR_TRUE;
318 return PR_SUCCESS;
319 }
320 PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
321 return PR_FAILURE;
322 }
323
324 /*--------------- private memio data -----------------------*/
325
326 /*
327 * Implement just the bare minimum number of methods needed to make ssl happy.
328 *
329 * Oddly, PR_Recv calls ssl_Recv calls ssl_SocketIsBlocking calls
330 * PR_GetSocketOption, so we have to provide an implementation of
331 * PR_GetSocketOption that just says "I'm nonblocking".
332 */
333
334 static struct PRIOMethods memio_layer_methods = {
335 PR_DESC_LAYERED,
336 memio_Close,
337 memio_Read,
338 memio_Write,
339 NULL,
340 NULL,
341 NULL,
342 NULL,
343 NULL,
344 NULL,
345 NULL,
346 NULL,
347 NULL,
348 NULL,
349 NULL,
350 NULL,
351 memio_Shutdown,
352 memio_Recv,
353 memio_Send,
354 NULL,
355 NULL,
356 NULL,
357 NULL,
358 NULL,
359 NULL,
360 memio_GetPeerName,
361 NULL,
362 NULL,
363 memio_GetSocketOption,
364 NULL,
365 NULL,
366 NULL,
367 NULL,
368 NULL,
369 NULL,
370 NULL,
371 };
372
373 static PRDescIdentity memio_identity = PR_INVALID_IO_LAYER;
374
375 static PRStatus memio_InitializeLayerName(void)
376 {
377 memio_identity = PR_GetUniqueIdentity("memio");
378 return PR_SUCCESS;
379 }
380
381 /*--------------- public memio functions -----------------------*/
382
383 PRFileDesc *memio_CreateIOLayer(int bufsize)
384 {
385 PRFileDesc *fd;
386 struct PRFilePrivate *secret;
387 static PRCallOnceType once;
388
389 PR_CallOnce(&once, memio_InitializeLayerName);
390
391 fd = PR_CreateIOLayerStub(memio_identity, &memio_layer_methods);
392 secret = malloc(sizeof(struct PRFilePrivate));
393 memset(secret, 0, sizeof(*secret));
394
395 memio_buffer_new(&secret->readbuf, bufsize);
396 memio_buffer_new(&secret->writebuf, bufsize);
397 fd->secret = secret;
398 return fd;
399 }
400
401 void memio_SetPeerName(PRFileDesc *fd, const PRNetAddr *peername)
402 {
403 PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
404 struct PRFilePrivate *secret = memiofd->secret;
405 secret->peername = *peername;
406 }
407
408 memio_Private *memio_GetSecret(PRFileDesc *fd)
409 {
410 PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
411 struct PRFilePrivate *secret = memiofd->secret;
412 return (memio_Private *)secret;
413 }
414
415 int memio_GetReadParams(memio_Private *secret, char **buf)
416 {
417 struct memio_buffer* mb = &((PRFilePrivate *)secret)->readbuf;
418 PR_ASSERT(mb->bufsize);
419
420 *buf = &mb->buf[mb->tail];
421 return memio_buffer_unused_contiguous(mb);
422 }
423
424 void memio_PutReadResult(memio_Private *secret, int bytes_read)
425 {
426 struct memio_buffer* mb = &((PRFilePrivate *)secret)->readbuf;
427 PR_ASSERT(mb->bufsize);
428
429 if (bytes_read > 0) {
430 mb->tail += bytes_read;
431 if (mb->tail == mb->bufsize)
432 mb->tail = 0;
433 } else if (bytes_read == 0) {
434 /* Record EOF condition and report to caller when buffer runs dry */
435 ((PRFilePrivate *)secret)->eof = PR_TRUE;
436 } else /* if (bytes_read < 0) */ {
437 mb->last_err = bytes_read;
438 }
439 }
440
441 int memio_GetWriteParams(memio_Private *secret, const char **buf)
442 {
443 struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
444 PR_ASSERT(mb->bufsize);
445
446 *buf = &mb->buf[mb->head];
447 return memio_buffer_used_contiguous(mb);
448 }
449
450 void memio_PutWriteResult(memio_Private *secret, int bytes_written)
451 {
452 struct memio_buffer* mb = &((PRFilePrivate *)secret)->writebuf;
453 PR_ASSERT(mb->bufsize);
454
455 if (bytes_written > 0) {
456 mb->head += bytes_written;
457 if (mb->head == mb->bufsize)
458 mb->head = 0;
459 } else if (bytes_written < 0) {
460 mb->last_err = bytes_written;
461 }
462 }
463
464 /*--------------- private memio_buffer self-test -----------------*/
465
466 /* Even a trivial unit test is very helpful when doing circular buffers. */
467 /*#define TRIVIAL_SELF_TEST*/
468 #ifdef TRIVIAL_SELF_TEST
469 #include <stdio.h>
470
471 #define TEST_BUFLEN 7
472
473 #define CHECKEQ(a, b) { \
474 if ((a) != (b)) { \
475 printf("%d != %d, Test failed line %d\n", a, b, __LINE__); \
476 exit(1); \
477 } \
478 }
479
480 int main()
481 {
482 struct memio_buffer mb;
483 char buf[100];
484 int i;
485
486 memio_buffer_new(&mb, TEST_BUFLEN);
487
488 CHECKEQ(memio_buffer_empty(&mb), TRUE);
489 CHECKEQ(memio_buffer_full(&mb), FALSE);
490 CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1);
491 CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1);
492 CHECKEQ(memio_buffer_used_contiguous(&mb), 0);
493 CHECKEQ(memio_buffer_used(&mb), 0);
494
495 CHECKEQ(memio_buffer_put(&mb, "howdy", 5), 5);
496
497 CHECKEQ(memio_buffer_empty(&mb), FALSE);
498 CHECKEQ(memio_buffer_full(&mb), FALSE);
499 CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5);
500 CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1-5);
501 CHECKEQ(memio_buffer_used_contiguous(&mb), 5);
502 CHECKEQ(memio_buffer_used(&mb), 5);
503
504 CHECKEQ(memio_buffer_put(&mb, "!", 1), 1);
505
506 CHECKEQ(memio_buffer_empty(&mb), FALSE);
507 CHECKEQ(memio_buffer_full(&mb), TRUE);
508 CHECKEQ(memio_buffer_unused_contiguous(&mb), 0);
509 CHECKEQ(memio_buffer_unused(&mb), 0);
510 CHECKEQ(memio_buffer_used_contiguous(&mb), 6);
511 CHECKEQ(memio_buffer_used(&mb), 6);
512
513 CHECKEQ(memio_buffer_get(&mb, buf, 6), 6);
514 CHECKEQ(memcmp(buf, "howdy!", 6), 0);
515
516 CHECKEQ(memio_buffer_empty(&mb), TRUE);
517 CHECKEQ(memio_buffer_full(&mb), FALSE);
518 CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1);
519 CHECKEQ(memio_buffer_unused_contiguous(&mb), 1);
520 CHECKEQ(memio_buffer_used_contiguous(&mb), 0);
521 CHECKEQ(memio_buffer_used(&mb), 0);
522
523 CHECKEQ(memio_buffer_put(&mb, "01234", 5), 5);
524
525 CHECKEQ(memio_buffer_empty(&mb), FALSE);
526 CHECKEQ(memio_buffer_full(&mb), FALSE);
527 CHECKEQ(memio_buffer_used(&mb), 5);
528 CHECKEQ(memio_buffer_used_contiguous(&mb), 1);
529 CHECKEQ(memio_buffer_unused_contiguous(&mb), TEST_BUFLEN-1-5);
530 CHECKEQ(memio_buffer_unused(&mb), TEST_BUFLEN-1-5);
531
532 CHECKEQ(memio_buffer_put(&mb, "5", 1), 1);
533
534 CHECKEQ(memio_buffer_empty(&mb), FALSE);
535 CHECKEQ(memio_buffer_full(&mb), TRUE);
536 CHECKEQ(memio_buffer_unused_contiguous(&mb), 0);
537 CHECKEQ(memio_buffer_unused(&mb), 0);
538 CHECKEQ(memio_buffer_used_contiguous(&mb), 1);
539 CHECKEQ(memio_buffer_used(&mb), 6);
540
541 /* TODO: add more cases */
542
543 printf("Test passed\n");
544 exit(0);
545 }
546
547 #endif
OLDNEW
« no previous file with comments | « net/base/nss_memio.h ('k') | net/base/ssl_client_socket_nss.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698