OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved. | 2 * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved. |
3 * Copyright (C) 2010 Apple Inc. All rights reserved. | 3 * Copyright (C) 2010 Apple Inc. All rights reserved. |
4 * Copyright (C) 2011, Code Aurora Forum. All rights reserved. | 4 * Copyright (C) 2011, Code Aurora Forum. All rights reserved. |
5 * | 5 * |
6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
8 * are met: | 8 * are met: |
9 * | 9 * |
10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
(...skipping 26 matching lines...) Expand all Loading... | |
37 #include "bindings/core/v8/SerializedScriptValue.h" | 37 #include "bindings/core/v8/SerializedScriptValue.h" |
38 #include "bindings/core/v8/SerializedScriptValueFactory.h" | 38 #include "bindings/core/v8/SerializedScriptValueFactory.h" |
39 #include "core/dom/Document.h" | 39 #include "core/dom/Document.h" |
40 #include "core/dom/ExceptionCode.h" | 40 #include "core/dom/ExceptionCode.h" |
41 #include "core/dom/ExecutionContext.h" | 41 #include "core/dom/ExecutionContext.h" |
42 #include "core/events/Event.h" | 42 #include "core/events/Event.h" |
43 #include "core/events/MessageEvent.h" | 43 #include "core/events/MessageEvent.h" |
44 #include "core/frame/LocalDOMWindow.h" | 44 #include "core/frame/LocalDOMWindow.h" |
45 #include "core/frame/LocalFrame.h" | 45 #include "core/frame/LocalFrame.h" |
46 #include "core/frame/csp/ContentSecurityPolicy.h" | 46 #include "core/frame/csp/ContentSecurityPolicy.h" |
47 #include "core/html/parser/TextResourceDecoder.h" | |
48 #include "core/inspector/ConsoleMessage.h" | 47 #include "core/inspector/ConsoleMessage.h" |
49 #include "core/inspector/InspectorInstrumentation.h" | 48 #include "core/inspector/InspectorInstrumentation.h" |
50 #include "core/loader/ThreadableLoader.h" | 49 #include "core/loader/ThreadableLoader.h" |
51 #include "core/page/EventSourceInit.h" | 50 #include "core/page/EventSourceInit.h" |
52 #include "platform/HTTPNames.h" | 51 #include "platform/HTTPNames.h" |
53 #include "platform/network/ResourceError.h" | 52 #include "platform/network/ResourceError.h" |
54 #include "platform/network/ResourceRequest.h" | 53 #include "platform/network/ResourceRequest.h" |
55 #include "platform/network/ResourceResponse.h" | 54 #include "platform/network/ResourceResponse.h" |
56 #include "platform/weborigin/SecurityOrigin.h" | 55 #include "platform/weborigin/SecurityOrigin.h" |
57 #include "public/platform/WebURLRequest.h" | 56 #include "public/platform/WebURLRequest.h" |
58 #include "wtf/ASCIICType.h" | |
59 #include "wtf/text/StringBuilder.h" | 57 #include "wtf/text/StringBuilder.h" |
60 | 58 |
61 namespace blink { | 59 namespace blink { |
62 | 60 |
63 const unsigned long long EventSource::defaultReconnectDelay = 3000; | 61 const unsigned long long EventSource::defaultReconnectDelay = 3000; |
64 | 62 |
65 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons t EventSourceInit& eventSourceInit) | 63 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons t EventSourceInit& eventSourceInit) |
66 : ActiveDOMObject(context) | 64 : ActiveDOMObject(context) |
67 , m_url(url) | 65 , m_url(url) |
68 , m_withCredentials(eventSourceInit.withCredentials()) | 66 , m_withCredentials(eventSourceInit.withCredentials()) |
69 , m_state(CONNECTING) | 67 , m_state(CONNECTING) |
70 , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8")) | |
71 , m_connectTimer(this, &EventSource::connectTimerFired) | 68 , m_connectTimer(this, &EventSource::connectTimerFired) |
72 , m_discardTrailingNewline(false) | |
73 , m_requestInFlight(false) | 69 , m_requestInFlight(false) |
74 , m_reconnectDelay(defaultReconnectDelay) | 70 , m_reconnectDelay(defaultReconnectDelay) |
75 { | 71 { |
76 } | 72 } |
77 | 73 |
78 EventSource* EventSource::create(ExecutionContext* context, const String& url, c onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) | 74 EventSource* EventSource::create(ExecutionContext* context, const String& url, c onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) |
79 { | 75 { |
80 if (url.isEmpty()) { | 76 if (url.isEmpty()) { |
81 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc e to an empty URL."); | 77 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc e to an empty URL."); |
82 return nullptr; | 78 return nullptr; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
121 ASSERT(m_state == CONNECTING); | 117 ASSERT(m_state == CONNECTING); |
122 ASSERT(!m_requestInFlight); | 118 ASSERT(!m_requestInFlight); |
123 ASSERT(executionContext()); | 119 ASSERT(executionContext()); |
124 | 120 |
125 ExecutionContext& executionContext = *this->executionContext(); | 121 ExecutionContext& executionContext = *this->executionContext(); |
126 ResourceRequest request(m_url); | 122 ResourceRequest request(m_url); |
127 request.setHTTPMethod(HTTPNames::GET); | 123 request.setHTTPMethod(HTTPNames::GET); |
128 request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream"); | 124 request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream"); |
129 request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); | 125 request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); |
130 request.setRequestContext(WebURLRequest::RequestContextEventSource); | 126 request.setRequestContext(WebURLRequest::RequestContextEventSource); |
131 if (!m_lastEventId.isEmpty()) { | 127 if (m_parser && !m_parser->lastEventId().isEmpty()) { |
132 // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header i s encoded as UTF-8. | 128 // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header i s encoded as UTF-8. |
133 // TODO(davidben): This should be captured in the type of setHTTPHeaderF ield's arguments. | 129 // TODO(davidben): This should be captured in the type of setHTTPHeaderF ield's arguments. |
134 CString lastEventIdUtf8 = m_lastEventId.utf8(); | 130 CString lastEventIdUtf8 = m_parser->lastEventId().utf8(); |
135 request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinte rpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length())); | 131 request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinte rpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length())); |
Tom Sepez
2016/02/12 17:14:30
So if this is untrusted, do we need to verfiy that
yhirano
2016/02/12 21:33:27
It cannot contain CR or LF, but it can contain oth
| |
136 } | 132 } |
137 | 133 |
138 SecurityOrigin* origin = executionContext.securityOrigin(); | 134 SecurityOrigin* origin = executionContext.securityOrigin(); |
139 | 135 |
140 ThreadableLoaderOptions options; | 136 ThreadableLoaderOptions options; |
141 options.preflightPolicy = PreventPreflight; | 137 options.preflightPolicy = PreventPreflight; |
142 options.crossOriginRequestPolicy = UseAccessControl; | 138 options.crossOriginRequestPolicy = UseAccessControl; |
143 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn ectSrcDirective; | 139 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn ectSrcDirective; |
144 | 140 |
145 ResourceLoaderOptions resourceLoaderOptions; | 141 ResourceLoaderOptions resourceLoaderOptions; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 { | 191 { |
196 return m_state; | 192 return m_state; |
197 } | 193 } |
198 | 194 |
199 void EventSource::close() | 195 void EventSource::close() |
200 { | 196 { |
201 if (m_state == CLOSED) { | 197 if (m_state == CLOSED) { |
202 ASSERT(!m_requestInFlight); | 198 ASSERT(!m_requestInFlight); |
203 return; | 199 return; |
204 } | 200 } |
201 if (m_parser) | |
202 m_parser->stop(); | |
205 | 203 |
206 // Stop trying to reconnect if EventSource was explicitly closed or if Activ eDOMObject::stop() was called. | 204 // Stop trying to reconnect if EventSource was explicitly closed or if Activ eDOMObject::stop() was called. |
207 if (m_connectTimer.isActive()) { | 205 if (m_connectTimer.isActive()) { |
208 m_connectTimer.stop(); | 206 m_connectTimer.stop(); |
209 } | 207 } |
210 | 208 |
211 if (m_requestInFlight) | 209 if (m_requestInFlight) |
212 m_loader->cancel(); | 210 m_loader->cancel(); |
213 | 211 |
214 m_state = CLOSED; | 212 m_state = CLOSED; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
253 message.appendLiteral("EventSource's response has a MIME type (\""); | 251 message.appendLiteral("EventSource's response has a MIME type (\""); |
254 message.append(response.mimeType()); | 252 message.append(response.mimeType()); |
255 message.appendLiteral("\") that is not \"text/event-stream\". Aborti ng the connection."); | 253 message.appendLiteral("\") that is not \"text/event-stream\". Aborti ng the connection."); |
256 // FIXME: We are missing the source line. | 254 // FIXME: We are missing the source line. |
257 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessa geSource, ErrorMessageLevel, message.toString())); | 255 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessa geSource, ErrorMessageLevel, message.toString())); |
258 } | 256 } |
259 } | 257 } |
260 | 258 |
261 if (responseIsValid) { | 259 if (responseIsValid) { |
262 m_state = OPEN; | 260 m_state = OPEN; |
261 AtomicString lastEventId; | |
262 if (m_parser) { | |
263 // The new parser takes over the event ID. | |
264 lastEventId = m_parser->lastEventId(); | |
265 } | |
266 m_parser = new EventSourceParser(lastEventId, this); | |
263 dispatchEvent(Event::create(EventTypeNames::open)); | 267 dispatchEvent(Event::create(EventTypeNames::open)); |
264 } else { | 268 } else { |
265 m_loader->cancel(); | 269 m_loader->cancel(); |
266 dispatchEvent(Event::create(EventTypeNames::error)); | 270 dispatchEvent(Event::create(EventTypeNames::error)); |
267 } | 271 } |
268 } | 272 } |
269 | 273 |
270 void EventSource::didReceiveData(const char* data, unsigned length) | 274 void EventSource::didReceiveData(const char* data, unsigned length) |
271 { | 275 { |
272 ASSERT(m_state == OPEN); | 276 ASSERT(m_state == OPEN); |
273 ASSERT(m_requestInFlight); | 277 ASSERT(m_requestInFlight); |
278 ASSERT(m_parser); | |
274 | 279 |
275 append(m_receiveBuf, m_decoder->decode(data, length)); | 280 m_parser->addBytes(data, length); |
276 parseEventStream(); | |
277 } | 281 } |
278 | 282 |
279 void EventSource::didFinishLoading(unsigned long, double) | 283 void EventSource::didFinishLoading(unsigned long, double) |
280 { | 284 { |
281 ASSERT(m_state == OPEN); | 285 ASSERT(m_state == OPEN); |
282 ASSERT(m_requestInFlight); | 286 ASSERT(m_requestInFlight); |
283 | 287 |
284 if (m_receiveBuf.size() > 0 || m_data.size() > 0) { | |
285 parseEventStream(); | |
286 | |
287 // Discard everything that has not been dispatched by now. | |
288 m_receiveBuf.clear(); | |
289 m_data.clear(); | |
290 m_eventName = emptyAtom; | |
291 m_currentlyParsedEventId = nullAtom; | |
292 } | |
293 networkRequestEnded(); | 288 networkRequestEnded(); |
294 } | 289 } |
295 | 290 |
296 void EventSource::didFail(const ResourceError& error) | 291 void EventSource::didFail(const ResourceError& error) |
297 { | 292 { |
298 ASSERT(m_state != CLOSED); | 293 ASSERT(m_state != CLOSED); |
299 ASSERT(m_requestInFlight); | 294 ASSERT(m_requestInFlight); |
300 | 295 |
301 if (error.isCancellation()) | 296 if (error.isCancellation()) |
302 m_state = CLOSED; | 297 m_state = CLOSED; |
303 networkRequestEnded(); | 298 networkRequestEnded(); |
304 } | 299 } |
305 | 300 |
306 void EventSource::didFailAccessControlCheck(const ResourceError& error) | 301 void EventSource::didFailAccessControlCheck(const ResourceError& error) |
307 { | 302 { |
308 String message = "EventSource cannot load " + error.failingURL() + ". " + er ror.localizedDescription(); | 303 String message = "EventSource cannot load " + error.failingURL() + ". " + er ror.localizedDescription(); |
309 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessageSource , ErrorMessageLevel, message)); | 304 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessageSource , ErrorMessageLevel, message)); |
310 | 305 |
311 abortConnectionAttempt(); | 306 abortConnectionAttempt(); |
312 } | 307 } |
313 | 308 |
314 void EventSource::didFailRedirectCheck() | 309 void EventSource::didFailRedirectCheck() |
315 { | 310 { |
316 abortConnectionAttempt(); | 311 abortConnectionAttempt(); |
317 } | 312 } |
318 | 313 |
314 void EventSource::onMessageEvent(const AtomicString& eventType, const String& da ta, const AtomicString& lastEventId) | |
315 { | |
316 RefPtrWillBeRawPtr<MessageEvent> e = MessageEvent::create(); | |
317 e->initMessageEvent(eventType, false, false, SerializedScriptValueFactory::i nstance().create(data), m_eventStreamOrigin, lastEventId, 0, nullptr); | |
318 | |
319 InspectorInstrumentation::willDispatchEventSourceEvent(executionContext(), t his, eventType, lastEventId, data); | |
320 dispatchEvent(e); | |
321 } | |
322 | |
323 void EventSource::onReconnectionTimeSet(unsigned long long reconnectionTime) | |
324 { | |
325 m_reconnectDelay = reconnectionTime; | |
326 } | |
327 | |
319 void EventSource::abortConnectionAttempt() | 328 void EventSource::abortConnectionAttempt() |
320 { | 329 { |
321 ASSERT(m_state == CONNECTING); | 330 ASSERT(m_state == CONNECTING); |
322 | 331 |
323 m_loader = nullptr; | 332 m_loader = nullptr; |
324 m_state = CLOSED; | 333 m_state = CLOSED; |
325 networkRequestEnded(); | 334 networkRequestEnded(); |
326 | 335 |
327 dispatchEvent(Event::create(EventTypeNames::error)); | 336 dispatchEvent(Event::create(EventTypeNames::error)); |
328 } | 337 } |
329 | 338 |
330 void EventSource::parseEventStream() | |
331 { | |
332 unsigned bufPos = 0; | |
333 unsigned bufSize = m_receiveBuf.size(); | |
334 while (bufPos < bufSize) { | |
335 if (m_discardTrailingNewline) { | |
336 if (m_receiveBuf[bufPos] == '\n') | |
337 bufPos++; | |
338 m_discardTrailingNewline = false; | |
339 } | |
340 | |
341 int lineLength = -1; | |
342 int fieldLength = -1; | |
343 for (unsigned i = bufPos; lineLength < 0 && i < bufSize; i++) { | |
344 switch (m_receiveBuf[i]) { | |
345 case ':': | |
346 if (fieldLength < 0) | |
347 fieldLength = i - bufPos; | |
348 break; | |
349 case '\r': | |
350 m_discardTrailingNewline = true; | |
351 case '\n': | |
352 lineLength = i - bufPos; | |
353 break; | |
354 } | |
355 } | |
356 | |
357 if (lineLength < 0) | |
358 break; | |
359 | |
360 parseEventStreamLine(bufPos, fieldLength, lineLength); | |
361 bufPos += lineLength + 1; | |
362 | |
363 // EventSource.close() might've been called by one of the message event handlers. | |
364 // Per spec, no further messages should be fired after that. | |
365 if (m_state == CLOSED) | |
366 break; | |
367 } | |
368 | |
369 if (bufPos == bufSize) | |
370 m_receiveBuf.clear(); | |
371 else if (bufPos) | |
372 m_receiveBuf.remove(0, bufPos); | |
373 } | |
374 | |
375 void EventSource::parseEventStreamLine(unsigned bufPos, int fieldLength, int lin eLength) | |
376 { | |
377 if (!lineLength) { | |
378 if (!m_data.isEmpty()) { | |
379 m_data.removeLast(); | |
380 if (!m_currentlyParsedEventId.isNull()) { | |
381 m_lastEventId = m_currentlyParsedEventId; | |
382 m_currentlyParsedEventId = nullAtom; | |
383 } | |
384 InspectorInstrumentation::willDispachEventSourceEvent(executionConte xt(), this, m_eventName.isEmpty() ? EventTypeNames::message : m_eventName, m_las tEventId, m_data); | |
385 dispatchEvent(createMessageEvent()); | |
386 } | |
387 if (!m_eventName.isEmpty()) | |
388 m_eventName = emptyAtom; | |
389 } else if (fieldLength) { | |
390 bool noValue = fieldLength < 0; | |
391 | |
392 String field(&m_receiveBuf[bufPos], noValue ? lineLength : fieldLength); | |
393 int step; | |
394 if (noValue) | |
395 step = lineLength; | |
396 else if (m_receiveBuf[bufPos + fieldLength + 1] != ' ') | |
397 step = fieldLength + 1; | |
398 else | |
399 step = fieldLength + 2; | |
400 bufPos += step; | |
401 int valueLength = lineLength - step; | |
402 | |
403 if (field == "data") { | |
404 if (valueLength) | |
405 m_data.append(&m_receiveBuf[bufPos], valueLength); | |
406 m_data.append('\n'); | |
407 } else if (field == "event") { | |
408 m_eventName = valueLength ? AtomicString(&m_receiveBuf[bufPos], valu eLength) : ""; | |
409 } else if (field == "id") { | |
410 m_currentlyParsedEventId = valueLength ? AtomicString(&m_receiveBuf[ bufPos], valueLength) : ""; | |
411 } else if (field == "retry") { | |
412 bool hasOnlyDigits = true; | |
413 for (int i = 0; i < valueLength && hasOnlyDigits; ++i) { | |
414 hasOnlyDigits = isASCIIDigit(m_receiveBuf[bufPos + i]); | |
415 } | |
416 if (!valueLength) { | |
417 m_reconnectDelay = defaultReconnectDelay; | |
418 } else if (hasOnlyDigits) { | |
419 String value(&m_receiveBuf[bufPos], valueLength); | |
420 bool ok; | |
421 unsigned long long retry = value.toUInt64(&ok); | |
422 if (ok) | |
423 m_reconnectDelay = retry; | |
424 } | |
425 } | |
426 } | |
427 } | |
428 | |
429 void EventSource::stop() | 339 void EventSource::stop() |
430 { | 340 { |
431 close(); | 341 close(); |
432 | 342 |
433 // (Non)Oilpan: In order to make Worker shutdowns clean, | 343 // (Non)Oilpan: In order to make Worker shutdowns clean, |
434 // deref the loader. This will in turn deref its | 344 // deref the loader. This will in turn deref its |
435 // RefPtr<WorkerGlobalScope>. | 345 // RefPtr<WorkerGlobalScope>. |
436 // | 346 // |
437 // Worth doing regardless, it is no longer of use. | 347 // Worth doing regardless, it is no longer of use. |
438 m_loader = nullptr; | 348 m_loader = nullptr; |
439 } | 349 } |
440 | 350 |
441 bool EventSource::hasPendingActivity() const | 351 bool EventSource::hasPendingActivity() const |
442 { | 352 { |
443 return m_state != CLOSED; | 353 return m_state != CLOSED; |
444 } | 354 } |
445 | 355 |
446 PassRefPtrWillBeRawPtr<MessageEvent> EventSource::createMessageEvent() | |
447 { | |
448 RefPtrWillBeRawPtr<MessageEvent> event = MessageEvent::create(); | |
449 event->initMessageEvent(m_eventName.isEmpty() ? EventTypeNames::message : m_ eventName, false, false, SerializedScriptValueFactory::instance().create(String( m_data)), m_eventStreamOrigin, m_lastEventId, 0, nullptr); | |
450 m_data.clear(); | |
451 return event.release(); | |
452 } | |
453 | |
454 DEFINE_TRACE(EventSource) | 356 DEFINE_TRACE(EventSource) |
455 { | 357 { |
358 visitor->trace(m_parser); | |
456 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); | 359 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); |
457 ActiveDOMObject::trace(visitor); | 360 ActiveDOMObject::trace(visitor); |
361 EventSourceParser::Client::trace(visitor); | |
458 } | 362 } |
459 | 363 |
460 } // namespace blink | 364 } // namespace blink |
OLD | NEW |