Fixed bug #895: Metaproxy fails some 2 tests on flurry. The reason
[metaproxy-moved-to-github.git] / src / pipe.cpp
1 /* $Id: pipe.cpp,v 1.10 2007-02-19 12:51:08 adam Exp $
2    Copyright (c) 2005-2007, Index Data.
3
4    See the LICENSE file for details
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 <yazpp/socket-observer.h>
40 #include <yaz/log.h>
41
42 #include "pipe.hpp"
43
44 namespace mp = metaproxy_1;
45
46 namespace metaproxy_1 {
47     class Pipe::Rep : public boost::noncopyable {
48         friend class Pipe;
49         Rep();
50         int m_fd[2];
51         int m_socket;
52         bool nonblock(int s);
53         void close(int &fd);
54     };
55 }
56
57 using namespace mp;
58
59 void Pipe::Rep::close(int &fd)
60 {
61 #ifdef WIN32
62     if (fd != -1)
63         ::closesocket(fd);
64 #else
65     if (fd != -1)
66         ::close(fd);
67 #endif
68     fd = -1;
69 }
70
71 Pipe::Rep::Rep()
72 {
73     m_fd[0] = m_fd[1] = -1;
74     m_socket = -1;
75 }
76
77 bool Pipe::Rep::nonblock(int s)
78 {
79 #ifdef WIN32
80     unsigned long tru = 1;
81     if (ioctlsocket(s, FIONBIO, &tru) < 0)
82         return false;
83 #else
84     if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
85         return false;
86 #ifndef MSG_NOSIGNAL
87     signal (SIGPIPE, SIG_IGN);
88 #endif
89 #endif
90     return true;
91 }
92
93 Pipe::Pipe(int port_to_use) : m_p(new Rep)
94 {
95 #ifdef WIN32
96     WSADATA wsaData;
97     WORD wVersionRequested = MAKEWORD(2, 0);
98     if (WSAStartup( wVersionRequested, &wsaData ))
99         throw Pipe::Error("WSAStartup failed");
100 #else
101     port_to_use = 0;  // we'll just use pipe on Unix
102 #endif
103     if (port_to_use)
104     {
105         // create server socket
106         m_p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
107         if (m_p->m_socket < 0)
108             throw Pipe::Error("could not create socket");
109 #ifndef WIN32
110         unsigned long one = 1;
111         if (setsockopt(m_p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*) 
112                        &one, sizeof(one)) < 0)
113             throw Pipe::Error("setsockopt error");
114 #endif
115         // bind server socket
116         struct sockaddr_in add;
117         add.sin_family = AF_INET;
118         add.sin_port = htons(port_to_use);
119         add.sin_addr.s_addr = INADDR_ANY;
120         struct sockaddr *addr = ( struct sockaddr *) &add;
121       
122         if (bind(m_p->m_socket, addr, sizeof(struct sockaddr_in)))
123             throw Pipe::Error("could not bind on socket");
124         
125         if (listen(m_p->m_socket, 3) < 0)
126             throw Pipe::Error("could not listen on socket");
127
128         // client socket
129         unsigned int tmpadd;
130         tmpadd = (unsigned) inet_addr("127.0.0.1");
131         if (tmpadd)
132             memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
133         else
134             throw Pipe::Error("inet_addr failed");
135             
136         m_p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
137         if (m_p->m_fd[1] < 0)
138             throw Pipe::Error("could not create socket");
139         
140         m_p->nonblock(m_p->m_fd[1]);
141
142         if (connect(m_p->m_fd[1], addr, sizeof(*addr)) < 0)
143         {
144 #ifdef WIN32
145             if (WSAGetLastError() != WSAEWOULDBLOCK)
146                 throw Pipe::Error("could not connect to socket");
147 #else
148             if (errno != EINPROGRESS)
149                 throw Pipe::Error("could not connect to socket");
150 #endif
151         }
152
153         // server accept
154         struct sockaddr caddr;
155 #ifdef WIN32
156         int caddr_len = sizeof(caddr);
157 #else
158         socklen_t caddr_len = sizeof(caddr);
159 #endif
160         m_p->m_fd[0] = accept(m_p->m_socket, &caddr, &caddr_len);
161         if (m_p->m_fd[0] < 0)
162             throw Pipe::Error("could not accept on socket");
163
164         // complete connect
165         fd_set write_set;
166         FD_ZERO(&write_set);
167         FD_SET(m_p->m_fd[1], &write_set);
168         int r = select(m_p->m_fd[1]+1, 0, &write_set, 0, 0);
169         if (r != 1)
170             throw Pipe::Error("could not complete connect");
171
172         m_p->close(m_p->m_socket);
173     }
174     else
175     {
176 #ifndef WIN32
177         if (pipe(m_p->m_fd))
178             throw Pipe::Error("pipe failed");
179         else
180         {
181             assert(m_p->m_fd[0] >= 0);
182             assert(m_p->m_fd[1] >= 0);
183         }
184 #endif
185     }
186 }
187
188 Pipe::~Pipe()
189 {
190     m_p->close(m_p->m_fd[0]);
191     m_p->close(m_p->m_fd[1]);
192     m_p->close(m_p->m_socket);
193 #ifdef WIN32
194     WSACleanup();
195 #endif
196 }
197
198 int &Pipe::read_fd() const
199 {
200     return m_p->m_fd[0];
201 }
202
203 int &Pipe::write_fd() const
204 {
205     return m_p->m_fd[1];
206 }
207
208 /*
209  * Local variables:
210  * c-basic-offset: 4
211  * indent-tabs-mode: nil
212  * c-file-style: "stroustrup"
213  * End:
214  * vim: shiftwidth=4 tabstop=8 expandtab
215  */
216