1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5 #include <OpenImageIO/imageio.h>
6
7 #include "socket_pvt.h"
8
9
10 OIIO_PLUGIN_NAMESPACE_BEGIN
11
12 // Export version number and create function symbols
13 OIIO_PLUGIN_EXPORTS_BEGIN
14
15 OIIO_EXPORT int socket_imageio_version = OIIO_PLUGIN_VERSION;
16
17 OIIO_EXPORT const char*
socket_imageio_library_version()18 socket_imageio_library_version()
19 {
20 return nullptr;
21 }
22
23 OIIO_EXPORT ImageInput*
socket_input_imageio_create()24 socket_input_imageio_create()
25 {
26 return new SocketInput;
27 }
28
29 OIIO_EXPORT const char* socket_input_extensions[] = { "socket", nullptr };
30
31 OIIO_PLUGIN_EXPORTS_END
32
33
34
SocketInput()35 SocketInput::SocketInput()
36 : socket(io)
37 {
38 }
39
40
41
42 bool
valid_file(const std::string & filename) const43 SocketInput::valid_file(const std::string& filename) const
44 {
45 // Pass it a configuration request that includes a "nowait" option
46 // so that it returns immediately rather than waiting for a socket
47 // connection that doesn't yet exist.
48 ImageSpec config;
49 config.attribute("nowait", (int)1);
50
51 ImageSpec tmpspec;
52 bool ok = const_cast<SocketInput*>(this)->open(filename, tmpspec, config);
53 if (ok)
54 const_cast<SocketInput*>(this)->close();
55 return ok;
56 }
57
58
59
60 bool
open(const std::string & name,ImageSpec & newspec)61 SocketInput::open(const std::string& name, ImageSpec& newspec)
62 {
63 return open(name, newspec, ImageSpec());
64 }
65
66
67
68 bool
open(const std::string & name,ImageSpec & newspec,const ImageSpec & config)69 SocketInput::open(const std::string& name, ImageSpec& newspec,
70 const ImageSpec& config)
71 {
72 // If there is a nonzero "nowait" request in the configuration, just
73 // return immediately.
74 if (config.get_int_attribute("nowait", 0)) {
75 return false;
76 }
77
78 if (!(accept_connection(name) && get_spec_from_client(newspec))) {
79 return false;
80 }
81 // Also send information about endianess etc.
82
83 m_spec = newspec;
84
85 return true;
86 }
87
88
89
90 bool
read_native_scanline(int subimage,int miplevel,int,int,void * data)91 SocketInput::read_native_scanline(int subimage, int miplevel, int /*y*/,
92 int /*z*/, void* data)
93 {
94 lock_guard lock(m_mutex);
95 if (!seek_subimage(subimage, miplevel))
96 return false;
97 try {
98 boost::asio::read(socket, buffer(reinterpret_cast<char*>(data),
99 m_spec.scanline_bytes()));
100 } catch (boost::system::system_error& err) {
101 errorf("Error while reading: %s", err.what());
102 return false;
103 } catch (...) {
104 errorf("Error while reading: unknown exception");
105 return false;
106 }
107
108 return true;
109 }
110
111
112
113 bool
read_native_tile(int subimage,int miplevel,int,int,int,void * data)114 SocketInput::read_native_tile(int subimage, int miplevel, int /*x*/, int /*y*/,
115 int /*z*/, void* data)
116 {
117 lock_guard lock(m_mutex);
118 if (!seek_subimage(subimage, miplevel))
119 return false;
120 try {
121 boost::asio::read(socket, buffer(reinterpret_cast<char*>(data),
122 m_spec.tile_bytes()));
123 } catch (boost::system::system_error& err) {
124 errorf("Error while reading: %s", err.what());
125 return false;
126 } catch (...) {
127 errorf("Error while reading: unknown exception");
128 return false;
129 }
130
131 return true;
132 }
133
134
135
136 bool
close()137 SocketInput::close()
138 {
139 socket.close();
140 return true;
141 }
142
143
144
145 bool
accept_connection(const std::string & name)146 SocketInput::accept_connection(const std::string& name)
147 {
148 std::map<std::string, std::string> rest_args;
149 std::string baseurl;
150 rest_args["port"] = socket_pvt::default_port;
151 rest_args["host"] = socket_pvt::default_host;
152
153 if (!Strutil::get_rest_arguments(name, baseurl, rest_args)) {
154 errorf("Invalid 'open ()' argument: %s", name);
155 return false;
156 }
157
158 int port = Strutil::stoi(rest_args["port"]);
159
160 try {
161 acceptor = std::shared_ptr<ip::tcp::acceptor>(
162 new ip::tcp::acceptor(io, ip::tcp::endpoint(ip::tcp::v4(), port)));
163 acceptor->accept(socket);
164 } catch (boost::system::system_error& err) {
165 errorf("Error while accepting: %s", err.what());
166 return false;
167 } catch (...) {
168 errorf("Error while accepting: unknown exception");
169 return false;
170 }
171
172 return true;
173 }
174
175
176
177 bool
get_spec_from_client(ImageSpec & spec)178 SocketInput::get_spec_from_client(ImageSpec& spec)
179 {
180 try {
181 int spec_length;
182
183 boost::asio::read(socket, buffer(reinterpret_cast<char*>(&spec_length),
184 sizeof(boost::uint32_t)));
185
186 char* spec_xml = new char[spec_length + 1];
187 boost::asio::read(socket, buffer(spec_xml, spec_length));
188
189 spec.from_xml(spec_xml);
190 delete[] spec_xml;
191 } catch (boost::system::system_error& err) {
192 errorf("Error while get_spec_from_client: %s", err.what());
193 return false;
194 } catch (...) {
195 errorf("Error while get_spec_from_client: unknown exception");
196 return false;
197 }
198
199 return true;
200 }
201
202 OIIO_PLUGIN_NAMESPACE_END
203