factorizing HTTP specific code out of SRU2Z3950 filter into utils.hpp
[metaproxy-moved-to-github.git] / src / pipe.cpp
1 /* $Id: pipe.cpp,v 1.8 2006-06-10 14:29:12 adam Exp $
2    Copyright (c) 2005-2006, 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 #endif
101     if (port_to_use)
102     {
103         // create server socket
104         m_p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
105         if (m_p->m_socket < 0)
106             throw Pipe::Error("could not create socket");
107 #ifndef WIN32
108         unsigned long one = 1;
109         if (setsockopt(m_p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*) 
110                        &one, sizeof(one)) < 0)
111             throw Pipe::Error("setsockopt error");
112 #endif
113         // bind server socket
114         struct sockaddr_in add;
115         add.sin_family = AF_INET;
116         add.sin_port = htons(port_to_use);
117         add.sin_addr.s_addr = INADDR_ANY;
118         struct sockaddr *addr = ( struct sockaddr *) &add;
119       
120         if (bind(m_p->m_socket, addr, sizeof(struct sockaddr_in)))
121             throw Pipe::Error("could not bind on socket");
122         
123         if (listen(m_p->m_socket, 3) < 0)
124             throw Pipe::Error("could not listen on socket");
125
126         // client socket
127         unsigned int tmpadd;
128         tmpadd = (unsigned) inet_addr("127.0.0.1");
129         if (tmpadd)
130             memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
131         else
132             throw Pipe::Error("inet_addr failed");
133             
134         m_p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
135         if (m_p->m_fd[1] < 0)
136             throw Pipe::Error("could not create socket");
137         
138         m_p->nonblock(m_p->m_fd[1]);
139
140         if (connect(m_p->m_fd[1], addr, sizeof(*addr)) < 0)
141         {
142 #ifdef WIN32
143             if (WSAGetLastError() != WSAEWOULDBLOCK)
144                 throw Pipe::Error("could not connect to socket");
145 #else
146             if (errno != EINPROGRESS)
147                 throw Pipe::Error("could not connect to socket");
148 #endif
149         }
150
151         // server accept
152         struct sockaddr caddr;
153 #ifdef WIN32
154         int caddr_len = sizeof(caddr);
155 #else
156         socklen_t caddr_len = sizeof(caddr);
157 #endif
158         m_p->m_fd[0] = accept(m_p->m_socket, &caddr, &caddr_len);
159         if (m_p->m_fd[0] < 0)
160             throw Pipe::Error("could not accept on socket");
161
162         // complete connect
163         fd_set write_set;
164         FD_ZERO(&write_set);
165         FD_SET(m_p->m_fd[1], &write_set);
166         int r = select(m_p->m_fd[1]+1, 0, &write_set, 0, 0);
167         if (r != 1)
168             throw Pipe::Error("could not complete connect");
169
170         m_p->close(m_p->m_socket);
171     }
172     else
173     {
174 #ifndef WIN32
175         pipe(m_p->m_fd);
176 #endif
177     }
178 }
179
180 Pipe::~Pipe()
181 {
182     m_p->close(m_p->m_fd[0]);
183     m_p->close(m_p->m_fd[1]);
184     m_p->close(m_p->m_socket);
185 #ifdef WIN32
186     WSACleanup();
187 #endif
188 }
189
190 int &Pipe::read_fd() const
191 {
192     return m_p->m_fd[0];
193 }
194
195 int &Pipe::write_fd() const
196 {
197     return m_p->m_fd[1];
198 }
199
200 /*
201  * Local variables:
202  * c-basic-offset: 4
203  * indent-tabs-mode: nil
204  * c-file-style: "stroustrup"
205  * End:
206  * vim: shiftwidth=4 tabstop=8 expandtab
207  */
208