Separate imp+rep from public interfaces for some clases, Routers,
[metaproxy-moved-to-github.git] / src / pipe.cpp
1 /* $Id: pipe.cpp,v 1.5 2005-11-10 23:10:42 adam Exp $
2    Copyright (c) 2005, Index Data.
3
4 %LICENSE%
5  */
6 #include "config.hpp"
7
8 #if HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11
12 #include <errno.h>
13 #ifdef WIN32
14 #include <winsock.h>
15 #else
16 #include <netinet/in.h>
17 #include <netdb.h>
18 #include <arpa/inet.h>
19 #include <netinet/tcp.h>
20
21 #include <fcntl.h>
22 #endif
23
24 #if HAVE_SYS_SOCKET_H
25 #include <sys/socket.h>
26 #endif
27 #if HAVE_SYS_SELECT_H
28 #include <sys/select.h>
29 #endif
30
31 #include <boost/thread/thread.hpp>
32 #include <boost/thread/mutex.hpp>
33 #include <boost/thread/condition.hpp>
34
35 #include <stdio.h>
36
37 #include <deque>
38
39 #include <yaz++/socket-observer.h>
40 #include <yaz/log.h>
41
42 #include "pipe.hpp"
43
44 namespace yp2 {
45     class Pipe::Rep : public boost::noncopyable {
46         friend class Pipe;
47         Rep();
48         int m_fd[2];
49         int m_socket;
50         bool nonblock(int s);
51         void close(int &fd);
52     };
53 }
54
55 using namespace yp2;
56
57 void Pipe::Rep::close(int &fd)
58 {
59 #ifdef WIN32
60     if (fd != -1)
61         ::closesocket(fd);
62 #else
63     if (fd != -1)
64         ::close(fd);
65 #endif
66     fd = -1;
67 }
68
69 Pipe::Rep::Rep()
70 {
71     m_fd[0] = m_fd[1] = -1;
72     m_socket = -1;
73 }
74
75 bool Pipe::Rep::nonblock(int s)
76 {
77 #ifdef WIN32
78     unsigned long tru = 1;
79     if (ioctlsocket(s, FIONBIO, &tru) < 0)
80         return false;
81 #else
82     if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
83         return false;
84 #ifndef MSG_NOSIGNAL
85     signal (SIGPIPE, SIG_IGN);
86 #endif
87 #endif
88     return true;
89 }
90
91 Pipe::Pipe(int port_to_use) : m_p(new Rep)
92 {
93 #ifdef WIN32
94     WSADATA wsaData;
95     WORD wVersionRequested = MAKEWORD(2, 0);
96     if (WSAStartup( wVersionRequested, &wsaData ))
97         throw Pipe::Error("WSAStartup failed");
98 #endif
99     if (port_to_use)
100     {
101         // create server socket
102         m_p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
103         if (m_p->m_socket < 0)
104             throw Pipe::Error("could not create socket");
105 #ifndef WIN32
106         unsigned long one = 1;
107         if (setsockopt(m_p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*) 
108                        &one, sizeof(one)) < 0)
109             throw Pipe::Error("setsockopt error");
110 #endif
111         // bind server socket
112         struct sockaddr_in add;
113         add.sin_family = AF_INET;
114         add.sin_port = htons(port_to_use);
115         add.sin_addr.s_addr = INADDR_ANY;
116         struct sockaddr *addr = ( struct sockaddr *) &add;
117       
118         if (bind(m_p->m_socket, addr, sizeof(struct sockaddr_in)))
119             throw Pipe::Error("could not bind on socket");
120         
121         if (listen(m_p->m_socket, 3) < 0)
122             throw Pipe::Error("could not listen on socket");
123
124         // client socket
125         unsigned int tmpadd;
126         tmpadd = (unsigned) inet_addr("127.0.0.1");
127         if (tmpadd)
128             memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
129         else
130             throw Pipe::Error("inet_addr failed");
131             
132         m_p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
133         if (m_p->m_fd[1] < 0)
134             throw Pipe::Error("could not create socket");
135         
136         m_p->nonblock(m_p->m_fd[1]);
137
138         if (connect(m_p->m_fd[1], addr, sizeof(*addr)) < 0)
139         {
140 #ifdef WIN32
141             if (WSAGetLastError() != WSAEWOULDBLOCK)
142                 throw Pipe::Error("could not connect to socket");
143 #else
144             if (errno != EINPROGRESS)
145                 throw Pipe::Error("could not connect to socket");
146 #endif
147         }
148
149         // server accept
150         struct sockaddr caddr;
151 #ifdef WIN32
152         int caddr_len = sizeof(caddr);
153 #else
154         socklen_t caddr_len = sizeof(caddr);
155 #endif
156         m_p->m_fd[0] = accept(m_p->m_socket, &caddr, &caddr_len);
157         if (m_p->m_fd[0] < 0)
158             throw Pipe::Error("could not accept on socket");
159
160         // complete connect
161         fd_set write_set;
162         FD_ZERO(&write_set);
163         FD_SET(m_p->m_fd[1], &write_set);
164         int r = select(m_p->m_fd[1]+1, 0, &write_set, 0, 0);
165         if (r != 1)
166             throw Pipe::Error("could not complete connect");
167
168         m_p->close(m_p->m_socket);
169     }
170     else
171     {
172 #ifndef WIN32
173         pipe(m_p->m_fd);
174 #endif
175     }
176 }
177
178 Pipe::~Pipe()
179 {
180     m_p->close(m_p->m_fd[0]);
181     m_p->close(m_p->m_fd[1]);
182     m_p->close(m_p->m_socket);
183 #ifdef WIN32
184     WSACleanup();
185 #endif
186 }
187
188 int &Pipe::read_fd() const
189 {
190     return m_p->m_fd[0];
191 }
192
193 int &Pipe::write_fd() const
194 {
195     return m_p->m_fd[1];
196 }
197
198 /*
199  * Local variables:
200  * c-basic-offset: 4
201  * indent-tabs-mode: nil
202  * c-file-style: "stroustrup"
203  * End:
204  * vim: shiftwidth=4 tabstop=8 expandtab
205  */
206