1 /*****************************************************************************
2 * http.c: HTTP access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: http.c,v 1.10.2.4 2002/10/03 22:14:58 massiot Exp $
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <sys/types.h>
34 #include <videolan/vlc.h>
38 #elif defined( _MSC_VER ) && defined( _WIN32 )
43 # include <winsock2.h>
44 # include <ws2tcpip.h>
46 # define IN_MULTICAST(a) IN_CLASSD(a)
49 # include <sys/socket.h>
52 #include "stream_control.h"
53 #include "input_ext-intf.h"
54 #include "input_ext-dec.h"
55 #include "input_ext-plugins.h"
59 /*****************************************************************************
61 *****************************************************************************/
62 static void input_getfunctions( function_list_t * );
63 static int HTTPOpen ( struct input_thread_s * );
64 static void HTTPClose ( struct input_thread_s * );
65 static int HTTPSetProgram ( struct input_thread_s * , pgrm_descriptor_t * );
66 static void HTTPSeek ( struct input_thread_s *, off_t );
68 /*****************************************************************************
69 * Build configuration tree.
70 *****************************************************************************/
75 SET_DESCRIPTION( _("HTTP access plug-in") )
76 ADD_CAPABILITY( ACCESS, 0 )
77 ADD_SHORTCUT( "http" )
78 ADD_SHORTCUT( "http4" )
79 ADD_SHORTCUT( "http6" )
83 input_getfunctions( &p_module->p_functions->access );
86 MODULE_DEACTIVATE_START
87 MODULE_DEACTIVATE_STOP
89 /*****************************************************************************
90 * Functions exported as capabilities. They are declared as static so that
91 * we don't pollute the namespace too much.
92 *****************************************************************************/
93 static void input_getfunctions( function_list_t * p_function_list )
95 #define input p_function_list->functions.access
96 input.pf_open = HTTPOpen;
97 input.pf_read = input_FDNetworkRead;
98 input.pf_close = HTTPClose;
99 input.pf_set_program = HTTPSetProgram;
100 input.pf_set_area = NULL;
101 input.pf_seek = HTTPSeek;
105 /*****************************************************************************
106 * _input_socket_t: private access plug-in data, modified to add private
108 *****************************************************************************/
109 typedef struct _input_socket_s
111 input_socket_t _socket;
114 network_socket_t socket_desc;
115 char psz_buffer[256];
119 /*****************************************************************************
120 * HTTPConnect: connect to the server and seek to i_tell
121 *****************************************************************************/
122 static int HTTPConnect( input_thread_t * p_input, off_t i_tell )
124 _input_socket_t * p_access_data = p_input->p_access_data;
125 struct module_s * p_network;
126 char psz_buffer[256];
129 char * psz_return_alpha;
131 /* Find an appropriate network module */
132 p_network = module_Need( MODULE_CAPABILITY_NETWORK,
133 p_access_data->psz_network,
134 &p_access_data->socket_desc );
135 if( p_network == NULL )
137 free( p_access_data );
140 module_Unneed( p_network );
142 p_access_data->_socket.i_handle = p_access_data->socket_desc.i_handle;
144 # define HTTP_USERAGENT "User-Agent: " COPYRIGHT_MESSAGE "\r\n"
145 # define HTTP_END "\r\n"
147 if ( p_input->stream.b_seekable )
149 snprintf( psz_buffer, sizeof(psz_buffer),
151 "Range: bytes=%lld-\r\n"
152 HTTP_USERAGENT HTTP_END,
153 p_access_data->psz_buffer, i_tell );
157 snprintf( psz_buffer, sizeof(psz_buffer),
159 HTTP_USERAGENT HTTP_END,
160 p_access_data->psz_buffer );
162 psz_buffer[sizeof(psz_buffer) - 1] = '\0';
165 if( send( p_access_data->_socket.i_handle, psz_buffer,
166 strlen( psz_buffer ), 0 ) == (-1) )
168 intf_ErrMsg( "http error: cannot send request (%s)", strerror(errno) );
169 input_FDNetworkClose( p_input );
173 /* Prepare the input thread for reading. */
174 p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
175 /* FIXME: we shouldn't have to do that ! */
176 p_input->pf_read = input_FDNetworkRead;
178 while( !input_FillBuffer( p_input ) )
180 if( p_input->b_die || p_input->b_error )
182 input_FDNetworkClose( p_input );
187 /* Parse HTTP header. */
188 #define MAX_LINE 1024
189 /* get the returncode */
190 if( input_Peek( p_input, &psz_parser, MAX_LINE ) <= 0 )
192 intf_ErrMsg( "not enough data" );
193 input_FDNetworkClose( p_input );
197 if( !strncmp( psz_parser, "HTTP/1.",
198 strlen("HTTP/1.") ) )
200 psz_parser += strlen("HTTP 1.") + 2;
201 i_returncode = atoi( psz_parser );
202 intf_WarnMsg( 3, "HTTP server replied: %i", i_returncode );
204 for ( i = 0; psz_parser[i] != '\r' || psz_parser[i+1] != '\n'; i++ )
208 psz_return_alpha = malloc( i + 1 );
209 memcpy( psz_return_alpha, psz_parser, i );
210 psz_return_alpha[i] = '\0';
214 intf_ErrMsg( "http error: invalid http reply" );
218 if ( i_returncode >= 400 ) /* something is wrong */
220 intf_ErrMsg( "http error: %i %s", i_returncode, psz_return_alpha );
226 if( input_Peek( p_input, &psz_parser, MAX_LINE ) <= 0 )
228 intf_ErrMsg( "http error: not enough data" );
229 input_FDNetworkClose( p_input );
233 if( psz_parser[0] == '\r' && psz_parser[1] == '\n' )
236 p_input->p_current_data += 2;
240 if( !strncmp( psz_parser, "Content-Length: ",
241 strlen("Content-Length: ") ) )
243 psz_parser += strlen("Content-Length: ");
244 vlc_mutex_lock( &p_input->stream.stream_lock );
246 p_input->stream.p_selected_area->i_size = atoll( psz_parser )
249 /* FIXME : this won't work for 64-bit lengths */
250 p_input->stream.p_selected_area->i_size = atoi( psz_parser )
253 vlc_mutex_unlock( &p_input->stream.stream_lock );
256 while( *psz_parser != '\r' && psz_parser < p_input->p_last_data )
260 p_input->p_current_data = psz_parser + 2;
263 if( p_input->stream.p_selected_area->i_size )
265 vlc_mutex_lock( &p_input->stream.stream_lock );
266 p_input->stream.p_selected_area->i_tell = i_tell
267 + (p_input->p_last_data - p_input->p_current_data);
268 p_input->stream.b_seekable = 1;
269 p_input->stream.b_changed = 1;
270 vlc_mutex_unlock( &p_input->stream.stream_lock );
276 /*****************************************************************************
277 * HTTPOpen: parse URL and open the remote file at the beginning
278 *****************************************************************************/
279 static int HTTPOpen( input_thread_t * p_input )
281 _input_socket_t * p_access_data;
282 char * psz_name = strdup(p_input->psz_name);
283 char * psz_parser = psz_name;
284 char * psz_server_addr = "";
285 char * psz_server_port = "";
286 char * psz_path = "";
288 int i_server_port = 0;
290 p_access_data = p_input->p_access_data = malloc( sizeof(_input_socket_t) );
291 if( p_access_data == NULL )
293 intf_ErrMsg( "http error: Out of memory" );
298 p_access_data->psz_name = psz_name;
299 p_access_data->psz_network = "";
300 if( config_GetIntVariable( "ipv4" ) )
302 p_access_data->psz_network = "ipv4";
304 if( config_GetIntVariable( "ipv6" ) )
306 p_access_data->psz_network = "ipv6";
308 if( *p_input->psz_access )
310 /* Find out which shortcut was used */
311 if( !strncmp( p_input->psz_access, "http6", 6 ) )
313 p_access_data->psz_network = "ipv6";
315 else if( !strncmp( p_input->psz_access, "http4", 6 ) )
317 p_access_data->psz_network = "ipv4";
321 /* Parse psz_name syntax :
322 * //<hostname>[:<port>][/<path>] */
323 while( *psz_parser == '/' )
327 psz_server_addr = psz_parser;
329 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
334 if ( *psz_parser == ':' )
338 psz_server_port = psz_parser;
340 while( *psz_parser && *psz_parser != '/' )
346 if( *psz_parser == '/' )
350 psz_path = psz_parser;
353 /* Convert port format */
354 if( *psz_server_port )
356 i_server_port = strtol( psz_server_port, &psz_parser, 10 );
359 intf_ErrMsg( "input error: cannot parse server port near %s",
361 free( p_input->p_access_data );
367 if( i_server_port == 0 )
372 if( !*psz_server_addr )
374 intf_ErrMsg( "input error: no server given" );
375 free( p_input->p_access_data );
381 if( (psz_proxy = getenv( "http_proxy" )) != NULL && *psz_proxy )
383 /* http://myproxy.mydomain:myport/ */
384 int i_proxy_port = 0;
386 /* Skip the protocol name */
387 while( *psz_proxy && *psz_proxy != ':' )
392 /* Skip the "://" part */
393 while( *psz_proxy && (*psz_proxy == ':' || *psz_proxy == '/') )
398 /* Found a proxy name */
401 char *psz_port = psz_proxy;
403 /* Skip the hostname part */
404 while( *psz_port && *psz_port != ':' && *psz_port != '/' )
409 /* Found a port name */
414 /* Replace ':' with '\0' */
419 while( *psz_junk && *psz_junk != '/' )
429 if( *psz_port != '\0' )
431 i_proxy_port = atoi( psz_port );
437 intf_ErrMsg( "input error: http_proxy environment variable is invalid !" );
438 free( p_input->p_access_data );
443 p_access_data->socket_desc.i_type = NETWORK_TCP;
444 p_access_data->socket_desc.psz_server_addr = psz_proxy;
445 p_access_data->socket_desc.i_server_port = i_proxy_port;
447 snprintf( p_access_data->psz_buffer, sizeof(p_access_data->psz_buffer),
448 "GET http://%s:%d/%s\r\n HTTP/1.0\r\n",
449 psz_server_addr, i_server_port, psz_path );
453 /* No proxy, direct connection. */
454 p_access_data->socket_desc.i_type = NETWORK_TCP;
455 p_access_data->socket_desc.psz_server_addr = psz_server_addr;
456 p_access_data->socket_desc.i_server_port = i_server_port;
458 snprintf( p_access_data->psz_buffer, sizeof(p_access_data->psz_buffer),
459 "GET /%s HTTP/1.1\r\nHost: %s\r\n",
460 psz_path, psz_server_addr );
462 p_access_data->psz_buffer[sizeof(p_access_data->psz_buffer) - 1] = '\0';
464 intf_WarnMsg( 2, "input: opening server=%s port=%d path=%s",
465 psz_server_addr, i_server_port, psz_path );
467 vlc_mutex_lock( &p_input->stream.stream_lock );
468 p_input->stream.b_pace_control = 1;
469 p_input->stream.b_seekable = 1;
470 p_input->stream.p_selected_area->i_tell = 0;
471 p_input->stream.p_selected_area->i_size = 0;
472 p_input->stream.i_method = INPUT_METHOD_NETWORK;
473 vlc_mutex_unlock( &p_input->stream.stream_lock );
476 if( HTTPConnect( p_input, 0 ) )
478 char * psz_pos = strstr(p_access_data->psz_buffer, "HTTP/1.1");
479 p_input->stream.b_seekable = 0;
481 return( HTTPConnect( p_input, 0 ) );
486 /*****************************************************************************
487 * HTTPClose: free unused data structures
488 *****************************************************************************/
489 static void HTTPClose( input_thread_t * p_input )
491 input_FDNetworkClose( p_input );
494 /*****************************************************************************
495 * HTTPSetProgram: do nothing
496 *****************************************************************************/
497 static int HTTPSetProgram( input_thread_t * p_input,
498 pgrm_descriptor_t * p_program )
503 /*****************************************************************************
504 * HTTPSeek: close and re-open a connection at the right place
505 *****************************************************************************/
506 static void HTTPSeek( input_thread_t * p_input, off_t i_pos )
508 _input_socket_t * p_access_data = p_input->p_access_data;
509 close( p_access_data->_socket.i_handle );
510 intf_WarnMsg( 2, "http: seeking to position %lld", i_pos );
511 HTTPConnect( p_input, i_pos );