示例#1
0
//parse all fields in the header
GF_Err RTSP_ParseResponseHeader(GF_RTSPSession *sess, GF_RTSPResponse *rsp, u32 BodyStart)
{
	char LineBuffer[1024];
	char ValBuf[400];
	char *buffer;
	s32 Pos, ret;
	u32 Size;

	Size = sess->CurrentSize - sess->CurrentPos;
	buffer = sess->TCPBuffer + sess->CurrentPos;

	//parse first line
	ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
	if (ret < 0)
		return GF_REMOTE_SERVICE_ERROR;
	//RTSP/1.0
	Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 400);
	if (Pos <= 0)
		return GF_REMOTE_SERVICE_ERROR;
	if (strcmp(ValBuf, GF_RTSP_VERSION))
		return GF_SERVICE_ERROR;
	//CODE
	Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 400);
	if (Pos <= 0)
		return GF_REMOTE_SERVICE_ERROR;
	rsp->ResponseCode = atoi(ValBuf);
	//string info
	Pos = gf_token_get(LineBuffer, Pos, "\t\r\n", ValBuf, 400);
	if (Pos > 0) rsp->ResponseInfo = gf_strdup(ValBuf);

	return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, NULL, rsp);
}
示例#2
0
GF_Err RTSP_ParseCommandHeader(GF_RTSPSession *sess, GF_RTSPCommand *com, u32 BodyStart)
{
	char LineBuffer[1024];
	char ValBuf[1024];
	char *buffer;
	s32 Pos, ret;
	u32 Size;

	Size = sess->CurrentSize - sess->CurrentPos;
	buffer = sess->TCPBuffer + sess->CurrentPos;

	//by default the command is wrong ;)
	com->StatusCode = NC_RTSP_Bad_Request;

	//parse first line
	ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
	if (ret < 0) return GF_REMOTE_SERVICE_ERROR;

	//method
	Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024);
	if (Pos <= 0) return GF_OK;
	com->method = gf_strdup((const char *) ValBuf);

	//URL
	Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
	if (Pos <= 0) return GF_OK;
	com->service_name = gf_strdup(ValBuf);
	
	//RTSP version
	Pos = gf_token_get(LineBuffer, Pos, "\t\r\n", ValBuf, 1024);
	if (Pos <= 0) return GF_OK;
	if (strcmp(ValBuf, GF_RTSP_VERSION)) {
		com->StatusCode = NC_RTSP_RTSP_Version_Not_Supported;
		return GF_OK;
	}

	com->StatusCode = NC_RTSP_OK;
	
	return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, com, NULL);
}
示例#3
0
GF_Err gf_rtsp_parse_header(char *buffer, u32 BufferSize, u32 BodyStart, GF_RTSPCommand *com, GF_RTSPResponse *rsp)
{
	char LineBuffer[1024];
	char HeaderBuf[100], ValBuf[1024], temp[400];
	s32 Pos, LinePos;
	u32 HeaderLine;

	//then parse the full header
	LinePos = 0;
	strcpy(HeaderBuf, "");
	while (1) {
		HeaderLine = 0;
		LinePos = gf_token_get_line(buffer, LinePos, BufferSize, LineBuffer, 1024);
		if (LinePos <= 0) return GF_REMOTE_SERVICE_ERROR;
		
		//extract field header and value. Warning: some params (transport, ..) may be on several lines 
		Pos = gf_token_get(LineBuffer, 0, ":\r\n", temp, 400);
		
		//end of header
		if (Pos <= 0) {
			HeaderLine = 2;
		}
		//this is a header
		else if (LineBuffer[0] != ' ') {
			HeaderLine = 1;
		} else {
			Pos = gf_token_get(LineBuffer, 0, ", \r\n", temp, 400);
			//end of header - process any pending one
			if (Pos <= 0) {
				HeaderLine = 2;
			} else {
				//n-line value - append
				strcat(ValBuf, "\r\n");
				strcat(ValBuf, temp);
				continue;
			}
		}
		//process current value
		if (HeaderLine && strlen(HeaderBuf)) {
			if (rsp) {
				gf_rtsp_set_response_value(rsp, HeaderBuf, ValBuf);
			}
			else {
				gf_rtsp_set_command_value(com, HeaderBuf, ValBuf);
			}
		}
		//done with the header
		if ( (HeaderLine == 2) || ((u32) LinePos >= BodyStart) ) return GF_OK;

		//process current line
		strcpy(HeaderBuf, temp);

		//skip ':'
		Pos += 1;
		//a server should normally reply with a space, but check it
		if (LineBuffer[Pos] == ' ') Pos += 1;
		/*!! empty value !! - DSS may send these for CSeq if something goes wrong*/
		if (!strcmp(LineBuffer+Pos, "\r\n")) {
			HeaderBuf[0] = 0;
			continue;
		}
		Pos = gf_token_get(LineBuffer, Pos, "\r\n", ValBuf, 400);
		if (Pos <= 0) break;

	}
	//if we get here we haven't reached the BodyStart
	return GF_REMOTE_SERVICE_ERROR;
}
示例#4
0
文件: sdp.c 项目: golgol7777/gpac
GF_EXPORT
GF_Err gf_sdp_info_parse(GF_SDPInfo *sdp, char *sdp_text, u32 text_size)
{
	GF_SDPBandwidth *bw;
	GF_SDPConnection *conn;
	GF_SDPMedia *media;
	GF_SDPTiming *timing;
	u32 i;
	s32 pos, LinePos;
	char LineBuf[3000], comp[3000];

	media = NULL;
	timing = NULL;

	if (!sdp) return GF_BAD_PARAM;

	//Clean SDP info
	gf_sdp_info_reset(sdp);

	LinePos = 0;
	while (1) {
		LinePos = gf_token_get_line(sdp_text, LinePos, text_size, LineBuf, 3000);
		if (LinePos <= 0) break;
		if (!strcmp(LineBuf, "\r\n") || !strcmp(LineBuf, "\n") || !strcmp(LineBuf, "\r")) continue;


		switch (LineBuf[0]) {
		case 'v':
			pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
			sdp->Version = atoi(comp);
			break;
		case 'o':
			pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
			sdp->o_username = gf_strdup(comp);
			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			sdp->o_session_id = gf_strdup(comp);
			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			sdp->o_version = gf_strdup(comp);
			
			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			sdp->o_net_type = gf_strdup(comp);

			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			sdp->o_add_type = gf_strdup(comp);

			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			sdp->o_address = gf_strdup(comp);
			break;
		case 's':
			pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
			sdp->s_session_name = gf_strdup(comp);
			break;
		case 'i':
			pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
			sdp->i_description = gf_strdup(comp);
			break;
		case 'u':
			pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
			sdp->u_uri = gf_strdup(comp);
			break;
		case 'e':
			pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
			sdp->e_email = gf_strdup(comp);
			break;
		case 'p':
			pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
			sdp->p_phone = gf_strdup(comp);
			break;
		case 'c':
			//if at session level, only 1 is allowed for all SDP
			if (sdp->c_connection) break;
			
			conn = gf_sdp_conn_new();

			pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
			conn->net_type = gf_strdup(comp);
			
			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			conn->add_type = gf_strdup(comp);

			pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000);
			conn->host = gf_strdup(comp);
			if (gf_sk_is_multicast_address(conn->host)) {
				//a valid SDP will have TTL if address is multicast
				pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000);
				if (pos > 0) {
					conn->TTL = atoi(comp);
					//multiple address indication is only valid for media
					pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000);
				}
				if (pos > 0) {
					if (!media) {
						gf_sdp_conn_del(conn);
						break;
					}
					conn->add_count = atoi(comp);
				}
			}
			if (!media) 
				sdp->c_connection = conn;
			else 
				gf_list_add(media->Connections, conn);

			break;
		case 'b':
			pos = gf_token_get(LineBuf, 2, ":\r\n", comp, 3000);
			if (strcmp(comp, "CT") && strcmp(comp, "AS") && (comp[0] != 'X')) break;

			bw = (GF_SDPBandwidth*)gf_malloc(sizeof(GF_SDPBandwidth));
			bw->name = gf_strdup(comp);
			pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000);
			bw->value = atoi(comp);
			if (media) {
				gf_list_add(media->Bandwidths, bw);
			} else {
				gf_list_add(sdp->b_bandwidth, bw);
			}
			break;

		case 't':
			if (media) break;
			//create a new time structure for each entry
			GF_SAFEALLOC(timing, GF_SDPTiming);
			pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
			timing->StartTime = atoi(comp);
			pos = gf_token_get(LineBuf, pos, "\r\n", comp, 3000);
			timing->StopTime = atoi(comp);
			gf_list_add(sdp->Timing, timing);
			break;
		case 'r':
			if (media) break;
			pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
			timing->RepeatInterval = SDP_MakeSeconds(comp);
			pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
			timing->ActiveDuration = SDP_MakeSeconds(comp);
			while (1) {
				pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
				if (pos <= 0) break;
				timing->OffsetFromStart[timing->NbRepeatOffsets] = SDP_MakeSeconds(comp);
				timing->NbRepeatOffsets += 1;
			}
			break;
		case 'z':
			if (media) break;
			pos = 2;
			while (1) {
				pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
				if (pos <= 0) break;
				timing->AdjustmentTime[timing->NbZoneOffsets] = atoi(comp);
				pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
				timing->AdjustmentOffset[timing->NbZoneOffsets] = SDP_MakeSeconds(comp);
				timing->NbZoneOffsets += 1;
			}
			break;
		case 'k':
			pos = gf_token_get(LineBuf, 2, ":\t\r\n", comp, 3000);
			if (media) {
				media->k_method = gf_strdup(comp);
			} else {
				sdp->k_method = gf_strdup(comp);
			}
			pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000);
			if (pos > 0) {
				if (media) {
					media->k_key = gf_strdup(comp);
				} else {
					sdp->k_key = gf_strdup(comp);
				}
			}
			break;
		case 'a':
			SDP_ParseAttribute(sdp, LineBuf+2, media);
			break;
		case 'm':
			pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
			if (strcmp(comp, "audio") 
				&& strcmp(comp, "data") 
				&& strcmp(comp, "control") 
				&& strcmp(comp, "video") 
				&& strcmp(comp, "text") 
				&& strcmp(comp, "application")) {
				return GF_SERVICE_ERROR;
			}
			media = gf_sdp_media_new();
			//media type
			if (!strcmp(comp, "video")) media->Type = 1;
			else if (!strcmp(comp, "audio")) media->Type = 2;
			else if (!strcmp(comp, "text")) media->Type = 3;
			else if (!strcmp(comp, "data")) media->Type = 4;
			else if (!strcmp(comp, "control")) media->Type = 5;
			else media->Type = 0;
			//port numbers			
			gf_token_get(LineBuf, pos, " ", comp, 3000);
			if (!strstr(comp, "/")) {
				pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
				media->PortNumber = atoi(comp);
				media->NumPorts = 0;
			} else {
				pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000);
				media->PortNumber = atoi(comp);
				pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
				media->NumPorts = atoi(comp);
			}
			//transport Profile
			pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
			media->Profile = gf_strdup(comp);
			pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
			media->fmt_list = gf_strdup(comp);

			gf_list_add(sdp->media_desc, media);
			break;
		}
	}
	//finally rewrite the fmt_list for all media, and remove dynamic payloads 
	//from the list
	i=0;
	while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) {
		pos = 0;
		LinePos = 1;
		strcpy(LineBuf, "");
		while (1) {
			if (!media->fmt_list) break;
			pos = gf_token_get(media->fmt_list, pos, " ", comp, 3000);
			if (pos <= 0) break;
			if (!SDP_IsDynamicPayload(media, comp)) {
				if (!LinePos) {
					strcat(LineBuf, " ");
				} else {
					LinePos = 0;
				}
				strcat(LineBuf, comp);
			}
			gf_free(media->fmt_list);
			media->fmt_list = NULL;
			if (strlen(LineBuf)) {
				media->fmt_list = gf_strdup(LineBuf);
			}
		}
	}
	return GF_OK;
}
示例#5
0
void http_do_requests(GF_DownloadSession *sess)
{
	GF_Err e;
	Bool is_ice;
	GF_NETIO_Parameter par;
	char sHTTP[GF_DOWNLOAD_BUFFER_SIZE];
	char buf[1024];
	char comp[400];
	char *new_location;
	char *hdr, *hdr_val;
	u32 bytesRead, res;
	s32 LinePos, Pos;
	u32 rsp_code, ContentLength, first_byte, last_byte, total_size, range, no_range;
	s32 BodyStart;

	/*sent HTTP request*/
	if (sess->status==GF_NETIO_CONNECTED) {
		char range_buf[1024];
		char pass_buf[1024];
		const char *user_agent;
		u32 size;
		Bool has_accept, has_connection, has_range, has_agent;

		/*setup authentification*/
		strcpy(pass_buf, "");
		if (sess->user) {
			if (!sess->passwd) {
				char szUSR[50], szPASS[50];
				strcpy(szUSR, sess->user);
				strcpy(szPASS, "");
				/*failed getting pass*/
				if (!sess->dm->GetUserPassword || !sess->dm->GetUserPassword(sess->dm->usr_cbk, sess->server_name, szUSR, szPASS)) {
					sess->status = GF_NETIO_STATE_ERROR;
					return;
				}
				sess->passwd = strdup(szPASS);
			}
			sprintf(pass_buf, "%s:%s", sess->user, sess->passwd);
			size = gf_base64_encode(pass_buf, strlen(pass_buf), range_buf, 1024);
			range_buf[size] = 0;
			sprintf(pass_buf, "Authorization: Basic %s", range_buf);
		}


		/*MIX2005 KMS project*/
#if 0
		if (strstr(sess->remote_path, "getKey.php?")) {
			char *sLogin, *sPass;
			sLogin = gf_cfg_get_key(sess->dm->cfg, "General", "KMS_User");
			sPass = gf_cfg_get_key(sess->dm->cfg, "General", "KMS_Password");
			if (!sLogin) sLogin = "******";
			if (!sPass) sPass = "******";
			sprintf(https_get_buffer, "%s&login=%s&password=%s", sess->remote_path, sLogin, sPass);
		}
#endif	

		user_agent = gf_cfg_get_key(sess->dm->cfg, "Downloader", "UserAgent");
		if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME;

		par.error = 0;
		par.msg_type = GF_NETIO_GET_METHOD;
		par.name = NULL;
		gf_dm_sess_user_io(sess, &par);

		if (par.name) {
			if (!strcmp(par.name, "GET")) sess->http_read_type = 0;
			else if (!strcmp(par.name, "HEAD")) sess->http_read_type = 1;
			else sess->http_read_type = 2;
		} else {
			sess->http_read_type = 0;
		}

		sprintf(sHTTP, "%s %s HTTP/1.0\r\nHost: %s\r\n" ,
			par.name ? par.name : "GET", sess->remote_path, sess->server_name);

		/*signal we support title streaming*/
		if (!strcmp(sess->remote_path, "/")) strcat(sHTTP, "icy-metadata:1\r\n");

		/*get all headers*/
		has_agent = has_accept = has_connection = has_range = 0;
		while (1) {
			par.msg_type = GF_NETIO_GET_HEADER;
			par.name = NULL;
			par.value = NULL;
			gf_dm_sess_user_io(sess, &par);
			if (!par.name) break;
			strcat(sHTTP, par.name);
			strcat(sHTTP, ": ");
			strcat(sHTTP, par.value);
			strcat(sHTTP, "\r\n");
			if (!strcmp(par.name, "Accept")) has_accept = 1;
			else if (!strcmp(par.name, "Connection")) has_connection = 1;
			else if (!strcmp(par.name, "Range")) has_range = 1;
			else if (!strcmp(par.name, "User-Agent")) has_agent = 1;
		}
		if (!has_agent) {
			strcat(sHTTP, "User-Agent: ");
			strcat(sHTTP, user_agent);
			strcat(sHTTP, "\r\n");
		}
		if (!has_accept) strcat(sHTTP, "Accept: */*\r\n");
		if (!has_connection) strcat(sHTTP, "Connection: Keep-Alive\r\n");
		if (!has_range && sess->cache_start_size) {
			sprintf(range_buf, "Range: bytes=%d-\r\n", sess->cache_start_size);
			strcat(sHTTP, range_buf);
		}
		if (strlen(pass_buf)) {
			strcat(sHTTP, pass_buf);
			strcat(sHTTP, "\r\n");
		}
		if (sess->flags & GF_DOWNLOAD_IS_ICY) strcat(sHTTP, "Icy-Metadata: 1\r\n");
		
		par.msg_type = GF_NETIO_GET_CONTENT;
		par.data = NULL;
		par.size = 0;
		gf_dm_sess_user_io(sess, &par);
		if (par.data && par.size) {
			sprintf(range_buf, "Content-Length: %d\r\n", par.size);
			strcat(sHTTP, range_buf);
		}
		strcat(sHTTP, "\r\n");

#ifdef GPAC_HAS_SSL
		if (sess->ssl) {
			e = GF_IP_NETWORK_FAILURE;
			if (!SSL_write(sess->ssl, sHTTP, strlen(sHTTP))) e = GF_OK;
		} else 
#endif
			e = gf_sk_send(sess->sock, sHTTP, strlen(sHTTP));

		GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", sHTTP));
		if (e) {
			sess->status = GF_NETIO_STATE_ERROR;
			sess->last_error = e;
			gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e);
			return;
		}

		if (par.size && par.data) {
			u32 done = 0;
			while (done<par.size) {
			
#ifdef GPAC_HAS_SSL
				if (sess->ssl) {
					e = GF_IP_NETWORK_FAILURE;
					if (!SSL_write(sess->ssl, par.data+done, par.size-done)) e = GF_OK;
				} else 
#endif
					e = gf_sk_send(sess->sock, par.data+done, par.size-done);

				if (e) {
					sess->status = GF_NETIO_STATE_ERROR;
					sess->last_error = e;
					gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e);
					return;
				}
			}
		}
		sess->status = GF_NETIO_WAIT_FOR_REPLY;
		gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
		return;
	}

	/*process HTTP request*/
	if (sess->status == GF_NETIO_WAIT_FOR_REPLY) {
		bytesRead = res = 0;
		new_location = NULL;
		while (1) {
			e = gf_dm_read_data(sess, sHTTP + bytesRead, GF_DOWNLOAD_BUFFER_SIZE - bytesRead, &res);
	
			switch (e) {
			case GF_IP_NETWORK_EMPTY:
				if (!bytesRead) return;
				continue;
			/*socket has been closed while configuring, retry (not sure if the server got the GET)*/
			case GF_IP_CONNECTION_CLOSED:
				gf_dm_disconnect(sess);
				if (sess->num_retry)
					sess->status = GF_NETIO_SETUP;
				else {
					sess->last_error = e;
					sess->status = GF_NETIO_STATE_ERROR;
				}
				return;
			case GF_OK:
				if (!res) return;
				break;
			default:
				goto exit;
			}
			bytesRead += res;

			/*locate body start*/
			BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\r\n\r\n");
			if (BodyStart <= 0) {
				BodyStart=0;
				continue;
			}
			BodyStart += 4;
			break;
		}
		if (bytesRead < 0) {
			e = GF_REMOTE_SERVICE_ERROR;
			goto exit;
		}
		if (!BodyStart) 
			BodyStart = bytesRead;

		sHTTP[BodyStart-1] = 0;
		GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s\n\n", sHTTP));

		LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024);
		Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400);

		if (sess->mime_type) free(sess->mime_type);
		sess->mime_type = NULL;

		is_ice = 0;
		if (!strncmp("ICY", comp, 4)) {
			is_ice = 1;
			/*be prepared not to recieve any mime type from ShoutCast servers*/
			sess->mime_type = strdup("audio/mpeg");
		} else if ((strncmp("HTTP", comp, 4) != 0)) {
			e = GF_REMOTE_SERVICE_ERROR;
			goto exit;
		}
		Pos = gf_token_get(buf, Pos, " ", comp, 400);
		if (Pos <= 0) {
			e = GF_REMOTE_SERVICE_ERROR;
			goto exit;
		}
		rsp_code = (u32) atoi(comp);
		Pos = gf_token_get(buf, Pos, " \r\n", comp, 400);

		no_range = range = ContentLength = first_byte = last_byte = total_size = 0;
		//parse header
		while (1) {
			char *sep, *hdr_sep;
			if ( (u32) LinePos + 4 > BodyStart) break;
			LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024);
			if (LinePos < 0) break;

			hdr_sep = NULL;
			hdr_val = NULL;
			hdr = buf;
			sep = strchr(buf, ':');
			if (sep) {
				sep[0]=0;
				hdr_val = sep+1;
				while (hdr_val[0]==' ') hdr_val++;
				hdr_sep = strrchr(hdr_val, '\r');
				if (hdr_sep) hdr_sep[0] = 0;
			}

			par.error = 0;
			par.msg_type = GF_NETIO_PARSE_HEADER;
			par.name = hdr;
			par.value = hdr_val;
			gf_dm_sess_user_io(sess, &par);

			if (!stricmp(hdr, "Content-Length") ) ContentLength = (u32) atoi(hdr_val);
			else if (!stricmp(hdr, "Content-Type")) {			
				if (sess->mime_type) free(sess->mime_type);
				sess->mime_type = strdup(hdr_val);
				while (1) {
					u32 len = strlen(sess->mime_type);
					char c = len ? sess->mime_type[len-1] : 0;
					if ((c=='\r') || (c=='\n')) {
						sess->mime_type[len-1] = 0;
					} else {
						break;
					}
				}
				hdr = strchr(sess->mime_type, ';');
				if (hdr) hdr[0] = 0;
			}
			else if (!stricmp(hdr, "Content-Range")) {			
				range = 1;
				if (!strncmp(hdr_val, "bytes", 5)) {
					hdr_val += 5;
					if (hdr_val[0] == ':') hdr_val += 1;
					hdr_val += http_skip_space(hdr_val);
					if (hdr_val[0] == '*') {
						sscanf(hdr_val, "*/%d", &total_size);
					} else {
						sscanf(hdr_val, "%d-%d/%d", &first_byte, &last_byte, &total_size);
					}
				}
			}
			else if (!stricmp(hdr, "Accept-Ranges")) {
				if (strstr(hdr_val, "none")) no_range = 1;
			}
			else if (!stricmp(hdr, "Location")) 
				new_location = strdup(hdr_val);
			else if (!stricmp(hdr, "icy-metaint")) 
				sess->icy_metaint = atoi(hdr_val);
			else if (!stricmp(hdr, "ice") || !stricmp(hdr, "icy") ) 
				is_ice = 1;

			if (sep) sep[0]=':';
			if (hdr_sep) hdr_sep[0] = '\r';
		}
		if (no_range) first_byte = 0;

		if (sess->cache_start_size) {
			if (total_size && (sess->cache_start_size >= total_size) ) {
				rsp_code = 200;
				ContentLength = total_size;
			}
			if (ContentLength && (sess->cache_start_size == ContentLength) ) rsp_code = 200;
		}	

		par.msg_type = GF_NETIO_PARSE_REPLY;
		par.error = GF_OK;
		par.reply = rsp_code;
		par.value = comp;

		switch (rsp_code) {
		case 200:
		case 201:
		case 202:
		case 206:
			gf_dm_sess_user_io(sess, &par);
			e = GF_OK;
			break;
		/*redirection: extract the new location*/
		case 301:
		case 302:
			if (!new_location || !strlen(new_location) ) {
				gf_dm_sess_user_io(sess, &par);
				e = GF_URL_ERROR;
				goto exit;
			}
			while (
				(new_location[strlen(new_location)-1] == '\n') 
				|| (new_location[strlen(new_location)-1] == '\r')  )
				new_location[strlen(new_location)-1] = 0;

			/*reset and reconnect*/
			gf_dm_disconnect(sess);
			sess->status = GF_NETIO_SETUP;
			e = gf_dm_setup_from_url(sess, new_location);
			if (e) {
				sess->status = GF_NETIO_STATE_ERROR;
				sess->last_error = e;
				gf_dm_sess_notify_state(sess, sess->status, e);
				return;
			}
			return;
		case 404:
		case 416:
			/*try without cache (some servers screw up when content-length is specified)*/
			if (sess->cache_start_size) {
				gf_dm_disconnect(sess);
				sess->status = GF_NETIO_SETUP;
				return;
			} else if (is_ice && !(sess->flags & GF_DOWNLOAD_IS_ICY)) {
				gf_dm_disconnect(sess);
				sess->status = GF_NETIO_SETUP;
				sess->flags |= GF_DOWNLOAD_IS_ICY;
				return;
			}
			gf_dm_sess_user_io(sess, &par);
			e = GF_URL_ERROR;
			goto exit;
		case 503:
		default:
			gf_dm_sess_user_io(sess, &par);
			e = GF_REMOTE_SERVICE_ERROR;
			goto exit;
		}

		/*head*/
		if (sess->http_read_type==1) {
			gf_dm_disconnect(sess);
			gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
			sess->status = GF_NETIO_DISCONNECTED;
			sess->http_read_type = 0;
			return;
		}

		if (!ContentLength && sess->mime_type && strstr(sess->mime_type, "ogg")) is_ice = 1;

		/*some servers may reply without content length, but we MUST have it*/
//		if (!is_ice && !ContentLength) e = GF_REMOTE_SERVICE_ERROR;
		if (e) goto exit;

		/*force disabling cache (no content length)*/
		if (is_ice) {
			sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
			if (sess->mime_type && !stricmp(sess->mime_type, "video/nsv")) {
				free(sess->mime_type);
				sess->mime_type = strdup("audio/aac");
			}
		}


		/*done*/
		if (sess->cache_start_size 
			&& ( (total_size && sess->cache_start_size >= total_size) || (sess->cache_start_size == ContentLength)) ) {
			sess->total_size = sess->bytes_done = sess->cache_start_size;
			/*disconnect*/
			gf_dm_disconnect(sess);
			BodyStart = bytesRead;
			gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
		}
		else if (sess->flags & GF_DOWNLOAD_IS_ICY) {
			sess->icy_bytes = 0;
			sess->status = GF_NETIO_DATA_EXCHANGE;
		}
		/*we don't expect anything*/
		else if (!ContentLength && sess->http_read_type) {
			gf_dm_disconnect(sess);
			gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
			sess->status = GF_NETIO_DISCONNECTED;
			sess->http_read_type = 0;
		}
		/*no range header, Accep-Ranges deny or dumb server : restart*/
		else if (!range || !first_byte || (first_byte != sess->cache_start_size) ) {
			sess->cache_start_size = sess->bytes_done = 0;
			sess->total_size = ContentLength;
			if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ) {
				sess->cache = fopen(sess->cache_name, "wb");
				if (!sess->cache) {
					e = GF_IO_ERR;
					goto exit;
				}
			}
			sess->status = GF_NETIO_DATA_EXCHANGE;
		}
		/*resume*/
		else {
			sess->total_size = ContentLength + sess->cache_start_size;
			if (! (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ) {
				sess->cache = fopen(sess->cache_name, "ab");
				if (!sess->cache) {
					e = GF_IO_ERR;
					goto exit;
				}
			}
			sess->status = GF_NETIO_DATA_EXCHANGE;
			sess->bytes_done = sess->cache_start_size;
		}

		sess->window_start = sess->start_time = gf_sys_clock();
		sess->bytes_in_wnd = 0;


		//we may have existing data in this buffer ...
		if (!e && (BodyStart < (u32) bytesRead)) {
			gf_dm_data_recieved(sess, sHTTP + BodyStart, bytesRead - BodyStart);
			/*store data if no callbacks or cache*/
			if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) {
				if (sess->init_data) free(sess->init_data);
				sess->init_data_size = bytesRead - BodyStart;
				sess->init_data = (char *) malloc(sizeof(char) * sess->init_data_size);
				memcpy(sess->init_data, sHTTP+BodyStart, sess->init_data_size);
			}
		}
exit:
		if (e) {
			gf_dm_disconnect(sess);
			sess->status = GF_NETIO_STATE_ERROR;
			sess->last_error = e;
			gf_dm_sess_notify_state(sess, sess->status, e);
		}
		return;
	}
	/*fetch data*/
	while (1) {
		u32 size;
#if 1
		if (sess->limit_data_rate && sess->bytes_per_sec) {
			if (sess->bytes_per_sec>sess->limit_data_rate) {
				/*update state*/
				u32 runtime = gf_sys_clock() - sess->window_start;
				sess->bytes_per_sec = (1000 * (sess->bytes_in_wnd)) / runtime;
				if (sess->bytes_per_sec > sess->limit_data_rate) return;
			}
		}
#endif
		e = gf_dm_read_data(sess, sHTTP, GF_DOWNLOAD_BUFFER_SIZE, &size);
		if (!size || e == GF_IP_NETWORK_EMPTY) {
		
			if (!sess->total_size && (gf_sys_clock() - sess->window_start > 1000)) {
				sess->total_size = sess->bytes_done;
				gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
			}
			return;
		}

		if (e) {
			gf_dm_disconnect(sess);
			sess->last_error = e;
			gf_dm_sess_notify_state(sess, sess->status, e);
			return;
		}
		gf_dm_data_recieved(sess, sHTTP, size);
		/*socket empty*/
		if (size < GF_DOWNLOAD_BUFFER_SIZE) return;
	}
}