/*
* Copyright (c) 1998-2014 Caucho Technology -- all rights reserved
*
* @author Scott Ferguson
*/
#ifdef WIN32
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <MSWSock.h>
#include <io.h>
#else
#define _GNU_SOURCE
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <dirent.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#ifdef EPOLL
#include <sys/epoll.h>
#endif
#ifdef POLL
#include <sys/poll.h>
#else
#include <sys/select.h>
#endif
#include <pwd.h>
#include <syslog.h>
#include <netdb.h>
#ifdef HAS_SENDFILE
#include <sys/sendfile.h>
#endif
#endif
#ifdef linux
#include <linux/version.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
/* probably system-dependent */
#include <jni.h>
#include <fcntl.h>
#ifdef linux
#include <sys/uio.h>
#endif
#include "resin_os.h"
#define STACK_BUFFER_SIZE (16 * 1024)
void
cse_log(char *fmt, ...)
{
#ifdef DEBUG
va_list list;
va_start(list, fmt);
vfprintf(stderr, fmt, list);
va_end(list);
#endif
}
static char *
q_strdup(char *str)
{
size_t len = strlen(str);
char *dup = cse_malloc(len + 1);
strcpy(dup, str);
return dup;
}
JNIEXPORT jlong JNICALL
Java_com_caucho_vfs_JniSocketImpl_nativeAllocate(JNIEnv *env,
jobject obj)
{
connection_t *conn;
conn = (connection_t *) malloc(sizeof(connection_t));
memset(conn, 0, sizeof(connection_t));
conn->fd = -1;
conn->client_sin = (struct sockaddr *) conn->client_data;
conn->server_sin = (struct sockaddr *) conn->server_data;
conn->ops = &std_ops;
#ifdef WIN32
// conn->event = WSACreateEvent();
#endif
return (jlong) (PTR) conn;
}
static int
resin_get_byte_array_region(JNIEnv *env,
jbyteArray j_buf,
jint offset,
jint sublen,
char *c_buf)
{
/* JDK uses GetByteArrayRegion */
(*env)->GetByteArrayRegion(env, j_buf, offset, sublen, (void*) c_buf);
/*
jbyte *cBuf = (*env)->GetPrimitiveArrayCritical(env, j_buf, 0);
if (! cBuf)
return 0;
memcpy(c_buf, cBuf + offset, sublen);
(*env)->ReleasePrimitiveArrayCritical(env, j_buf, cBuf, 0);
*/
return 1;
}
static int
resin_tcp_nodelay(connection_t *conn)
{
int fd = conn->fd;
int flag = 1;
int result;
result = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
(char *) &flag, sizeof(int));
return result;
}
static int
resin_tcp_cork(connection_t *conn)
{
#ifdef TCP_CORK
int fd = conn->fd;
int flag = 1;
int result;
if (! conn->tcp_cork || conn->is_cork) {
return;
}
conn->is_cork = 1;
result = setsockopt(fd, IPPROTO_TCP, TCP_CORK,
(char *) &flag, sizeof(int));
return result;
#else
return 1;
#endif
}
static int
resin_tcp_uncork(connection_t *conn)
{
#ifdef TCP_CORK
int fd = conn->fd;
int flag = 0;
int result;
if (! conn->tcp_cork || ! conn->is_cork) {
return;
}
conn->is_cork = 0;
result = setsockopt(fd, IPPROTO_TCP, TCP_CORK,
(char *) &flag, sizeof(int));
return result;
#else
return 1;
#endif
}
JNIEXPORT jint JNICALL
Java_com_caucho_vfs_JniSocketImpl_readNative(JNIEnv *env,
jobject obj,
jlong conn_fd,
jbyteArray buf,
jint offset,
jint length,
jlong timeout_ms)
{
connection_t *conn = (connection_t *) (PTR) conn_fd;
int sublen;
char buffer[STACK_BUFFER_SIZE];
char *temp_buf;
if (! conn || conn->fd <= 0 || ! buf) {
return -1;
}
conn->jni_env = env;
if (length < STACK_BUFFER_SIZE)
sublen = length;
else
sublen = STACK_BUFFER_SIZE;
sublen = conn->ops->read(conn, buffer, sublen, (int) timeout_ms);
/* Should probably have a different response for EINTR */
if (sublen <= 0) {
return sublen;
}
if (length < sublen) {
sublen = length;
}
resin_set_byte_array_region(env, buf, offset, sublen, buffer);
return sublen;
}
JNIEXPORT jint JNICALL
Java_com_caucho_vfs_JniStream_readNonBlockNative(JNIEnv *env,
jobject obj,
jlong conn_fd,
jbyteArray buf,
jint offset,
jint length)
{
connection_t *conn = (connection_t *) (PTR) conn_fd;
int sublen;
char buffer[STACK_BUFFER_SIZE];
if (! conn || conn->fd <= 0 || ! buf) {
return -1;
}
conn->jni_env = env;
if (length < STACK_BUFFER_SIZE)
sublen = length;
else
sublen = STACK_BUFFER_SIZE;
sublen = conn->ops->read_nonblock(conn, buffer, sublen);
/* Should probably have a different response for EINTR */
if (sublen < 0)
return sublen;
resin_set_byte_array_region(env, buf, offset, sublen, buffer);
return sublen;
}
JNIEXPORT jint JNICALL
Java_com_caucho_vfs_JniSocketImpl_writeNative(JNIEnv *env,
jobject obj,
jlong conn_fd,
jbyteArray j_buf,
jint offset,
jint length)
{
connection_t *conn = (connection_t *) (PTR) conn_fd;
char buffer[STACK_BUFFER_SIZE];
char *c_buf;
int sublen;
int write_length = 0;
int result;
if (! conn || conn->fd <= 0 || ! j_buf) {
return -1;
}
conn->jni_env = env;
/*
resin_tcp_cork(conn);
*/
while (length > 0) {
jbyte *cBuf;
if (length < sizeof(buffer))
sublen = length;
else
sublen = sizeof(buffer);
resin_get_byte_array_region(env, j_buf, offset, sublen, buffer);
result = conn->ops->write(conn, buffer, sublen);
if (result == length) {
return result + write_length;
}
else if (result < 0) {
/*
fprintf(stdout, "write-ops: write result=%d errno=%d\n",
result, errno);
fflush(stdout);
*/
return result;
}
length -= result;
offset += result;
write_length += result;
}
return write_length;
}
#undef HAS_SPLICE
#ifdef HAS_SPLICE
static int
write_splice(connection_t *conn,
jlong mmap_address,
int sublen)
{
struct iovec io;
int result;
int fd = conn->fd;
int write_len = 0;
if (fd < 0) {
return -1;
}
if (conn->ssl_sock) {
return conn->ops->write(conn,
(void*) (PTR) (mmap_address),
sublen);
}
io.iov_base = (void*) (mmap_address);
io.iov_len = sublen;
if (conn->pipe[0] <= 0) {
if (pipe(conn->pipe) < 0) {
fprintf(stderr, "BADPIPE\n");
}
}
sublen = vmsplice(conn->pipe[1], &io, 1, SPLICE_F_MOVE);
if (sublen < 0) {
if (errno != EAGAIN && errno != ECONNRESET && errno != EPIPE) {
fprintf(stderr, "vmsplice addr:%lx result:%d %d\n",
mmap_address,
sublen, errno);
}
return -1;
}
write_len = 0;
while (write_len < sublen) {
int delta = sublen - write_len;
result = splice(conn->pipe[0], 0, fd, 0, delta,
SPLICE_F_MOVE|SPLICE_F_MORE);
if (result <= 0) {
if (errno != EAGAIN && errno != ECONNRESET && errno != EPIPE) {
fprintf(stderr, "splice result:%d pipe:%d fd:%d addr:%lx errno:%d\n",
result, conn->pipe[0], fd, mmap_address, errno);
}
return -1;
}
write_len += result;
}
return sublen;
}
#else
static int
write_splice(connection_t *conn,
long mmap_address,
int sublen)
{
return conn->ops->write(conn,
(void*) (PTR) (mmap_address),
sublen);
}
#endif
JNIEXPORT jint JNICALL
Java_com_caucho_vfs_JniSocketImpl_writeMmapNative(JNIEnv *env,
jobject obj,
jlong conn_fd,
jlong mmap_address,
jlongArray mmap_blocks_arr,