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
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.
19 #include "nss_memio.h"
21 /*--------------- private memio types -----------------------*/
23 /*----------------------------------------------------------------------
24 Simple private circular buffer class. Size cannot be changed once allocated.
25 ----------------------------------------------------------------------*/
28 int head
; /* where to take next byte out of buf */
29 int tail
; /* where to put next byte into buf */
30 int bufsize
; /* number of bytes allocated to buf */
31 /* TODO(port): error handling is pessimistic right now.
32 * Once an error is set, the socket is considered broken
33 * (PR_WOULD_BLOCK_ERROR not included).
40 /* The 'secret' field of a PRFileDesc created by memio_CreateIOLayer points
42 * In the public header, we use struct memio_Private as a typesafe alias
43 * for this. This causes a few ugly typecasts in the private file, but
46 struct PRFilePrivate
{
47 /* read requests are satisfied from this buffer */
48 struct memio_buffer readbuf
;
50 /* write requests are satisfied from this buffer */
51 struct memio_buffer writebuf
;
53 /* SSL needs to know socket peer's name */
56 /* if set, empty I/O returns EOF instead of EWOULDBLOCK */
59 /* if set, the number of bytes requested from readbuf that were not
60 * fulfilled (due to readbuf being empty) */
64 /*--------------- private memio_buffer functions ---------------------*/
66 /* Forward declarations. */
68 /* Allocate a memio_buffer of given size. */
69 static void memio_buffer_new(struct memio_buffer
*mb
, int size
);
71 /* Deallocate a memio_buffer allocated by memio_buffer_new. */
72 static void memio_buffer_destroy(struct memio_buffer
*mb
);
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
);
77 /* How many bytes exist after the wrap? */
78 static int memio_buffer_wrapped_bytes(const struct memio_buffer
*mb
);
80 /* How many bytes can be written into the buffer without wrapping */
81 static int memio_buffer_unused_contiguous(const struct memio_buffer
*mb
);
83 /* Write n bytes into the buffer. Returns number of bytes written. */
84 static int memio_buffer_put(struct memio_buffer
*mb
, const char *buf
, int n
);
86 /* Read n bytes from the buffer. Returns number of bytes read. */
87 static int memio_buffer_get(struct memio_buffer
*mb
, char *buf
, int n
);
89 /* Allocate a memio_buffer of given size. */
90 static void memio_buffer_new(struct memio_buffer
*mb
, int size
)
95 mb
->buf
= malloc(size
);
98 /* Deallocate a memio_buffer allocated by memio_buffer_new. */
99 static void memio_buffer_destroy(struct memio_buffer
*mb
)
107 /* How many bytes can be read out of the buffer without wrapping */
108 static int memio_buffer_used_contiguous(const struct memio_buffer
*mb
)
110 return (((mb
->tail
>= mb
->head
) ? mb
->tail
: mb
->bufsize
) - mb
->head
);
113 /* How many bytes exist after the wrap? */
114 static int memio_buffer_wrapped_bytes(const struct memio_buffer
*mb
)
116 return (mb
->tail
>= mb
->head
) ? 0 : mb
->tail
;
119 /* How many bytes can be written into the buffer without wrapping */
120 static int memio_buffer_unused_contiguous(const struct memio_buffer
*mb
)
122 if (mb
->head
> mb
->tail
) return mb
->head
- mb
->tail
- 1;
123 return mb
->bufsize
- mb
->tail
- (mb
->head
== 0);
126 /* Write n bytes into the buffer. Returns number of bytes written. */
127 static int memio_buffer_put(struct memio_buffer
*mb
, const char *buf
, int n
)
132 /* Handle part before wrap */
133 len
= PR_MIN(n
, memio_buffer_unused_contiguous(mb
));
135 /* Buffer not full */
136 memcpy(&mb
->buf
[mb
->tail
], buf
, len
);
138 if (mb
->tail
== mb
->bufsize
)
144 /* Handle part after wrap */
145 len
= PR_MIN(n
, memio_buffer_unused_contiguous(mb
));
147 /* Output buffer still not full, input buffer still not empty */
148 memcpy(&mb
->buf
[mb
->tail
], buf
, len
);
150 if (mb
->tail
== mb
->bufsize
)
160 /* Read n bytes from the buffer. Returns number of bytes read. */
161 static int memio_buffer_get(struct memio_buffer
*mb
, char *buf
, int n
)
166 /* Handle part before wrap */
167 len
= PR_MIN(n
, memio_buffer_used_contiguous(mb
));
169 memcpy(buf
, &mb
->buf
[mb
->head
], len
);
171 if (mb
->head
== mb
->bufsize
)
177 /* Handle part after wrap */
178 len
= PR_MIN(n
, memio_buffer_used_contiguous(mb
));
180 memcpy(buf
, &mb
->buf
[mb
->head
], len
);
182 if (mb
->head
== mb
->bufsize
)
191 /*--------------- private memio functions -----------------------*/
193 static PRStatus PR_CALLBACK
memio_Close(PRFileDesc
*fd
)
195 struct PRFilePrivate
*secret
= fd
->secret
;
196 memio_buffer_destroy(&secret
->readbuf
);
197 memio_buffer_destroy(&secret
->writebuf
);
203 static PRStatus PR_CALLBACK
memio_Shutdown(PRFileDesc
*fd
, PRIntn how
)
205 /* TODO: pass shutdown status to app somehow */
209 /* If there was a network error in the past taking bytes
210 * out of the buffer, return it to the next call that
211 * tries to read from an empty buffer.
213 static int PR_CALLBACK
memio_Recv(PRFileDesc
*fd
, void *buf
, PRInt32 len
,
214 PRIntn flags
, PRIntervalTime timeout
)
216 struct PRFilePrivate
*secret
;
217 struct memio_buffer
*mb
;
221 PR_SetError(PR_NOT_IMPLEMENTED_ERROR
, 0);
226 mb
= &secret
->readbuf
;
227 PR_ASSERT(mb
->bufsize
);
228 rv
= memio_buffer_get(mb
, buf
, len
);
229 if (rv
== 0 && !secret
->eof
) {
230 secret
->read_requested
= len
;
231 /* If there is no more data in the buffer, report any pending errors
232 * that were previously observed. Note that both the readbuf and the
233 * writebuf are checked for errors, since the application may have
234 * encountered a socket error while writing that would otherwise not
235 * be reported until the application attempted to write again - which
239 PR_SetError(mb
->last_err
, 0);
240 else if (secret
->writebuf
.last_err
)
241 PR_SetError(secret
->writebuf
.last_err
, 0);
243 PR_SetError(PR_WOULD_BLOCK_ERROR
, 0);
247 secret
->read_requested
= 0;
251 static int PR_CALLBACK
memio_Read(PRFileDesc
*fd
, void *buf
, PRInt32 len
)
253 /* pull bytes from buffer */
254 return memio_Recv(fd
, buf
, len
, 0, PR_INTERVAL_NO_TIMEOUT
);
257 static int PR_CALLBACK
memio_Send(PRFileDesc
*fd
, const void *buf
, PRInt32 len
,
258 PRIntn flags
, PRIntervalTime timeout
)
260 struct PRFilePrivate
*secret
;
261 struct memio_buffer
*mb
;
265 mb
= &secret
->writebuf
;
266 PR_ASSERT(mb
->bufsize
);
268 /* Note that the read error state is not reported, because it cannot be
269 * reported until all buffered data has been read. If there is an error
270 * with the next layer, attempting to call Send again will report the
271 * error appropriately.
274 PR_SetError(mb
->last_err
, 0);
277 rv
= memio_buffer_put(mb
, buf
, len
);
279 PR_SetError(PR_WOULD_BLOCK_ERROR
, 0);
285 static int PR_CALLBACK
memio_Write(PRFileDesc
*fd
, const void *buf
, PRInt32 len
)
287 /* append bytes to buffer */
288 return memio_Send(fd
, buf
, len
, 0, PR_INTERVAL_NO_TIMEOUT
);
291 static PRStatus PR_CALLBACK
memio_GetPeerName(PRFileDesc
*fd
, PRNetAddr
*addr
)
293 /* TODO: fail if memio_SetPeerName has not been called */
294 struct PRFilePrivate
*secret
= fd
->secret
;
295 *addr
= secret
->peername
;
299 static PRStatus
memio_GetSocketOption(PRFileDesc
*fd
, PRSocketOptionData
*data
)
302 * Even in the original version for real tcp sockets,
303 * PR_SockOpt_Nonblocking is a special case that does not
304 * translate to a getsockopt() call
306 if (PR_SockOpt_Nonblocking
== data
->option
) {
307 data
->value
.non_blocking
= PR_TRUE
;
310 PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR
, 0);
314 /*--------------- private memio data -----------------------*/
317 * Implement just the bare minimum number of methods needed to make ssl happy.
319 * Oddly, PR_Recv calls ssl_Recv calls ssl_SocketIsBlocking calls
320 * PR_GetSocketOption, so we have to provide an implementation of
321 * PR_GetSocketOption that just says "I'm nonblocking".
324 static struct PRIOMethods memio_layer_methods
= {
353 memio_GetSocketOption
,
363 static PRDescIdentity memio_identity
= PR_INVALID_IO_LAYER
;
365 static PRStatus
memio_InitializeLayerName(void)
367 memio_identity
= PR_GetUniqueIdentity("memio");
371 /*--------------- public memio functions -----------------------*/
373 PRFileDesc
*memio_CreateIOLayer(int readbufsize
, int writebufsize
)
376 struct PRFilePrivate
*secret
;
377 static PRCallOnceType once
;
379 PR_CallOnce(&once
, memio_InitializeLayerName
);
381 fd
= PR_CreateIOLayerStub(memio_identity
, &memio_layer_methods
);
382 secret
= malloc(sizeof(struct PRFilePrivate
));
383 memset(secret
, 0, sizeof(*secret
));
385 memio_buffer_new(&secret
->readbuf
, readbufsize
);
386 memio_buffer_new(&secret
->writebuf
, writebufsize
);
391 void memio_SetPeerName(PRFileDesc
*fd
, const PRNetAddr
*peername
)
393 PRFileDesc
*memiofd
= PR_GetIdentitiesLayer(fd
, memio_identity
);
394 struct PRFilePrivate
*secret
= memiofd
->secret
;
395 secret
->peername
= *peername
;
398 memio_Private
*memio_GetSecret(PRFileDesc
*fd
)
400 PRFileDesc
*memiofd
= PR_GetIdentitiesLayer(fd
, memio_identity
);
401 struct PRFilePrivate
*secret
= memiofd
->secret
;
402 return (memio_Private
*)secret
;
405 int memio_GetReadRequest(memio_Private
*secret
)
407 return ((PRFilePrivate
*)secret
)->read_requested
;
410 int memio_GetReadParams(memio_Private
*secret
, char **buf
)
412 struct memio_buffer
* mb
= &((PRFilePrivate
*)secret
)->readbuf
;
413 PR_ASSERT(mb
->bufsize
);
415 *buf
= &mb
->buf
[mb
->tail
];
416 return memio_buffer_unused_contiguous(mb
);
419 int memio_GetReadableBufferSize(memio_Private
*secret
)
421 struct memio_buffer
* mb
= &((PRFilePrivate
*)secret
)->readbuf
;
422 PR_ASSERT(mb
->bufsize
);
424 return memio_buffer_used_contiguous(mb
);
427 void memio_PutReadResult(memio_Private
*secret
, int bytes_read
)
429 struct memio_buffer
* mb
= &((PRFilePrivate
*)secret
)->readbuf
;
430 PR_ASSERT(mb
->bufsize
);
432 if (bytes_read
> 0) {
433 mb
->tail
+= bytes_read
;
434 if (mb
->tail
== mb
->bufsize
)
436 } else if (bytes_read
== 0) {
437 /* Record EOF condition and report to caller when buffer runs dry */
438 ((PRFilePrivate
*)secret
)->eof
= PR_TRUE
;
439 } else /* if (bytes_read < 0) */ {
440 mb
->last_err
= bytes_read
;
444 void memio_GetWriteParams(memio_Private
*secret
,
445 const char **buf1
, unsigned int *len1
,
446 const char **buf2
, unsigned int *len2
)
448 struct memio_buffer
* mb
= &((PRFilePrivate
*)secret
)->writebuf
;
449 PR_ASSERT(mb
->bufsize
);
451 *buf1
= &mb
->buf
[mb
->head
];
452 *len1
= memio_buffer_used_contiguous(mb
);
454 *len2
= memio_buffer_wrapped_bytes(mb
);
457 void memio_PutWriteResult(memio_Private
*secret
, int bytes_written
)
459 struct memio_buffer
* mb
= &((PRFilePrivate
*)secret
)->writebuf
;
460 PR_ASSERT(mb
->bufsize
);
462 if (bytes_written
> 0) {
463 mb
->head
+= bytes_written
;
464 if (mb
->head
>= mb
->bufsize
)
465 mb
->head
-= mb
->bufsize
;
466 } else if (bytes_written
< 0) {
467 mb
->last_err
= bytes_written
;
471 /*--------------- private memio_buffer self-test -----------------*/
473 /* Even a trivial unit test is very helpful when doing circular buffers. */
474 /*#define TRIVIAL_SELF_TEST*/
475 #ifdef TRIVIAL_SELF_TEST
478 #define TEST_BUFLEN 7
480 #define CHECKEQ(a, b) { \
482 printf("%d != %d, Test failed line %d\n", a, b, __LINE__); \
489 struct memio_buffer mb
;
493 memio_buffer_new(&mb
, TEST_BUFLEN
);
495 CHECKEQ(memio_buffer_unused_contiguous(&mb
), TEST_BUFLEN
-1);
496 CHECKEQ(memio_buffer_used_contiguous(&mb
), 0);
498 CHECKEQ(memio_buffer_put(&mb
, "howdy", 5), 5);
500 CHECKEQ(memio_buffer_unused_contiguous(&mb
), TEST_BUFLEN
-1-5);
501 CHECKEQ(memio_buffer_used_contiguous(&mb
), 5);
502 CHECKEQ(memio_buffer_wrapped_bytes(&mb
), 0);
504 CHECKEQ(memio_buffer_put(&mb
, "!", 1), 1);
506 CHECKEQ(memio_buffer_unused_contiguous(&mb
), 0);
507 CHECKEQ(memio_buffer_used_contiguous(&mb
), 6);
508 CHECKEQ(memio_buffer_wrapped_bytes(&mb
), 0);
510 CHECKEQ(memio_buffer_get(&mb
, buf
, 6), 6);
511 CHECKEQ(memcmp(buf
, "howdy!", 6), 0);
513 CHECKEQ(memio_buffer_unused_contiguous(&mb
), 1);
514 CHECKEQ(memio_buffer_used_contiguous(&mb
), 0);
516 CHECKEQ(memio_buffer_put(&mb
, "01234", 5), 5);
518 CHECKEQ(memio_buffer_used_contiguous(&mb
), 1);
519 CHECKEQ(memio_buffer_wrapped_bytes(&mb
), 4);
520 CHECKEQ(memio_buffer_unused_contiguous(&mb
), TEST_BUFLEN
-1-5);
522 CHECKEQ(memio_buffer_put(&mb
, "5", 1), 1);
524 CHECKEQ(memio_buffer_unused_contiguous(&mb
), 0);
525 CHECKEQ(memio_buffer_used_contiguous(&mb
), 1);
527 /* TODO: add more cases */
529 printf("Test passed\n");